diff -Naurb qmail-ldap-0802-i/Makefile qmail-la-mod/Makefile --- qmail-ldap-0802-i/Makefile Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/Makefile Fri Aug 10 10:57:21 2001 @@ -8,10 +8,20 @@ # -DCLEARTEXTPASSWD to the LDAPFLAGS #LDAPFLAGS=-DQLDAP_CLUSTER +# SMTP Authentication +# Comment out the next three lines if you do NOT want SMTP Authentication +# The following line will restrict relaying (after authorization) to only +# the accounts associated with the authorized uid, and rejects AUTH commands +# until after STARTTLS has been successfuly established. +SMTPAUTH=-DUSE_SMTPAUTH -DUSE_OLD_GREETING -DUSE_NEW_GREETING -DSMTPAUTH_LIMIT_RELAY -DAUTH_REQUIRES_TLS +#SMTPAUTH=-DUSE_SMTPAUTH -DUSE_OLD_GREETING -DUSE_NEW_GREETING +SMTPAUTHOBJS=base64.o +SMTPAUTHINCLUDES=base64.h + # Perhaps you have different ldap libraries, change them here -LDAPLIBS=-L/usr/local/lib -lldap -llber +LDAPLIBS= -lldap -llber # and change the location of the include files here -LDAPINCLUDES=-I/usr/local/include +# LDAPINCLUDES=-I/usr/local/include # on Slowaris you need -lresolv added like this: #LDAPLIBS=-L/opt/OpenLDAP/lib -lldap -llber -lresolv # for example on my Linux box I use: @@ -22,37 +32,39 @@ # TLS (SMTP encryption) in qmail-smtpd and qmail-remote, see TLS.readme # You need OpenSSL for this # TLS enable -#TLSON=-DTLS +TLSON=-DTLS # Path to OpenSSL includes #TLSINCLUDES=-I/usr/local/include # Path to OpenSSL libraries -#TLSLIBS=-L/usr/local/lib -lssl -lcrypto +TLSLIBS=-lssl -lcrypto # Path to OpenSSL binary -#OPENSSLBIN=/usr/local/bin/openssl +OPENSSLBIN=/usr/bin/openssl # to make the Netscape download progress bar work with qmail-pop3d # uncomment the next line (allready done) MNW=-DMAKE_NETSCAPE_WORK # to enable the auto-maildir-make feature uncomment the next line -#MDIRMAKE=-DAUTOMAILDIRMAKE +MDIRMAKE=-DAUTOMAILDIRMAKE # to enable the auto-homedir-make feature uncomment the next line -#HDIRMAKE=-DAUTOHOMEDIRMAKE +HDIRMAKE=-DAUTOHOMEDIRMAKE # on most systems we need this to make checkpassword -SHADOWLIBS=-lcrypt +#SHADOWLIBS=-lcrypt # OpenBSD Systems seems to have no libcrypt, so comment the line out if you # get linking problems # To use shadow passwords under some Linux OS, uncomment the next two lines. -#SHADOWLIBS=-lcrypt -lshadow -#SHADOWOPTS=-DPW_SHADOW +SHADOWLIBS=-lcrypt -lshadow +SHADOWOPTS=-DPW_SHADOW # To use shadow passwords under Solaris, uncomment the SHADOWOPTS line. # to enable the possibility to log and debug imap and pop uncoment the # next line -#DEBUG=-DDEBUG -# WARNING: you need a NONE DEBUG auth_* to run with inetd +DEBUG=-DDEBUG +# WARNING: you need a NONE DEBUG auth_* to run with inetd, so we do them separately +DEBUG_AUTH=-DDEBUG + # for profiling ... #INCTAI=../libtai-0.60 @@ -68,7 +80,7 @@ default: it qldap -qldap: qmail-quotawarn qmail-reply auth_pop auth_imap digest qmail-ldaplookup +qldap: qmail-quotawarn qmail-reply auth_pop auth_imap auth_smtp digest qmail-ldaplookup addresses.0: \ addresses.5 @@ -89,7 +101,7 @@ auth_imap.o: \ compile auth_imap.c error.h qldap-errno.h readwrite.h stralloc.h env.h \ str.h timeoutread.h auth_mod.h qldap-mdm.h exit.h qldap-debug.h - ./compile $(LDAPFLAGS) $(HDIRMAKE) $(DEBUG) auth_imap.c + ./compile $(LDAPFLAGS) $(HDIRMAKE) $(DEBUG_AUTH) auth_imap.c auth_imap: \ load auth_imap.o checkpassword.o check.o control.o getln.a qldap-debug.o \ @@ -108,7 +120,7 @@ compile auth_pop.c error.h qldap-errno.h readwrite.h stralloc.h env.h \ str.h timeoutread.h auth_mod.h qldap-mdm.h exit.h fmt.h sig.h wait.h \ scan.h alloc.h qldap-debug.h - ./compile $(LDAPFLAGS) $(HDIRMAKE) $(DEBUG) auth_pop.c + ./compile $(LDAPFLAGS) $(HDIRMAKE) $(DEBUG_AUTH) auth_pop.c auth_pop: \ load auth_pop.o checkpassword.o check.o control.o getln.a qldap-debug.o \ @@ -121,7 +133,7 @@ ip.o base64.o digest_md4.o digest_md5.o digest_rmd160.o digest_sha1.o \ ipalloc.o getln.a open.a env.a stralloc.a alloc.a substdio.a str.a \ qldap-mdm.o wait.a qldap-errno.o error.a fs.a ndelay.a prot.o \ - $(LDAPLIBS) $(SHADOWLIBS) `cat dns.lib` `cat socket.lib` + $(SHADOWLIBS) `cat dns.lib` `cat socket.lib` $(LDAPLIBS) auto-ccld.sh: \ conf-cc conf-ld warn-auto.sh @@ -399,7 +411,15 @@ qldap-errno.h readwrite.h error.h str.h open.h substdio.h getln.h select.h \ digest_md4.h digest_md5.h digest_rmd160.h digest_sha1.h dns.h \ ipalloc.h timeoutconn.h byte.h scan.h fmt.h alloc.h qldap-debug.h - ./compile $(LDAPFLAGS) $(SHADOWOPTS) $(LDAPINCLUDES) $(DEBUG) checkpassword.c + ./compile $(LDAPFLAGS) $(SHADOWOPTS) $(LDAPINCLUDES) $(DEBUG_AUTH) checkpassword.c + +checkpassword_smtp.o: \ +compile checkpassword_smtp.c qmail-ldap.h stralloc.h auth_mod.h qldap-ldaplib.h \ +qldap-errno.h readwrite.h error.h str.h open.h substdio.h getln.h select.h \ +digest_md4.h digest_md5.h digest_rmd160.h digest_sha1.h dns.h \ +ipalloc.h timeoutconn.h byte.h scan.h fmt.h alloc.h qldap-debug.h + ./compile $(LDAPFLAGS) $(SHADOWOPTS) $(LDAPINCLUDES) $(DEBUG_AUTH) \ + checkpassword_smtp.c chkshsgr: \ load chkshsgr.o @@ -1778,13 +1798,13 @@ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o dns.lib socket.lib +fs.a auto_qmail.o dns.lib socket.lib ${SMTPAUTHOBJS} ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ alloc.a substdio.a error.a fs.a auto_qmail.o dns.o str.a \ - `cat dns.lib` `cat socket.lib` ${TLSLIBS} + ${SMTPAUTHOBJS} `cat dns.lib` `cat socket.lib` ${TLSLIBS} qmail-smtpd.0: \ qmail-smtpd.8 @@ -1795,8 +1815,9 @@ substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ -exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h - ./compile ${TLSON} ${TLSINCLUDES} qmail-smtpd.c +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h \ +${SMTPAUTHINCLUDES} + ./compile ${TLSON} ${TLSINCLUDES} ${SMTPAUTH} qmail-smtpd.c qmail-start: \ load qmail-start.o prot.o fd.a auto_uids.o @@ -2402,3 +2423,25 @@ backup: \ clean tar cf $(BACKUPPATH) . + + + +auth_smtp.o: \ +compile auth_smtp.c error.h qldap-errno.h readwrite.h stralloc.h env.h \ +str.h timeoutread.h auth_mod.h qldap-mdm.h exit.h fmt.h sig.h wait.h \ +scan.h alloc.h + ./compile $(LDAPFLAGS) $(HDIRMAKE) $(DEBUG_AUTH) auth_smtp.c + +auth_smtp: \ +load auth_smtp.o checkpassword_smtp.o check.o control.o getln.a qldap-debug.o \ +fs.a open.a stralloc.a alloc.a substdio.a error.a env.a auto_qmail.o \ +str.a base64.o digest_md4.o digest_md5.o digest_rmd160.o digest_sha1.o \ +dns.o timeoutconn.o ndelay.a ipalloc.o dns.lib socket.lib qldap-ldaplib.o \ +timeoutread.o qldap-mdm.o wait.a prot.o qldap-errno.o str.a + ./load auth_smtp checkpassword_smtp.o check.o control.o qldap-ldaplib.o \ + qldap-debug.o output.o auto_qmail.o dns.o timeoutconn.o timeoutread.o ip.o \ + ipalloc.o getln.a open.a env.a stralloc.a alloc.a substdio.a str.a \ + base64.o digest_md4.o digest_md5.o digest_rmd160.o digest_sha1.o \ + qldap-mdm.o wait.a qldap-errno.o error.a fs.a ndelay.a prot.o \ + $(LDAPLIBS) $(SHADOWLIBS) `cat dns.lib` `cat socket.lib` str.a + diff -Naurb qmail-ldap-0802-i/README.qldap.oddsandends qmail-la-mod/README.qldap.oddsandends --- qmail-ldap-0802-i/README.qldap.oddsandends Wed Dec 31 19:00:00 1969 +++ qmail-la-mod/README.qldap.oddsandends Sun Aug 5 05:53:12 2001 @@ -0,0 +1,117 @@ + + + This patch will probably only work with the OpenLDAP 2.0.11 libraries. +You should peruse the Makefile and qmail-ldap.h (you _have_ to diddle with +qmail-ldap.h at least as specified below). I've left the Makefile to +the defaults I use, so you should also make sure they're appropriate for you. + The diff is with respect to the qmail-ldap 0801 patch and contains the +smtp auth patch (with extensions). + I've only done enough testing to make sure there aren't any really silly +segmentation faults and to make sure it basically works. In particular +I haven't tested all the different options, or what happens if you undefine +some of the required definitions for domain specific dns/domain alias chasing. +Please send me (lynn@freespeech.org/owinebar@free-expression.org) any bugs +you encounter. + +Features added: + +Domain specific base dn's. You _must_ define your own domain/aliased domain +object classes/attributes in a schema somewhere. If you've set +DOMAIN_DEREF_LIMIT to some non-zero number, aliased domains will be chased +to that limit. If you set it to 0, the alias chasing code won't get called. +The domains are assumed to be exactly one level below the base dn you specify +in control/ldapbasedn. Make sure your ldapuser can read the domain attributes +you specify. There is no #ifdef to eliminate this part of the patch. + Under a given domain, account names can omit the domain part. However, +if the a mail(alternateaddress) contains the full user@domain, it is +prefered to just user. This is meant to work when you have aliased domains, +like foobar.(com|net|org), so if you set up the canonical domain with a +mail user luser, it receives mail destined for any of the 3 domains. _But_, +if you create a separate entry with mail=luser@foobar.com, then that mailbox +recieves mail for 'luser@foobar.com', and luser@foobar.(org|net) goes to +the directory for just plain 'luser'. + Note that with this patch mailAlternateAddresses can't be used to hijack +addresses for other domains (that aren't aliased to the one the mail account +is contained in). You may consider this a misfeature, but that's what +forwarding is for. + +LDAP URI's for your hostname: +USE_LDAP_URI makes the code use the undocumented ldap_initialize() function +from OpenLDAP 2.0.11. It's not documented, but it _is_ used by the standard +tools in that package. In this case, set control/ldapserver to an URI, like +ldap(|s|i)://your.ldap.server. Note this works automagically with SSL ldap +uri's (ldaps://) without any STARTTLS in the ldap connection. + +SMTP auth related enhancements: + + checkpassword_smtp.c/auth_smtp.c have been modified to make auth_success +write write all mail and mailalternateaddresses to fd 4 (a pipe to the +qmail-smtpd process that invoked it), separated by nulls. + If you define SMTPAUTH_LIMIT_RELAY, then an authenticated user will only +be able to send mails with an envelope sender as one of the mail addresses +from their entry. Please note that if it's a domain with aliases, the user +can log in as user@. However, all the mail addresses without a +domain part will be considered to be @aliasdomain, so that if user A does an +auth as A@foobar.com, s/he can't send mail as A@foobar.org (unless there's +a mail/alternateaddress A@foobar.org in the entry for some reason). But +if A logs in as A@foobar.org, s/he can send as A@foobar.org (but not as +A@foobar.com). + Without SMTPAUTH_LIMIT_RELAY, any client that does a successful auth can +send from any bogus address they like, even ones you don't host - this is +the default behaviour of the smtp auth patch (!). + If AUTH_REQUIRES_TLS is set, attempts to AUTH are rejected unless STARTTLS +has successfully executed. Please note I have not tested this at all because +'telnet 25' isn't amenable to STARTTLS. But it's just a little simple +logic, so it should work. (famous last words). + +Workaround (?) + If you don't define TLS_REMOTE, qmail-remote.c will be compiled without support +for TLS. There have been reports on the qmail-ldap list of qmail-remote not +working well with TLS compiled in. The problem looks like it happens +when the remote host has STARTTLS, but the tls connection doesn't start +successfully - the TLS patch just bails even if TLS isn't required for that +remote host(!). Why the tls connection doesn't start successfully is a +question I don't have an answer for (but I didn't write the TLS patch or know +the details of the problem). + It should be fine to undef this option for many installations. If you need +it on, you probably know it. + +Things you might consider misfeatures: + +Homedirectory/mailmessagestore is reverted to the old interpretation. That is, +homedirectory is an absolute path, mailmessagestore is some directory of that +path. homeDirectory is switched to by auth_*/qmail-lspawn, mailmessagestore is +switched to by the process invoked by that program (imap/pop/qmail-local). +mailmessagestore corresponds to the 1st argument of qmail-start and imapd (for +courier imap). + Correspondingly, I've added a control file ldapmailroot for an absolute path +containing all mail directories, to which homedirectory is appended (absolute +homedirectories get the leading slash removed before being appended to the mailroot +path). control/ldapmailmessagestore contains the default place to put mail within +the home directory. This is used if the mailmessagestore attribute is not defined +in the user's LDAP entry. If the control file is nonexistent, the compiled in +default DEFAULT_MAILDIR is used. Note: the existence of a trailing slash in the +maildirectory is how qmail-local decides whether to treat the message store as +an mbox file or a maildir directory - _NOT_ by statting the filename and seeing +whether it's a directory or a file. + If this really bothers you, you can always set LDAP_HOMEDIR to mailmessagestore, +and DEFAULT_MAILDIR to "./" + + If IGNORE_ABSOLUTE_MAILDIR is set to a true value, absolute maildir paths have +their leading / stripped off. This is useful because imapd does a chdir() to the +maildir after invocation, so you can prevent ignore attempts by malicious users +to set their mailmessagestore to someone else's maildirectory (assuming you allow +them access to the ldap directory). + +-------------------------- + +This work was sponsored by FreeSpeech Media LLC, http://www.freespeech.org/ +It's released under the GNU GPL. +There is NO WARRANTY. + +Lynn Winebarger +lynn@freespeech.org +owinebar@free-expression.org + + + diff -Naurb qmail-ldap-0802-i/TARGETS qmail-la-mod/TARGETS --- qmail-ldap-0802-i/TARGETS Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/TARGETS Wed Aug 1 17:52:11 2001 @@ -393,6 +393,7 @@ digest_sha1.o checkpassword checkpassword.o +checkpassword_smtp.o digest digest.o endian @@ -404,6 +405,7 @@ maildir++.o auth_imap.o auth_pop.o +auth_smtp.o qldap-debug.o qldap-ldaplib.o qldap-mdm.o diff -Naurb qmail-ldap-0802-i/auth_imap.c qmail-la-mod/auth_imap.c --- qmail-ldap-0802-i/auth_imap.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/auth_imap.c Wed Aug 1 23:24:50 2001 @@ -42,6 +42,9 @@ const char *a=env_get("AUTHENTICATED"); int waitstat; + + log(256,"Starting auth_imap\n"); + if (!argv[1]) { qldap_errno = AUTH_NEEDED; auth_error(); @@ -76,29 +79,35 @@ } close(3); + log(256,"up = %s\n",up); /* get the different fields: serviceAUTHTYPEAUTHDATA */ i = 0; s = up + i; /* service, for us uninteresting, but we could check for imap */ while (up[i] && up[i] != '\n' ) if (++i == uplen) { + log(256,"1\n"); qldap_errno = AUTH_NEEDED; auth_error(); } if (i == uplen) { + log(256,"2\n"); qldap_errno = AUTH_NEEDED; auth_error(); } up[i++] = '\0'; t = up + i; /* type has to be "login" else fail ... */ while (up[i] && up[i] != '\n' ) if (++i == uplen) { + log(256,"3\n"); qldap_errno = AUTH_NEEDED; auth_error(); } if (i == uplen) { + log(256,"4\n"); qldap_errno = AUTH_NEEDED; auth_error(); } up[i++] = '\0'; if ( str_diff("login", t) ) { + log(256,"5\n"); /* this modul supports only "login"-type, fail with AUTH_NOSUCH, so the * next modul is called, perhaps with greater success */ qldap_errno = AUTH_NOSUCH; @@ -106,20 +115,24 @@ } l = up + i; /* next login */ while (up[i] && up[i] != '\n' ) if (++i == uplen) { + log(256,"6\n"); qldap_errno = AUTH_NEEDED; auth_error(); } if (i == uplen) { + log(256,"7\n"); qldap_errno = AUTH_NEEDED; auth_error(); } up[i++] = '\0'; p = up + i; /* and the password */ while (up[i] && up[i] != '\n' ) if (++i == uplen) { + log(256,"8\n"); qldap_errno = AUTH_NEEDED; auth_error(); } if (i == uplen) { + log(256,"9\n"); qldap_errno = AUTH_NEEDED; auth_error(); } diff -Naurb qmail-ldap-0802-i/auth_smtp.c qmail-la-mod/auth_smtp.c --- qmail-ldap-0802-i/auth_smtp.c Wed Dec 31 19:00:00 1969 +++ qmail-la-mod/auth_smtp.c Sat Aug 4 19:46:37 2001 @@ -0,0 +1,175 @@ +/* auth_smtp.c, Henning Brauer */ +#include +#include "error.h" +#include "qldap-errno.h" +#include "readwrite.h" +#include "stralloc.h" +#include "env.h" +#include "str.h" +#include "exit.h" +#include "timeoutread.h" +#include "prot.h" +#include "auth_mod.h" +#include "qmail-ldap.h" +#include "qldap-debug.h" + +unsigned int auth_port; + + +void auth_init(int argc, char **argv, stralloc *login, stralloc *authdata) +/* this function should return the 0-terminated string login and authdata + * argc and argv are the arguments of the next auth_module. */ +{ +#define UP_LEN 513 + char up[UP_LEN]; + char *l; + char *p; + int uplen; + int i; + + + if (!argv[1]) { + qldap_errno = AUTH_NEEDED; + auth_error(); + } + + uplen = 0; + log(256,"Starting auth_init()\n"); + for (;;) + { + do i = read(3,up + uplen,sizeof(up) - uplen); + while ((i == -1) && (errno == EINTR)); + if (i == -1) { + qldap_errno = ERRNO; + auth_error(); + } + if (i == 0) break; + uplen += i; + if (uplen >= sizeof(up)) { + qldap_errno = AUTH_PANIC; + auth_error(); + } + } + close(3); + + log(256,"up = %s\n",up); + + i = 0; + l = up + i; + while (up[i++]) if (i == uplen) { + qldap_errno = AUTH_NEEDED; + auth_error(); + } + p = up + i; + if (i == uplen) { + qldap_errno = AUTH_NEEDED; + auth_error(); + } + while (up[i++]) if (i == uplen) { + qldap_errno = AUTH_NEEDED; + auth_error(); + } + + if (!stralloc_copys(login, l) ) { + qldap_errno = ERRNO; + auth_error(); + } + if (!stralloc_0(login) ) { + qldap_errno = ERRNO; + auth_error(); + } + + if (!stralloc_copys(authdata, p) ) { + qldap_errno = ERRNO; + auth_error(); + } + if (!stralloc_0(authdata) ) { + qldap_errno = ERRNO; + auth_error(); + } + + /* up no longer needed so delete it */ + for ( i=0; is; + int left = addresses->len; + int wrote = 0; + log(16, "auth_success: login=%s, uid=%u, ", login, uid); + log(16, "gid=%u, first valid address=%s, maildir=%s, aliasempty=%s, hdm=%s\n", + gid, addresses->s, md, argv[2], homedirmake ); + /* pipe home to fd 4 */ + while (left > 0) { + switch (wrote = write(4,buf,left)) + { + case -1: + if (errno == error_intr) continue; + goto BAD_EXIT; + break; + case 0: + if (left > 0) goto BAD_EXIT; + break; + default: + left -= wrote; + buf += wrote; + } + } + close(4); + _exit(0); + BAD_EXIT: + close(4); + _exit(1); + /* end */ +} + +void auth_error(void) +/* error handler for this module, does not return */ +{ + /* Error exit codes: + * 1 = error in server configuration + * 2 = unable to contact authorization server + * 25= user record incorrect + * 3 = authorization failed + * 4 = account disabled + * 5 = mailhost is unreachable + * 6 = mailbox is corrupted + * 7 = unable to start subprogram + * 8 = out of memory + */ + log(2, "warning: auth_error: authorization failed (%s)\n", + qldap_err_str(qldap_errno) ); + + if ( qldap_errno == LDAP_INIT ) _exit(1); + if ( qldap_errno == LDAP_BIND ) _exit(2); + if ( qldap_errno == AUTH_FAILED || qldap_errno == LDAP_REBIND || + qldap_errno == AUTH_NOSUCH ) _exit(3); + if ( qldap_errno == LDAP_SEARCH || qldap_errno == LDAP_NEEDED || + qldap_errno == ILL_AUTH || qldap_errno == ILL_PATH ) _exit(25); + if ( qldap_errno == ACC_DISABLED ) _exit(4); + if ( qldap_errno == BADCLUSTER ) _exit(5); + if ( qldap_errno == MAILDIR_CORRUPT ) _exit(6); + if ( qldap_errno == AUTH_EXEC ) _exit(7); + if ( qldap_errno == ERRNO && errno == error_nomem ) _exit(8); + _exit(111); +} + +void auth_forward(int fd, char *login, char *passwd) +{ +} diff -Naurb qmail-ldap-0802-i/checkpassword.c qmail-la-mod/checkpassword.c --- qmail-ldap-0802-i/checkpassword.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/checkpassword.c Sun Aug 5 02:00:30 2001 @@ -27,6 +27,7 @@ #include "check.h" #include "qldap-debug.h" #include "output.h" +#include "qlx.h" /* Edit the first lines in the Makefile to enable local passwd lookups * and debug options. @@ -45,7 +46,6 @@ extern stralloc qldap_me; extern stralloc qldap_objectclass; - int rebind; int cluster; @@ -75,7 +75,7 @@ static int make_filter(stralloc *value, stralloc *filter); static void free_stralloc(stralloc *sa); -void main(int argc, char **argv) +int main(int argc, char **argv) { int locald; stralloc login = {0}; @@ -86,8 +86,9 @@ unsigned long uid; unsigned long gid; - log_init(STDERR, 255, 0); /* XXX limited to 64 so it is not possible to get + /* XXX limited to 255 so it is not possible to get * XXX passwords via debug on normal systems */ + log_init(STDERR, AUTH_LOG_LIMIT, 0); auth_init(argc, argv, &login, &authdata); log(256, "auth_init: login=%s, authdata=%s\n", login.s, authdata.s); @@ -159,7 +160,7 @@ } search.filter = filter.s; - ret = ldap_lookup(&search, attrs, &info, extra); + ret = ldap_lookup(&search, attrs, &info, extra,login,1); free_stralloc(&filter); /* free the old filter */ if ( ret != 0 ) { log(4, "warning: check_ldap: ldap_lookup not successful!\n"); @@ -362,10 +363,14 @@ return -1; } byte_copy(&hashed[32], 33, salt); - } else if (!str_diffn("{SHA}", encrypted, 5) ) { + } else if (!str_diffn("{SHA", encrypted, 5) ) { /* SHA */ shift = 5; SHA1DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{SHA1}", encrypted, 6) ) { + /* another name for SHA */ + shift = 6; + SHA1DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); } else if (!str_diffn("{RMD160}", encrypted, 8) ) { /* RMD160 */ shift = 8; @@ -580,6 +585,10 @@ /* create a searchfilter, "(uid=VALUE)" */ { stralloc tmp = {0}; + stralloc local = {0}; + int n; + char sep = ID_SEP; + int len = value->len; if ( !stralloc_copy(&tmp, value) ) { @@ -590,43 +599,54 @@ qldap_errno = ERRNO; return 0; } - if ( !stralloc_copys(filter,"(" ) ) { - qldap_errno = ERRNO; - return 0; + if ((n = byte_chr(tmp.s, len, sep)) == len) { + if (!stralloc_copy(&local,&tmp)) goto BAD_STRALLOC; + } else { + if (!stralloc_copyb(&local,tmp.s,n)) goto BAD_STRALLOC; } + if ( !stralloc_copys(filter,"(" ) ) goto BAD_STRALLOC; + if ( qldap_objectclass.len && ( !stralloc_cats(filter,"&(" ) || !stralloc_cats(filter,LDAP_OBJECTCLASS) || !stralloc_cats(filter,"=") || !stralloc_cat(filter,&qldap_objectclass) || - !stralloc_cats(filter,")(") ) ) { - qldap_errno = ERRNO; - return 0; - } - if ( !stralloc_cats(filter, LDAP_UID) || + !stralloc_cats(filter,")(") ) ) + goto BAD_STRALLOC; + + if ( !stralloc_cats(filter, "|(") || + !stralloc_cats(filter, LDAP_UID) || !stralloc_cats(filter, "=") || !stralloc_cat(filter, &tmp) || - !stralloc_cats(filter, ")") ) { - qldap_errno = ERRNO; - return 0; - } + !stralloc_cats(filter, ")(") || + !stralloc_cats(filter, LDAP_UID) || + !stralloc_cats(filter, "=") || + !stralloc_cat(filter, &local) || + !stralloc_cats(filter, "))") ) + goto BAD_STRALLOC; + if ( qldap_objectclass.len && - !stralloc_cats(filter,")") ) { - qldap_errno = ERRNO; - return 0; - } - if ( !stralloc_0(filter) ) { - qldap_errno = ERRNO; - return 0; - } + !stralloc_cats(filter,")") ) + goto BAD_STRALLOC; + + if ( !stralloc_0(filter) ) + goto BAD_STRALLOC; + free_stralloc(&tmp); + free_stralloc(&local); return 1; + + BAD_STRALLOC: + qldap_errno = ERRNO; + return 0; } static void free_stralloc(stralloc* sa) { alloc_free(sa->s); sa->s = 0; + sa->len = 0; + sa->a = 0; return; } diff -Naurb qmail-ldap-0802-i/checkpassword_smtp.c qmail-la-mod/checkpassword_smtp.c --- qmail-ldap-0802-i/checkpassword_smtp.c Wed Dec 31 19:00:00 1969 +++ qmail-la-mod/checkpassword_smtp.c Sun Aug 5 02:00:52 2001 @@ -0,0 +1,650 @@ +/* checkpasswd_smtp.c, Henning Brauer */ +#include "qmail-ldap.h" +#include "stralloc.h" +#include "auth_mod.h" +#include "qldap-ldaplib.h" +#include "qldap-errno.h" +#include "readwrite.h" +#include "error.h" +#include "str.h" +#include "open.h" +#include "substdio.h" +#include "getln.h" +#include +#include +#include "digest_md4.h" +#include "digest_md5.h" +#include "digest_rmd160.h" +#include "digest_sha1.h" +#include "select.h" +#include "ipalloc.h" +#include "dns.h" +#include "timeoutconn.h" +#include "byte.h" +#include "scan.h" +#include "fmt.h" +#include "alloc.h" +#include "check.h" +#include "qldap-debug.h" +#include "output.h" +#include "qlx.h" + +/* Edit the first lines in the Makefile to enable local passwd lookups + * and debug options. + * To use shadow passwords under Solaris, uncomment the 'SHADOWOPTS' line + * in the Makefile. + * To use shadow passwords under Linux, uncomment the 'SHADOWOPTS' line and + * the 'SHADOWLIBS=-lshadow' line in the Makefile. + */ +#include +#ifdef PW_SHADOW +#include +#endif +#ifdef AIX +#include +#endif + +extern stralloc qldap_me; +extern stralloc qldap_objectclass; + +int rebind; +int cluster; + +static int check_ldap(stralloc *login, + stralloc *authdata, + unsigned long *uid, + unsigned long *gid, + stralloc *home, + stralloc *maildir); + +static int check_passwd(stralloc *login, + stralloc *authdata, + unsigned long *uid, + unsigned long *gid, + stralloc *home, + stralloc *md); + +static int cmp_passwd(unsigned char *clear, char *encrypted); + +static int get_local_maildir(stralloc *home, stralloc *maildir); + +#ifdef QLDAP_CLUSTER +static void copyloop(int infd, int outfd, int timeout); +static void forward_session(char *host, char *name, char *passwd); +#endif + +static int make_filter(stralloc *value, stralloc *filter); +static void free_stralloc(stralloc *sa); +/* this is horrible - how many times will I repeat the same data */ + +static int is_local(char *mail) +{ + int r = str_rchr(mail,'@'); + return (mail[r] != '@') ? 1 : 0; +} + +int main(int argc, char **argv) +{ + int locald; + stralloc login = {0}; + stralloc authdata = {0}; + stralloc home = {0}; + stralloc homemaker = {0}; + stralloc maildir = {0}; + unsigned long uid; + unsigned long gid; + + /* XXX limited to 255 so it is not possible to get + * XXX passwords via debug on normal systems */ + log_init(STDERR, AUTH_LOG_LIMIT,0); + + auth_init(argc, argv, &login, &authdata); + log(256, "auth_init: login=%s, authdata=%s\n", login.s, authdata.s); + + if ( authdata.len <= 1 ) { + log(1, "alert: null password.\n"); + qldap_errno = AUTH_NEEDED; + auth_fail(argc, argv, login.s); + } + + if ( init_ldap(&locald, &cluster, &rebind, &homemaker, 0, 0, 0) == -1 ) { + log(1, "alert: init_ldap failed.\n"); + _exit(1); + } + log(64, "init_ldap: ld=%i, cluster=%i, rebind=%i, hdm=%s\n", + locald, cluster, rebind, homemaker.s); + + if ( check_ldap(&login, &authdata, &uid, &gid, &home, &maildir) ) { + log(16, "authentication with ldap was not successful\n"); + if ( locald == 1 && + (qldap_errno == LDAP_NOSUCH || qldap_errno == LDAP_SEARCH) ) { + log(16, "trying to authenticate with the local passwd db\n"); + if ( check_passwd(&login, &authdata, &uid, &gid, &home, &maildir) ) { + auth_fail(argc, argv, login.s); + } + } else { + auth_fail(argc, argv, login.s); + } + } + + auth_success(argc, argv, login.s, uid, gid, (char *) &home, homemaker.s, maildir.s); + _exit(1); /* should never get here */ +} + +/* since we're not using the 2 directory fields to actually hold directories, + we'll repurpose them to hold valid email addresses for the authorization uid */ + +int check_ldap(stralloc *login, stralloc *authdata, unsigned long *uid, + unsigned long *gid, stralloc *home, stralloc *maildir) +{ + userinfo info; + extrainfo extra[4] = { { LDAP_MAIL, 0 }, + { LDAP_MAILALTERNATE, 0 }, + { LDAP_PASSWD, 0 }, + { 0 , 0 } }; + searchinfo search; + stralloc filter = {0}; + int ret,i; + char **vals; + char *attrs[] = { LDAP_UID, /* the first 6 attrs are default */ + LDAP_QMAILUID, + LDAP_QMAILGID, + LDAP_ISACTIVE, + LDAP_MAILHOST, + LDAP_MAILSTORE, + LDAP_HOMEDIR, + LDAP_MAIL, + LDAP_MAILALTERNATE, + LDAP_PASSWD, 0 }; /* passwd is extra */ + + /* initalize the different info objects */ + if ( rebind ) { + extra[2].what = 0; /* under rebind mode no additional info is needed */ + search.bindpw = authdata->s; + attrs[9] = 0; + /* rebind on, check passwd via ldap rebind */ + } else { + extra[2].what = LDAP_PASSWD; /* need to get the crypted password */ + search.bindpw = 0; /* rebind off */ + } + + if ( !make_filter(login, &filter ) ) { + /* create search filter */ + log(4, "warning: check_ldap: could not make a filter\n"); + /* qldap_errno set by make_filter */ + return -1; + } + search.filter = filter.s; + + ret = ldap_lookup(&search, attrs, &info, extra, login, 1); + free_stralloc(&filter); /* free the old filter */ + if ( ret != 0 ) { + log(4, "warning: check_ldap: ldap_lookup not successful!\n"); + /* qldap_errno set by ldap_lookup */ + return -1; + } + /* check the status of the account !!! */ + if ( info.status == STATUS_BOUNCE ) { + qldap_errno = ACC_DISABLED; + return -1; + } + + + + scan_ulong(info.uid, uid); /* get uid, gid and home */ + scan_ulong(info.gid, gid); /* the values are checked later */ + + + /* free a part of the info struct */ + alloc_free(info.user); + alloc_free(info.uid); + alloc_free(info.gid); + if (info.homedir) alloc_free(info.homedir); + if (info.mms) alloc_free(info.mms); + + + + if ( rebind && search.bind_ok ) { + log(32, + "check_ldap: ldap_lookup sucessfully authenticated with rebind\n"); + goto SUCCESS; + return 0; + /* if we got till here under rebind mode, the user is authenticated */ + } else if ( rebind ) { + log(32, + "check_ldap: ldap_lookup authentication failed with rebind\n"); + qldap_errno = AUTH_FAILED; + return -1; /* user authentification failed */ + } + + + if ( ! extra[0].vals ) { + log(2, "warning: check_ldap: password is missing for uid %s\n", + login); + qldap_errno = AUTH_NEEDED; + return -1; + } + + ret = cmp_passwd((unsigned char*) authdata->s, extra[2].vals[0]); + log(32, "check_ldap: password compare was %s\n", + ret==0?"successful":"not successful"); + ldap_value_free(extra[2].vals); + + if (!ret) goto SUCCESS; + else return ret; + + SUCCESS: + /* use the home stralloc for a zero separated list of valid mail addresses */ + home->len = 0; + + for( i = 0; i < 2; i++ ) { + vals = extra[i].vals; + while (vals && *vals) { + if (!stralloc_cats(home,*vals)) goto BAD_STRALLOC; + if (is_local(*vals)) { + if (!stralloc_cats(home,"@") || +#if DOMAIN_DEREF_LIMIT + !stralloc_cats(home,canonical_domain.s) +#else + !stralloc_cats(home,domain.s) +#endif + ) goto BAD_STRALLOC; + } + if (!stralloc_0(home)) goto BAD_STRALLOC; + vals++; + } + ldap_value_free(extra[i].vals); + } + + return 0; + + BAD_STRALLOC: + qldap_errno = ERRNO; + return -1; +} + +static int check_passwd(stralloc *login, stralloc *authdata, unsigned long *uid, + unsigned long *gid, stralloc *home, stralloc *md) +{ + int ret; + struct passwd *pw; +#ifdef PW_SHADOW + struct spwd *spw; +#endif +#ifdef AIX + struct userpw *spw; +#endif + + pw = getpwnam(login->s); + if (!pw) { + /* XXX: unfortunately getpwnam() hides temporary errors */ + log(32, "check_passwd: user %s not found in passwd db\n", login->s); + qldap_errno = AUTH_NOSUCH; + return -1; + } + *gid = pw->pw_gid; + *uid = pw->pw_uid; + + /* here we don't check the home and maildir path, if a user has a faked + * passwd entry, then you have a bigger problem on your system than just + * a guy how can read the mail of other users/customers */ + if (!stralloc_copys(home, pw->pw_dir) ) { + qldap_errno = ERRNO; + return -1; + } + + if ( get_local_maildir(home, md) == -1 ) { + /* function sets qldap_errno */ + return -1; + } + log(32, "get_local_maildir: maildir=%s\n", md->s); + + if (!stralloc_0(home) ) { + qldap_errno = ERRNO; + auth_error(); + } + +#ifdef PW_SHADOW + spw = getspnam(login->s); + if (!spw) { + /* XXX: again, temp hidden */ + qldap_errno = AUTH_ERROR; + return -1; + } + ret = cmp_passwd((unsigned char*) authdata->s, spw->sp_pwdp); +#else /* no PW_SHADOW */ +#ifdef AIX + spw = getuserpw(login->s); + if (!spw) { + /* XXX: and again */ + qldap_errno = AUTH_ERROR; + return -1; + } + ret = cmp_passwd((unsigned char*) authdata->s, spw->upw_passwd); +#else /* no AIX */ + ret = cmp_passwd((unsigned char*) authdata->s, pw->pw_passwd); +#endif /* END AIX */ +#endif /* END PW_SHADOW */ + log(32, "check_pw: password compare was %s\n", + ret==0?"successful":"not successful"); + return ret; + +} + +static int cmp_passwd(unsigned char *clear, char *encrypted) +{ +#define HASH_LEN 100 /* XXX is this enough, I think yes */ + /* What do you think ? */ + char hashed[HASH_LEN]; /* these to buffers can not be used for exploits */ + char salt[33]; + int shift; + + if (encrypted[0] == '{') { /* hashed */ + if (!str_diffn("{crypt}", encrypted, 7) ) { + /* CRYPT */ + shift = 7; + str_copy(hashed, crypt(clear, encrypted+shift) ); + } else if (!str_diffn("{MD4}", encrypted, 5) ) { + /* MD4 */ + shift = 5; + MD4DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{MD5}", encrypted, 5) ) { + /* MD5 */ + shift = 5; + MD5DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{NS-MTA-MD5}", encrypted, 12) ) { + /* NS-MTA-MD5 */ + shift = 12; + if (!str_len(encrypted) == 76) { + qldap_errno = ILL_AUTH; + return -1; + } /* boom */ + byte_copy(salt, 32, &encrypted[44]); + salt[32] = 0; + ns_mta_hash_alg(hashed, salt, (char *) clear); + byte_copy(&hashed[32], 33, salt); + } else if (!str_diffn("{SHA}", encrypted, 5) ) { + /* SHA */ + shift = 5; + SHA1DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{SHA1}", encrypted, 6) ) { + /* SHA */ + shift = 6; + SHA1DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{RMD160}", encrypted, 8) ) { + /* RMD160 */ + shift = 8; + RMD160DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else { + /* unknown hash function detected */ + shift = 0; + qldap_errno = ILL_AUTH; + return -1; + } + /* End getting correct hash-func hashed */ + log(256, "cpm_passwd: comparing hashed passwd (%s == %s)\n", + hashed, encrypted); + if (!*encrypted || str_diff(hashed,encrypted+shift) ) { + qldap_errno = AUTH_FAILED; + return -1; + } + /* hashed passwds are equal */ + } else { /* crypt or clear text */ + log(256, "cpm_passwd: comparing standart passwd (%s == %s)\n", + crypt(clear,encrypted), encrypted); + if (!*encrypted || str_diff(encrypted, crypt(clear,encrypted) ) ) { + /* CLEARTEXTPASSWD ARE NOT GOOD */ + /* so they are disabled by default */ +#ifdef CLEARTEXTPASSWD +#warning ___CLEARTEXT_PASSWORD_SUPPORT_IS_ON___ + if (!*encrypted || str_diff(encrypted, clear) ) { +#endif + qldap_errno = AUTH_FAILED; + return -1; +#ifdef CLEARTEXTPASSWD + } +#endif + /* crypted or cleartext passwd ok */ + } + } /* end -- hashed or crypt/clear text */ + + return 0; + +} + +static int get_local_maildir(stralloc *home, stralloc *maildir) +{ + substdio ss; + stralloc dotqmail = {0}; + char buf[512]; + int match; + int fd; + + if ( ! stralloc_copy(&dotqmail, home) ) { + qldap_errno = ERRNO; + return -1; + } + if ( ! stralloc_cats(&dotqmail, "/.qmail") ) { + qldap_errno = ERRNO; + return -1; + } + if ( ! stralloc_0(&dotqmail) ) { + qldap_errno = ERRNO; + return -1; + } + + if ( ( fd = open_read(dotqmail.s) ) == -1 ) { + if ( errno == error_noent ) return 0; + qldap_errno = ERRNO; + return -1; + } + + substdio_fdbuf(&ss,read,fd,buf,sizeof(buf)); + while (1) { + if (getln(&ss,&dotqmail,&match,'\n') != 0) goto tryclose; + if (!match && !dotqmail.len) break; + if ( (dotqmail.s[0] == '.' || dotqmail.s[0] == '/') && + dotqmail.s[dotqmail.len-2] == '/' ) { /* is a maildir line ? */ + if ( ! stralloc_copy(maildir, &dotqmail) ) goto tryclose; + maildir->s[maildir->len-1] = '\0'; + break; + } + } + + close(fd); + for (match = 0; match<512; buf[match++]=0 ) ; /* trust nobody */ + free_stralloc(&dotqmail); + return 0; + +tryclose: + for (match = 0; match<512; buf[match++]=0 ) ; /* trust nobody */ + match = errno; /* preserve errno */ + close(fd); + free_stralloc(&dotqmail); + errno = match; + qldap_errno = ERRNO; + return -1; + +} + +#ifdef QLDAP_CLUSTER +static void copyloop(int infd, int outfd, int timeout) +{ + fd_set iofds; + fd_set savedfds; + int maxfd; /* Maximum numbered fd used */ + struct timeval tv; + unsigned long bytes; + char buf[4096]; /* very big buffer ethernet pkgs are normaly + around 1500 bytes long */ + + /* file descriptor bits */ + FD_ZERO(&savedfds); + FD_SET(infd, &savedfds); + FD_SET(outfd, &savedfds); + + if (infd > outfd) { + maxfd = infd; + } else { + maxfd = outfd; + } + + while(1) { + /* Set up timeout *//* because of LINUX this has to be done everytime */ + tv.tv_sec = timeout; + tv.tv_usec = 0; + + byte_copy(&iofds, sizeof(iofds), &savedfds); + + if ( select( maxfd + 1, &iofds, (fd_set *)0, (fd_set *)0, &tv) <= 0 ) { + break; + } + + if(FD_ISSET(infd, &iofds)) { + if((bytes = read(infd, buf, sizeof(buf))) <= 0) + break; + if(write(outfd, buf, bytes) != bytes) + break; + } + if(FD_ISSET(outfd, &iofds)) { + if((bytes = read(outfd, buf, sizeof(buf))) <= 0) + break; + if(write(infd, buf, bytes) != bytes) + break; + } + } + + shutdown(infd,0); + shutdown(outfd,0); + close(infd); + close(outfd); + for(bytes=0; bytes<4096; buf[bytes++] = 0 ) ; /* paranoia */ + return; +} + +static void forward_session(char *host, char *name, char *passwd) +{ + ipalloc ip = {0}; + stralloc host_stralloc = {0}; + int ffd; + int timeout = 31*60; /* ~30 min timeout RFC1730 */ + int ctimeout = 20; + + if (!stralloc_copys(&host_stralloc, host)) { + qldap_errno = ERRNO; + auth_error(); + } + + dns_init(0); + switch (dns_ip(&ip,&host_stralloc)) { + case DNS_MEM: + qldap_errno = ERRNO; + auth_error(); + case DNS_SOFT: + qldap_errno = BADCLUSTER; + auth_error(); + case DNS_HARD: + qldap_errno = BADCLUSTER; + auth_error(); + case 1: + if (ip.len <= 0) { + qldap_errno = BADCLUSTER; + auth_error(); + } + } + if ( ip.len != 1 ) { + qldap_errno = BADCLUSTER; + auth_error(); + } + + ffd = socket(AF_INET,SOCK_STREAM,0); + if (ffd == -1) { + qldap_errno = ERRNO; + auth_error(); + } + + if (timeoutconn(ffd, &ip.ix[0].ip, auth_port, ctimeout) != 0) { + qldap_errno = ERRNO; + auth_error(); + } + + /* We have a connection, first send user and pass */ + auth_forward(ffd, name, passwd); + copyloop(0, ffd, timeout); + + _exit(0); /* all went ok, exit normaly */ + +} +#endif /* QLDAP_CLUSTER */ + +static int make_filter(stralloc *value, stralloc *filter) +/* create a searchfilter, "(uid=VALUE)" */ +{ + stralloc tmp = {0}; + stralloc local = {0}; + int n,m; + char sep = ID_SEP; + int len = value->len; + + if ( !stralloc_copy(&tmp, value) ) { + qldap_errno = ERRNO; + return 0; + } + if ( !escape_forldap(&tmp) ) { + qldap_errno = ERRNO; + return 0; + } + if ((n = byte_chr(tmp.s, str_len(tmp.s), sep)) == len) { + if (!stralloc_copy(&local,&tmp)) _exit(QLX_NOMEM); + } else { + if (!stralloc_copyb(&local,tmp.s,n)) _exit(QLX_NOMEM); + } + if ( !stralloc_copys(filter,"(" ) ) { + qldap_errno = ERRNO; + return 0; + } + if ( qldap_objectclass.len && ( + !stralloc_cats(filter,"&(" ) || + !stralloc_cats(filter,LDAP_OBJECTCLASS) || + !stralloc_cats(filter,"=") || + !stralloc_cat(filter,&qldap_objectclass) || + !stralloc_cats(filter,")(") ) ) { + qldap_errno = ERRNO; + return 0; + } + if ( !stralloc_cats(filter, "|(") || + !stralloc_cats(filter, LDAP_UID) || + !stralloc_cats(filter, "=") || + !stralloc_cat(filter, &tmp) || + !stralloc_cats(filter, ")(") || + !stralloc_cats(filter, LDAP_UID) || + !stralloc_cats(filter, "=") || + !stralloc_cat(filter, &local) || + !stralloc_cats(filter, "))") ) + { + qldap_errno = ERRNO; + return 0; + } + if ( qldap_objectclass.len && + !stralloc_cats(filter,")") ) { + qldap_errno = ERRNO; + return 0; + } + if ( !stralloc_0(filter) ) { + qldap_errno = ERRNO; + return 0; + } + free_stralloc(&tmp); + free_stralloc(&local); + return 1; +} + +static void free_stralloc(stralloc* sa) +{ + alloc_free(sa->s); + sa->s = 0; + sa->len = 0; + sa->a = 0; + return; +} + diff -Naurb qmail-ldap-0802-i/hier.c qmail-la-mod/hier.c --- qmail-ldap-0802-i/hier.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/hier.c Sun Aug 5 06:25:54 2001 @@ -150,8 +150,9 @@ c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-reply",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-quotawarn",auto_uido,auto_gidq,0755); - c(auto_qmail,"bin","auth_pop",auto_uido,auto_gidq,0700); - c(auto_qmail,"bin","auth_imap",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","auth_pop",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","auth_imap",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","auth_smtp",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-ldaplookup",auto_uido,auto_gidq,0700); c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); diff -Naurb qmail-ldap-0802-i/qldap-domainspec.h qmail-la-mod/qldap-domainspec.h --- qmail-ldap-0802-i/qldap-domainspec.h Wed Dec 31 19:00:00 1969 +++ qmail-la-mod/qldap-domainspec.h Wed Aug 1 18:18:57 2001 @@ -0,0 +1,6 @@ + +#ifndef __QLDAP_DOMAIN_SPEC_H_ +#define __QLDAP_DOMAIN_SPEC_H_ + + +#endif /* __QLDAP_DOMAIN_SPEC_H_ */ diff -Naurb qmail-ldap-0802-i/qldap-errno.c qmail-la-mod/qldap-errno.c --- qmail-ldap-0802-i/qldap-errno.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qldap-errno.c Wed Aug 1 18:18:57 2001 @@ -24,7 +24,18 @@ return "needed object/field is missing"; case LDAP_COUNT: return "too many entries found"; - + case LDAP_DOM_DEREF: + return "Exceeded domain alias chase limit (possible alias loop)"; + case LDAP_DOM_SEGFAULT: + return "Domain alias does not exist"; + case LDAP_DOM_NEXIST: + return "Domain does not exist (and no aliasing)"; + case LDAP_DOM_TIMEOUT: + return "Domain alias search timed out"; + case LDAP_MISBEHAVING: + return "LDAP libraries are being inconsistent"; + case LDAP_DOM_SEARCH: + return "Alias LDAP search failed"; case AUTH_FAILED: return "authorization failed wrong password"; case AUTH_ERROR: diff -Naurb qmail-ldap-0802-i/qldap-errno.h qmail-la-mod/qldap-errno.h --- qmail-ldap-0802-i/qldap-errno.h Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qldap-errno.h Wed Aug 1 18:18:57 2001 @@ -18,6 +18,13 @@ #define LDAP_ERRNO ERRNO /* check errno for more info */ #define LDAP_NEEDED 7 /* needed db field missing */ #define LDAP_COUNT 8 /* too many entries found */ +#define LDAP_DOM_SEARCH 34 /* random search error in alias chasing */ +#define LDAP_DOM_DEREF 35 /* too many dereferences*/ +#define LDAP_DOM_SEGFAULT 36 /* couldn't find the alias */ +#define LDAP_DOM_NEXIST 37 /* domain isn't aliased, just non-existent*/ +#define LDAP_DOM_TIMEOUT 38 /* domain alias search timed out */ +#define LDAP_DOM_MULTIPLE 39 /* more than one alias */ +#define LDAP_MISBEHAVING 30 /* ldap library is being inconsistent */ /* now the checkpassword errnos */ #define AUTH_FAILED 9 /* authorization failed wrong password */ diff -Naurb qmail-ldap-0802-i/qldap-ldaplib.c qmail-la-mod/qldap-ldaplib.c --- qmail-ldap-0802-i/qldap-ldaplib.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qldap-ldaplib.c Sat Aug 4 10:37:30 2001 @@ -15,6 +15,7 @@ #include "qldap-debug.h" #include "fmt.h" #include /* for ldap search timeout */ +#include "qlx.h" #define QLDAP_PORT LDAP_PORT #ifndef PORT_LDAP /* this is for testing purposes, so you can overwrite @@ -36,14 +37,29 @@ /* internal use only vars */ static stralloc qldap_server = {0}; /* name of ldap server */ static stralloc qldap_basedn = {0}; /* the search basedn */ +static stralloc qldap_domain_basedn = {0}; /* domain specific basedn */ static stralloc qldap_user = {0}; /* the ldap user ( for login ) */ static stralloc qldap_password = {0}; /* the ldap login password */ static stralloc qldap_uid = {0}; /* UID if not specified in db */ static stralloc qldap_gid = {0}; /* UID if not specified in db */ -static stralloc qldap_messagestore = {0}; /* prefix for maildirpaths */ +static stralloc qldap_messagestore = {0}; /* default maildir relative to home */ +static stralloc qldap_mailroot = {0}; /* prefix for maildirpaths */ static long qldap_timeout = QLDAP_TIMEOUT; /* default timeout is 30 secs */ +/* hack for supporting per-domain users */ +static int find_domain(LDAP *ld); +static int set_user_domain(stralloc *identifier, int auth); +static int specify_domain_basedn(); + +stralloc domain = {0}; +stralloc localname = {0}; +/* these are for keeping track of the last domain chased, + and the alias found. (so catchall lookups don't incur another chase) */ +stralloc canonical_domain = {0}; +stralloc aliasdomain = {0}; + + /* char replacement */ static unsigned int replace(char *s, register unsigned int len, char f, char r) { @@ -142,6 +158,14 @@ qldap_messagestore.s); t = cf; + t += fmt_strn(cf, "control/ldapmailroot", 64); *t=0; + if (control_rldef(&qldap_mailroot, ctrl_file, 0, "") == -1) + return -1; + if (!stralloc_0(&qldap_mailroot)) return -1; + log(64, "init_ldap: control/ldapmailroot: %s\n", + qldap_mailroot.s); + + t = cf; t += fmt_strn(cf, "control/ldaptimeout", 64); *t=0; if (control_readint(&qldap_timeout, ctrl_file) == -1) return -1; @@ -206,7 +230,7 @@ } int ldap_lookup(searchinfo *search, char **attrs, userinfo *info, - extrainfo *extra) + extrainfo *extra, stralloc *user, int auth) /* searches a db entry as specified in search, and fills up info and extra with * the coresponding db entries or NULL if not available. * Returns 0 if a entry was found, 1 if more than one or no corresponding entry @@ -219,30 +243,40 @@ int version; int num_entries; struct timeval ldaptimeout = {0}; + int authmethod = LDAP_AUTH_SIMPLE; #ifndef USE_CLDAP log(128, "ldap_lookup: "); /* allocate the connection */ +#if USE_LDAP_URI + if ( ldap_initialize(&ld,qldap_server.s) != LDAP_SUCCESS ) { +#else if ( (ld = ldap_init(qldap_server.s,PORT_LDAP)) == 0 ) { +#endif /* USE_LDAP_URI */ + log(128, "init not successful, return code 0x%x:%s\n",rc,ldap_err2string(rc)); qldap_errno = LDAP_INIT; return -1; } - log(128, "init successful"); + log(128, "init successful\n"); #ifdef LDAP_OPT_PROTOCOL_VERSION /* set LDAP connection options (only with Mozilla LDAP SDK) */ - version = LDAP_VERSION2; + /* gets invoked with openldap SDK too */ + version = LDAP_VERSION3; if ( ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) - != LDAP_SUCCESS ) { + != LDAP_OPT_SUCCESS ) + { + log(128, "set_option failed!\n"); qldap_errno = LDAP_INIT; return -1; } - log(128, ", set_option successful"); + log(128, "set_option successful\n"); #endif /* connect to the LDAP server */ - if ( (rc = ldap_simple_bind_s(ld,qldap_user.s,qldap_password.s)) - != LDAP_SUCCESS ) { + if ( (rc = ldap_bind_s( ld, qldap_user.s, qldap_password.s, authmethod )) + != LDAP_SUCCESS ) + { log(128, ", bind NOT successful (%s)\n", ldap_err2string(rc) ); /* probably more detailed information should be returned, eg.: @@ -267,9 +301,15 @@ ldaptimeout.tv_sec = qldap_timeout; ldaptimeout.tv_usec = 0; + if (set_user_domain(user, auth) == -1) return -1; +#if DOMAIN_DEREF_LIMIT + if (find_domain(ld)) return -1; +#endif + if (specify_domain_basedn() == -1) return -1; /* do the search for the login uid */ - if ( (rc = ldap_search_st(ld, qldap_basedn.s, LDAP_SCOPE_SUBTREE, - search->filter, attrs, 0, &ldaptimeout, &res )) != LDAP_SUCCESS ) { + if ( (rc = ldap_search_st(ld, qldap_domain_basedn.s, LDAP_SCOPE_SUBTREE, + search->filter, attrs, 0, &ldaptimeout, &res )) != LDAP_SUCCESS ) + { log(64, "ldap_lookup: search for %s failed (%s)\n", search->filter, ldap_err2string(rc) ); @@ -290,13 +330,12 @@ case LDAP_TIMELIMIT_EXCEEDED: case LDAP_BUSY: qldap_errno = LDAP_SEARCH_TIMEOUT; - + return -1; default: qldap_errno = LDAP_SEARCH; - } - return -1; } + } #else /* USE_CLDAP */ log(128, "ldap_lookup: "); /* allocate the connection */ @@ -324,7 +363,7 @@ /* count the results, we must have exactly one */ if ( (num_entries = ldap_count_entries(ld,msg)) != 1) { - log(64, "ldap_lookup: Too many (less) entries found (%i)\n", + log(64, "ldap_lookup: Too many (few) entries found (%i)\n", num_entries); if ( num_entries ) qldap_errno = LDAP_COUNT; @@ -517,71 +556,69 @@ { int i; int s; - - if ( hdval ) { - if ( hdval[0][0] != '/' ) { - log(64, "non absolute homedirectory path!\n"); - qldap_errno = LDAP_NEEDED; - return -1; - } - if ( (*homedir = alloc( str_len( hdval[0] ) + 1 ) ) == 0 ) { - qldap_errno = LDAP_ERRNO; - return -1; - } - log(64, "%s=%s (from server)\n", LDAP_HOMEDIR, hdval[0]); - str_copy( *homedir, hdval[0] ); + stralloc home_path = {0}; + stralloc maildir = {0}; + char slash = '/'; + + log(256,"Constructing home directory:\n"); + if (qldap_mailroot.s) { + if (*qldap_mailroot.s == slash) { + if (!stralloc_copys(&home_path,qldap_mailroot.s)) goto NO_MEM; } else { - log(64, "%s=undefined\n", LDAP_HOMEDIR); - *homedir = 0; + log(1,"Mail root (%s) is not absolute, ignoring.\n",qldap_mailroot.s); + if (!stralloc_append(&home_path,&slash)) goto NO_MEM; } - if ( mmsval ) { + } else if (!stralloc_append(&home_path,&slash)) goto NO_MEM; + + if (home_path.s[home_path.len-1] != slash) + if (!stralloc_append(&home_path,&slash)) goto NO_MEM; + + if ( hdval && *hdval ) { log(64, "%s=%s (from server)\n", LDAP_MAILSTORE, mmsval[0]); - if ( mmsval[0][0] != '/' ) { - /* local path, use ldapmessagestore as prefix or return a error */ - if ( (!qldap_messagestore.s || qldap_messagestore.s[0] != '/') - && *homedir == 0 ) { - log(64, "non absolute path but neither ctrl/ldapmessagestore nor homedir defined!\n"); - qldap_errno = LDAP_NEEDED; - return -1; - } - i = 0; s = -1; - if ( *homedir == 0 ) { - log(64, " using %s as prefix\n", qldap_messagestore.s); - /* XXX if both homedir and ldapmms are defined homedir has - * higher priority (ldapmms will be ignored (not prefixed ) ) */ - if ( qldap_messagestore.s[qldap_messagestore.len - 1] != '/' ) { - /* arrg need to add a / between the two */ - s = 0; - } - i = qldap_messagestore.len + s; - /* qldap_mms is one char too long so reduce the length */ - } - i += str_len( mmsval[0] ) + 1; - if ( (*mms = alloc( i ) ) == 0 ) { - qldap_errno = LDAP_ERRNO; - return -1; - } - if ( *homedir == 0) { - str_copy( *mms, qldap_messagestore.s ); - if ( s == 0 ) str_copy( *mms + str_len(*mms), "/" ); - /* str_cat done with str_copy because djb has no str_cat :-( */ - str_copy( *mms + str_len(*mms), mmsval[0] ); - } else { - str_copy( *mms, mmsval[0] ); - } - } else { - i = str_len( mmsval[0] ) + 1; - if ( (*mms = alloc( i ) ) == 0 ) { - qldap_errno = LDAP_ERRNO; - return -1; - } - str_copy( *mms, mmsval[0] ); + if ( **hdval == '/') { + if (!stralloc_cats(&home_path,*hdval + 1)) goto NO_MEM; + } else + if (!stralloc_cats(&home_path,*hdval)) goto NO_MEM; } + else + log(2+64,"Home directory is undefined for mail %s,domain %s\n", + localname.s,domain.s); + + /* this process will switch to the home directory */ + if (home_path.s[home_path.len-1] == slash) { + home_path.len--; + home_path.s[home_path.len] = 0; + } else + if (!stralloc_0(&home_path)) goto NO_MEM; + + /* the next process will switch to the mms directory */ + if ( mmsval && *mmsval) { + log(64, "%s=%s (from server)\n", LDAP_MAILSTORE, mmsval[0]); +#if IGNORE_ABSOLUTE_MAILDIR + if ( mmsval[0][0] == '/' ) + { if (!stralloc_copys(&maildir, *mmsval + 1)) goto NO_MEM; } + else +#endif + if (!stralloc_copys(&maildir, *mmsval)) goto NO_MEM; + } else if (qldap_messagestore.s) { + if (!stralloc_copys(&maildir, qldap_messagestore.s)) goto NO_MEM; } else { - log(64, "%s=undefined\n", LDAP_MAILSTORE); - *mms = 0; +#ifdef DEFAULT_MAILDIR + log(2+64,"Mailstore directory is undefined for mail %s,domain %s;" + " using " DEFAULT_MAILDIR , localname.s, domain.s); + if (!stralloc_copys(&maildir,DEFAULT_MAILDIR)) goto NO_MEM; +#endif } + if (!stralloc_0(&maildir)) goto NO_MEM; + + *homedir = home_path.s; + *mms = maildir.s; return 0; + + NO_MEM: + qldap_errno = ERRNO; + return -1; + } int escape_forldap(stralloc *toescape) @@ -628,4 +665,256 @@ alloc_free(tmp); return 1; } + + +int set_user_domain(stralloc *identifier, int auth) +{ + char *cp; + char *dp; + int n = identifier->len; + int dots = 0; + char sep = auth ? ID_SEP : '@'; + + log(128,"Setting userdomain for %s\n",identifier->s); + cp = identifier->s; + while (*cp && *cp != sep) { cp++; } + if (!stralloc_copyb(&localname, identifier->s, cp - identifier->s) || + !stralloc_0(&localname)) + { qldap_errno = ERRNO; return -1; } + log(128,"Found localname %s\n",localname.s); + if (*cp == ID_SEP && *(++cp)) ; + else { cp = DOMAIN_DEFAULT; n = sizeof(DOMAIN_DEFAULT); } + + log(128,"Found initial domain part: %s\n",cp); + /* make sure we have something like foo.com and not www.foo.com */ + dp = cp; + cp = cp + n; + + while (cp > dp) { + if (*cp == '.') { + dots++; + if (dots > 1) { + cp++; + break; + } + } + cp--; + } + + if (!stralloc_copys(&domain,cp) || + !stralloc_0(&domain) ) + { qldap_errno = ERRNO; return -1; } + log(128,"Found domain to lookup (only 1 .): %s\n",domain.s); + return 0; +} + +int specify_domain_basedn() +{ + log(128, "Specifying domain basedn with domain %s\n",domain.s); + if (!stralloc_copys(&qldap_domain_basedn,DOMAIN_ADD) || + !stralloc_cats(&qldap_domain_basedn,DOMAIN_NAME_ATTR) || + !stralloc_cats(&qldap_domain_basedn,"=") || + !stralloc_cats(&qldap_domain_basedn,domain.s)) + goto BAD_STRALLOC; + + log(128, "Specifying domain basedn with domain %s\n",domain.s); + + if (qldap_basedn.s && qldap_basedn.len > 0 && + *qldap_basedn.s != 0 && + !stralloc_cats(&qldap_domain_basedn,",") || + !stralloc_cat(&qldap_domain_basedn,&qldap_basedn)) + goto BAD_STRALLOC; + + log(128, "Specifying domain basedn with domain %s\n",domain.s); + + if (!stralloc_0(&qldap_domain_basedn)) + goto BAD_STRALLOC; + + log(128,"Set domain-specific basedn to %s\n",qldap_domain_basedn.s); + return 0; + + BAD_STRALLOC: + qldap_errno = ERRNO; + return -1; +} + + +/* chase down aliases and leave the found domain in domain*/ +int find_domain(LDAP *ld) +{ + char *attrs[] = { LDAP_OBJECTCLASS, DOMAIN_ALIAS_ATTR, 0 }; + char **vals = 0; + stralloc filter = {0}; + LDAPMessage *res = 0; + LDAPMessage *msg = 0; + int domain_found = 0; + int i = 0; + int rc,num_entries; + struct timeval ldaptimeout = {0}; + + /* reuse previous results if it's for the same domain name. + meant to catch qmail-lspawn's catchall feature. + */ + + if (canonical_domain.s) { + if (!str_diff(canonical_domain.s,domain.s)) + { + if (aliasdomain.s) { + if (!stralloc_copy(&domain,&aliasdomain) || + !stralloc_0(&domain)) + goto BAD_STRALLOC; + log(128,"Already chased %s, found %s\n",canonical_domain.s,aliasdomain.s); + return 0; + } + /* else this is very odd but we'll redo the lookup */ + } + } + + /* keep for future lookups */ + if (!stralloc_copys(&canonical_domain,domain.s) || + !stralloc_0(&canonical_domain)) + goto BAD_STRALLOC; + + log(128,"Set canonical domain to %s from domain %s\n",canonical_domain.s,domain.s); + /* set up the ldap search timeout */ + ldaptimeout.tv_sec = qldap_timeout; + ldaptimeout.tv_usec = 0; + + log(64,"Entering find_domain\n"); + /* make sure we even do this the first time */ + while (!domain_found && i <= DOMAIN_DEREF_LIMIT) { + log(64,"find_domain: Chasing potential aliased domain %s\n",domain.s); + + if (!escape_forldap(&domain)) { qldap_errno = ERRNO; return -1; } + + /* (&(|(objectclass=domain_alias)(objectclass=valid_domain))(domain_alias_attr=...)) */ + if (!stralloc_copys(&filter,"(" ) || + !stralloc_cats(&filter,"&(|(") || + !stralloc_cats(&filter,LDAP_OBJECTCLASS) || + !stralloc_cats(&filter,"=") || + !stralloc_cats(&filter,DOMAIN_ALIAS_OC) || + !stralloc_cats(&filter,")(") || + !stralloc_cats(&filter,LDAP_OBJECTCLASS) || + !stralloc_cats(&filter,"=") || + !stralloc_cats(&filter,DOMAIN_VALID_OC) || + !stralloc_cats(&filter,"))(") || + !stralloc_cats(&filter,DOMAIN_NAME_ATTR) || + !stralloc_cats(&filter,"=") || + !stralloc_cats(&filter,domain.s) || + !stralloc_cats(&filter,"))") || + !stralloc_0(&filter) ) + goto BAD_STRALLOC; + + log(128,"find_domain: filter set to %s,basedn to %s\n",filter.s,qldap_basedn.s); + if ( (rc = ldap_search_st(ld, qldap_basedn.s, LDAP_SCOPE_ONELEVEL, + filter.s, attrs, 0, &ldaptimeout, &res )) != LDAP_SUCCESS ) { + log(64, "find_domain: search for %s failed (%s)\n", + filter.s, ldap_err2string(rc) ); + switch(rc) + { + case LDAP_TIMEOUT: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_BUSY: + qldap_errno = LDAP_DOM_TIMEOUT; + break; + + default: + log(128,"find_domain: ldap error %x:%s\n",rc,ldap_err2string(rc)); + qldap_errno = LDAP_DOM_SEARCH; + } + return -1; + } + /* go to the first entry */ + msg = ldap_first_entry(ld,res); + + /* count the results, we must have exactly one */ + if ( (num_entries = ldap_count_entries(ld,msg)) != 1) { + log(64, "find_domain: Too many (few) entries found (%i)\n", + num_entries); + if ( num_entries ) + qldap_errno = LDAP_DOM_MULTIPLE; + else + if ( i == 0 ) + qldap_errno = LDAP_DOM_NEXIST; + else + qldap_errno = LDAP_DOM_SEGFAULT; + ldap_msgfree(res); + return -1; + } + + if ( (vals = ldap_get_values(ld, msg, LDAP_OBJECTCLASS)) != 0) { + char **v = vals; + while ( *v && str_diff(*v,DOMAIN_ALIAS_OC)) { + log(64, "find_domain: Ignoring object class %s\n", *v); + v++; + } + if (!*v) domain_found = 1; + ldap_value_free(vals); + vals = 0; + } else { + log(1,"find_domain: ldap_get_values irrational!\n"); + qldap_errno = LDAP_MISBEHAVING; + return -1; + } + + /* if the entry isn't a DOMAIN_ALIAS_OC, then our current domain name + is the right one. */ + if (domain_found) { + ldap_msgfree(res); + res = 0; + break; + } + + if ( (vals = ldap_get_values(ld, msg, DOMAIN_ALIAS_ATTR)) != 0 ) + { + /* set domain for next search */ + if (*vals) { + log(64, "find_domain:domain alias name is %s\n", vals[0]); + if (!stralloc_copys(&domain,*vals) || + !stralloc_0(&domain)) + goto BAD_STRALLOC; + } else { + log(128,"entry with objectclass %s doesn't have %s attribute!\n", + DOMAIN_ALIAS_OC,DOMAIN_ALIAS_ATTR); + ldap_value_free(vals); + vals = 0; + qldap_errno = LDAP_MISBEHAVING; + return -1; + } + ldap_value_free(vals); + vals = 0; + } else { + log(128,"LDAP error in retrieving values\n"); + ldap_msgfree(res); + qldap_errno = LDAP_MISBEHAVING; + return -1; + } + + ldap_msgfree(res); + res = msg = 0; + + i++; + } + + if (domain_found) { + /* keep for future lookups */ + if (!stralloc_copy(&aliasdomain,&domain) || + !stralloc_0(&aliasdomain)) + goto BAD_STRALLOC; + log(128,"Found domain %s, set aliasdomain to %s\n", domain.s, aliasdomain.s); + return 0; + } + log(128,"Couldn't find a domain for %s\n",domain.s); + qldap_errno = LDAP_DOM_DEREF; + return -1; + + BAD_STRALLOC: + if (res) ldap_msgfree(res); + if (vals) ldap_value_free(vals); + qldap_errno = ERRNO; + return -1; +} + + + diff -Naurb qmail-ldap-0802-i/qldap-ldaplib.h qmail-la-mod/qldap-ldaplib.h --- qmail-ldap-0802-i/qldap-ldaplib.h Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qldap-ldaplib.h Sat Aug 4 10:38:48 2001 @@ -32,18 +32,29 @@ * Also bind and cluster are set to 0 and 1 as in their files described */ int ldap_lookup(searchinfo *search, char **attrs, userinfo *info, - extrainfo *extra); + extrainfo *extra, stralloc *user, int auth); /* searches a db entry as specified in search, and fills up info and extra with * the coresponding db entries or NULL if not available. * Returns 0 if a entry was found, 1 if more than one or no corresponding entry - * was found. On error it returns -1 and sets the appropriate qldap_errno. */ + * was found. On error it returns -1 and sets the appropriate qldap_errno. + * user the full identifier (with domain) + * auth tells whether this is for authentication purposes of for looking up mail + * addresses. + */ int escape_forldap(stralloc *toescape); /* Under LDAP, '(', ')', '\', '*' and '\0' have to be escaped with '\' * on success returns 1 else 0 */ +stralloc* ldap_get_basedn(); + extern void ldap_value_free(); /* LDAP function to free **vals */ + +extern stralloc domain; +extern stralloc localname; +extern stralloc canonical_domain; +extern stralloc aliasdomain; #endif diff -Naurb qmail-ldap-0802-i/qmail-ldap.h qmail-la-mod/qmail-ldap.h --- qmail-ldap-0802-i/qmail-ldap.h Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qmail-ldap.h Fri Aug 10 07:22:27 2001 @@ -12,11 +12,11 @@ #define QUOTA_WARNING_LEVEL 70 /* the maximum and minimum uid allowed */ -#define UID_MIN 100 +#define UID_MIN 500 #define UID_MAX 65535 /* the maximum and minimum gid allowed */ -#define GID_MIN 100 +#define GID_MIN 500 #define GID_MAX 65535 /* if the sanitycheck function should be less restricted for @@ -75,6 +75,52 @@ #define ISACTIVE_DELETE "deleted" #define ISACTIVE_NOPOP "nopop" #define ISACTIVE_ACTIVE "active" + +/* NB: these have to be defined in your own schema. !!!*/ +/* _OC is for object class names, _ATTR for attribute names */ +/* the object class for an aliased domain */ +#define DOMAIN_ALIAS_OC "DomainRef" +/* object class for an actual domain */ +#define DOMAIN_VALID_OC "Domain" +/* attribute name for a domain name (should be in both alias and valid oc's)*/ +#define DOMAIN_NAME_ATTR "DomainName" +/* the name of the real domain for an aliased domain */ +#define DOMAIN_ALIAS_ATTR "DomainAlias" +/* how long should I chase for - set to 0 for no domain dereferencing */ +#define DOMAIN_DEREF_LIMIT 1 +/* the domain name to use for login's without a domain part */ +#define DOMAIN_DEFAULT "my.domain" +/* If there's something to add to the basedn after the domain name, eg. "cn=mail," */ +#define DOMAIN_ADD "" +/* this is the local-domain separator to use for authorization rather than mail lookup, + * so you don't have to use '@' + */ +#define ID_SEP '@' + +/* various other features */ +/* only allow relaying for addresses associated with the uid authorized */ +#define SMTPAUTH_LIMIT_RELAY +/* don't allow auth without an encrypted connection */ +#define AUTH_REQUIRES_TLS +/* compile TLS support into qmail-remote (or not) + * TLS patch doesn't deal well with failures even when TLS isn't required + * for the remote host (but it does offer STARTTLS). ??? + */ +#undef TLS_REMOTE +/* limit on what's logged by auth modules - 255 keeps passwords from getting + * logged, even if the LOGLEVEL environment variable has the bit flag on + * for password logging. Keep in mind it's a bitmask, not an absolute value + * limit. + */ +#define AUTH_LOG_LIMIT 255 + +/* default maildirectory, relative to home */ +#define DEFAULT_MAILDIR "./Maildir/" +/* don't allow absolute mailmessagestores */ +#define IGNORE_ABSOLUTE_MAILDIR 1 +/* use ldap URIs (ldaps:// support) */ +#define USE_LDAP_URI 1 + /********************************************************************* diff -Naurb qmail-ldap-0802-i/qmail-ldaplookup.c qmail-la-mod/qmail-ldaplookup.c --- qmail-ldap-0802-i/qmail-ldaplookup.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qmail-ldaplookup.c Wed Aug 1 18:27:21 2001 @@ -97,6 +97,11 @@ LDAP_PASSWD, 0 }; /* passwd is extra */ + stralloc local = {0}; + stralloc username = {0}; + char sep; + int at; + log_init(STDERR, -1, 0); substdio_fdbuf(&ssout, write, STDOUT, buffer, sizeof(buffer) ); @@ -109,8 +114,10 @@ } if (!str_diff(argv[1], "-u") ) { mode = uid; + sep = ID_SEP; } else if (!str_diff(argv[1], "-m") ) { mode = mail; + sep = '@'; } else usage(); if ( init_ldap( &locald, &cluster, &rebind, &homemaker, &defdot, &defquota, @@ -154,6 +161,11 @@ extra[8].what = LDAP_PASSWD; /* need to get the crypted password */ search.bindpw = 0; /* rebind off */ } + /* grab the full name before escaping */ + if (!stralloc_copy(&username,&value) || + !stralloc_0(&username)) + strerr_die2x(1, "ERROR: can't copy username: ", + error_str(errno)); if ( !escape_forldap(&value) ) { strerr_die2x(1, "ERROR: escape_forldap failed: ", error_str(errno) ); } @@ -161,6 +173,15 @@ strerr_die2x(1, "ERROR: can not create a filter: ", error_str(errno)); } + + at = value.len; + while (at >= 0 && value.s[at] != sep) { at--; } + if (at < 0) + local.len = 0; + else + if (!stralloc_copyb(&local,value.s,at)) + strerr_die2x(1, "ERROR: can't get local part of username: ", + error_str(errno)); if ( mode == mail) { /* build the search string for the email address */ if ( qldap_objectclass.len && ( @@ -180,12 +201,33 @@ !stralloc_cats(&filter,LDAP_MAILALTERNATE ) || !stralloc_cats(&filter, "=") || !stralloc_cat(&filter,&value) || - !stralloc_cats(&filter,"))") ) { + !stralloc_cats(&filter,")" )) + { + strerr_die2x(1, "ERROR: can not create a filter: ", + error_str(errno)); + } + if (local.len > 0) + if (!stralloc_cats(&filter,"(" ) || + !stralloc_cats(&filter, LDAP_MAIL ) || + !stralloc_cats(&filter, "=" ) || + !stralloc_cat(&filter,&local) || + !stralloc_cats(&filter,")(" ) || + !stralloc_cats(&filter,LDAP_MAILALTERNATE ) || + !stralloc_cats(&filter, "=") || + !stralloc_cat(&filter,&local) || + !stralloc_cats(&filter,")") ) + { + strerr_die2x(1, "ERROR: can not create a filter: ", + error_str(errno)); + } + if ( !stralloc_cats(&filter,")" )) + { strerr_die2x(1, "ERROR: can not create a filter: ", error_str(errno)); } if ( qldap_objectclass.len && - !stralloc_cats(&filter,")") ) { + !stralloc_cats(&filter,")") ) + { strerr_die2x(1, "ERROR: can not create a filter: ", error_str(errno)); } @@ -203,10 +245,27 @@ strerr_die2x(1, "ERROR: can not create a filter: ", error_str(errno)); } - if ( !stralloc_cats(&filter, LDAP_UID) || + if ( !stralloc_cats(&filter,"|(" ) || + !stralloc_cats(&filter, LDAP_UID) || !stralloc_cats(&filter, "=") || !stralloc_cat(&filter, &value) || - !stralloc_cats(&filter, ")") ) { + !stralloc_cats(&filter, ")") ) + { + strerr_die2x(1, "ERROR: can not create a filter: ", + error_str(errno)); + } + if ( local.len > 0 ) + if ( !stralloc_cats(&filter, "(") || + !stralloc_cats(&filter, LDAP_UID) || + !stralloc_cats(&filter, "=") || + !stralloc_cat(&filter, &local) || + !stralloc_cats(&filter, ")") ) + { + strerr_die2x(1, "ERROR: can not create a filter: ", + error_str(errno)); + } + if ( !stralloc_cats(&filter,")" )) + { strerr_die2x(1, "ERROR: can not create a filter: ", error_str(errno)); } @@ -222,7 +281,7 @@ } search.filter = filter.s; output(&ssout, "ldap_lookup:\tsearching with %s\n", filter.s); - ret = ldap_lookup(&search, attrs, &info, extra); + ret = ldap_lookup(&search, attrs, &info, extra, &username, (mode == uid) ? 1 : 0); if ( ret != 0 ) { output(&ssout, "ERROR: ldap_lookup not successful: ", qldap_err_str(qldap_errno)); diff -Naurb qmail-ldap-0802-i/qmail-local.c qmail-la-mod/qmail-local.c --- qmail-ldap-0802-i/qmail-local.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qmail-local.c Fri Aug 3 17:52:00 2001 @@ -80,7 +80,7 @@ char outbuf[1024]; /* child process */ -char fntmptph[80 + FMT_ULONG * 2]; +char fntmptph[80 + FMT_ULONG * 2]; /* tmp/time.pid.host */ char fnnewtph[83 + FMT_ULONG * 3]; void tryunlinktmp() { unlink(fntmptph); } void sigalrm() { tryunlinktmp(); _exit(3); } @@ -115,7 +115,7 @@ if (error_temp(errno)) _exit(1); else _exit(2); } -/* this one handles the case where the aliasempty is "./" */ + /* this one handles the case where the aliasempty is "./" */ #ifdef AUTOMAILDIRMAKE if ( !str_diff(dir, "./") ) { umask(077); diff -Naurb qmail-ldap-0802-i/qmail-lspawn.c qmail-la-mod/qmail-lspawn.c --- qmail-ldap-0802-i/qmail-lspawn.c Sun Aug 5 06:17:53 2001 +++ qmail-la-mod/qmail-lspawn.c Fri Aug 3 17:14:14 2001 @@ -116,6 +116,10 @@ substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); REPORT_RETURN; + case QLX_BUG: + substdio_puts(ss,"ZBug in qmail-lspawn.\n"); + REPORT_RETURN; + case QLX_NOMEM: substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); REPORT_RETURN; @@ -301,7 +305,14 @@ substdio_put(ss,s,i); } - +/* name + uid + gid + home directory + dash + extension (dash trick(?)) + = nughde +*/ stralloc nughde = {0}; /* LDAP server query routines */ @@ -331,37 +342,74 @@ int at; int i; int force_forward; - char *r; + char *r = 0; stralloc filter = {0}; + stralloc localpart = {0}; + stralloc username = {0}; + stralloc domain = {0}; unsigned long tid; + /* grab this before escaping mail */ + if (!stralloc_copy(&username,mail) || + !stralloc_0(&username)) + goto NO_MEM; + i = byte_rchr(username.s,username.len,'@'); /* check the mailaddress for illegal characters * * escape '*', ,'\', '(' and ')' with a preceding '\' */ - if (!escape_forldap(mail) ) _exit(QLX_NOMEM); + if (!escape_forldap(mail) ) goto NO_MEM; + + /* Have to recalculate this */ + at = byte_rchr(mail->s,mail->len,'@'); + + log(256,"at = %x, i = %x mail = %s\n", + at,i,mail->s); + + /* local part is already escaped */ + if (at < mail->len) + if (!stralloc_copyb(&localpart,mail->s,at) || + !stralloc_0(&localpart)) + goto NO_MEM; /* build the search string for the email address */ if (!stralloc_copys(&filter,"(" ) ) _exit(QLX_NOMEM); /* optional objectclass */ - if ( qldap_objectclass.len ) { - if (!stralloc_cats(&filter,"&(")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_OBJECTCLASS)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cat(&filter,&qldap_objectclass)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,")(")) _exit(QLX_NOMEM); - } /* end */ - if (!stralloc_cats(&filter,"|(")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_MAIL)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cat(&filter,mail)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,")(")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_MAILALTERNATE)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cat(&filter,mail)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"))")) _exit(QLX_NOMEM); - if ( qldap_objectclass.len ) { - if (!stralloc_cats(&filter,")")) _exit(QLX_NOMEM); - } - if (!stralloc_0(&filter)) _exit(QLX_NOMEM); + /* build the search string for the email address */ + if ( qldap_objectclass.len && + ( !stralloc_cats(&filter,"&(") || + !stralloc_cats(&filter,LDAP_OBJECTCLASS) || + !stralloc_cats(&filter,"=") || + !stralloc_cat(&filter,&qldap_objectclass) || + !stralloc_cats(&filter,")(") ) ) + goto NO_MEM; + if ( !stralloc_cats(&filter,"|(" ) || + !stralloc_cats(&filter, LDAP_MAIL ) || + !stralloc_cats(&filter, "=" ) || + !stralloc_cat(&filter,mail) || + !stralloc_cats(&filter,")(" ) || + !stralloc_cats(&filter,LDAP_MAILALTERNATE ) || + !stralloc_cats(&filter, "=") || + !stralloc_cat(&filter,mail) || + !stralloc_cats(&filter,")" )) + goto NO_MEM; + if (at < mail->len) + if (!stralloc_cats(&filter,"(" ) || + !stralloc_cats(&filter, LDAP_MAIL ) || + !stralloc_cats(&filter, "=" ) || + !stralloc_cats(&filter,localpart.s) || + !stralloc_cats(&filter,")(" ) || + !stralloc_cats(&filter,LDAP_MAILALTERNATE ) || + !stralloc_cats(&filter, "=") || + !stralloc_cats(&filter,localpart.s) || + !stralloc_cats(&filter,")") ) + goto NO_MEM; + + if ( !stralloc_cats(&filter,")" )) + goto NO_MEM; + if ( qldap_objectclass.len && + !stralloc_cats(&filter,")") ) + goto NO_MEM; + if ( !stralloc_0(&filter) ) + goto NO_MEM; log(16, "ldapfilter: '%s'\n", filter.s); search.filter = filter.s; @@ -377,46 +425,83 @@ extra[6].what = 0; /* do the search for the email address */ - ret = ldap_lookup(&search, attrs, &info, extra); + ret = ldap_lookup(&search, attrs, &info, extra, &username, 0); if ( ret != 0 && qldap_errno == LDAP_NOSUCH ) { /* this handles the "catch all" extension */ - at = 0; - r = mail->s; - i = mail->len; - for (at = i - 1; r[at] != '@' && at >= 0 ; at--) ; - /* handels also mail with 2 @ */ + log(256,"Lookup for %s failed, trying catchall address\n",username.s); + + /* i is the value of at recieved originally */ + if (i < username.len) + if (!stralloc_copys(&domain,username.s + at)) + goto NO_MEM; + + if ( !stralloc_copys(&username, LDAP_CATCH_ALL) || + !stralloc_cat(&username, &domain) || + !stralloc_0(&username)) + goto NO_MEM; + /* this assumes the catchall address is already escaped as + necessary - it's just a constant. */ + /* build the search string for the email address */ - if (!stralloc_copys(&filter,"(" ) ) _exit(QLX_NOMEM); + at = i; + r = mail->s + at; + i = mail->len - at; + log (256,"qldapget: r = %x, i = %x, at = %x\nusername = %s, domain = %s, mail=%s\n", + (unsigned int) r, i, at, username.s, (domain.s ? domain.s : "\"domain undefined\""), + mail->s); + + if (!stralloc_copys(&filter,"(" ) ) + goto NO_MEM; + /* optional objectclass */ - if (qldap_objectclass.len) { - if (!stralloc_cats(&filter,"&(")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_OBJECTCLASS)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cat(&filter,&qldap_objectclass)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,")(")) _exit(QLX_NOMEM); - } /* end */ - if (!stralloc_cats(&filter,"|(")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_MAIL)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_CATCH_ALL)) _exit(QLX_NOMEM); - if (!stralloc_catb(&filter,r+at, i-at)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,")(")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_MAILALTERNATE)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,LDAP_CATCH_ALL)) _exit(QLX_NOMEM); - if (!stralloc_catb(&filter,r+at, i-at)) _exit(QLX_NOMEM); - if (!stralloc_cats(&filter,"))")) _exit(QLX_NOMEM); - if ( qldap_objectclass.len ) { - if (!stralloc_cats(&filter,")")) _exit(QLX_NOMEM); - } - if (!stralloc_0(&filter)) _exit(QLX_NOMEM); + if ( qldap_objectclass.len && + ( !stralloc_cats(&filter,"&(") || + !stralloc_cats(&filter,LDAP_OBJECTCLASS) || + !stralloc_cats(&filter,"=") || + !stralloc_cat(&filter,&qldap_objectclass) || + !stralloc_cats(&filter,")(") ) ) + goto NO_MEM; + if ( !stralloc_cats(&filter,"|(" ) || + !stralloc_cats(&filter, LDAP_MAIL ) || + !stralloc_cats(&filter, "=" ) || + !stralloc_cats(&filter, LDAP_CATCH_ALL) || + !stralloc_catb(&filter, r, i) || + !stralloc_cats(&filter,")(" ) || + !stralloc_cats(&filter,LDAP_MAILALTERNATE ) || + !stralloc_cats(&filter, "=") || + !stralloc_cats(&filter, LDAP_CATCH_ALL) || + !stralloc_catb(&filter, r, i) || + !stralloc_cats(&filter,")" )) + goto NO_MEM; + if (i) + if (!stralloc_cats(&filter,"(" ) || + !stralloc_cats(&filter, LDAP_MAIL ) || + !stralloc_cats(&filter, "=" ) || + !stralloc_cats(&filter, LDAP_CATCH_ALL) || + !stralloc_cats(&filter,")(" ) || + !stralloc_cats(&filter, LDAP_MAILALTERNATE ) || + !stralloc_cats(&filter, "=") || + !stralloc_cats(&filter, LDAP_CATCH_ALL) || + !stralloc_cats(&filter,")") ) + goto NO_MEM; + if ( !stralloc_cats(&filter,")" )) + goto NO_MEM; + if ( qldap_objectclass.len && + !stralloc_cats(&filter,")") ) + goto NO_MEM; + if ( !stralloc_0(&filter) ) + goto NO_MEM; log(16, "retry with filter '%s'\n", filter.s); /* do the search for the catchall address */ - ret = ldap_lookup(&search, attrs, &info, extra); + ret = ldap_lookup(&search, attrs, &info, extra, &username, 0); } alloc_free(filter.s); filter.s = 0; + alloc_free(username.s); username.s = 0; + if (localpart.s) { alloc_free(localpart.s); localpart.s = 0; } + if (domain.s) { alloc_free(domain.s); domain.s = 0; } + if ( ret != 0 ) { switch(qldap_errno) { @@ -447,7 +532,7 @@ /* check if the ldap entry is active */ if ( info.status == STATUS_BOUNCE ) { - log(2, "warning: %s's accountsatus is bounce\n", info.user); + log(2, "warning: %s's accountstatus is bounce\n", info.user); _exit(225); } @@ -490,10 +575,6 @@ if (!chck_paths(info.mms) ) return 23; aliasempty = info.mms; } - } else if ( info.mms ) { - if (!chck_paths(info.mms) ) return 23; - if (!stralloc_cats(&nughde, info.mms)) _exit(QLX_NOMEM); - alloc_free(info.mms); } else { /* XXX nothing defined use ~alias as home and * XXX ALIASDEVNULL as aliasempty */ @@ -628,6 +709,9 @@ } /* ok, we finished, lets clean up and disconnect from the LDAP server */ return 0; + + NO_MEM: + _exit(QLX_NOMEM); } /* end -- LDAP server query routines */ diff -Naurb qmail-ldap-0802-i/qmail-remote.c qmail-la-mod/qmail-remote.c --- qmail-ldap-0802-i/qmail-remote.c Sun Aug 5 06:17:54 2001 +++ qmail-la-mod/qmail-remote.c Sun Aug 5 01:56:23 2001 @@ -27,12 +27,12 @@ #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" -#ifndef TLS +#if !defined(TLS) || !defined(TLS_REMOTE) #include "timeoutread.h" #include "timeoutwrite.h" #endif -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) #include #include @@ -121,7 +121,7 @@ int smtpfd; int timeout = 1200; -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) int flagtimedout = 0; void sigalrm() { @@ -156,7 +156,7 @@ int saferead(fd,buf,len) int fd; char *buf; int len; { int r; -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) r = ssl_timeoutread(timeout,smtpfd,buf,len); #else r = timeoutread(timeout,smtpfd,buf,len); @@ -167,7 +167,7 @@ int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) r = ssl_timeoutwrite(timeout,smtpfd,buf,len); #else r = timeoutwrite(timeout,smtpfd,buf,len); @@ -302,7 +302,7 @@ unsigned long code; int flagbother; int i; -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) int needtlsauth = 0; SSL_CTX *ctx; int saveerrno, r; @@ -323,7 +323,7 @@ if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) substdio_puts(&smtpto,"EHLO "); #else substdio_puts(&smtpto,"HELO "); @@ -331,8 +331,9 @@ substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); -#ifdef TLS - if (smtpcode() != 250){ +#if defined(TLS) && defined(TLS_REMOTE) + if (smtpcode() != 250) + { substdio_puts(&smtpto,"HELO "); substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); @@ -343,7 +344,7 @@ if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); #endif -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) i = 0; while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) && str_diffn(smtptext.s+i+4,"STARTTLS\n",9)); @@ -358,67 +359,81 @@ #endif SSLeay_add_ssl_algorithms(); if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) + { #ifdef DEBUG - {out("ZTLS not available: error initializing ctx"); + out("ZTLS not available: error initializing ctx"); out(": "); out(ERR_error_string(ERR_get_error(), buf)); out("\n"); #else - {out("ZTLS not available: error initializing ctx\n"); + out("ZTLS not available: error initializing ctx\n"); #endif out("\n"); - zerodie();} + zerodie(); + } SSL_CTX_use_RSAPrivateKey_file(ctx, "control/cert.pem", SSL_FILETYPE_PEM); SSL_CTX_use_certificate_file(ctx, "control/cert.pem", SSL_FILETYPE_PEM); /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/ - if (needtlsauth){ + if (needtlsauth) + { if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL)) - {out("ZTLS unable to load "); out(servercert.s); out("\n"); - zerodie();} + { + out("ZTLS unable to load "); out(servercert.s); out("\n"); + zerodie(); + } SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } if(!(ssl=SSL_new(ctx))) + { #ifdef DEBUG - {out("ZTLS not available: error initializing ssl"); + out("ZTLS not available: error initializing ssl"); out(": "); out(ERR_error_string(ERR_get_error(), buf)); #else - {out("ZTLS not available: error initializing ssl"); + out("ZTLS not available: error initializing ssl"); #endif out("\n"); - zerodie();} + zerodie(); + } SSL_set_fd(ssl,smtpfd); alarm(timeout); r = SSL_connect(ssl); saveerrno = errno; alarm(0); if (flagtimedout) - {out("ZTLS not available: connect timed out\n"); - zerodie();} + { + out("ZTLS not available: connect timed out\n"); + zerodie(); + } errno = saveerrno; if (r<=0) - {if (needtlsauth && (r=SSL_get_verify_result(ssl)) != X509_V_OK) - {out("ZTLS unable to verify server with "); + { + if (needtlsauth && (r=SSL_get_verify_result(ssl)) != X509_V_OK) + { + out("ZTLS unable to verify server with "); out(servercert.s); out(": "); out(X509_verify_cert_error_string(r)); out("\n");} else + { #ifdef DEBUG - {out("ZTLS not available: connect failed"); + out("ZTLS not available: connect failed"); out(": "); out(ERR_error_string(ERR_get_error(), buf)); - out("\n");} + out("\n"); #else out("ZTLS not available: connect failed\n"); #endif - zerodie();} + } + zerodie(); + } if (needtlsauth) /* should also check alternate names */ - {char commonName[256]; - X509_NAME_get_text_by_NID(X509_get_subject_name( - SSL_get_peer_certificate(ssl)), + { + char commonName[256]; + X509_NAME_get_text_by_NID(X509_get_subject_name(SSL_get_peer_certificate(ssl)), NID_commonName, commonName, 256); if (case_diffs(fqdn,commonName)){ out("ZTLS connection to "); out(fqdn); @@ -439,8 +454,10 @@ } } if ((!ssl) && needtlsauth) - {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); - zerodie();} + { + out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); + zerodie(); + } #endif substdio_puts(&smtpto,"MAIL FROM:<"); @@ -553,7 +570,7 @@ int flagalias; char *relayhost; -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) sig_alarmcatch(sigalrm); #endif sig_pipeignore(); @@ -637,7 +654,7 @@ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; -#ifdef TLS +#if defined(TLS) && defined(TLS_REMOTE) fqdn = ip.ix[i].fqdn; #endif smtp(); /* does not return */ diff -Naurb qmail-ldap-0802-i/qmail-smtpd.c qmail-la-mod/qmail-smtpd.c --- qmail-ldap-0802-i/qmail-smtpd.c Sun Aug 5 06:17:54 2001 +++ qmail-la-mod/qmail-smtpd.c Sun Aug 5 01:46:47 2001 @@ -1,4 +1,7 @@ #include "sig.h" +#include +#include +#include #include "readwrite.h" #include "stralloc.h" #include "substdio.h" @@ -17,6 +20,7 @@ #include "scan.h" #include "byte.h" #include "case.h" +#include "wait.h" #include "env.h" #include "now.h" #include "exit.h" @@ -32,13 +36,30 @@ SSL *ssl = NULL; stralloc clientcert = {0}; #endif +#ifdef USE_SMTPAUTH +#include "base64.h" +#define MAX_VALID_ADDRESSES 512 +static unsigned char authenticated=0; +static char **smtpauth_argv; +static char *auth_argv[4]; +static stralloc smtpauth = {0}; +static char smtpauthlogin[65]; +static char smtpauthpass[65]; +static char smtpauthtimestamp[65]; +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc validaddresses[MAX_VALID_ADDRESSES] = {0}; +int num_validaddresses = 0; +char authinfobuf[512]; +#endif #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; #ifdef TLS +int encrypted = 0; int flagtimedout = 0; + void sigalrm() { flagtimedout = 1; @@ -120,6 +141,12 @@ substdio_puts(subfderr," "); } +void logplain(level,string) int level; char *string; +{ + if (level > loglevel) return; + substdio_puts(subfderr,string); +} + void logflush(level) int level; { if (level > loglevel) return; @@ -130,6 +157,25 @@ void die_read() { logline(1,"read error, connection closed"); _exit(1); } void die_alarm() { out("451 timeout (#4.4.2)\r\n"); logline(1,"connection timed out, closing connection"); flush(); _exit(1); } void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); logline(1,"out of memory, closing connection"); flush(); _exit(1); } +void die_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); } +void die_fork() { out("421 unable to start checkpassword.\r\n"); flush(); _exit(1); } +#ifdef USE_SMTPAUTH +void die_userinfo() { out("421 unable to retrieve authorized addresses.\r\n"); flush(); _exit(1); } +void die_addressnotauth(char *addr) +{ + out("421 You are not authorized to relay for that address (#4.3.0)\r\n"); + logstring(2,"Authorized user"); logstring(2,smtpauthlogin); + logstring(2,"attempted to relay mail from unauthorized address"); + logline(2,addr); flush(); _exit(1); +} +void err_addressnotauth(char *addr) +{ + out("421 You are not authorized to relay for that address (#4.3.0)\r\n"); + logstring(2,"Authorized user"); logstring(2,smtpauthlogin); + logstring(2,"attempted to relay mail from unauthorized address"); + logline(2,addr); flush(); +} +#endif void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); logline(1,"unable to real controls, closing connection"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); logline(1,"unable to figure out my IP address, closing connection"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); logline(1,"stray new line detected, closing connection"); flush(); _exit(1); } @@ -154,6 +200,31 @@ void err_spam() { out("553 sorry, mail from your location is not accepted here (#5.7.1)\r\n"); } void err_badrcptto() { out("553 sorry, mail to that recipient is not accepted on this system (#5.7.1)\r\n"); } +#ifdef USE_SMTPAUTH + +#if defined(TLS) && defined(AUTH_REQUIRES_TLS) +/* this is the message advised by RFC 2554 */ +void err_authtls() { out(" 538 Encryption required for requested authentication mechanism(#5.3.8)\r\n"); logline("AUTH attempted without TLS"); } +#endif + +int is_authorized_address(char *s) +{ +#ifdef SMTPAUTH_LIMIT_RELAY + int i = 0; + + logpid(3);logplain(3,"Testing valid addresses against "); + logplain(3,s);logplain(3,"\n");logflush(3); + + for ( i = 0; i < sizeof(validaddresses) && validaddresses[i].s; i++) { + if (!str_diff(validaddresses[i].s,s)) + return 1; + } + return 0; +#else + return 1; +#endif +} +#endif /* USE_SMTPAUTH */ stralloc greeting = {0}; int brtok = 0; @@ -574,23 +645,21 @@ { smtp_greet("250-"); smtpsize[fmt_ulong(smtpsize,(unsigned long) databytes)] = 0; -#ifdef TLS - if (ssl) { out("\r\n250-PIPELINING\r\n"); - out("250-SIZE "); out(smtpsize); out("\r\n"); - out("250 8BITMIME\r\n"); - } - else { +#ifdef USE_SMTPAUTH +#ifdef USE_OLD_GREETING + out("250-AUTH=LOGIN PLAIN\r\n"); +#endif +#ifdef USE_NEW_GREETING + out("250-AUTH LOGIN PLAIN\r\n"); +#endif #endif - out("\r\n250-PIPELINING\r\n"); #ifdef TLS + if (!ssl) out("250-STARTTLS\r\n"); #endif out("250-SIZE "); out(smtpsize); out("\r\n"); out("250 8BITMIME\r\n"); -#ifdef TLS - } -#endif seenmail = 0; dohelo(arg); logpid(3); logstring(3,"remote ehlo ="); logstring(3,arg); logflush(3); logpid(3); logstring(3,"max msg size ="); logstring(3,smtpsize); logflush(3); @@ -625,6 +694,14 @@ return; } +#ifdef USE_SMTPAUTH + if (authenticated) + { + if (!is_authorized_address(addr.s)) die_addressnotauth(addr.s); + logstring(2,"Relaying for"); logstring(2,smtpauthlogin); + logstring(2,"with envelope sender"); logline(2,addr.s); + } +#endif /* Allow relaying based on envelope sender address */ if (!relayok) { @@ -1100,14 +1177,366 @@ remotehost = env_get("TCPREMOTEHOST"); if (!remotehost) remotehost = "unknown"; + encrypted = 1; dohelo(remotehost); } #endif +#ifdef USE_SMTPAUTH + +/* 5 minutes should be plenty for the auth program to write out valid + * addresses. + */ +int authinfo_timeout = 300; + +static int authinfo_read(int fd, char *buf, int len) +{ + int r; + char rstring[FMT_ULONG] = { 0 }; + rstring[FMT_ULONG-1] = 0; + logline(3,"Entered authinfo_read"); logflush(3); + r = timeoutread(authinfo_timeout, fd, buf, len); + fmt_uint(rstring,r); + logstring(3,"Return value is"); logline(3,rstring); + return r; +} + +static int smtpauth_getl(void) { + int i; + if (!stralloc_copys(&smtpauth, "")) return -1; + for (;;) { + if (!stralloc_readyplus(&smtpauth,1)) return -1; + i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1); + if (i != 1) return i; + if (smtpauth.s[smtpauth.len] == '\n') break; + ++smtpauth.len; + } + if (smtpauth.len > 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len; + smtpauth.s[smtpauth.len] = 0; + return smtpauth.len; +} + +static void smtpauth_authenticate(void) +{ + int st, pid, fds[2], userinfo[2]; + + if (pipe(fds)) { + out("535 pipe failure\r\n"); + flush(); + _exit(0); + } + + if (pipe(userinfo)) { + out("535 pipe failure\r\n"); + flush(); + _exit(0); + } + /* spawn external program + + external program should return '0' if it was successful, + + submit: /bin/checkpassword /bin/true + + */ + switch ((pid=fork())) { + case -1: die_fork(); + case 0: close(fds[1]); + close(userinfo[0]); + fd_copy(3,fds[0]); + fd_copy(4,userinfo[1]); + flush(); + execvp(auth_argv[1], auth_argv+1); + die_fork(); + }; + close(fds[0]); + close(userinfo[1]); + write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1); + write(fds[1], smtpauthpass, str_len(smtpauthpass)+1); + if (str_len(smtpauthtimestamp)) + { + write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1); + } + close(fds[1]); + { + /* read in user mail addresses from auth_smtp */ + substdio ssuserinfo; + int i; + char ch; + char *p; + char nread[FMT_ULONG + 1] = { 0 }; + substdio_fdbuf(&ssuserinfo,authinfo_read,userinfo[0],authinfobuf,sizeof(authinfobuf)); + for (i = 0; i < sizeof(validaddresses); i++) { + /* prevent premature memory allocation */ + if (!substdio_get(&ssuserinfo,&ch,1)) { + if (!i) i = -1; + goto OUT; + } + if (!stralloc_ready(&validaddresses[i],64)) die_nomem(); + if (!stralloc_append(&validaddresses[i],&ch)) die_nomem(); + for (;;) { + if (!substdio_get(&ssuserinfo,&ch,1)) { + if (!stralloc_0(&validaddresses[i])) die_nomem(); + goto OUT; + } + if (!stralloc_append(&validaddresses[i],&ch)) die_nomem(); + if (!ch) break; + } + } + OUT: + close(userinfo[0]); + if (i == sizeof(validaddresses)) { + logpid(2); logstring(2,"Max valid addresses for authenticated user"); + logline(2,smtpauthlogin); logflush(2); + } + if (i == -1) { + logpid(2); logstring(2,"No valid relaying addresses for authenticated user"); + logline(2,smtpauthlogin); logflush(2); + } + } + + wait_pid(&st, pid); + if (wait_crashed(st)) + die_crash(); + if (wait_exitcode(st) == 0) { + out("235 go ahead\r\n"); + flush(); + relayok=relayclient=""; + authenticated=1; + remoteinfo=smtpauthlogin; + return; + } + sleep(2); + out("535 auth failure\r\n"); + flush(); + return; +} + +void smtp_auth(arg) char *arg; { + int ret; + /* netscape 4.5 sends AUTH LOGIN + microsoft outlook express sends AUTH LOGIN + + idea is simple + + use an external program to test authority + if success, set 'RELAYCLIENT' + otherwise, let them know nicely (hangup) + + note, i really don't like djb's coding style even though i'm using it here. + i think using spaces for tabs is bad. + -mrs.brisby@nimh.org + */ + /* Here i've added support for other auth types. + + -brush@elysium.pl */ + if (!authenticated) + { +#if defined(TLS) && defined(AUTH_REQUIRES_TLS) + if (!encrypted) + { + err_authtls(); + return; + } +#endif + if ((ret=strncasecmp(arg,"login",5))==0) + { + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) { + /* here's the base64 encoded login */ + b64_pton(arg, smtpauthlogin, sizeof(smtpauthlogin)); + } else { + out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */ + flush(); + if (smtpauth_getl() > 0) + b64_pton(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin)); + else + die_read(); + } + out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */ + flush(); + if (smtpauth_getl() > 0) + b64_pton(smtpauth.s, smtpauthpass, sizeof(smtpauthpass)); + else + die_read(); + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"plain",5))==0) + { + int start; + static char smtpauthloginpass[200]; + + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) + { + if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } + /* here's the base64 encoded login/password */ + b64_pton(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } else { + int i; + out("334 ok. go on.\r\n"); + flush(); + i=smtpauth_getl(); + if(i <= 0) + die_read(); + else if(i*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } else b64_pton(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } + smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0; + start=strlen(smtpauthloginpass)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthlogin,smtpauthloginpass+start); + + start+=strlen(smtpauthlogin)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthpass,smtpauthloginpass+start); + + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"cram-md5",8))==0) + { + char *helper; + char *s; + int i; + static stralloc me = {0}; + static stralloc greet = {0}; + static stralloc greetenc = {0}; + i = control_readline(&me,"control/me"); + if (i != 1) + { + out("535 internal server error\r\n"); flush(); _exit(0); + } + for (i=0;i <= me.len;i++) + { + if (me.s[i]=='\n') + { + me.s[i]=0; + break; + } + } + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (greet.len) + { + for (i=0;i"); + greet.s[greet.len]=0; // obscure fix but it works + stralloc_readyplus(&greet,3); + if (greetenc.len) + { + for (i=0;i 0) + { + s=calloc((size_t) strlen(smtpauth.s),(size_t)1); + b64_pton(smtpauth.s, s, strlen(smtpauth.s)); + } + helper=strtok(s," "); + if(helper!=NULL) + { + strncpy (smtpauthlogin,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + helper=strtok(NULL," "); + if(helper!=NULL) + { + strncpy (smtpauthtimestamp,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + strncpy (smtpauthpass,greet.s,64); + auth_argv[1]=smtpauth_argv[3]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[4]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else + { + out("504 auth type not supported\r\n"); + flush(); + return; + } + } + else + { + out("503 you are already authenticated\r\n"); + flush(); + return; + } +} +#endif + struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +#ifdef USE_SMTPAUTH +, { "auth", smtp_auth, flush } +#endif , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } @@ -1121,8 +1550,11 @@ , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) int argc; char **argv; { +#ifdef USE_SMTPAUTH + smtpauth_argv = argv; +#endif #ifdef TLS sig_alarmcatch(sigalrm); #endif