3. MDA modules

Well, MDA modules are actually those modules able to write emails. It is the MDA module that actually decides if to write the mail on the file system, where on the file system, to write the mail on a database, to send it over to another host using some other protocol or so on...

There are actually two kind of modules: local filesystem based and non local or non filesystem based. There is no real difference between them and there is no real way to distinguish the two kind of modules. However, while non local or non file system based modules are all very different from each other, most of local filesystem based modules look alike. Internally, they all:

Even database based local filesystem modules need a directory somewhere to be used, and to write emails in some files that may be local to the user or organized in a directory hierarchy.

Many MDA modules are thus actually split-up into two smaller components:

Examples of SDA may be the "maildir" module or the "mailbox" module, the first one able to create a "maildir" in the directory specified by the MDA, the second able to write a plain mailbox file in that same directory. Examples of MDA can instead be the "dscmtree" module, which organizes users in order to have each one its own directory named after his own uid or gid, or the "systemtree", which organizes folders based on their own home directory in the passwd file.

Note also that while most MDA use one same UID/GID to deliver emails to users, the general PigeonDeliver approach is to have each user with its own uid/gid. No, this doesn't mean you have to add all of your users in the passwd file. The passwd file is just used by some tools to map uid/gid numbers (stored in the filesystem, used by processes) to user names. You can use an uid/gid even if the name of the user is not in the passwd file. If you don't like this, you can always use nsswitch.conf and read uid/username mappings from the database of your choice.

If you don't like having each user with its own uid/gid, or your system still has only 16 bits for uids, limiting the system to at most 64K users, you can always configure the various modules to use one single uid/gid to handle permissions.

In the next few sections, we will talk about some of the main MDA modules available for pigeondeliver.

3.1. dscmtree

dscmtree is the default PigeonDeliver MDA agent. It stores mails on the local file system. If the default is not changed from the configuration file, in order to use this module you will need to create a directory named /dscm/data, where all of your users will be put. To change the name of this directory, you can use the "mailStore_base" configuration parameter.

In this directory, an hierarchy based on server name and user id is created. The default server name is "local". To set a server name, use the parameter "mailStore_server". For example, if you are using the "dscmtree" with the default parameters and you correctly created the /dscm/data directory, after the first mail is received, you will see something like:


$ cd /dscm/data
$ ls
local
$ cd local
$ ls
00  07  0E  15  1C  23  2A  31  38 ..
01  08  0F  16  1D  24  2B  32  39 ..
02  09  10  17  1E  25  2C  33  3A ..
03  0A  11  18  1F  26  2D  34  3B ..
04  0B  12  19  20  27  2E  35  3C ..
05  0C  13  1A  21  28  2F  36  3D ..
06  0D  14  1B  22  29  30  37  3E ..

don't panic! It's all right... Every time a mail is received, the "dscmtree" MDA module is called to deliver the mail. The dscmtree module checks the user configuration. If the user has a mailuid assigned, and if his home directory (read below!) seems to exist, the user is assumed to have already been created.

In any other case, the "creator" module will call the MDA asking for it to "allocate" the user. The "dscmtree" module simply assignes a "mailuid" to every mail user of the system. This mailuid is used as the name of the home directory of the user himself, and, if privilege handling is not disabled, as the uid to be used to perform the delivery. mailuids are always assigned from an administrator specified range, which defaults to 1000 - 65531 on systems with 16bit uids, while to 1000 - 500000 on systems with 32bits uids.

So, when a mail for ccontavalli@masobit.net is first received and needs to be stored on the server, a dscmtree with the default configuration will first assign a mailuid to the user, and then create his own home directory. mailuid are always 32 bits long, and the home directory will look something like: /dscm/data/local/05/000B89/, where 000B8905 (least significat two digits first) is just the mailuid of the user, in this case 755973. So, the mailuid 755973 is assigned to the user. A corresponding directory has been created, the delivery process has switched to the privileges of that user and the mail has finally been delivered.

The dscmtree module is very tunable: you can easily specify the range of mailuids to use (minuid, maxuid), you can tell to always perform deliveries with the same uid (defuid), to use group uid or the same gid for everyone (defgid), which root to use (base, /dscm/data), whenever to trust any eventual gid already written in user configurations (strictgid) or uid (strictuid) or if they should be verified against the specified range, if to trust the uid to correspond to an already created directory (looseid set to false) and if the uid specified in the database must be mandatory. Obviously, some uid/gid are never accepted for security reasons.

