2023-05-05

New DNS and (C)LDAP Clients for Delphi and FPC in mORMot 2

DNS and LDAP are the two protocols on which the Internet and the Intranet are built.
Most of the time, you don't have to care about them. But sometimes, you need to access them directly, especially in a corporate environment.

We just introduced in our Open Source mORMot 2 framework two client units to access DNS and LDAP/CLDAP servers.
You can resolve IP addresses and services using DNS, and ask for information about your IT infrastructure using LDAP.

The DNS Protocol

The Domain Name System (DNS) is a hierarchical and distributed naming system for the Internet.
You can connect to a DNS server, and for instance resolve a computer name (e.g. 'synopse.info') and retrieve its IP (e.g. 62.210.254.173). Or you can ask for some known services, e.g. "which DC server do you know?".

Do implement this, a client should send some message to one server, which works as a cache toward some other reference servers. Those messages are usually transmitted as UDP packets, but could use TCP over TLS or some other protocols.

A lot of information is available https://en.wikipedia.org/wiki/Domain_Name_System.
As you can imagine, the premises are quiet simple, but the actual implementation could be pretty complex, because it is really the corner stone of the whole Internet.

The LDAP / CLDAP Protocols

The Lightweight Directory Access Protocol (LDAP) is a standard application protocol for accessing and maintaining distributed directories.
Each LDAP server maintain a hierarchical database of information about the computers, users, services and protocols available in a given IT infrastructure.

Usually, some binary messages (using ASN.1 encoding) are sent and received over TCP (and TLS), but you may have to use UDP in the discovery phase. You can authenticate using username/password credentials, or Kerberos (i.e. the Windows authentication system). And the objects are stored in a tree of name/value attributes.

https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol gives high level information about the protocol, whereas https://ldap.com offers a detailed and didactic presentation of how it works.

DNS and LDAP Servers

Since DNS is needed at the local level (there is usually a DNS server in most computer systems), there are plenty of DNS servers around.
Some are local DNS servers (you have typically one in your Internet box), other are distributed DNS servers offered as Service (e.g. GoogleDNS, CloudFlare, OpenDNS...).

The most well-known proprietary LDAP server is part of the ActiveDirectory product from Microsoft.
OpenLDAP is the most well known and used Open Source LDAP server around.
But of course, both systems are not fully compatible. For instance, the AD authentication is incompatible with OpenLDAP. Here comes Samba to the rescue.

The Samba project is an amazing Open Source implementation of it. Samba is an important component to seamlessly integrate Linux/Unix Servers and Desktops into Active Directory environments. It can function both as a domain controller (server) or as a regular domain member (client).

Samba is a growing alternative to Microsoft for most companies. Especially for those wanting to keep their IT information local - not everyone wants to share everything with Microsoft, which pushes everyone to migrate to its Azure cloud services. Samba could be an almost turn-key solution to manage in-house your IT infrastructure with a proven and audited OpenSource software. Disclaimer: the company I currently work with offers support for Samba setup or migration, and invests for the Samba development. Perhaps because we are French, we like to be free as a bird and as a glass of wine with saucisson. ;)

DNS and LDAP Clients

A basic DNS client is not so difficult to implement, thanks to its simple but proven protocol, based on an efficient binary encoding.
Usually, you would rely on the Operating System to resolve a host name into an IP address. But if you want more than this basic feature, like locating some services addresses, you would need a proper DNS client.

The LDAP protocol is more complex. It is based on the ASN.1 encoding and some conventions.
Even if it is documented (there are several RFC to consider), the actual implementation by Microsoft does not follow the specs. Apart from the libldap source code, there are very few LDAP client libraries (all languages considered) which properly work today on most Microsoft servers. For Delphi/FPC, you have an old library from Synapse - not Synopse :) - but it does not support Kerberos authentication or LDAP signing sealing which is mandatory on modern AD setups, and is not maintained since years.

mORMot 2 to the Rescue

We introduced the mormot.net.dns.pas unit.
It implements the low-level DNS protocol (over UDP or TCP if the messages are too big), and offers some high-level wrappers, for DNS hostname (reverse) resolution, or service location.

It could also be used as alternate DNS resolver, instead of the Operating System one, for our mormot.net.sock.pas core network unit. You can therefore let your application bypass the Internet or corporate IP resolver, and access other networks without messing with the Operating System or VPN settings.

Something more complex - and harder to stabilize - was the mormot.net.ldap.pas unit.
We needed to implement basic ASN.1 encoding/decoding functions, then all LDAP messages generation and parsing, and finally implement a client able to connect to both Microsoft AD or Samba servers, with the best security possible by default - of course.

