diff -u -N qmail-1.03-patch20010201-orig/Makefile qmail-1.03-patch20010201/Makefile --- qmail-1.03-patch20010201-orig/Makefile Wed Feb 14 18:07:28 2001 +++ qmail-1.03-patch20010201/Makefile Wed Feb 14 18:44:50 2001 @@ -6,7 +6,7 @@ # -DQLDAP_CLUSTER for enabling cluster support # to use cleartext passwords (a bad idea on production systems) add # -DCLEARTEXTPASSWD to the LDAPFLAGS -#LDAPFLAGS=-DQLDAP_CLUSTER +LDAPFLAGS=-DQLDAP_CLUSTER -DSMTP_AFTER_POP -DMXPS -DDASH_EXT # Perhaps you have different ldap libraries, change them here LDAPLIBS=-L/usr/local/lib -lldap -llber @@ -20,26 +20,26 @@ # 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 +TLSINCLUDES=-I/usr/local/include # Path to OpenSSL libraries -#TLSLIBS=-L/usr/local/lib -lssl -lcrypto +TLSLIBS=-L/usr/local/lib -lssl -lcrypto # Path to OpenSSL binary -#OPENSSLBIN=/usr/local/bin/openssl +OPENSSLBIN=/usr/sbin/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. diff -u -N qmail-1.03-patch20010201-orig/auth_pop.c qmail-1.03-patch20010201/auth_pop.c --- qmail-1.03-patch20010201-orig/auth_pop.c Wed Feb 14 18:07:28 2001 +++ qmail-1.03-patch20010201/auth_pop.c Wed Feb 14 18:42:32 2001 @@ -15,6 +15,14 @@ #ifdef AUTOHOMEDIRMAKE #include "qldap-mdm.h" #endif +#ifdef SMTP_AFTER_POP +#include +#include + +int child; +int wstat; +char *opensmtp = "/usr/local/bin/pop3-record"; +#endif unsigned int auth_port; @@ -125,8 +133,22 @@ qldap_errno = AUTH_ERROR; auth_error(); } - - /* first set the group id */ + +#ifdef SMTP_AFTER_POP + if (!env_put2("AUTHUSER",login)) { + qldap_errno = ERRNO; + auth_error(); + } + + switch(child = fork()) { + case -1: _exit(111); break; + case 0: execl(opensmtp, opensmtp, 0); _exit(111); break; + } + waitpid(child, &wstat, 0); + if (!WIFEXITED(wstat)) _exit(111); +#endif + + /* first set the group id */ if (prot_gid(gid) == -1) { qldap_errno = AUTH_ERROR; auth_error(); diff -u -N qmail-1.03-patch20010201-orig/auth_pop.c.orig qmail-1.03-patch20010201/auth_pop.c.orig --- qmail-1.03-patch20010201-orig/auth_pop.c.orig Wed Feb 14 18:07:28 2001 +++ qmail-1.03-patch20010201/auth_pop.c.orig Wed Feb 14 18:07:28 2001 @@ -0,0 +1,340 @@ +/* auth_pop.c, jeker@n-r-g.com, best viewed with tabsize = 4 */ +#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" +#ifdef AUTOHOMEDIRMAKE +#include "qldap-mdm.h" +#endif + +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; + 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); + + 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; i uid || uid > UID_MAX ) { + debug(2, "warning: auth_success: uid (%u) is to big or small (%u < uid < %u)\n", + uid, UID_MIN, UID_MAX); + qldap_errno = AUTH_ERROR; + auth_error(); + } + + if ( GID_MIN > gid || gid > GID_MAX ) { + debug(2, "warning: auth_success: gid (%u) is to big or small (%u < gid < %u)\n", + gid, GID_MIN, GID_MAX); + qldap_errno = AUTH_ERROR; + auth_error(); + } + + /* first set the group id */ + if (prot_gid(gid) == -1) { + qldap_errno = AUTH_ERROR; + auth_error(); + } + debug(32, "auth_success: setgid succeeded (%i)\n", gid); + /* ... then the user id */ + if (prot_uid(uid) == -1) { + qldap_errno = AUTH_ERROR; + auth_error(); + } + debug(32, "auth_success: setuid succeeded (%i)\n", uid); + + /* ... go to home dir and create it if needed */ + if (chdir(home) == -1) { +#ifdef AUTOHOMEDIRMAKE + /* XXX homedirmake is not everywhere #ifdef'd because this would be too + * XXX hard. If you compile with a good compiler this should have the + * XXX same effect or you are probably loosing a few bytes of free mem + */ + if ( homedirmake && *homedirmake ) { + int ret; + + debug(8, "auth_success: makeing homedir with %s %s %s\n", + homedirmake, home, (md && *md)? md: argv[2] ); + if (md && *md) { + ret = make_homedir(home, md, homedirmake ); + } else { + ret = make_homedir(home, argv[2], homedirmake ); + } + if (ret != 0 ) { + if ( qldap_errno == ERRNO ) { + debug(2, "warning: auth_success: dirmaker failed (%s)\n", + error_str(errno)); + } else { + debug(2, "warning: auth_success: dirmaker failed (%s)\n", + qldap_errno == MAILDIR_CRASHED? "program crashed": + "bad exit status"); + } + qldap_errno = MAILDIR_CORRUPT; + auth_error(); + } + if (chdir(home) == -1) { + debug(2, + "warning: auth_success: chdir failed after dirmaker (%s)\n", + error_str(errno)); + qldap_errno = MAILDIR_CORRUPT; + auth_error(); + } + debug(32, "auth_success: homedir successfully made\n"); + } else { +#endif + qldap_errno = MAILDIR_CORRUPT; + auth_error(); +#ifdef AUTOHOMEDIRMAKE + } +#endif + } + + /* set up the environment for the execution of qmail-pop3d */ + if (!env_put2("USER",login)) { + qldap_errno = ERRNO; + auth_error(); + } + if (!env_put2("HOME",home)) { + qldap_errno = ERRNO; + auth_error(); + } + if ( md && *md ) { + if (!env_put2("MAILDIR",md)) { + qldap_errno = ERRNO; + auth_error(); + } + } else { + if ( !env_unset("MAILDIR") ) { + qldap_errno = ERRNO; + auth_error(); + } + } + + debug(32, "auth_success: environment successfully set: USER=%s, HOME=%s, MAILDIR=%s\n", + login, home, (md && *md)? md:"unset using aliasempty" ); + + /* start qmail-pop3d */ + /* ... now check that we are realy not running as root */ + if (!getuid()) { + qldap_errno = AUTH_ERROR; + auth_error(); + } + execvp( argv[1],argv + 1); + + qldap_errno = AUTH_EXEC; + auth_error(); + /* 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 + */ + debug(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); +} + +#ifdef QLDAP_CLUSTER + +static void get_ok(int fd) +/* get the ok for the next command, wait for "+OK.*\r\n" */ +/* XXX this could be a mostly correct solution (adapted from fetchmail) */ +{ +#define AUTH_TIMEOUT 10 /* 10 sec timeout */ +#define OK_LEN 512 /* max length of response (RFC1939) */ + char ok[OK_LEN]; + char *c; + int len; + int i; + + /* first get one single line from the other pop server */ + len = timeoutread(AUTH_TIMEOUT, fd, ok, OK_LEN); + if ( len == -1 ) { + /* OK an error occured, giving up */ + qldap_errno = ERRNO; + auth_error(); + } + if ( len != 0 ) { + c = ok; + if ( *c == '+' || *c == '-' ) { + c++; + } else { + qldap_errno = BADCLUSTER; /* BAD POP3 Protocol */ + auth_error(); + } + for ( i = 1; i < len /* paranoia */ && + ('A' < *c && *c < 'Z') ; ) { i++; c++; } + + if ( i < len ) { + *c = '\0'; + if ( str_diff(ok, "+OK") == 0 ) { + return; + } else if ( str_diffn(ok, "-ERR", 4) ) { + qldap_errno = BADCLUSTER; /* other server is not happy */ + auth_error(); + } + } + } + /* ARRG, very strange POP3 answer */ + qldap_errno = BADCLUSTER; + auth_error(); +} + +static int allwrite(op,fd,buf,len) +/* copied from substdo.c */ +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + register int w; + + while (len) { + w = op(fd,buf,len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +void auth_forward(int fd, char *login, char *passwd) +/* for connection forwarding, makes the login part and returns after sending the + * last command immidiatly so the user gets the possible error */ +{ + get_ok(fd); + allwrite(write, fd, "user ", 5); + allwrite(write, fd, login, str_len(login) ); + allwrite(write, fd, "\n\r", 1); + get_ok(fd); + allwrite(write, fd, "pass ", 5); + allwrite(write, fd, passwd, str_len(passwd) ); + allwrite(write, fd, "\n\r",1); + +} + +#endif /* QLDAP_CLUSTER */ + diff -u -N qmail-1.03-patch20010201-orig/auth_pop.c~ qmail-1.03-patch20010201/auth_pop.c~ --- qmail-1.03-patch20010201-orig/auth_pop.c~ Thu Jan 1 01:00:00 1970 +++ qmail-1.03-patch20010201/auth_pop.c~ Wed Feb 14 18:41:58 2001 @@ -0,0 +1,362 @@ +/* auth_pop.c, jeker@n-r-g.com, best viewed with tabsize = 4 */ +#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" +#ifdef AUTOHOMEDIRMAKE +#include "qldap-mdm.h" +#endif +#ifdef SMTP_AFTER_POP +#include +#include + +int child; +int wstat; +char *opensmtp = '/usr/local/bin/pop3-record'; +#endif + +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; + 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); + + 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; i uid || uid > UID_MAX ) { + debug(2, "warning: auth_success: uid (%u) is to big or small (%u < uid < %u)\n", + uid, UID_MIN, UID_MAX); + qldap_errno = AUTH_ERROR; + auth_error(); + } + + if ( GID_MIN > gid || gid > GID_MAX ) { + debug(2, "warning: auth_success: gid (%u) is to big or small (%u < gid < %u)\n", + gid, GID_MIN, GID_MAX); + qldap_errno = AUTH_ERROR; + auth_error(); + } + +#ifdef SMTP_AFTER_POP + if (!env_put2("AUTHUSER",login)) { + qldap_errno = ERRNO; + auth_error(); + } + + switch(child = fork()) { + case -1: _exit(111); break; + case 0: execl(opensmtp, opensmtp, 0); _exit(111); break; + } + waitpid(child, &wstat, 0); + if (!WIFEXITED(wstat)) _exit(111); +#endif + + /* first set the group id */ + if (prot_gid(gid) == -1) { + qldap_errno = AUTH_ERROR; + auth_error(); + } + debug(32, "auth_success: setgid succeeded (%i)\n", gid); + /* ... then the user id */ + if (prot_uid(uid) == -1) { + qldap_errno = AUTH_ERROR; + auth_error(); + } + debug(32, "auth_success: setuid succeeded (%i)\n", uid); + + /* ... go to home dir and create it if needed */ + if (chdir(home) == -1) { +#ifdef AUTOHOMEDIRMAKE + /* XXX homedirmake is not everywhere #ifdef'd because this would be too + * XXX hard. If you compile with a good compiler this should have the + * XXX same effect or you are probably loosing a few bytes of free mem + */ + if ( homedirmake && *homedirmake ) { + int ret; + + debug(8, "auth_success: makeing homedir with %s %s %s\n", + homedirmake, home, (md && *md)? md: argv[2] ); + if (md && *md) { + ret = make_homedir(home, md, homedirmake ); + } else { + ret = make_homedir(home, argv[2], homedirmake ); + } + if (ret != 0 ) { + if ( qldap_errno == ERRNO ) { + debug(2, "warning: auth_success: dirmaker failed (%s)\n", + error_str(errno)); + } else { + debug(2, "warning: auth_success: dirmaker failed (%s)\n", + qldap_errno == MAILDIR_CRASHED? "program crashed": + "bad exit status"); + } + qldap_errno = MAILDIR_CORRUPT; + auth_error(); + } + if (chdir(home) == -1) { + debug(2, + "warning: auth_success: chdir failed after dirmaker (%s)\n", + error_str(errno)); + qldap_errno = MAILDIR_CORRUPT; + auth_error(); + } + debug(32, "auth_success: homedir successfully made\n"); + } else { +#endif + qldap_errno = MAILDIR_CORRUPT; + auth_error(); +#ifdef AUTOHOMEDIRMAKE + } +#endif + } + + /* set up the environment for the execution of qmail-pop3d */ + if (!env_put2("USER",login)) { + qldap_errno = ERRNO; + auth_error(); + } + if (!env_put2("HOME",home)) { + qldap_errno = ERRNO; + auth_error(); + } + if ( md && *md ) { + if (!env_put2("MAILDIR",md)) { + qldap_errno = ERRNO; + auth_error(); + } + } else { + if ( !env_unset("MAILDIR") ) { + qldap_errno = ERRNO; + auth_error(); + } + } + + debug(32, "auth_success: environment successfully set: USER=%s, HOME=%s, MAILDIR=%s\n", + login, home, (md && *md)? md:"unset using aliasempty" ); + + /* start qmail-pop3d */ + /* ... now check that we are realy not running as root */ + if (!getuid()) { + qldap_errno = AUTH_ERROR; + auth_error(); + } + execvp( argv[1],argv + 1); + + qldap_errno = AUTH_EXEC; + auth_error(); + /* 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 + */ + debug(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); +} + +#ifdef QLDAP_CLUSTER + +static void get_ok(int fd) +/* get the ok for the next command, wait for "+OK.*\r\n" */ +/* XXX this could be a mostly correct solution (adapted from fetchmail) */ +{ +#define AUTH_TIMEOUT 10 /* 10 sec timeout */ +#define OK_LEN 512 /* max length of response (RFC1939) */ + char ok[OK_LEN]; + char *c; + int len; + int i; + + /* first get one single line from the other pop server */ + len = timeoutread(AUTH_TIMEOUT, fd, ok, OK_LEN); + if ( len == -1 ) { + /* OK an error occured, giving up */ + qldap_errno = ERRNO; + auth_error(); + } + if ( len != 0 ) { + c = ok; + if ( *c == '+' || *c == '-' ) { + c++; + } else { + qldap_errno = BADCLUSTER; /* BAD POP3 Protocol */ + auth_error(); + } + for ( i = 1; i < len /* paranoia */ && + ('A' < *c && *c < 'Z') ; ) { i++; c++; } + + if ( i < len ) { + *c = '\0'; + if ( str_diff(ok, "+OK") == 0 ) { + return; + } else if ( str_diffn(ok, "-ERR", 4) ) { + qldap_errno = BADCLUSTER; /* other server is not happy */ + auth_error(); + } + } + } + /* ARRG, very strange POP3 answer */ + qldap_errno = BADCLUSTER; + auth_error(); +} + +static int allwrite(op,fd,buf,len) +/* copied from substdo.c */ +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + register int w; + + while (len) { + w = op(fd,buf,len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +void auth_forward(int fd, char *login, char *passwd) +/* for connection forwarding, makes the login part and returns after sending the + * last command immidiatly so the user gets the possible error */ +{ + get_ok(fd); + allwrite(write, fd, "user ", 5); + allwrite(write, fd, login, str_len(login) ); + allwrite(write, fd, "\n\r", 1); + get_ok(fd); + allwrite(write, fd, "pass ", 5); + allwrite(write, fd, passwd, str_len(passwd) ); + allwrite(write, fd, "\n\r",1); + +} + +#endif /* QLDAP_CLUSTER */ + diff -u -N qmail-1.03-patch20010201-orig/qmail-lspawn.c qmail-1.03-patch20010201/qmail-lspawn.c --- qmail-1.03-patch20010201-orig/qmail-lspawn.c Wed Feb 14 18:07:28 2001 +++ qmail-1.03-patch20010201/qmail-lspawn.c Wed Feb 14 18:41:30 2001 @@ -347,6 +347,9 @@ int reply; int at; int i; +#ifdef DASH_EXT + int dash; +#endif int force_forward; char *r; stralloc filter = {0}; @@ -397,37 +400,76 @@ ret = ldap_lookup(&search, attrs, &info, extra); if ( ret != 0 && qldap_errno == LDAP_NOSUCH ) { - /* this handles the "catch all" extension */ + /* extensions: catchall and dash-trick */ at = 0; r = mail->s; i = mail->len; for (at = i - 1; r[at] != '@' && at >= 0 ; at--) ; /* handels also mailwith 2 @ */ - /* build the search string for the email address */ - if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); - if (!stralloc_copys(&filter,"(|(" ) ) _exit(QLX_NOMEM); - /* optional objectclass */ - if (qldap_objectclass.len) { - if (!stralloc_cats(&filter,LDAP_OBJECTCLASS)) _exit(QLX_NOMEM); + +#ifdef DASH_EXT + dash=0; + for (dash = at-1; r[dash] != '-' && dash > 0; dash--); + + /* dash trick */ + if ((dash > 0) && (dash < at)) { + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + if (!stralloc_copys(&filter,"(|(" ) ) _exit(QLX_NOMEM); + /* optional objectclass */ + if (qldap_objectclass.len) { + 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,LDAP_MAIL)) _exit(QLX_NOMEM); if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); - if (!stralloc_cat(&filter,qldap_objectclass)) _exit(QLX_NOMEM); + if (!stralloc_catb(&filter,r,dash)) _exit(QLX_NOMEM); + if (!stralloc_catb(&filter,r+at, i-at)) _exit(QLX_NOMEM); if (!stralloc_cats(&filter,")(")) _exit(QLX_NOMEM); - } /* end */ - 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 (!stralloc_0(&filter)) _exit(QLX_NOMEM); + if (!stralloc_cats(&filter,LDAP_MAILALTERNATE)) _exit(QLX_NOMEM); + if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); + if (!stralloc_catb(&filter,r,dash)) _exit(QLX_NOMEM); + if (!stralloc_catb(&filter,r+at, i-at)) _exit(QLX_NOMEM); + if (!stralloc_cats(&filter,"))")) _exit(QLX_NOMEM); + if (!stralloc_0(&filter)) _exit(QLX_NOMEM); + + debug(16, "retry with filter '%s'\n", filter.s); + /* do the search */ + ret = ldap_lookup(&search, attrs, &info, extra); + } + + if (ret != 0 && qldap_errno == LDAP_NOSUCH) { +#endif + /* catchall */ + /* build the search string for the email address */ + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + if (!stralloc_copys(&filter,"(|(" ) ) _exit(QLX_NOMEM); + /* optional objectclass */ + if (qldap_objectclass.len) { + 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,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 (!stralloc_0(&filter)) _exit(QLX_NOMEM); - debug(16, "retry with filter '%s'\n", filter.s); - /* do the search for the catchall address */ - ret = ldap_lookup(&search, attrs, &info, extra); + debug(16, "retry with filter '%s'\n", filter.s); + /* do the search for the catchall address */ + ret = ldap_lookup(&search, attrs, &info, extra); +#ifdef DASH_EXT + } +#endif } alloc_free(filter.s); filter.s = 0; if ( ret != 0 ) { @@ -517,11 +559,23 @@ } if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); - /* At the moment we ignore the dash-field and the extension field * - * so we fill up the nughde structure with '\0' */ - - if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); - if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); +#ifdef DASH_EXT + /* Here we fill the nughde structure with the dash-field the extension field */ + if ((dash > 0) && (dash < (at-1))) { + if (!stralloc_cats(&nughde,"-")) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_catb(&nughde,r+dash+1,at-dash-1)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + + } else { +#endif + + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + +#ifdef DASH_EXT + } +#endif /* get the quota for the user of that maildir mbox */ if ( extra[0].vals != 0 ) { diff -u -N qmail-1.03-patch20010201-orig/qmail-lspawn.c.orig qmail-1.03-patch20010201/qmail-lspawn.c.orig --- qmail-1.03-patch20010201-orig/qmail-lspawn.c.orig Mon Jun 15 12:53:16 1998 +++ qmail-1.03-patch20010201/qmail-lspawn.c.orig Wed Feb 14 18:07:28 2001 @@ -14,14 +14,104 @@ #include "auto_uids.h" #include "qlx.h" +#include "qmail-ldap.h" +#include "qldap-ldaplib.h" +#include "qldap-errno.h" +#include "qldap-debug.h" +#include "alloc.h" +#include "env.h" +#include "fmt.h" +#include "check.h" +#include "sig.h" +#include "auto_usera.h" +#include "auto_uids.h" +#include "byte.h" +#include "open.h" +#include "readwrite.h" +#include "str.h" +#include +#include +#ifdef QLDAP_CLUSTER +#include "seek.h" +#include "getln.h" +#endif + char *aliasempty; +/* initialize the string arrays, this uses DJB's libs */ +extern stralloc qldap_me; +extern stralloc qldap_objectclass; +stralloc qldap_defdotmode = {0}; +stralloc qldap_defaultquota = {0}; +stralloc qldap_quotawarning = {0}; +stralloc qldap_dirmaker = {0}; +int qldap_localdelivery; +int qldap_cluster; + +stralloc foo = {0}; + +/* init done */ + +#ifdef QLDAP_CLUSTER +static int allwrite(op,fd,buf,len) +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + register int w; + + while (len) { + w = op(fd,buf,len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +/* declaration of the mail forwarder function */ +void forward_mail(char *host, stralloc *to, char *from, int fdmess); +#endif + +/* this is a simple wrapper for the signal handler */ +void get_qldap_controls() +{ + if ( init_ldap( &qldap_localdelivery, &qldap_cluster, 0, &qldap_dirmaker, + &qldap_defdotmode, &qldap_defaultquota, &qldap_quotawarning ) == -1 ) + _exit(1); + + if ( qldap_dirmaker.len != 0 ) { + if ( !env_put2(ENV_HOMEDIRMAKE, qldap_dirmaker.s )) _exit(QLX_NOMEM); + } else { + if ( !env_unset(ENV_HOMEDIRMAKE) ) _exit(QLX_NOMEM); + } + + if ( qldap_quotawarning.len != 0 ) { + if ( !env_put2(ENV_QUOTAWARNING, qldap_quotawarning.s )) _exit(QLX_NOMEM); + } else { + if ( !env_unset(ENV_QUOTAWARNING) ) _exit(QLX_NOMEM); + } +} + +/* here it is not possible to log something */ void initialize(argc,argv) int argc; char **argv; { - aliasempty = argv[1]; - if (!aliasempty) _exit(100); + aliasempty = argv[1]; + if (!aliasempty) { + _exit(100); + } + + /* read the control files */ + get_qldap_controls(); + sig_hangupcatch(get_qldap_controls); + sig_hangupunblock(); } int truncreport = 3000; @@ -32,58 +122,537 @@ char *s; int len; { +#ifdef DEBUG +#define REPORT_RETURN for (i = 0;i < len;++i) if (!s[i]) break; substdio_put(ss,s,i); return +#else +#define REPORT_RETURN return +#endif int i; - if (wait_crashed(wstat)) - { substdio_puts(ss,"Zqmail-local crashed.\n"); return; } - switch(wait_exitcode(wstat)) - { + if (wait_crashed(wstat)) { + substdio_puts(ss,"Zqmail-local crashed.\n"); + REPORT_RETURN; + } + switch(wait_exitcode(wstat)) { case QLX_CDB: - substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); return; + substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); + REPORT_RETURN; + case QLX_NOMEM: - substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); return; + substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); + REPORT_RETURN; + case QLX_SYS: - substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); return; + substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); + REPORT_RETURN; + case QLX_NOALIAS: - substdio_puts(ss,"ZUnable to find alias user!\n"); return; + substdio_puts(ss,"ZUnable to find alias user!\n"); + REPORT_RETURN; + case QLX_ROOT: - substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); return; + substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); + REPORT_RETURN; + case QLX_USAGE: - substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); return; + substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); + REPORT_RETURN; + case QLX_NFS: - substdio_puts(ss,"ZNFS failure in qmail-local.\n"); return; + substdio_puts(ss,"ZNFS failure in qmail-local.\n"); + REPORT_RETURN; + case QLX_EXECHARD: - substdio_puts(ss,"DUnable to run qmail-local.\n"); return; + substdio_puts(ss,"DUnable to run qmail-local.\n"); + REPORT_RETURN; + case QLX_EXECSOFT: - substdio_puts(ss,"ZUnable to run qmail-local.\n"); return; + substdio_puts(ss,"ZUnable to run qmail-local.\n"); + REPORT_RETURN; + case QLX_EXECPW: - substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); return; + substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); + REPORT_RETURN; + case 111: case 71: case 74: case 75: - substdio_put(ss,"Z",1); break; + substdio_put(ss,"Z",1); + break; + case 0: - substdio_put(ss,"K",1); break; + substdio_put(ss,"K",1); + break; + + /* report LDAP errors */ + case 198: /* XXX */ + substdio_puts(ss, "DInternal qmail-ldap-lspawn bug. (LDAP-ERR #198)\n"); + REPORT_RETURN; + + case 199: /* XXX */ + substdio_puts(ss, "ZMissing ~control/ldapserver. (LDAP-ERR #199)\n"); + REPORT_RETURN; + + case 200: /* XXX */ + substdio_puts(ss, "DReceipient email address is not a valid email address. (LDAP-ERR #200)\n"); + REPORT_RETURN; + + case 201: + substdio_puts(ss, "DInternal error initializing LDAP structure (LDAP-ERR #201).\n"); + REPORT_RETURN; + + case 202: /* XXX */ + substdio_puts(ss, "DInternal error in ldap_set_option. (LDAP-ERR #202)\n"); + REPORT_RETURN; + + case 203: + substdio_puts(ss, "ZUnable to login into LDAP server. (bad username/password?). (LDAP-ERR #203)\n"); + REPORT_RETURN; + + case 204: /* XXX */ + substdio_puts(ss, "DInternal error in ldap_search_ext_s. (LDAP-ERR #204)\n"); + REPORT_RETURN; + + case 205: + substdio_puts(ss, "ZUnable to contact LDAP server (bad server address or server down?). (LDAP-ERR #205)"); + REPORT_RETURN; + + case 210: + substdio_puts(ss, "DLDAP attribute qmailUser contains illegal characters. (LDAP-ERR #210)\n"); + REPORT_RETURN; + + case 211: + substdio_puts(ss, "DLDAP attribute qmailUID is too high/low or not numeric. (LDAP-ERR #211)\n"); + REPORT_RETURN; + + case 212: + substdio_puts(ss, "DLDAP attribute qmailGID is too high/low or not numeric. (LDAP-ERR #212)\n"); + REPORT_RETURN; + + case 213: + substdio_puts(ss, "DLDAP attribute mailMessageStore contains illegal characters. (LDAP-ERR #213)\n"); + REPORT_RETURN; + + case 214: /* XXX */ + substdio_puts(ss, "ZLDAP attribute mailMessageStore in ~control/ldapmessagestore contains illegal characters. (LDAP-ERR #214)\n"); + REPORT_RETURN; + + case 215: /* XXX */ + substdio_puts(ss, "DLDAP attribute mailMessageStore is not given but mandatory. (LDAP-ERR #215)\n"); + REPORT_RETURN; + + case 220: /* XXX */ + substdio_puts(ss, "DLDAP attribute mailForwardingAddress contains illegal characters. (LDAP-ERR #220)\n"); + REPORT_RETURN; + + case 221: /* XXX */ + substdio_puts(ss, "DLDAP attribute deliveryProgramPath contains illegal characters. (LDAP-ERR #221)\n"); + REPORT_RETURN; + + case 222: /* XXX */ + substdio_puts(ss, "ZError while reading ~control files. (LDAP-ERR #222)\n"); + REPORT_RETURN; + + case 225: + substdio_puts(ss, "DMailaddress is administrativley disabled. (LDAP-ERR #220)\n"); + REPORT_RETURN; + + case 230: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapusername is missing/empty and LDAP qmailUser is not given. (LDAP-ERR #230)\n"); + REPORT_RETURN; + + case 231: + substdio_puts(ss, "ZConfiguration file ~control/ldapusername contains illegal characters. (LDAP-ERR #231)\n"); + REPORT_RETURN; + + case 232: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapuid is missing/empty and LDAP qmailUID is not given. (LDAP-ERR #232)\n"); + REPORT_RETURN; + + case 233: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapuid is too high/low or not numeric. (LDAP-ERR #233)\n"); + REPORT_RETURN; + + case 234: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapgid is missing/empty and LDAP qmailGID is not given. (LDAP-ERR #234)\n"); + REPORT_RETURN; + + case 235: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapgid is too high/low or not numeric. (LDAP-ERR #235)\n"); + REPORT_RETURN; + + case 236: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapmessagestore does not begin with an / or is emtpy. (LDAP-ERR #236)\n"); + REPORT_RETURN; + + case 237: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapmessagestore does not end with an / or is empty. (LDAP-ERR #237)\n"); + REPORT_RETURN; + + case 238: + substdio_puts(ss, "Zqmail-qmqpc (as mail forwarder) crashed (LDAP-ERR #238)\n"); + REPORT_RETURN; + +#ifdef QLDAP_CLUSTER + case 239: + substdio_puts(ss, "ZTemporary error in qmail-qmqpc (as mail forwarder) (LDAP-ERR #239)\n"); + REPORT_RETURN; + + case 240: + substdio_puts(ss, "DPermanet error in qmail-qmqpc (as mail forwarder) (LDAP-ERR #240)\n"); + REPORT_RETURN; + + case 241: + substdio_puts(ss, "DThis message is looping: it already has my Delivered-To line. (LDAP-ERR #241 CLUSTERLOOP)\n"); + REPORT_RETURN; +#endif /* QLDAP_CLUSTER */ +/* end -- report LDAP errors */ + case 100: default: - substdio_put(ss,"D",1); break; + substdio_put(ss,"D",1); + break; } - for (i = 0;i < len;++i) if (!s[i]) break; - substdio_put(ss,s,i); + for (i = 0;i < len;++i) + if (!s[i]) + break; + + substdio_put(ss,s,i); } -stralloc lower = {0}; + stralloc nughde = {0}; + +/* LDAP server query routines */ + +int qldap_get( stralloc *mail, char *from, int fdmess) +{ + userinfo info; + extrainfo extra[7]; + searchinfo search; + char *attrs[] = { /* LDAP_MAIL, */ /* not needed */ + /* LDAP_MAILALTERNATE, */ + LDAP_UID, /* the first 6 attrs are the default ones */ + LDAP_QMAILUID, + LDAP_QMAILGID, + LDAP_ISACTIVE, + LDAP_MAILHOST, + LDAP_MAILSTORE, + LDAP_HOMEDIR, + LDAP_QUOTA, /* the last 6 are extra infos */ + LDAP_FORWARDS, + LDAP_PROGRAM, + LDAP_MODE, + LDAP_REPLYTEXT, + LDAP_DOTMODE, 0 }; + int ret; + int reply; + int at; + int i; + int force_forward; + char *r; + stralloc filter = {0}; + unsigned long tid; + + /* check the mailaddress for illegal characters * + * escape '*', ,'\', '(' and ')' with a preceding '\' */ + if (!escape_forldap(mail) ) _exit(QLX_NOMEM); + + /* 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); + + debug(16, "ldapfilter: '%s'\n", filter.s); + search.filter = filter.s; + search.bindpw = 0; /* rebind off */ + + /* initalize the different objects */ + extra[0].what = LDAP_QUOTA; + extra[1].what = LDAP_FORWARDS; + extra[2].what = LDAP_PROGRAM; + extra[3].what = LDAP_MODE; + extra[4].what = LDAP_REPLYTEXT; + extra[5].what = LDAP_DOTMODE; + extra[6].what = 0; + + /* do the search for the email address */ + ret = ldap_lookup(&search, attrs, &info, extra); + + 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 mailwith 2 @ */ + /* build the search string for the email address */ + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + if (!stralloc_copys(&filter,"(|(" ) ) _exit(QLX_NOMEM); + /* optional objectclass */ + if (qldap_objectclass.len) { + 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,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 (!stralloc_0(&filter)) _exit(QLX_NOMEM); + + debug(16, "retry with filter '%s'\n", filter.s); + /* do the search for the catchall address */ + ret = ldap_lookup(&search, attrs, &info, extra); + } + alloc_free(filter.s); filter.s = 0; + if ( ret != 0 ) { + switch(qldap_errno) { + case LDAP_INIT: + return 11; + break; + case LDAP_BIND: + return 13; + break; + case LDAP_BIND_UNREACH: + return 15; + break; + default: + return 1; + break; + } + return 1; /* just in case... */ + } + + /* go through the attributes and set the proper args for qmail-local * + * this can probably done with some sort of loop, but hey, how cares? */ + debug(32, "found: user='%s' uid=%s gid=%s homedir='%s' mms='%s' host='%s' status=%i\n", + info.user, info.uid, info.gid, info.homedir, + info.mms, info.host, info.status); + + /* check if the ldap entry is active */ + if ( info.status == STATUS_BOUNCE ) { + debug(2, "warning: %s's accountsatus is bounce\n", info.user); + _exit(225); + } + +#ifdef QLDAP_CLUSTER + /* check if the I'm the right host */ + if ( qldap_cluster && info.host && str_diff(qldap_me.s, info.host) ) { + /* hostname is different, so I reconnect */ + forward_mail(info.host, mail, from, fdmess); + /* that's it. Function does not return */ + } +#endif + + if (!chck_users(info.user) ) return 20; + /* set the value for qmail-local... */ + if (!stralloc_copys(&nughde, info.user) ) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + alloc_free(info.user); + + /* get the UID for delivery on the local system */ + scan_ulong(info.uid, &tid); + if (UID_MIN > tid || tid > UID_MAX ) return 21; + if (!stralloc_cats(&nughde, info.uid)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + alloc_free(info.uid); + + /* get the GID for delivery on the local system */ + scan_ulong(info.gid, &tid); + if (GID_MIN > tid || tid > GID_MAX ) return 22; + if (!stralloc_cats(&nughde, info.gid)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + alloc_free(info.gid); + + /* get the path of the maildir or mbox */ + force_forward = 0; + if ( info.homedir ) { + if (!chck_paths(info.homedir) ) return 23; + if (!stralloc_cats(&nughde, info.homedir)) _exit(QLX_NOMEM); + alloc_free(info.homedir); + if ( info.mms ) { + 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 */ + struct passwd *pw; + pw = getpwnam(auto_usera); + if (!pw) { + _exit(QLX_NOALIAS); + } + if (!stralloc_cats(&nughde, pw->pw_dir)) _exit(QLX_NOMEM); + aliasempty = ALIASDEVNULL; + force_forward = 1; + } + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + + /* At the moment we ignore the dash-field and the extension field * + * so we fill up the nughde structure with '\0' */ + + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + + /* get the quota for the user of that maildir mbox */ + if ( extra[0].vals != 0 ) { + debug(32, "%s: %s\n", ENV_QUOTA, extra[0].vals[0]); + if ( !env_put2(ENV_QUOTA, extra[0].vals[0] ) ) _exit(QLX_NOMEM); + } else { + if ( qldap_defaultquota.s ) { + debug(32, "%s: %s\n", ENV_QUOTA, qldap_defaultquota.s); + if ( !env_put2(ENV_QUOTA, qldap_defaultquota.s )) _exit(QLX_NOMEM); + } else { + debug(32, "no quota set\n"); + if ( !env_unset(ENV_QUOTA) ) _exit(QLX_NOMEM); + } + } + ldap_value_free(extra[0].vals); + + /* get the forwarding addresses and build a list * + * equals to &jdoe@heaven.af.mil in .qmail */ + if ( extra[1].vals != 0 ) { + if (!stralloc_copys(&foo, "")) _exit(QLX_NOMEM); + for ( i = 0; extra[1].vals[i] != 0; i++ ) { + if (!stralloc_cats(&foo, extra[1].vals[i])) _exit(QLX_NOMEM); + if (extra[1].vals[i+1] == 0 ) break; + if (!stralloc_cats(&foo, ",") ) _exit(QLX_NOMEM); + } + if (!stralloc_0(&foo) ) _exit(QLX_NOMEM); + debug(32, "%s: %s\n", ENV_FORWARDS, foo.s ); + if ( !env_put2(ENV_FORWARDS, foo.s) ) _exit(QLX_NOMEM); + } else { + /* default */ + if ( !env_unset(ENV_FORWARDS) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[1].vals); + + /* get the path of the local delivery program * + * equals to |/usr/bin/program in .qmail */ + if ( extra[2].vals != 0 ) { + if (!stralloc_copys(&foo, "")) _exit(QLX_NOMEM); + for ( i = 0; extra[2].vals[i] != 0; i++ ) { + /* append */ + if (!chck_progs(extra[2].vals[i]) ) return 31; /* XXX */ + if (!stralloc_cats(&foo, extra[2].vals[i])) _exit(QLX_NOMEM); + if (extra[2].vals[i+1] == 0 ) break; + if (!stralloc_cats(&foo, ",") ) _exit(QLX_NOMEM); + } + if (!stralloc_0(&foo) ) _exit(QLX_NOMEM); + debug(32, "%s: %s\n", ENV_PROGRAM, foo.s ); + if ( !env_put2(ENV_PROGRAM, foo.s) ) _exit(QLX_NOMEM); + } else { + /* default */ + if ( !env_unset(ENV_PROGRAM) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[2].vals); + + /* get the deliverymode of the mailbox: * + * reply, echo, forwardonly, normal, nombox, localdelivery */ + reply = 0; + if ( extra[3].vals != 0 ) { + if (!stralloc_copys(&foo, "")) _exit(QLX_NOMEM); + for ( i = 0; extra[3].vals[i] != 0; i++ ) { + /* append */ + case_lowers(extra[3].vals[i]); + if ( !str_diff(MODE_REPLY, extra[3].vals[i]) ) reply = 1; + if (!stralloc_cats(&foo, extra[3].vals[i])) _exit(QLX_NOMEM); + if (extra[3].vals[i+1] == 0 ) break; + if (!stralloc_cats(&foo, ",") ) _exit(QLX_NOMEM); + } + if (!stralloc_0(&foo) ) _exit(QLX_NOMEM); + debug(32, "%s: %s\n", ENV_MODE, foo.s ); + if ( !env_put2(ENV_MODE, foo.s) ) _exit(QLX_NOMEM); + } else { + /* default */ + if ( !env_unset(ENV_MODE) ) _exit(QLX_NOMEM); + if ( !env_unset(ENV_REPLYTEXT) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[3].vals); + + if ( reply ) { + if ( extra[4].vals != 0 ) { + debug(32, "%s: %s\n", ENV_REPLYTEXT, extra[4].vals[0] ); + if ( !env_put2(ENV_REPLYTEXT, extra[4].vals[0]) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[4].vals); + } + + /* get the mode of the .qmail interpretion: ldaponly, dotonly, both, none */ + if ( extra[5].vals != 0 ) { + case_lowers(extra[5].vals[0]); + if ( !str_diff(DOTMODE_LDAPONLY, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_LDAPONLY) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_LDAPWITHPROG, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_LDAPWITHPROG) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_DOTONLY, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_DOTONLY) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_BOTH, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_BOTH) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_NONE, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_NONE) ) _exit(QLX_NOMEM); + } else { + if ( !env_put2(ENV_DOTMODE, qldap_defdotmode.s) ) _exit(QLX_NOMEM); + } + } else { + /* default */ + if ( !env_put2(ENV_DOTMODE, qldap_defdotmode.s) ) _exit(QLX_NOMEM); + } + debug(32, "%s: %s\n", ENV_DOTMODE, env_get(ENV_DOTMODE) ); + ldap_value_free(extra[5].vals); + + if ( force_forward ) { + /* XXX forcing forward only for useres with no homedir */ + if ( !env_put2(ENV_DOTMODE, DOTMODE_LDAPONLY) ) _exit(QLX_NOMEM); + if ( !env_put2(ENV_MODE, MODE_FORWARD) ) _exit(QLX_NOMEM); + } + /* ok, we finished, lets clean up and disconnect from the LDAP server */ + return 0; +} +/* end -- LDAP server query routines */ + +stralloc lower = {0}; stralloc wildchars = {0}; void nughde_get(local) char *local; { char *(args[3]); - int pi[2]; - int gpwpid; - int gpwstat; - int r; - int fd; - int flagwild; + int pi[2], + gpwpid, + gpwstat, + r, + fd, + flagwild; if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM); if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM); @@ -97,8 +666,7 @@ if (errno != error_noent) _exit(QLX_CDB); - if (fd != -1) - { + if (fd != -1) { uint32 dlen; unsigned int i; @@ -111,20 +679,17 @@ i = lower.len; flagwild = 0; - do - { + do { /* i > 0 */ - if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) - { + if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) { r = cdb_seek(fd,lower.s,i,&dlen); if (r == -1) _exit(QLX_CDB); - if (r == 1) - { + if (r == 1) { if (!stralloc_ready(&nughde,(unsigned int) dlen)) _exit(QLX_NOMEM); nughde.len = dlen; if (cdb_bread(fd,nughde.s,nughde.len) == -1) _exit(QLX_CDB); if (flagwild) - if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM); if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); close(fd); return; @@ -132,8 +697,7 @@ } --i; flagwild = 1; - } - while (i); + } while (i); close(fd); } @@ -142,10 +706,10 @@ args[0] = "bin/qmail-getpw"; args[1] = local; args[2] = 0; - switch(gpwpid = vfork()) - { + switch(gpwpid = vfork()) { case -1: _exit(QLX_SYS); + case 0: if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE); if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE); @@ -158,8 +722,7 @@ if (slurpclose(pi[0],&nughde,128) == -1) _exit(QLX_SYS); - if (wait_pid(&gpwstat,gpwpid) != -1) - { + if (wait_pid(&gpwstat,gpwpid) != -1) { if (wait_crashed(gpwstat)) _exit(QLX_SYS); if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat)); } @@ -171,54 +734,128 @@ { int f; - if (!(f = fork())) - { + if (!(f = fork())) { char *(args[11]); unsigned long u; - int n; - int uid; - int gid; + int n, + uid, + gid; char *x; unsigned int xlen; + + stralloc ra = {0}; + int rv; + + /* XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX + * DEBUG should be handled better, so that it will not be included + * in maildelivery-failures mails */ + init_debug(fdout, -1); /* here are no critical data handled + * so debuglevel is free */ + sig_hangupdefault(); /* clear the hup sig handler for the child */ + + /* copy the whole email address before the @ gets destroyed */ + if (!stralloc_copys(&ra,r)) _exit(QLX_NOMEM); + debug(16, "mailaddr: %S\n", &ra); + /* end -- save the @ */ + r[at] = 0; if (!r[0]) _exit(0); /* <> */ if (chdir(auto_qmail) == -1) _exit(QLX_USAGE); - nughde_get(r); + /* do the address lookup */ + rv = qldap_get(&ra, s, fdmess); + switch( rv ) { + case 0: + debug(16, "LDAP lookup succeeded\n"); + break; + + case 1: + if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM); + if ( qldap_localdelivery == 1 ) { + /* do the address lookup local */ + /* this is the standart qmail lookup funktion */ + debug(4, "LDAP lookup failed using local db\n"); + nughde_get(r); + + /* the alias-user handling for LDAP only mode */ + } else { + struct passwd *pw; + char num[FMT_ULONG]; + + debug(4, "LDAP lookup failed using alias (no local db)\n"); + pw = getpwnam(auto_usera); + if (!pw) { + _exit(QLX_NOALIAS); + } + + if (!stralloc_copys(&nughde, pw->pw_name)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_catb(&nughde,num,fmt_ulong(num, (long) pw->pw_uid))) + _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_catb(&nughde,num,fmt_ulong(num, (long) pw->pw_gid))) + _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde, pw->pw_dir)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde,"-")) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde,r)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + } + /* end -- alias-user handling */ + break; + + default: + debug(2, "warning: ldap lookup failed with %i\n", rv); + _exit(190 + rv); + break; + } /* end switch */ + /* debug(16, "nughde: %S\n", &nughde); */ x = nughde.s; xlen = nughde.len; args[0] = "bin/qmail-local"; args[1] = "--"; args[2] = x; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; scan_ulong(x,&u); uid = u; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; scan_ulong(x,&u); gid = u; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; args[3] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; args[4] = r; + args[5] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; args[6] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; args[7] = r + at + 1; args[8] = s; args[9] = aliasempty; args[10] = 0; + debug(8, "executing 'qmail-local -- %s %s %s %s %s %s %s %s' under uid=%i, gid=%i\n", + args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], + uid, gid); + if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); if (fd_move(1,fdout) == -1) _exit(QLX_SYS); if (fd_copy(2,1) == -1) _exit(QLX_SYS); @@ -232,3 +869,94 @@ } return f; } + + +#ifdef QLDAP_CLUSTER +stralloc dtline = {0}; + +void bouncexf(int fdmess) +{ + char buf[1024]; + int match; + substdio ss; + + if (seek_begin(fdmess) == -1) _exit(QLX_SYS); + substdio_fdbuf(&ss,read,fdmess,buf,sizeof(buf)); + for (;;) + { + if (getln(&ss,&foo,&match,'\n') != 0) _exit(QLX_SYS); + if (!match) break; + if (foo.len <= 1) + break; + if (foo.len == dtline.len) + if (!str_diffn(foo.s,dtline.s,dtline.len)) + _exit(241); + } +} + +void forward_mail(char *host, stralloc *to, char* from, int fdmess) +{ + char *(args[3]); + int pi[2]; + int wstat; + int child; + int i; + + if (!stralloc_copys(&dtline, "Delivered-To: CLUSTERHOST ")) _exit(QLX_NOMEM); + if (!stralloc_catb(&dtline, qldap_me.s, qldap_me.len - 1 )) _exit(QLX_NOMEM); + if (!stralloc_cats(&dtline, " ")) _exit(QLX_NOMEM); + if (!stralloc_cat(&dtline, to)) _exit(QLX_NOMEM); + for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_'; + if (!stralloc_cats(&dtline,"\n")) _exit(QLX_NOMEM); + + bouncexf(fdmess); + + if (seek_begin(fdmess) == -1) _exit(QLX_SYS); + if (pipe(pi) == -1) _exit(QLX_SYS); + + switch( child = fork() ) { + case -1: + if (error_temp(errno)) _exit(QLX_EXECSOFT); + _exit(QLX_EXECHARD); + case 0: + close(pi[1]); + if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); + if (fd_move(1,pi[0]) == -1) _exit(QLX_SYS); + args[0]="bin/qmail-qmqpc"; args[1]=host; args[2]=0; + sig_pipedefault(); + execv(*args,args); + _exit(QLX_EXECHARD); + } + + debug(8, "Forwarding to %S at host %s from %s ", to, host, from); + close(pi[0]); + allwrite(write, pi[1], "F", 1); + allwrite(write, pi[1], from, str_len(from)); + allwrite(write, pi[1], "",1); + allwrite(write, pi[1], "T",1); + allwrite(write, pi[1], to->s, to->len); + allwrite(write, pi[1], "", 1); + allwrite(write, pi[1], "", 1); + allwrite(write, pi[1], "H",1); + allwrite(write, pi[1], qldap_me.s, qldap_me.len); + allwrite(write, pi[1], "", 1); + close(pi[1]); + wait_pid(&wstat,child); + if (wait_crashed(wstat)) { + _exit(238); + } + + switch(i=wait_exitcode(wstat)) { + case 0: + debug(8, "was successful\n"); + _exit(0); + case 31: case 61: + debug(8, "failed (hard error %i)/n", i); + _exit(240); + default: + debug(8, "failed (soft error %i)/n", i); + _exit(239); + } +} +#endif + diff -u -N qmail-1.03-patch20010201-orig/qmail-lspawn.c.rej qmail-1.03-patch20010201/qmail-lspawn.c.rej --- qmail-1.03-patch20010201-orig/qmail-lspawn.c.rej Thu Jan 1 01:00:00 1970 +++ qmail-1.03-patch20010201/qmail-lspawn.c.rej Wed Feb 14 18:14:31 2001 @@ -0,0 +1,114 @@ +*************** +*** 393,429 **** + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + /* XXX doesn't free mem */ + 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 mailwith 2 @ */ +- /* 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,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,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 (!stralloc_0(&filter)) _exit(QLX_NOMEM); + +- debug(16, "retry with filter '%s'\n", filter.s); +- /* do the search for the email address */ +- ret = ldap_lookup(&search, attrs, &info, extra); +- /* count the results, we must have exactly one */ + } + alloc_free(filter.s); filter.s = 0; + if ( ret != 0 ) { +--- 396,469 ---- + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + /* XXX doesn't free mem */ + if ( ret != 0 && qldap_errno == LDAP_NOSUCH ) { ++ /* extensions: dash-trick and catchall */ + at = 0; + r = mail->s; + i = mail->len; + for (at = i - 1; r[at] != '@' && at >= 0 ; at--) ; ++ ++ #ifdef DASH_EXT ++ dash = 0; ++ for (dash = at-1; r[dash] != '-' && dash > 0 ; dash--); ++ ++ ++ /* dash trick */ ++ if ((dash > 0) && (dash < at)) { ++ if (!stralloc_copys(&filter,"(|(")) _exit(QLX_NOMEM); ++ /* optional objectclass */ ++ if (qldap_objectclass.len) { ++ 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, LDAP_MAIL)) _exit(QLX_NOMEM); ++ if (!stralloc_cats(&filter, "=")) _exit(QLX_NOMEM); ++ if (!stralloc_catb(&filter,r,dash)) _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_catb(&filter,r,dash)) _exit(QLX_NOMEM); ++ if (!stralloc_catb(&filter,r+at,i-at)) _exit(QLX_NOMEM); ++ if (!stralloc_cats(&filter,"))")) _exit(QLX_NOMEM); ++ if (!stralloc_0(&filter)) _exit(QLX_NOMEM); ++ ++ debug(16, "retry with filter '%s'\n", filter.s); ++ ret = ldap_lookup(&search, attrs, &info, extra); ++ } + ++ if (ret != 0 && qldap_errno == LDAP_NOSUCH) { ++ #endif ++ /* catchall */ ++ /* handles also mailwith 2 @ */ ++ /* 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,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,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 (!stralloc_0(&filter)) _exit(QLX_NOMEM); ++ ++ debug(16, "retry with filter '%s'\n", filter.s); ++ /* do the search for the email address */ ++ ret = ldap_lookup(&search, attrs, &info, extra); ++ #ifdef DASH_EXT ++ } ++ #endif + } + alloc_free(filter.s); filter.s = 0; + if ( ret != 0 ) { diff -u -N qmail-1.03-patch20010201-orig/qmail-lspawn.c~ qmail-1.03-patch20010201/qmail-lspawn.c~ --- qmail-1.03-patch20010201-orig/qmail-lspawn.c~ Thu Jan 1 01:00:00 1970 +++ qmail-1.03-patch20010201/qmail-lspawn.c~ Wed Feb 14 18:25:05 2001 @@ -0,0 +1,1016 @@ +#include "fd.h" +#include "wait.h" +#include "prot.h" +#include "substdio.h" +#include "stralloc.h" +#include "scan.h" +#include "exit.h" +#include "fork.h" +#include "error.h" +#include "cdb.h" +#include "case.h" +#include "slurpclose.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "qlx.h" + +#include "qmail-ldap.h" +#include "qldap-ldaplib.h" +#include "qldap-errno.h" +#include "qldap-debug.h" +#include "alloc.h" +#include "env.h" +#include "fmt.h" +#include "check.h" +#include "sig.h" +#include "auto_usera.h" +#include "auto_uids.h" +#include "byte.h" +#include "open.h" +#include "readwrite.h" +#include "str.h" +#include +#include +#ifdef QLDAP_CLUSTER +#include "seek.h" +#include "getln.h" +#endif + +char *aliasempty; + +/* initialize the string arrays, this uses DJB's libs */ +extern stralloc qldap_me; +extern stralloc qldap_objectclass; +stralloc qldap_defdotmode = {0}; +stralloc qldap_defaultquota = {0}; +stralloc qldap_quotawarning = {0}; +stralloc qldap_dirmaker = {0}; +int qldap_localdelivery; +int qldap_cluster; + +stralloc foo = {0}; + +/* init done */ + +#ifdef QLDAP_CLUSTER +static int allwrite(op,fd,buf,len) +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + register int w; + + while (len) { + w = op(fd,buf,len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +/* declaration of the mail forwarder function */ +void forward_mail(char *host, stralloc *to, char *from, int fdmess); +#endif + +/* this is a simple wrapper for the signal handler */ +void get_qldap_controls() +{ + if ( init_ldap( &qldap_localdelivery, &qldap_cluster, 0, &qldap_dirmaker, + &qldap_defdotmode, &qldap_defaultquota, &qldap_quotawarning ) == -1 ) + _exit(1); + + if ( qldap_dirmaker.len != 0 ) { + if ( !env_put2(ENV_HOMEDIRMAKE, qldap_dirmaker.s )) _exit(QLX_NOMEM); + } else { + if ( !env_unset(ENV_HOMEDIRMAKE) ) _exit(QLX_NOMEM); + } + + if ( qldap_quotawarning.len != 0 ) { + if ( !env_put2(ENV_QUOTAWARNING, qldap_quotawarning.s )) _exit(QLX_NOMEM); + } else { + if ( !env_unset(ENV_QUOTAWARNING) ) _exit(QLX_NOMEM); + } +} + +/* here it is not possible to log something */ +void initialize(argc,argv) +int argc; +char **argv; +{ + aliasempty = argv[1]; + if (!aliasempty) { + _exit(100); + } + + /* read the control files */ + get_qldap_controls(); + sig_hangupcatch(get_qldap_controls); + sig_hangupunblock(); +} + +int truncreport = 3000; + +void report(ss,wstat,s,len) +substdio *ss; +int wstat; +char *s; +int len; +{ +#ifdef DEBUG +#define REPORT_RETURN for (i = 0;i < len;++i) if (!s[i]) break; substdio_put(ss,s,i); return +#else +#define REPORT_RETURN return +#endif + int i; + if (wait_crashed(wstat)) { + substdio_puts(ss,"Zqmail-local crashed.\n"); + REPORT_RETURN; + } + switch(wait_exitcode(wstat)) { + case QLX_CDB: + substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); + REPORT_RETURN; + + case QLX_NOMEM: + substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); + REPORT_RETURN; + + case QLX_SYS: + substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); + REPORT_RETURN; + + case QLX_NOALIAS: + substdio_puts(ss,"ZUnable to find alias user!\n"); + REPORT_RETURN; + + case QLX_ROOT: + substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); + REPORT_RETURN; + + case QLX_USAGE: + substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); + REPORT_RETURN; + + case QLX_NFS: + substdio_puts(ss,"ZNFS failure in qmail-local.\n"); + REPORT_RETURN; + + case QLX_EXECHARD: + substdio_puts(ss,"DUnable to run qmail-local.\n"); + REPORT_RETURN; + + case QLX_EXECSOFT: + substdio_puts(ss,"ZUnable to run qmail-local.\n"); + REPORT_RETURN; + + case QLX_EXECPW: + substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); + REPORT_RETURN; + + case 111: case 71: case 74: case 75: + substdio_put(ss,"Z",1); + break; + + case 0: + substdio_put(ss,"K",1); + break; + + /* report LDAP errors */ + case 198: /* XXX */ + substdio_puts(ss, "DInternal qmail-ldap-lspawn bug. (LDAP-ERR #198)\n"); + REPORT_RETURN; + + case 199: /* XXX */ + substdio_puts(ss, "ZMissing ~control/ldapserver. (LDAP-ERR #199)\n"); + REPORT_RETURN; + + case 200: /* XXX */ + substdio_puts(ss, "DReceipient email address is not a valid email address. (LDAP-ERR #200)\n"); + REPORT_RETURN; + + case 201: + substdio_puts(ss, "DInternal error initializing LDAP structure (LDAP-ERR #201).\n"); + REPORT_RETURN; + + case 202: /* XXX */ + substdio_puts(ss, "DInternal error in ldap_set_option. (LDAP-ERR #202)\n"); + REPORT_RETURN; + + case 203: + substdio_puts(ss, "ZUnable to login into LDAP server. (bad username/password?). (LDAP-ERR #203)\n"); + REPORT_RETURN; + + case 204: /* XXX */ + substdio_puts(ss, "DInternal error in ldap_search_ext_s. (LDAP-ERR #204)\n"); + REPORT_RETURN; + + case 205: + substdio_puts(ss, "ZUnable to contact LDAP server (bad server address or server down?). (LDAP-ERR #205)"); + REPORT_RETURN; + + case 210: + substdio_puts(ss, "DLDAP attribute qmailUser contains illegal characters. (LDAP-ERR #210)\n"); + REPORT_RETURN; + + case 211: + substdio_puts(ss, "DLDAP attribute qmailUID is too high/low or not numeric. (LDAP-ERR #211)\n"); + REPORT_RETURN; + + case 212: + substdio_puts(ss, "DLDAP attribute qmailGID is too high/low or not numeric. (LDAP-ERR #212)\n"); + REPORT_RETURN; + + case 213: + substdio_puts(ss, "DLDAP attribute mailMessageStore contains illegal characters. (LDAP-ERR #213)\n"); + REPORT_RETURN; + + case 214: /* XXX */ + substdio_puts(ss, "ZLDAP attribute mailMessageStore in ~control/ldapmessagestore contains illegal characters. (LDAP-ERR #214)\n"); + REPORT_RETURN; + + case 215: /* XXX */ + substdio_puts(ss, "DLDAP attribute mailMessageStore is not given but mandatory. (LDAP-ERR #215)\n"); + REPORT_RETURN; + + case 220: /* XXX */ + substdio_puts(ss, "DLDAP attribute mailForwardingAddress contains illegal characters. (LDAP-ERR #220)\n"); + REPORT_RETURN; + + case 221: /* XXX */ + substdio_puts(ss, "DLDAP attribute deliveryProgramPath contains illegal characters. (LDAP-ERR #221)\n"); + REPORT_RETURN; + + case 222: /* XXX */ + substdio_puts(ss, "ZError while reading ~control files. (LDAP-ERR #222)\n"); + REPORT_RETURN; + + case 225: + substdio_puts(ss, "DMailaddress is administrativley disabled. (LDAP-ERR #220)\n"); + REPORT_RETURN; + + case 230: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapusername is missing/empty and LDAP qmailUser is not given. (LDAP-ERR #230)\n"); + REPORT_RETURN; + + case 231: + substdio_puts(ss, "ZConfiguration file ~control/ldapusername contains illegal characters. (LDAP-ERR #231)\n"); + REPORT_RETURN; + + case 232: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapuid is missing/empty and LDAP qmailUID is not given. (LDAP-ERR #232)\n"); + REPORT_RETURN; + + case 233: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapuid is too high/low or not numeric. (LDAP-ERR #233)\n"); + REPORT_RETURN; + + case 234: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapgid is missing/empty and LDAP qmailGID is not given. (LDAP-ERR #234)\n"); + REPORT_RETURN; + + case 235: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapgid is too high/low or not numeric. (LDAP-ERR #235)\n"); + REPORT_RETURN; + + case 236: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapmessagestore does not begin with an / or is emtpy. (LDAP-ERR #236)\n"); + REPORT_RETURN; + + case 237: /* XXX */ + substdio_puts(ss, "ZConfiguration file ~control/ldapmessagestore does not end with an / or is empty. (LDAP-ERR #237)\n"); + REPORT_RETURN; + + case 238: + substdio_puts(ss, "Zqmail-qmqpc (as mail forwarder) crashed (LDAP-ERR #238)\n"); + REPORT_RETURN; + +#ifdef QLDAP_CLUSTER + case 239: + substdio_puts(ss, "ZTemporary error in qmail-qmqpc (as mail forwarder) (LDAP-ERR #239)\n"); + REPORT_RETURN; + + case 240: + substdio_puts(ss, "DPermanet error in qmail-qmqpc (as mail forwarder) (LDAP-ERR #240)\n"); + REPORT_RETURN; + + case 241: + substdio_puts(ss, "DThis message is looping: it already has my Delivered-To line. (LDAP-ERR #241 CLUSTERLOOP)\n"); + REPORT_RETURN; +#endif /* QLDAP_CLUSTER */ +/* end -- report LDAP errors */ + + case 100: + default: + substdio_put(ss,"D",1); + break; + } + + for (i = 0;i < len;++i) + if (!s[i]) + break; + + substdio_put(ss,s,i); +} + + +stralloc nughde = {0}; + +/* LDAP server query routines */ + +int qldap_get( stralloc *mail, char *from, int fdmess) +{ + userinfo info; + extrainfo extra[7]; + searchinfo search; + char *attrs[] = { /* LDAP_MAIL, */ /* not needed */ + /* LDAP_MAILALTERNATE, */ + LDAP_UID, /* the first 6 attrs are the default ones */ + LDAP_QMAILUID, + LDAP_QMAILGID, + LDAP_ISACTIVE, + LDAP_MAILHOST, + LDAP_MAILSTORE, + LDAP_HOMEDIR, + LDAP_QUOTA, /* the last 6 are extra infos */ + LDAP_FORWARDS, + LDAP_PROGRAM, + LDAP_MODE, + LDAP_REPLYTEXT, + LDAP_DOTMODE, 0 }; + int ret; + int reply; + int at; + int i; +#ifdef DASH_EXT + int dash; +#endif + int force_forward; + char *r; + stralloc filter = {0}; + unsigned long tid; + + /* check the mailaddress for illegal characters * + * escape '*', ,'\', '(' and ')' with a preceding '\' */ + if (!escape_forldap(mail) ) _exit(QLX_NOMEM); + + /* 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); + + debug(16, "ldapfilter: '%s'\n", filter.s); + search.filter = filter.s; + search.bindpw = 0; /* rebind off */ + + /* initalize the different objects */ + extra[0].what = LDAP_QUOTA; + extra[1].what = LDAP_FORWARDS; + extra[2].what = LDAP_PROGRAM; + extra[3].what = LDAP_MODE; + extra[4].what = LDAP_REPLYTEXT; + extra[5].what = LDAP_DOTMODE; + extra[6].what = 0; + + /* do the search for the email address */ + ret = ldap_lookup(&search, attrs, &info, extra); + + if ( ret != 0 && qldap_errno == LDAP_NOSUCH ) { + /* extensions: catchall and dash-trick */ + at = 0; + r = mail->s; + i = mail->len; + for (at = i - 1; r[at] != '@' && at >= 0 ; at--) ; + /* handels also mailwith 2 @ */ + +#ifdef DASH_EXT + dash=0; + for (dash = at-1; r[dash] != '-' && dash > 0; dash--); + + /* dash trick */ + if ((dash > 0) && (dash < at) { + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + if (!stralloc_copys(&filter,"(|(" ) ) _exit(QLX_NOMEM); + /* optional objectclass */ + if (qldap_objectclass.len) { + 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,LDAP_MAIL)) _exit(QLX_NOMEM); + if (!stralloc_cats(&filter,"=")) _exit(QLX_NOMEM); + if (!stralloc_catb(&filter,r,dash)) _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_catb(&filter,r,dash)) _exit(QLX_NOMEM); + if (!stralloc_catb(&filter,r+at, i-at)) _exit(QLX_NOMEM); + if (!stralloc_cats(&filter,"))")) _exit(QLX_NOMEM); + if (!stralloc_0(&filter)) _exit(QLX_NOMEM); + + debug(16, "retry with filter '%s'\n", filter.s); + /* do the search */ + ret = ldap_lookup(&search, attrs, &info, extra); + } + + if (ret != 0 && qldap_errno == LDAP_NOSUCH) { +#endif + /* catchall */ + /* build the search string for the email address */ + if (!stralloc_copys(&filter, "")) _exit(QLX_NOMEM); + if (!stralloc_copys(&filter,"(|(" ) ) _exit(QLX_NOMEM); + /* optional objectclass */ + if (qldap_objectclass.len) { + 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,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 (!stralloc_0(&filter)) _exit(QLX_NOMEM); + + debug(16, "retry with filter '%s'\n", filter.s); + /* do the search for the catchall address */ + ret = ldap_lookup(&search, attrs, &info, extra); +#ifdef DASH_EXT + } +#endif + } + alloc_free(filter.s); filter.s = 0; + if ( ret != 0 ) { + switch(qldap_errno) { + case LDAP_INIT: + return 11; + break; + case LDAP_BIND: + return 13; + break; + case LDAP_BIND_UNREACH: + return 15; + break; + default: + return 1; + break; + } + return 1; /* just in case... */ + } + + /* go through the attributes and set the proper args for qmail-local * + * this can probably done with some sort of loop, but hey, how cares? */ + debug(32, "found: user='%s' uid=%s gid=%s homedir='%s' mms='%s' host='%s' status=%i\n", + info.user, info.uid, info.gid, info.homedir, + info.mms, info.host, info.status); + + /* check if the ldap entry is active */ + if ( info.status == STATUS_BOUNCE ) { + debug(2, "warning: %s's accountsatus is bounce\n", info.user); + _exit(225); + } + +#ifdef QLDAP_CLUSTER + /* check if the I'm the right host */ + if ( qldap_cluster && info.host && str_diff(qldap_me.s, info.host) ) { + /* hostname is different, so I reconnect */ + forward_mail(info.host, mail, from, fdmess); + /* that's it. Function does not return */ + } +#endif + + if (!chck_users(info.user) ) return 20; + /* set the value for qmail-local... */ + if (!stralloc_copys(&nughde, info.user) ) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + alloc_free(info.user); + + /* get the UID for delivery on the local system */ + scan_ulong(info.uid, &tid); + if (UID_MIN > tid || tid > UID_MAX ) return 21; + if (!stralloc_cats(&nughde, info.uid)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + alloc_free(info.uid); + + /* get the GID for delivery on the local system */ + scan_ulong(info.gid, &tid); + if (GID_MIN > tid || tid > GID_MAX ) return 22; + if (!stralloc_cats(&nughde, info.gid)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + alloc_free(info.gid); + + /* get the path of the maildir or mbox */ + force_forward = 0; + if ( info.homedir ) { + if (!chck_paths(info.homedir) ) return 23; + if (!stralloc_cats(&nughde, info.homedir)) _exit(QLX_NOMEM); + alloc_free(info.homedir); + if ( info.mms ) { + 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 */ + struct passwd *pw; + pw = getpwnam(auto_usera); + if (!pw) { + _exit(QLX_NOALIAS); + } + if (!stralloc_cats(&nughde, pw->pw_dir)) _exit(QLX_NOMEM); + aliasempty = ALIASDEVNULL; + force_forward = 1; + } + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + +#ifdef DASH_EXT + /* Here we fill the nughde structure with the dash-field the extension field */ + if ((dash > 0) && (dash < (at-1))) { + if (!stralloc_cats(&nughde,"-")) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_catb(&nughde,r+dash+1,at-dash-1)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + + } else { +#endif + + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + +#ifdef DASH_EXT + } +#endif + + /* get the quota for the user of that maildir mbox */ + if ( extra[0].vals != 0 ) { + debug(32, "%s: %s\n", ENV_QUOTA, extra[0].vals[0]); + if ( !env_put2(ENV_QUOTA, extra[0].vals[0] ) ) _exit(QLX_NOMEM); + } else { + if ( qldap_defaultquota.s ) { + debug(32, "%s: %s\n", ENV_QUOTA, qldap_defaultquota.s); + if ( !env_put2(ENV_QUOTA, qldap_defaultquota.s )) _exit(QLX_NOMEM); + } else { + debug(32, "no quota set\n"); + if ( !env_unset(ENV_QUOTA) ) _exit(QLX_NOMEM); + } + } + ldap_value_free(extra[0].vals); + + /* get the forwarding addresses and build a list * + * equals to &jdoe@heaven.af.mil in .qmail */ + if ( extra[1].vals != 0 ) { + if (!stralloc_copys(&foo, "")) _exit(QLX_NOMEM); + for ( i = 0; extra[1].vals[i] != 0; i++ ) { + if (!stralloc_cats(&foo, extra[1].vals[i])) _exit(QLX_NOMEM); + if (extra[1].vals[i+1] == 0 ) break; + if (!stralloc_cats(&foo, ",") ) _exit(QLX_NOMEM); + } + if (!stralloc_0(&foo) ) _exit(QLX_NOMEM); + debug(32, "%s: %s\n", ENV_FORWARDS, foo.s ); + if ( !env_put2(ENV_FORWARDS, foo.s) ) _exit(QLX_NOMEM); + } else { + /* default */ + if ( !env_unset(ENV_FORWARDS) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[1].vals); + + /* get the path of the local delivery program * + * equals to |/usr/bin/program in .qmail */ + if ( extra[2].vals != 0 ) { + if (!stralloc_copys(&foo, "")) _exit(QLX_NOMEM); + for ( i = 0; extra[2].vals[i] != 0; i++ ) { + /* append */ + if (!chck_progs(extra[2].vals[i]) ) return 31; /* XXX */ + if (!stralloc_cats(&foo, extra[2].vals[i])) _exit(QLX_NOMEM); + if (extra[2].vals[i+1] == 0 ) break; + if (!stralloc_cats(&foo, ",") ) _exit(QLX_NOMEM); + } + if (!stralloc_0(&foo) ) _exit(QLX_NOMEM); + debug(32, "%s: %s\n", ENV_PROGRAM, foo.s ); + if ( !env_put2(ENV_PROGRAM, foo.s) ) _exit(QLX_NOMEM); + } else { + /* default */ + if ( !env_unset(ENV_PROGRAM) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[2].vals); + + /* get the deliverymode of the mailbox: * + * reply, echo, forwardonly, normal, nombox, localdelivery */ + reply = 0; + if ( extra[3].vals != 0 ) { + if (!stralloc_copys(&foo, "")) _exit(QLX_NOMEM); + for ( i = 0; extra[3].vals[i] != 0; i++ ) { + /* append */ + case_lowers(extra[3].vals[i]); + if ( !str_diff(MODE_REPLY, extra[3].vals[i]) ) reply = 1; + if (!stralloc_cats(&foo, extra[3].vals[i])) _exit(QLX_NOMEM); + if (extra[3].vals[i+1] == 0 ) break; + if (!stralloc_cats(&foo, ",") ) _exit(QLX_NOMEM); + } + if (!stralloc_0(&foo) ) _exit(QLX_NOMEM); + debug(32, "%s: %s\n", ENV_MODE, foo.s ); + if ( !env_put2(ENV_MODE, foo.s) ) _exit(QLX_NOMEM); + } else { + /* default */ + if ( !env_unset(ENV_MODE) ) _exit(QLX_NOMEM); + if ( !env_unset(ENV_REPLYTEXT) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[3].vals); + + if ( reply ) { + if ( extra[4].vals != 0 ) { + debug(32, "%s: %s\n", ENV_REPLYTEXT, extra[4].vals[0] ); + if ( !env_put2(ENV_REPLYTEXT, extra[4].vals[0]) ) _exit(QLX_NOMEM); + } + ldap_value_free(extra[4].vals); + } + + /* get the mode of the .qmail interpretion: ldaponly, dotonly, both, none */ + if ( extra[5].vals != 0 ) { + case_lowers(extra[5].vals[0]); + if ( !str_diff(DOTMODE_LDAPONLY, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_LDAPONLY) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_LDAPWITHPROG, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_LDAPWITHPROG) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_DOTONLY, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_DOTONLY) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_BOTH, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_BOTH) ) _exit(QLX_NOMEM); + } else if ( !str_diff(DOTMODE_NONE, extra[5].vals[0]) ) { + if ( !env_put2(ENV_DOTMODE, DOTMODE_NONE) ) _exit(QLX_NOMEM); + } else { + if ( !env_put2(ENV_DOTMODE, qldap_defdotmode.s) ) _exit(QLX_NOMEM); + } + } else { + /* default */ + if ( !env_put2(ENV_DOTMODE, qldap_defdotmode.s) ) _exit(QLX_NOMEM); + } + debug(32, "%s: %s\n", ENV_DOTMODE, env_get(ENV_DOTMODE) ); + ldap_value_free(extra[5].vals); + + if ( force_forward ) { + /* XXX forcing forward only for useres with no homedir */ + if ( !env_put2(ENV_DOTMODE, DOTMODE_LDAPONLY) ) _exit(QLX_NOMEM); + if ( !env_put2(ENV_MODE, MODE_FORWARD) ) _exit(QLX_NOMEM); + } + /* ok, we finished, lets clean up and disconnect from the LDAP server */ + return 0; +} +/* end -- LDAP server query routines */ + +stralloc lower = {0}; +stralloc wildchars = {0}; + +void nughde_get(local) +char *local; +{ + char *(args[3]); + int pi[2], + gpwpid, + gpwstat, + r, + fd, + flagwild; + + if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM); + if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM); + if (!stralloc_0(&lower)) _exit(QLX_NOMEM); + case_lowerb(lower.s,lower.len); + + if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM); + + fd = open_read("users/cdb"); + if (fd == -1) + if (errno != error_noent) + _exit(QLX_CDB); + + if (fd != -1) { + uint32 dlen; + unsigned int i; + + r = cdb_seek(fd,"",0,&dlen); + if (r != 1) _exit(QLX_CDB); + if (!stralloc_ready(&wildchars,(unsigned int) dlen)) _exit(QLX_NOMEM); + wildchars.len = dlen; + if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) _exit(QLX_CDB); + + i = lower.len; + flagwild = 0; + + do { + /* i > 0 */ + if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) { + r = cdb_seek(fd,lower.s,i,&dlen); + if (r == -1) _exit(QLX_CDB); + if (r == 1) { + if (!stralloc_ready(&nughde,(unsigned int) dlen)) _exit(QLX_NOMEM); + nughde.len = dlen; + if (cdb_bread(fd,nughde.s,nughde.len) == -1) _exit(QLX_CDB); + if (flagwild) + if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + close(fd); + return; + } + } + --i; + flagwild = 1; + } while (i); + + close(fd); + } + + if (pipe(pi) == -1) _exit(QLX_SYS); + args[0] = "bin/qmail-getpw"; + args[1] = local; + args[2] = 0; + switch(gpwpid = vfork()) { + case -1: + _exit(QLX_SYS); + + case 0: + if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE); + if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE); + close(pi[0]); + if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS); + execv(*args,args); + _exit(QLX_EXECPW); + } + close(pi[1]); + + if (slurpclose(pi[0],&nughde,128) == -1) _exit(QLX_SYS); + + if (wait_pid(&gpwstat,gpwpid) != -1) { + if (wait_crashed(gpwstat)) _exit(QLX_SYS); + if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat)); + } +} + +int spawn(fdmess,fdout,s,r,at) +int fdmess; int fdout; +char *s; char *r; int at; +{ + int f; + + if (!(f = fork())) { + char *(args[11]); + unsigned long u; + int n, + uid, + gid; + char *x; + unsigned int xlen; + + stralloc ra = {0}; + int rv; + + /* XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX + * DEBUG should be handled better, so that it will not be included + * in maildelivery-failures mails */ + init_debug(fdout, -1); /* here are no critical data handled + * so debuglevel is free */ + + sig_hangupdefault(); /* clear the hup sig handler for the child */ + + /* copy the whole email address before the @ gets destroyed */ + if (!stralloc_copys(&ra,r)) _exit(QLX_NOMEM); + debug(16, "mailaddr: %S\n", &ra); + /* end -- save the @ */ + + r[at] = 0; + if (!r[0]) _exit(0); /* <> */ + + if (chdir(auto_qmail) == -1) _exit(QLX_USAGE); + + /* do the address lookup */ + rv = qldap_get(&ra, s, fdmess); + switch( rv ) { + case 0: + debug(16, "LDAP lookup succeeded\n"); + break; + + case 1: + if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM); + if ( qldap_localdelivery == 1 ) { + /* do the address lookup local */ + /* this is the standart qmail lookup funktion */ + debug(4, "LDAP lookup failed using local db\n"); + nughde_get(r); + + /* the alias-user handling for LDAP only mode */ + } else { + struct passwd *pw; + char num[FMT_ULONG]; + + debug(4, "LDAP lookup failed using alias (no local db)\n"); + pw = getpwnam(auto_usera); + if (!pw) { + _exit(QLX_NOALIAS); + } + + if (!stralloc_copys(&nughde, pw->pw_name)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_catb(&nughde,num,fmt_ulong(num, (long) pw->pw_uid))) + _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_catb(&nughde,num,fmt_ulong(num, (long) pw->pw_gid))) + _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde, pw->pw_dir)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde,"-")) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + if (!stralloc_cats(&nughde,r)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + } + /* end -- alias-user handling */ + break; + + default: + debug(2, "warning: ldap lookup failed with %i\n", rv); + _exit(190 + rv); + break; + } /* end switch */ + + /* debug(16, "nughde: %S\n", &nughde); */ + x = nughde.s; + xlen = nughde.len; + + args[0] = "bin/qmail-local"; + args[1] = "--"; + args[2] = x; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + scan_ulong(x,&u); + uid = u; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; + + scan_ulong(x,&u); + gid = u; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; + + args[3] = x; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; + + args[4] = r; + + args[5] = x; + + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; + + args[6] = x; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(198); x += n; xlen -= n; + + args[7] = r + at + 1; + args[8] = s; + args[9] = aliasempty; + args[10] = 0; + + debug(8, "executing 'qmail-local -- %s %s %s %s %s %s %s %s' under uid=%i, gid=%i\n", + args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], + uid, gid); + + if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); + if (fd_move(1,fdout) == -1) _exit(QLX_SYS); + if (fd_copy(2,1) == -1) _exit(QLX_SYS); + if (prot_gid(gid) == -1) _exit(QLX_USAGE); + if (prot_uid(uid) == -1) _exit(QLX_USAGE); + if (!getuid()) _exit(QLX_ROOT); + + execv(*args,args); + if (error_temp(errno)) _exit(QLX_EXECSOFT); + _exit(QLX_EXECHARD); + } + return f; +} + + +#ifdef QLDAP_CLUSTER +stralloc dtline = {0}; + +void bouncexf(int fdmess) +{ + char buf[1024]; + int match; + substdio ss; + + if (seek_begin(fdmess) == -1) _exit(QLX_SYS); + substdio_fdbuf(&ss,read,fdmess,buf,sizeof(buf)); + for (;;) + { + if (getln(&ss,&foo,&match,'\n') != 0) _exit(QLX_SYS); + if (!match) break; + if (foo.len <= 1) + break; + if (foo.len == dtline.len) + if (!str_diffn(foo.s,dtline.s,dtline.len)) + _exit(241); + } +} + +void forward_mail(char *host, stralloc *to, char* from, int fdmess) +{ + char *(args[3]); + int pi[2]; + int wstat; + int child; + int i; + + if (!stralloc_copys(&dtline, "Delivered-To: CLUSTERHOST ")) _exit(QLX_NOMEM); + if (!stralloc_catb(&dtline, qldap_me.s, qldap_me.len - 1 )) _exit(QLX_NOMEM); + if (!stralloc_cats(&dtline, " ")) _exit(QLX_NOMEM); + if (!stralloc_cat(&dtline, to)) _exit(QLX_NOMEM); + for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_'; + if (!stralloc_cats(&dtline,"\n")) _exit(QLX_NOMEM); + + bouncexf(fdmess); + + if (seek_begin(fdmess) == -1) _exit(QLX_SYS); + if (pipe(pi) == -1) _exit(QLX_SYS); + + switch( child = fork() ) { + case -1: + if (error_temp(errno)) _exit(QLX_EXECSOFT); + _exit(QLX_EXECHARD); + case 0: + close(pi[1]); + if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); + if (fd_move(1,pi[0]) == -1) _exit(QLX_SYS); + args[0]="bin/qmail-qmqpc"; args[1]=host; args[2]=0; + sig_pipedefault(); + execv(*args,args); + _exit(QLX_EXECHARD); + } + + debug(8, "Forwarding to %S at host %s from %s ", to, host, from); + close(pi[0]); + allwrite(write, pi[1], "F", 1); + allwrite(write, pi[1], from, str_len(from)); + allwrite(write, pi[1], "",1); + allwrite(write, pi[1], "T",1); + allwrite(write, pi[1], to->s, to->len); + allwrite(write, pi[1], "", 1); + allwrite(write, pi[1], "", 1); + allwrite(write, pi[1], "H",1); + allwrite(write, pi[1], qldap_me.s, qldap_me.len); + allwrite(write, pi[1], "", 1); + close(pi[1]); + wait_pid(&wstat,child); + if (wait_crashed(wstat)) { + _exit(238); + } + + switch(i=wait_exitcode(wstat)) { + case 0: + debug(8, "was successful\n"); + _exit(0); + case 31: case 61: + debug(8, "failed (hard error %i)/n", i); + _exit(240); + default: + debug(8, "failed (soft error %i)/n", i); + _exit(239); + } +} +#endif + diff -u -N qmail-1.03-patch20010201-orig/qmail-remote.c qmail-1.03-patch20010201/qmail-remote.c --- qmail-1.03-patch20010201-orig/qmail-remote.c Wed Feb 14 18:07:28 2001 +++ qmail-1.03-patch20010201/qmail-remote.c Wed Feb 14 18:29:47 2001 @@ -32,8 +32,15 @@ #include "timeoutwrite.h" #endif +#ifdef MXPS +#include +#include "fmt.h" +#endif + #ifdef TLS +#ifndef MXPS #include +#endif #include SSL *ssl = 0; @@ -42,11 +49,10 @@ #define HUGESMTPTEXT 5000 -#ifndef PORT_SMTP /* this is for testing purposes, so you can overwrite - this port via a simple -D argument */ -#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +unsigned long smtp_port = 25; /* silly rabbit, /etc/services is for users */ +#ifdef MXPS +unsigned long qmtp_port = 209; #endif -unsigned long port = PORT_SMTP; GEN_ALLOC_typedef(saa,stralloc,sa,len,a) GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) @@ -84,6 +90,10 @@ Unable to switch to home directory. (#4.3.0)\n"); zerodie(); } void temp_control() { out("Z\ Unable to read control files. (#4.3.0)\n"); zerodie(); } +#ifdef MXPS +void temp_proto() { out("Z\ +recipient did not talk proper QMTP (#4.3.0)\n"); zerodie(); } +#endif void perm_partialline() { out("D\ SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); } void perm_usage() { out("D\ @@ -175,10 +185,14 @@ if (r <= 0) dropped(); return r; } - +#ifdef MXPS +char inbuf[1500]; +char smtptobuf[1500]; +#else char inbuf[1024]; -substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); char smtptobuf[1024]; +#endif +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof smtptobuf); char smtpfrombuf[128]; substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf); @@ -486,6 +500,115 @@ quit("K"," accepted message"); } +#ifdef MXPS +int qmtp_priority(int pref) +{ + if (pref < 12800) return 0; + if (pref > 13055) return 0; + if (pref % 16 == 1) return 1; + return 0; +} + +void qmtp() +{ + struct stat st; + unsigned long len; + int len2; + char *x; + int i; + int n; + unsigned char ch; + char num[FMT_ULONG]; + int flagallok; + + if (fstat(0,&st) == -1) quit("Z", " unable to fstat stdin"); + len = st.st_size; + + /* the following code was substantially taken from serialmail'ss serialqmtp.c */ + substdio_put(&smtpto,num,fmt_ulong(num,len+1)); + substdio_put(&smtpto,":\n",2); + while (len > 0) { + n = substdio_feed(&ssin); + if (n <= 0) _exit(32); /* wise guy again */ + x = substdio_PEEK(&ssin); + substdio_put(&smtpto,x,n); + substdio_SEEK(&ssin,n); + len -= n; + } + substdio_put(&smtpto,",",1); + + len = sender.len; + substdio_put(&smtpto,num,fmt_ulong(num,len)); + substdio_put(&smtpto,":",1); + substdio_put(&smtpto,sender.s,sender.len); + substdio_put(&smtpto,",",1); + + len = 0; + for (i = 0;i < reciplist.len;++i) + len += fmt_ulong(num,reciplist.sa[i].len) + 1 + reciplist.sa[i].len + 1; + substdio_put(&smtpto,num,fmt_ulong(num,len)); + substdio_put(&smtpto,":",1); + for (i = 0;i < reciplist.len;++i) { + substdio_put(&smtpto,num,fmt_ulong(num,reciplist.sa[i].len)); + substdio_put(&smtpto,":",1); + substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len); + substdio_put(&smtpto,",",1); + } + substdio_put(&smtpto,",",1); + substdio_flush(&smtpto); + + flagallok = 1; + + for (i = 0;i < reciplist.len;++i) { + len = 0; + for (;;) { + get(&ch); + if (ch == ':') break; + if (len > 200000000) temp_proto(); + if (ch - '0' > 9) temp_proto(); + len = 10 * len + (ch - '0'); + } + if (!len) temp_proto(); + get(&ch); --len; + if ((ch != 'Z') && (ch != 'D') && (ch != 'K')) temp_proto(); + + if (!stralloc_copyb(&smtptext,&ch,1)) temp_proto(); + if (!stralloc_cats(&smtptext,"qmtp: ")) temp_nomem(); + + while (len > 0) { + get(&ch); + --len; + } + + for (len = 0;len < smtptext.len;++len) { + ch = smtptext.s[len]; + if ((ch < 32) || (ch > 126)) smtptext.s[len] = '?'; + } + get(&ch); + if (ch != ',') temp_proto(); + smtptext.s[smtptext.len-1] = '\n'; + + if (smtptext.s[0] == 'K') out("r"); + else if (smtptext.s[0] == 'D') { + out("h"); + flagallok = 0; + } + else { /* if (smtptext.s[0] == 'Z') */ + out("s"); + flagallok = 0; + } + if (substdio_put(subfdoutsmall,smtptext.s+1,smtptext.len-1) == -1) temp_noconn(); + zero(); + } + if (!flagallok) { + out("DGiving up on ");outhost();out("\n"); + } else { + out("KAll received okay by ");outhost();out("\n"); + } + zerodie(); +} +#endif + stralloc canonhost = {0}; stralloc canonbox = {0}; @@ -574,7 +697,7 @@ if (relayhost) { i = str_chr(relayhost,':'); if (relayhost[i]) { - scan_ulong(relayhost + i + 1,&port); + scan_ulong(relayhost + i + 1,&smtp_port); relayhost[i] = 0; } if (!stralloc_copys(&host,relayhost)) temp_nomem(); @@ -633,8 +756,21 @@ /* performace hack to send TCP ACK's without delay */ setsockopt(smtpfd, IPPROTO_TCP, TCP_NODELAY, &tcpnodelay, sizeof(tcpnodelay)); - - if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { + +#ifdef MXPS + if (qmtp_priority(ip.ix[i].pref)) { + if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) qmtp_port,timeoutconnect) == 0) { + tcpto_err(&ip.ix[i].ip,0); + partner = ip.ix[i].ip; + qmtp(); /* does not return */ + } + close(smtpfd); /* in case of failure: it is not allowed to call connect twice on the same socket */ + smtpfd = socket(AF_INET,SOCK_STREAM,0); + if (smtpfd == -1) temp_oserr(); + } +#endif + + if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) smtp_port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; #ifdef TLS diff -u -N qmail-1.03-patch20010201-orig/qmail-remote.c.orig qmail-1.03-patch20010201/qmail-remote.c.orig --- qmail-1.03-patch20010201-orig/qmail-remote.c.orig Mon Jun 15 12:53:16 1998 +++ qmail-1.03-patch20010201/qmail-remote.c.orig Wed Feb 14 18:07:28 2001 @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "sig.h" #include "stralloc.h" @@ -26,12 +27,25 @@ #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif + +#ifdef TLS +#include +#include + +SSL *ssl = 0; +char *fqdn = 0; +#endif #define HUGESMTPTEXT 5000 +#ifndef PORT_SMTP /* this is for testing purposes, so you can overwrite + this port via a simple -D argument */ #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +#endif unsigned long port = PORT_SMTP; GEN_ALLOC_typedef(saa,stralloc,sa,len,a) @@ -107,17 +121,57 @@ int smtpfd; int timeout = 1200; +#ifdef TLS +int flagtimedout = 0; +void sigalrm() +{ + flagtimedout = 1; +} +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) r = SSL_read(ssl,buf,n); else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) r = SSL_write(ssl,buf,n); else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} +#endif + int saferead(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutread(timeout,smtpfd,buf,len); +#else r = timeoutread(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,smtpfd,buf,len); +#else r = timeoutwrite(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } @@ -179,6 +233,33 @@ char *prepend; char *append; { +/* TAG */ +#if defined(TLS) && defined(DEBUG) +#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) + + if(ssl){ + X509 *peer; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + + /* we want certificate details */ + peer=SSL_get_peer_certificate(ssl); + if (peer != NULL) { + char *str; + + str=ONELINE_NAME(X509_get_subject_name(peer)); + out("; subject="); out(str); + Free(str); + str=ONELINE_NAME(X509_get_issuer_name(peer)); + out("; issuer="); out(str); + Free(str); + X509_free(peer); + } + out(";\n"); + } +#endif + substdio_putsflush(&smtpto,"QUIT\r\n"); /* waiting for remote side is just too ridiculous */ out(prepend); @@ -221,15 +302,147 @@ unsigned long code; int flagbother; int i; - +#ifdef TLS + int needtlsauth = 0; + SSL_CTX *ctx; + int saveerrno, r; +#ifdef DEBUG + char buf[1024]; +#endif + stralloc servercert = {0}; + struct stat st; + + if( fqdn && *fqdn ) { + if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem(); + if(!stralloc_cats(&servercert, fqdn)) temp_nomem(); + if(!stralloc_cats(&servercert, ".pem")) temp_nomem(); + if(!stralloc_0(&servercert)) temp_nomem(); + if (stat(servercert.s,&st) == 0) needtlsauth = 1; + } +#endif + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); +#ifdef TLS + substdio_puts(&smtpto,"EHLO "); +#else substdio_puts(&smtpto,"HELO "); +#endif substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); +#ifdef TLS + if (smtpcode() != 250){ + substdio_puts(&smtpto,"HELO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); + } +#else if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - +#endif + +#ifdef TLS + i = 0; + while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) && + str_diffn(smtptext.s+i+4,"STARTTLS\n",9)); + if (i+12 < smtptext.len) + { + substdio_puts(&smtpto,"STARTTLS\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 220) + { +#ifdef DEBUG + SSL_load_error_strings(); +#endif + SSLeay_add_ssl_algorithms(); + if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) +#ifdef DEBUG + {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"); +#endif + out("\n"); + 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 (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL)) + {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(": "); + out(ERR_error_string(ERR_get_error(), buf)); +#else + {out("ZTLS not available: error initializing ssl"); +#endif + out("\n"); + 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();} + errno = saveerrno; + if (r<=0) + {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(": "); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n");} +#else + out("ZTLS not available: connect failed\n"); +#endif + 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)), + NID_commonName, commonName, 256); + if (case_diffs(fqdn,commonName)){ + out("ZTLS connection to "); out(fqdn); + out(" wanted, certificate for "); out(commonName); + out(" received\n"); + zerodie();} + } + + substdio_puts(&smtpto,"EHLO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + + if (smtpcode() != 250) + { + quit("ZTLS connected to "," but my name was rejected"); + } + } + } + if ((!ssl) && needtlsauth) + {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); + zerodie();} +#endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); @@ -332,13 +545,17 @@ { static ipalloc ip = {0}; int i; + int tcpnodelay = 1; unsigned long random; char **recips; unsigned long prefme; int flagallaliases; int flagalias; char *relayhost; - + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); @@ -413,10 +630,16 @@ smtpfd = socket(AF_INET,SOCK_STREAM,0); if (smtpfd == -1) temp_oserr(); + + /* performace hack to send TCP ACK's without delay */ + setsockopt(smtpfd, IPPROTO_TCP, TCP_NODELAY, &tcpnodelay, sizeof(tcpnodelay)); 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 + fqdn = ip.ix[i].fqdn; +#endif smtp(); /* does not return */ } tcpto_err(&ip.ix[i].ip,errno == error_timeout);