3.1.1. Razionale

Purpose of the whole mechanism is to:

  • Allow users to be asily renamed without accessing the filesystem or without loosing emails or having to recreate the mailbox. With an uid based mechanim, users can be easily renamed without troubles.

  • Avoid a directory hierarchy based on domains and usernames. We wanted the ability to distribute users among a cluster of machines independently from their own domain, to move the users from one domain to another without having to delete the mailbox and to support single domains with huge number of accounts.

  • be simple, fast and reliable, even on clustered environments. Using the file system as the only source of informations for usable uids means that a consistent view will always be maintained, no matter what happens. No locking, communication between machines or similar mechanisms are needed to keep everything up and running, even when there is a crash on one of the nodes or some data is lost.

  • Use file system and OS facility as much as possible. We didn't like to use a single UID to perform deliveries. We wanted the daemons (IMAP/POP3/Delivery) to have at most the privileges granted to the user they were acting upon, and not more.

    When enabled, using the mailuid as the system UID has at least three great advantages:

    • file system quotas can be used.

    • the daemons run with the privilege of the users.

    • privileges, ACL, and so on are enforced by your OS kernel.

  • Keep the web interfaces, the database and the administrative commands as simple as possible. Most web interfaces out there calculate an uid to be used for the user, write the home directory in the database, and all kind of stuff related to the system and to the kernel.

    Those ideas simply don't fit in a clustered environment: a web interface shouldn't decide the home directory of a user and shouldn't calculate an uid for them, as much as it shouldn't access the file system in any way and shouldn't perform any change outside the database.

    When somebody writes a VB application or a Perl script to handle users, he shouldn't worry about choosing an uid or in which directory to write his own data or how to handle files and directory when the user is renamed or moved to another domain.

The final result is that you can easily switch from one mechanism to another, add nodes to a cluster without troubles and generally be sure that things will keep working without troubles as time goes on.

3.1.2. UID allocation algorithm

In order to calculate an UID to be used for the user, the "dscmtree" module simply gets the name of the user and calculates an hash from that name. The hash is mapped in the range of the allowed uids (minuid and maxuid parameters), and the corresponding directory is created.

If the creation succedes, the UID is valid and used. If it does not, the algoritm uses a quadrating hashing technique to calculate a new hash, and the process starts over.

After quadlimit attempts, the algorithm is switched to a linear search to find the first available uid. If it is found, that uid is used. Otherwise, the "dscmtree" module fails since no more uids are available. In this case, you should increase the range of available uids by tuning the minuid and maxuid parameters.

On average, the algorithm is really fast. It is very reliable, since it will always find an uid to be used as long as there are uids available, and it won't go out of sync with the content of the file system, even in case of disastrous events.

3.1.3. Quirks about using sparse 32bits uids

The "dscmtree" module chooses pseudo random uids to be used in the range specified by the "minuid" and "maxuid" parameters.

Most modern *nix systems support 32 bits uids without any trouble. Just keep in mind:

  • if your system only supports 16 bits uids and you want to have more than 60000 users on your system, you better switch to "single uid" for all users or switch operating system.

  • if you are planning on using file system quota, please verify the on-disk format used by your OS to store quota information on the disk. As an example, old versions of linux (< 2.4, I believe) using quota v1.0 use a plain binary file where the user uid is used as some sort of offset in the quota file. If you specify a range from 1000 to 1*10^9, and the algorithm picks up an uid close to 10^9, your quota file will probably use a couple hundreds gigabytes on your disk (yes, GB!).

    Whatch out that the a format very close to linux v1.0 quota files is being used by other BSDish systems.

    At time of writing, the filesystem with better support for quota we could try is XFS.

  • if you want your system to scale well and smoothly, keep the range of uids usable at least 20-30% bigger compared to the number of users you have. It does not hurt performance in any way to have even a bigger range. So, if you are planning on having at most 200000 users, use a minuid and maxuid that will leave about 250000 uids usable.

3.1.4. Security risks of this mechanism

/dscm and /dscm/data must not be writeable by anyone else beshide the mail system. Never disable the strictuid or strictgid options, even when porting users from an old system.

Those options tell PigeonDeliver to trust the values of the uid written into the database, without performing any check on the range of the uid. Unless you are really sure about what you are doing, do not use them.

3.2. sharedfolder

Shared folder is probably the simplest MDA available: it simply reads a directory name from the configuration file, and stores the emails in that directory (directory). The directory must exist before this module is ever used.