The easy and safe solution - used by some tools like LdapAdmin - is to call the Windows API. But we could not, because we wanted our client to be truly cross-platform. So we re-invented the wheel, starting from the Synapse library, reverse engineering both OpenLDAP and actual MS ADs (thank you WireShark!), and wrote and debugged the current code. We will certainly find some nasty limitations in the next months, but at least we have a good code base to work with. The latest addition was to be able to use DNS and CLDAP over UDP to locate the proper LDAP server to use with no pre-configuration.
Now you have in Delphi or FPC a LDAP client able to connect - and even auto-connect with the automated best configuration after service discovery through DNS and CLDAP - to most Microsoft AD or Samba servers. Working and actively tested on both Windows and Linux, over a multitude of actual LDAP servers, thanks to the main product using our framework.
I don't know many libraries featuring CLDAP server discovery, as our unit does. None in the Delphi/FPC world last time I checked. Feel free to point some libraries in other languages properly implementing CLDAP, if you know any.

First Steps

If you are part of a corporate network, the most simple code to use is the following:

  with TLdapClient.Create do
  try
    if BindSaslKerberos then
      writeln(' authenticated via Kerberos');
    Search(WellKnownObjects.Computers, false, '', []);
    writeln(SearchResult.Dump);
  finally
    Free;
  end;

It will locate and connect to the LDAP server optimum for you, then use your current Windows credentials to login, then list all known computers of the network with full information. And if you are on a Linux (or even Mac) part of the domain... the very same code will also work with no further configuration, using GSSAPI instead of the Windows SSPI calls.
This is a clean demonstration of what mORMot DNS and LDAP client classes could do for you.

Real Work

Most of time, you would like to avoid direct LDAP filter searches.
So our TLdapClient class provides some ready-to-use high-level methods, mainly:

  • GetGroups() and GetUsers() to retrieve the list of group or user stored names;
  • GetGroupInfo() and GetUserInfo() to retrieve the main information about a given user or group;
  • GetIsMemberOf() to test membership of a user.

Those methods feature some high level TGroupType and TUserAccountControl enumerates and sets, which allow to easily get the users or groups from their attributes:

  disabled := GetUsers([uacAccountDisable]); // all disabled user names
  mygroups := GetGroups([gtGlobal], 'grp*'); // all global groups which name starts with grp.. characters

To be fair, testing membership of a user over LDAP a very complex task, and GetIsMemberOf() may not cover all cases.

So we even defined an inherited TLdapCheckMember class which can check if a user name is actually a member of one or several groups, maintaining a single connection to the LDAP server for the task, with an internal in-memory cache, featuring all the twisted aspect of user membership (e.g. primary groups). This is the purpose of its TLdapCheckMember.Authorize() method:

  var
    usr: RawUtf8;
    grps: TRawUtf8DynArray;
  ...
  with TLdapCheckMember.Create do
  try
    // no CLDAP discovery, but specify the LDAP address and its Kerberos DN
    Settings.TargetUri := 'ldaps://dc-siteone.ad.corporate.it/ad.corporate.it';
    // we may also set Settings.UserName/Password on a service with no logged user
    if BindSaslKerberos('', @usr) then
      writeln('Authenticated as ', usr, ' via Kerberos to ', Settings.TargetUri);
    // expect the users to be part of any of two groups (specified as sAMAccountName)
    AllowGroupAN('grp_one,grp_two');
    // loop to ask for user sAMAccountName or userPrincipalName
    repeat
      readln(usr);
      if usr = '' then
        break;
      if Authorize(usr, @grps) then
        writeln('Authorized  with groups = ', RawUtf8DynArrayToCsv(grps))
      else
        writeln('Rejected');
    until false;
  finally
    Free;
  end;

This last class is used e.g. by both TBasicAuthServerKerberos and TBasicAuthServerLdap for user group authorization before the actual Kerberos or LDAP password authentication, to implement the "search and bind" authorization/authentication pattern on client and/or server sides, from a computer part of the domain or not.

Better Safe than Sorry

Perhaps you don't need to use DNS or LDAP directly in your projects now.

But it may be good to know you could with mORMot, if you need to. Those protocols are clearly the lingua franca of all modern computer infrastructures.

As usual, feedback is welcome on our forum! :)

2023-04-19

New Command Line Parser in mORMot 2

For most projects, we want to be able to pass some custom values when starting it.
The command line is then used to add this additional information.

We have ParamStr and ParamCount global functions, enough to retrieve the information. You may also use FindCmdLineSwitch for something more easy to work with.
The Lazarus RTL offers some additional methods like hasOption or getOptionValue or checkOptions in its TCustomApplication class. Their are better, but not so easy to use, and not available on Delphi.

