Alexander Gromnitsky's Blog

Cygwin, exim & a pop3 server

Latest update:

What I wanted is local mail on a lan. To make this interesting I also wanted to do it under Windows, but w/o using any Windows-specific mail software. Why?

A man can't just sit around.
-- Larry Walters

Turns out, you can't even compile cyrus-imap, courier imap or dovecot under Windows. This leaves us w/ an unmaintainable port of uw-imap & a curious package called mailutils.

The latter is so bizarre that I almost gave up on trying to setup its pop3d server. Considering the fact that my debug skills under Windows are wanting, I compiled it under Fedora & wasted a couple hours running it under strace(1) & reading its source code. (I think it would have been more interesting just to write a fresh pop3 server instead.)

The final setup includes a single Windows box with:

  • a dns server: unbound, that has a native Windows binary;
  • an smtp server: exim, that routes mail inside lan, but employs a smart host for non-lan domains; most mail accounts are virtual; the storage format is mbox, for it's simple & 'performance' quirks are tangential for a small network;
  • a pop3 server: mailutils' pop3d that uses 'virtual domains' feature for auth.

DNS

I had 0 problems w/ unbound, hence I won't talk about it. Just run unbound_setup_x.y.z.exe, then edit its config. E.g.:

$ cat /cygdrive/c/Program\ Files/Unbound/service.conf
server:
        verbosity: 0
        use-syslog: yes

        # we need this, for by default it listens/grants_access on
        # localhost only
        access-control: 192.168.0.0/16 allow
        interface: 0.0.0.0

        # this should be the default, but it's not
        private-address: 192.168.0.0/16
        private-address: 172.16.0.0/12
        private-address: 10.0.0.0/8
        private-address: 169.254.0.0/16
        private-address: fd00::/8
        private-address: fe80::/10

        private-domain: "lan"
        local-zone: "lan." static

        local-data:     "castle.lan.         IN A 192.168.111.1"
        local-data-ptr: "192.168.111.1       castle.lan"
        # ...

forward-zone:
        name: "."
        forward-addr: 1.1.1.1
        forward-addr: 1.0.0.1

remote-control:
        # If you want to use unbound-control.exe from the command line, use
        control-enable: yes
        control-interface: 127.0.0.1
        control-use-cert: no

The installation should have created a Windows service.

You can't setup mail servers w/o working dns. Don't proceed further, unless you can resolve my-server.lan from client machines.

Exim

# exim-config

When asked 'Enter the value of CYGWIN for the daemon', I typed ntsec.

If you're like me, who have never configured Exim before, read at least first 4 chapters of an excellent The Exim SMTP mail server book.

Because some hosts in the lan have its own smtp servers running, this route

# a perfectly conventional DNS routing operation,
# but only for the domains that match *.lan
my_local_network:
  driver = dnslookup
  transport = remote_smtp
  domains = ! +local_domains : *.lan
  no_more

sends emails straight to them, when a email have an address of bob@castle.lan form.

I won't paste here anything about a smart host--the default config covers that already. What it lacks is support for virtual users. The idea is: we search for a virtual user name in /etc/exim.users.$domain text file. The route that does this must come before any other route that has check_local_user option:

my_virtual_local:
  driver = accept
  domains = +local_domains
  local_parts = lsearch;/etc/exim.users.$domain
  transport = my_virtual_mbox

The corresponding transport is almost a copy of local_delivery:

my_virtual_mbox:
  driver = appendfile
  file = /var/spool/mail-virtual/$local_part/INBOX
  delivery_date_add
  envelope_to_add
  return_path_add
# group = mail
  initgroups = no
# modes are wide open to allow unprivileged direct delivery
  directory_mode = 01777
  lockfile_mode = 0666
  mode = 0666
  mode_fail_narrower = no
  check_group = no
  check_owner = no

You don't need to create any directories yourself--Exim does it on the fly.

Mailutils

This mysterious package has 1 of the most grotesque documentation I've ever encountered. It contains thorough, elaborate descriptions of its configurable parameters, multiple little, tiny configuration chunks, scattered through the manual, but not a single, complete example of a working config file. The only example I managed to locate was a Tcl script (in the repo) that generated a partial config for a test suite. (It was very helpful.)

I failed to find a single tutorial/article/comment that describes how to setup pop3d/imap4d daemons--there is absolutely nothing, as if 'a swiss army knife of electronic mail handling' was announced a mere week ago.