We just committed a new command line parser to our Open Source mORMot 2 framework, which works on both Delphi and FPC, follows both Windows and POSIX/Linux conventions, and has much more features (like automated generation of the help message), in an innovative and easy workflow.

Continue reading

2023-01-10

mORMot 2 Release Candidate

The mORMot 2 framework is about to be released as its first 2.0 stable version.

The framework feature set should now be considered as sealed for this release.
There is no issue reported still open at github or in the forum.

Please test it, and give here some feedback to fix any problem before the actual release!
We enter a framework code-freeze phase until then.
:-)

Continue reading

2022-12-28

Efficient Routing for Christmas

This is perhaps the last new feature of mORMot 2 before its first stable release: a very efficient custom URI routing for our HTTP/HTTPS servers.

At ORM and SOA level, there is by-convention routing of the URI, depending on the ORM table, SOA interface and method, and TOrmModel.Root value. Even for our MVC web part, we rely on a /root/ URI prefix, which may not be always needed.
Relying on convention is perfect between mORMot clients and servers, but in some cases, it may be handy to have something smoother, e.g. to publish a truly REST scheme.

We introduced two routing abilities to mORMot 2, with amazing performance (6-12 million parsings per CPU core), via a new THttpServerGeneric.Route property:

  • Internal URI rewrite, to redirect internally from a human/REST-friendly request e.g. to a SOA /root/interface.method layout, or to a MVC web page;
  • Direct callback execution, with optional parameter parsing.

Article edited on 28th December:
Fixed performance numbers (much higher than reported), and introduced latest source changes.

Continue reading

2022-11-26

Modern Pascal is Still in the Race

A recent poll on the Lazarus/FPC forum highlighted a fact: pascal coders are older than most coders. Usually, at our age, we should be managers, not developers. But we like coding in pascal. It is still fun after decades!
But does it mean that you should not use pascal for any new project? Are the language/compilers/libraries outdated?
In the company I currently work for, we have young coders, just out-of-school or still-in-school, which joined the team and write great code!

And a recent thread in this very same forum was about comparing languages to implement a REST server, in C#, Go, Scala, TypeScript, Elixir and Rust.
Several pascal versions are about to be contributed, one in which mORMot shines.

Continue reading

2022-08-12

New Client for MongoDB 5.1/6 Support

Starting with its version 5.1, MongoDB disabled the legacy protocol used for communication since its beginning.
As a consequence, our mORMot client was not able to communicate any more with the latest versions of MongoDB instances.

Last week, we made a deep rewrite of mormot.db.nosql.mongodb.pas, which changed the default protocol to use the new layout on the wire. Now messages use regular MongoDB Database Commands, with automated compression if needed.

No change is needed in your end-user MongoDB or ORM/ODM code. The upgrade is as simple as update your mORMot 2 source, then recompile.

Continue reading

2022-07-09

Native TLS Support for mORMot 2 REST or WebSockets Servers

Since the beginning, we delegated the TLS encryption support to a reverse proxy server, mainly Nginx. Under Windows, you could setup the http.sys HTTPS layer as usual, as a native - even a bit complicated - solution.
Nginx has several advantages, the first being a proven and efficient technology, with plenty of documentation and configuration tips. It interfaces nicely with Let's Encrypt, and is very good for any regular website, using static content and PHP. This very blog and the Synopse web site is hosted via Ngnix on a small Linux server.

But in mORMot 2, we introduced a new set of asynchronous web server classes. So stability and performance are not a problem any more. Some benchmarks even consider this server to be faster than nginx (the stability issue mentioned in this post has been fixed in-between).
We just introduced TLS support of our socket-based servers, both the blocking and asynchronous classes. It would use OpenSSL if available, or the SChannel API layer of Windows. Serving HTTPS or WSS with a self-signed certificate is just a matter of a single parameter now, and performance seems pretty good, especially with OpenSSL.

Continue reading

2022-05-21

New Async HTTP/WebSocket Server on mORMot 2

The HTTP server is one main part of any SOA/REST service, by design.
It is the main entry point of all incoming requests. So it should better be stable and efficient. And should be able to scale in the future, if needed.

There have always been several HTTP servers in mORMot. You can use the HTTP server class you need.
In mORMot 2, we added two new server classes, one for publishing over HTTP, another able to upgrade to WebSockets. The main difference is that they are fully event-driven, so their thread pool is able to scale with thousands of concurrent connections, with a fixed number of threads. They are a response to the limitations of our previous socket server.

Continue reading

- page 1 of 49