Mailutils is > 20 years old. It had multiple maintainers through its life, it has a GNU-branded website, an active maintainer, several of established disros (Debian, Arch) have it in their repos (IBM Fedora doesn't). It even had 7 CVEs in the distant past. Why there is no 'community' of any kind, except for bug-mailutils mailing list? What went wrong?

Anyway, by default pop3d produces very scarce error output & there is no 'be verbose' option, hence, before doing anything, create /etc/mailutils.conf file w/ 1 line:

include /etc/mailutils/debug.conf;

that included file doesn't exist obviously--you need to populate it with the following pretty nonsense:

debug {
  level auth.trace9;
  level address.trace9;
  level attribute.trace9;
  level auth.trace9;
  level body.trace9;
  level config.trace9;
  level envelope.trace9;
  level filter.trace9;
  level folder.trace9;
  level header.trace9;
  level iterator.trace9;
  level list.trace9;
  level locker.trace9;
  level mailbox.trace9;
  level mailer.trace9;
  level message.trace9;
  level mime.trace9;
  level mailcap.trace9;
  level property.trace9;
  level remote.trace9;
  level stream.trace9;
  level ticket.trace9;
  level url.trace9;
  level wicket.trace9;
  level assoc.trace9;
  level acl.trace9;
  level server.trace9;
  level tls.trace9;
  level app.trace9;
}

Next, create mail group in Windows. The name is hardcoded in pop3d sources, & it won't start w/o it in the daemon mode.

Then you need to decide how pop3d will authenticate+authorize users. Under Linux you may do nothing: if all pop3 users have real accounts on a machine, then pop3d should auth successfully. Under Cygwin this assumption fails, for modern Cygwin doesn't have /etc/passwd, if you create /etc/passwd it won't contain hashed passwords, & if you manually make a proper /etc/shadow, this won't work either, for mailutils has bugs.

pop3d has a notion of 'virtual mail domains': it can read a passwd-like file, where a home directory field is not a home directory, but a directory with (again) a hardcoded INBOX file. This is the file that our my_virtual_mbox transport in Exim appends to.

To make the passwd-like file, run:

# cd /etc/mailutils
# mkpasswd -bl > castle.lan.template

edit out irrelevant accounts, add any number of virtual users, replace each 2nd field with a real password, then run:

$ cat castle.lan.template | awk 'BEGIN { FS=OFS=":" } { "openssl passwd -6 " $2 | getline $2; print}' > castle.lan

It produces something like:

# cat /etc/mailutils/castle.lan
alex:$6$0bATz46nrnNO/O1x$CHU...:197610:197121::/home/alex:
luddite:$6$KVmsoeOgRnTIjXZx$wlz...:999:999::/var/spool/mail-virtual/luddite:

Here, alex is a real Windows account, luddite is not (its uid/gid are fake, which doesn't matter). alex account also has a real home directory under Cygwin, thus to satisfy mailutils, it's necessary to do ln -s /var/spool/mail/alex /home/alex/INBOX.

Add to /etc/mailutils.conf:

virtdomain {
  passwd-dir /etc/mailutils;
}

program pop3d {
  mode daemon;
  foreground true;
}

server 0.0.0.0 {
  acl {
    allow 127.0.0.1;
    allow 192.168.0.0/16;
    deny any;
  }
}

logging {
  syslog no;
  session-id yes;
}

# mbox
mandatory-locking {
  lock-directory /tmp;
}

The mandatory-locking 'statement' is undocumented (of course!). It hints pop3d where to create lock files for mboxes. By default it tries somewhere in /var/spool, miscarries miserably & collapses.

Now, under an administrator account, type:

# /usr/sbin/pop3d -d1 --foreground

then, under a usual user account in another terminal:

$ nc 127.0.0.1 110
+OK POP3 Ready <50944.1584657081@castle.lan>
user luddite@castle.lan
+OK
pass 12345
+OK opened mailbox for luddite
^D

Pop! goes the weasel. (sorry)

Pay attention to an argument of the user command. It includes a domain name that is equal to the file name in /etc/mailutils dir. When you omit the domain portion, pop3d tries to parse /etc/passwd & /etc/shadow files, connect to radius, ldap, whatnot enterprise solution. The process is configurable through auth statement that I have no desire to discuss today.

Optionally, if you want support for a so called apop command in pop3 sessions (I didn't bother), there's popauth util. It makes a dbm file (the path /etc/apop.db is hardcoded in pop3d/pop3d.h, imagine that) with name/password pairs:

# printf "bob@castle.lan 12345\nalice@castle.lan 67890\n" | popauth -c
# popauth -l
alice@castle.lan 67890
bob@castle.lan 12345

The last thing left is to make a windows service:

# cygrunsrv -I pop3d -p /usr/sbin/pop3d -y tcpip

and start it:

# net start pop3d


Tags: ойті
Authors: ag