diff -u -N qmail-1.03-patch20010301-orig/Makefile inst2/Makefile --- qmail-1.03-patch20010301-orig/Makefile Thu Mar 1 23:33:39 2001 +++ inst2/Makefile Fri Mar 9 02:44:28 2001 @@ -6,7 +6,13 @@ # -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 -DMXPS -DSMTP_AFTER_POP -DDASH_EXT + +# SMTP Authentication +# Comment out the next three lines if you do NOT want SMTP Authentication +SMTPAUTH=-DUSE_SMTPAUTH -DUSE_OLD_GREETING -DUSE_NEW_GREETING +SMTPAUTHOBJS=base64.o +SMTPAUTHINCLUDES=base64.h # Perhaps you have different ldap libraries, change them here LDAPLIBS=-L/usr/local/lib -lldap -llber @@ -28,7 +34,7 @@ # Path to OpenSSL libraries 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) @@ -41,7 +47,7 @@ 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. @@ -68,7 +74,7 @@ default: it qldap -qldap: qmail-quotawarn qmail-reply auth_pop auth_imap digest qmail-ldaplookup +qldap: qmail-quotawarn qmail-reply auth_pop auth_imap auth_smtp digest qmail-ldaplookup addresses.0: \ addresses.5 @@ -1772,13 +1778,13 @@ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o dns.lib socket.lib +fs.a auto_qmail.o dns.lib socket.lib ${SMTPAUTHOBJS} ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ alloc.a substdio.a error.a fs.a auto_qmail.o dns.o str.a \ - `cat dns.lib` `cat socket.lib` ${TLSLIBS} + ${SMTPAUTHOBJS} `cat dns.lib` `cat socket.lib` ${TLSLIBS} qmail-smtpd.0: \ qmail-smtpd.8 @@ -1789,8 +1795,9 @@ substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ -exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h - ./compile ${TLSON} ${TLSINCLUDES} qmail-smtpd.c +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h \ +${SMTPAUTHINCLUDES} + ./compile ${TLSON} ${TLSINCLUDES} ${SMTPAUTH} qmail-smtpd.c qmail-start: \ load qmail-start.o prot.o fd.a auto_uids.o @@ -2396,3 +2403,30 @@ backup: \ clean tar cf $(BACKUPPATH) . + +auth_smtp.o: \ +compile auth_smtp.c error.h qldap-errno.h readwrite.h stralloc.h env.h \ +str.h timeoutread.h auth_mod.h qldap-mdm.h exit.h fmt.h sig.h wait.h \ +scan.h alloc.h + ./compile $(LDAPFLAGS) $(HDIRMAKE) $(DEBUG) auth_smtp.c + +auth_smtp: \ +load auth_smtp.o checkpassword_smtp.o check.o control.o getln.a qldap-debug.o \ +fs.a open.a stralloc.a alloc.a substdio.a error.a env.a auto_qmail.o \ +str.a base64.o digest_md4.o digest_md5.o digest_rmd160.o digest_sha1.o \ +dns.o timeoutconn.o ndelay.a ipalloc.o dns.lib socket.lib qldap-ldaplib.o \ +timeoutread.o qldap-mdm.o wait.a prot.o qldap-errno.o + ./load auth_smtp checkpassword_smtp.o check.o control.o qldap-ldaplib.o \ + qldap-debug.o auto_qmail.o dns.o timeoutconn.o timeoutread.o ip.o \ + ipalloc.o getln.a open.a env.a stralloc.a alloc.a substdio.a str.a \ + base64.o digest_md4.o digest_md5.o digest_rmd160.o digest_sha1.o \ + qldap-mdm.o wait.a qldap-errno.o error.a fs.a ndelay.a prot.o \ + $(LDAPLIBS) $(SHADOWLIBS) `cat dns.lib` `cat socket.lib` + + +checkpassword_smtp.o: \ +compile checkpassword_smtp.c qmail-ldap.h stralloc.h auth_mod.h qldap-ldaplib.h \ +qldap-errno.h readwrite.h error.h str.h open.h substdio.h getln.h select.h \ +compatibility.h digest_md4.h digest_md5.h digest_rmd160.h digest_sha1.h dns.h \ +ipalloc.h timeoutconn.h byte.h scan.h fmt.h alloc.h qldap-debug.h + ./compile $(LDAPFLAGS) $(SHADOWOPTS) $(LDAPINCLUDES) checkpassword_smtp.c diff -u -N qmail-1.03-patch20010301-orig/auth_smtp.c inst2/auth_smtp.c --- qmail-1.03-patch20010301-orig/auth_smtp.c Thu Mar 8 23:34:19 2001 +++ inst2/auth_smtp.c Fri Mar 9 02:36:51 2001 @@ -0,0 +1,152 @@ +/* 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 +#include +#include "compatibility.h" +#include "digest_md4.h" +#include "digest_md5.h" +#include "digest_rmd160.h" +#include "digest_sha1.h" +#include "select.h" +#include "ipalloc.h" +#include "dns.h" +#include "timeoutconn.h" +#include "byte.h" +#include "scan.h" +#include "fmt.h" +#include "alloc.h" +#include "check.h" +#include "qldap-debug.h" + +/* Edit the first lines in the Makefile to enable local passwd lookups + * and debug options. + * To use shadow passwords under Solaris, uncomment the 'SHADOWOPTS' line + * in the Makefile. + * To use shadow passwords under Linux, uncomment the 'SHADOWOPTS' line and + * the 'SHADOWLIBS=-lshadow' line in the Makefile. + */ +#include +#ifdef PW_SHADOW +#include +#endif +#ifdef AIX +#include +#endif + +extern stralloc qldap_me; +extern stralloc qldap_objectclass; + +int rebind; +int cluster; + +static int check_ldap(stralloc *login, + stralloc *authdata, + unsigned long *uid, + unsigned long *gid, + stralloc *home, + stralloc *maildir); + +static int check_passwd(stralloc *login, + stralloc *authdata, + unsigned long *uid, + unsigned long *gid, + stralloc *home, + stralloc *md); + +static int cmp_passwd(unsigned char *clear, char *encrypted); + +static int get_local_maildir(stralloc *home, stralloc *maildir); + +#ifdef QLDAP_CLUSTER +static void copyloop(int infd, int outfd, int timeout); +static void forward_session(char *host, char *name, char *passwd); +#endif + +static int make_filter(stralloc *value, stralloc *filter); +static void free_stralloc(stralloc *sa); + +void main(int argc, char **argv) +{ + int locald; + stralloc login = {0}; + stralloc authdata = {0}; + stralloc home = {0}; + stralloc homemaker = {0}; + stralloc maildir = {0}; + unsigned long uid; + unsigned long gid; + + init_debug(STDERR, 255); /* XXX limited to 64 so it is not possible to get + * XXX passwords via debug on normal systems */ + + auth_init(argc, argv, &login, &authdata); + debug(256, "auth_init: login=%s, authdata=%s\n", login.s, authdata.s); + + if ( init_ldap(&locald, &cluster, &rebind, &homemaker, 0, 0, 0) == -1 ) { + debug(1, "alert: init_ldap failed.\n"); + _exit(1); + } + debug(64, "init_ldap: ld=%i, cluster=%i, rebind=%i, hdm=%s\n", + locald, cluster, rebind, homemaker.s); + + if ( check_ldap(&login, &authdata, &uid, &gid, &home, &maildir) ) { + debug(16, "authentication with ldap was not successful\n"); + if ( locald == 1 && + (qldap_errno == LDAP_NOSUCH || qldap_errno == LDAP_SEARCH) ) { + debug(16, "trying to authenticate with the local passwd db\n"); + if ( check_passwd(&login, &authdata, &uid, &gid, &home, &maildir) ) { + auth_fail(argc, argv, login.s); + } + } else { + auth_fail(argc, argv, login.s); + } + } + + auth_success(argc, argv, login.s, uid, gid, home.s, homemaker.s, maildir.s); + _exit(1); /* should never get here */ +} + +int check_ldap(stralloc *login, stralloc *authdata, unsigned long *uid, + unsigned long *gid, stralloc *home, stralloc *maildir) +{ + userinfo info; + extrainfo extra[2]; + searchinfo search; + stralloc filter = {0}; + int ret; + char *attrs[] = { LDAP_UID, /* the first 6 attrs are default */ + LDAP_QMAILUID, + LDAP_QMAILGID, + LDAP_ISACTIVE, + LDAP_MAILHOST, + LDAP_MAILSTORE, + LDAP_HOMEDIR, + LDAP_PASSWD, 0 }; /* passwd is extra */ + + /* initalize the different info objects */ + if ( rebind ) { + extra[0].what = 0; /* under rebind mode no additional info is needed */ + search.bindpw = authdata->s; + attrs[7] = 0; + /* rebind on, check passwd via ldap rebind */ + } else { + extra[0].what = LDAP_PASSWD; /* need to get the crypted password */ + search.bindpw = 0; /* rebind off */ + } + extra[1].what = 0; /* end marker for extra info */ + + if ( !make_filter(login, &filter ) ) { + /* create search filter */ + debug(4, "warning: check_ldap: could not make a filter\n"); + /* qldap_errno set by make_filter */ + return -1; + } + search.filter = filter.s; + + ret = ldap_lookup(&search, attrs, &info, extra); + free_stralloc(&filter); /* free the old filter */ + if ( ret != 0 ) { + debug(4, "warning: check_ldap: ldap_lookup not successful!\n"); + /* qldap_errno set by ldap_lookup */ + return -1; + } + /* check the status of the account !!! */ + if ( info.status == STATUS_BOUNCE ) { + qldap_errno = ACC_DISABLED; + return -1; + } + + + + scan_ulong(info.uid, uid); /* get uid, gid and home */ + scan_ulong(info.gid, gid); /* the values are checked later */ + + /* XXX have a look at check.c and qmail-ldap.h for chck_pathb */ +/* if ( !chck_pathb(home->s,home->len) ) { + debug(2, "warning: check_ldap: path contains illegal chars!\n"); + qldap_errno = ILL_PATH; + return -1; + } */ +/* if (!stralloc_0(home) ) { + qldap_errno = ERRNO; + return -1; + } + if (!stralloc_0(maildir) ) { + qldap_errno = ERRNO; + return -1; + }*/ + + + /* free a part of the info struct */ + alloc_free(info.user); + alloc_free(info.uid); + alloc_free(info.gid); + if (info.homedir) alloc_free(info.homedir); + if (info.mms) alloc_free(info.mms); + + + + if ( rebind && search.bind_ok ) { + debug(32, + "check_ldap: ldap_lookup sucessfully authenticated with rebind\n"); + return 0; + /* if we got till here under rebind mode, the user is authenticated */ + } else if ( rebind ) { + debug(32, + "check_ldap: ldap_lookup authentication failed with rebind\n"); + qldap_errno = AUTH_FAILED; + return -1; /* user authentification failed */ + } + + + if ( ! extra[0].vals ) { + debug(2, "warning: check_ldap: password is missing for uid %s\n", + login); + qldap_errno = AUTH_NEEDED; + return -1; + } + + ret = cmp_passwd((unsigned char*) authdata->s, extra[0].vals[0]); + debug(32, "check_ldap: password compare was %s\n", + ret==0?"successful":"not successful"); + ldap_value_free(extra[0].vals); + return ret; +} + +static int check_passwd(stralloc *login, stralloc *authdata, unsigned long *uid, + unsigned long *gid, stralloc *home, stralloc *md) +{ + int ret; + struct passwd *pw; +#ifdef PW_SHADOW + struct spwd *spw; +#endif +#ifdef AIX + struct userpw *spw; +#endif + + pw = getpwnam(login->s); + if (!pw) { + /* XXX: unfortunately getpwnam() hides temporary errors */ + debug(32, "check_passwd: user %s not found in passwd db\n", login->s); + qldap_errno = AUTH_NOSUCH; + return -1; + } + *gid = pw->pw_gid; + *uid = pw->pw_uid; + + /* here we don't check the home and maildir path, if a user has a faked + * passwd entry, then you have a bigger problem on your system than just + * a guy how can read the mail of other users/customers */ + if (!stralloc_copys(home, pw->pw_dir) ) { + qldap_errno = ERRNO; + return -1; + } + + if ( get_local_maildir(home, md) == -1 ) { + /* function sets qldap_errno */ + return -1; + } + debug(32, "get_local_maildir: maildir=%s\n", md->s); + + if (!stralloc_0(home) ) { + qldap_errno = ERRNO; + auth_error(); + } + +#ifdef PW_SHADOW + spw = getspnam(login->s); + if (!spw) { + /* XXX: again, temp hidden */ + qldap_errno = AUTH_ERROR; + return -1; + } + ret = cmp_passwd((unsigned char*) authdata->s, spw->sp_pwdp); +#else /* no PW_SHADOW */ +#ifdef AIX + spw = getuserpw(login->s); + if (!spw) { + /* XXX: and again */ + qldap_errno = AUTH_ERROR; + return -1; + } + ret = cmp_passwd((unsigned char*) authdata->s, spw->upw_passwd); +#else /* no AIX */ + ret = cmp_passwd((unsigned char*) authdata->s, pw->pw_passwd); +#endif /* END AIX */ +#endif /* END PW_SHADOW */ + debug(32, "check_pw: password compare was %s\n", + ret==0?"successful":"not successful"); + return ret; + +} + +static int cmp_passwd(unsigned char *clear, char *encrypted) +{ +#define HASH_LEN 100 /* XXX is this enough, I think yes */ + /* What do you think ? */ + char hashed[HASH_LEN]; /* these to buffers can not be used for exploits */ + char salt[33]; + int shift; + + if (encrypted[0] == '{') { /* hashed */ + if (!str_diffn("{crypt}", encrypted, 7) ) { + /* CRYPT */ + shift = 7; + str_copy(hashed, crypt(clear, encrypted+shift) ); + } else if (!str_diffn("{MD4}", encrypted, 5) ) { + /* MD4 */ + shift = 5; + MD4DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{MD5}", encrypted, 5) ) { + /* MD5 */ + shift = 5; + MD5DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{NS-MTA-MD5}", encrypted, 12) ) { + /* NS-MTA-MD5 */ + shift = 12; + if (!str_len(encrypted) == 76) { + qldap_errno = ILL_AUTH; + return -1; + } /* boom */ + byte_copy(salt, 32, &encrypted[44]); + salt[32] = 0; + ns_mta_hash_alg(hashed, salt, (char *) clear); + byte_copy(&hashed[32], 33, salt); + } else if (!str_diffn("{SHA}", encrypted, 5) ) { + /* SHA */ + shift = 5; + SHA1DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else if (!str_diffn("{RMD160}", encrypted, 8) ) { + /* RMD160 */ + shift = 8; + RMD160DataBase64(clear, str_len(clear), hashed, sizeof(hashed)); + } else { + /* unknown hash function detected */ + shift = 0; + qldap_errno = ILL_AUTH; + return -1; + } + /* End getting correct hash-func hashed */ + debug(256, "cpm_passwd: comparing hashed passwd (%s == %s)\n", + hashed, encrypted); + if (!*encrypted || str_diff(hashed,encrypted+shift) ) { + qldap_errno = AUTH_FAILED; + return -1; + } + /* hashed passwds are equal */ + } else { /* crypt or clear text */ + debug(256, "cpm_passwd: comparing standart passwd (%s == %s)\n", + crypt(clear,encrypted), encrypted); + if (!*encrypted || str_diff(encrypted, crypt(clear,encrypted) ) ) { + /* CLEARTEXTPASSWD ARE NOT GOOD */ + /* so they are disabled by default */ +#ifdef CLEARTEXTPASSWD +#warning ___CLEARTEXT_PASSWORD_SUPPORT_IS_ON___ + if (!*encrypted || str_diff(encrypted, clear) ) { +#endif + qldap_errno = AUTH_FAILED; + return -1; +#ifdef CLEARTEXTPASSWD + } +#endif + /* crypted or cleartext passwd ok */ + } + } /* end -- hashed or crypt/clear text */ + + return 0; + +} + +static int get_local_maildir(stralloc *home, stralloc *maildir) +{ + substdio ss; + stralloc dotqmail = {0}; + char buf[512]; + int match; + int fd; + + if ( ! stralloc_copy(&dotqmail, home) ) { + qldap_errno = ERRNO; + return -1; + } + if ( ! stralloc_cats(&dotqmail, "/.qmail") ) { + qldap_errno = ERRNO; + return -1; + } + if ( ! stralloc_0(&dotqmail) ) { + qldap_errno = ERRNO; + return -1; + } + + if ( ( fd = open_read(dotqmail.s) ) == -1 ) { + if ( errno == error_noent ) return 0; + qldap_errno = ERRNO; + return -1; + } + + substdio_fdbuf(&ss,read,fd,buf,sizeof(buf)); + while (1) { + if (getln(&ss,&dotqmail,&match,'\n') != 0) goto tryclose; + if (!match && !dotqmail.len) break; + if ( (dotqmail.s[0] == '.' || dotqmail.s[0] == '/') && + dotqmail.s[dotqmail.len-2] == '/' ) { /* is a maildir line ? */ + if ( ! stralloc_copy(maildir, &dotqmail) ) goto tryclose; + maildir->s[maildir->len-1] = '\0'; + break; + } + } + + close(fd); + for (match = 0; match<512; buf[match++]=0 ) ; /* trust nobody */ + free_stralloc(&dotqmail); + return 0; + +tryclose: + for (match = 0; match<512; buf[match++]=0 ) ; /* trust nobody */ + match = errno; /* preserve errno */ + close(fd); + free_stralloc(&dotqmail); + errno = match; + qldap_errno = ERRNO; + return -1; + +} + +#ifdef QLDAP_CLUSTER +static void copyloop(int infd, int outfd, int timeout) +{ + fd_set iofds; + fd_set savedfds; + int maxfd; /* Maximum numbered fd used */ + struct timeval tv; + unsigned long bytes; + char buf[4096]; /* very big buffer ethernet pkgs are normaly + around 1500 bytes long */ + + /* file descriptor bits */ + FD_ZERO(&savedfds); + FD_SET(infd, &savedfds); + FD_SET(outfd, &savedfds); + + if (infd > outfd) { + maxfd = infd; + } else { + maxfd = outfd; + } + + while(1) { + /* Set up timeout *//* because of LINUX this has to be done everytime */ + tv.tv_sec = timeout; + tv.tv_usec = 0; + + byte_copy(&iofds, sizeof(iofds), &savedfds); + + if ( select( maxfd + 1, &iofds, (fd_set *)0, (fd_set *)0, &tv) <= 0 ) { + break; + } + + if(FD_ISSET(infd, &iofds)) { + if((bytes = read(infd, buf, sizeof(buf))) <= 0) + break; + if(write(outfd, buf, bytes) != bytes) + break; + } + if(FD_ISSET(outfd, &iofds)) { + if((bytes = read(outfd, buf, sizeof(buf))) <= 0) + break; + if(write(infd, buf, bytes) != bytes) + break; + } + } + + shutdown(infd,0); + shutdown(outfd,0); + close(infd); + close(outfd); + for(bytes=0; bytes<4096; buf[bytes++] = 0 ) ; /* paranoia */ + return; +} + +static void forward_session(char *host, char *name, char *passwd) +{ + ipalloc ip = {0}; + stralloc host_stralloc = {0}; + int ffd; + int timeout = 31*60; /* ~30 min timeout RFC1730 */ + int ctimeout = 20; + + if (!stralloc_copys(&host_stralloc, host)) { + qldap_errno = ERRNO; + auth_error(); + } + + dns_init(0); + switch (dns_ip(&ip,&host_stralloc)) { + case DNS_MEM: + qldap_errno = ERRNO; + auth_error(); + case DNS_SOFT: + qldap_errno = BADCLUSTER; + auth_error(); + case DNS_HARD: + qldap_errno = BADCLUSTER; + auth_error(); + case 1: + if (ip.len <= 0) { + qldap_errno = BADCLUSTER; + auth_error(); + } + } + if ( ip.len != 1 ) { + qldap_errno = BADCLUSTER; + auth_error(); + } + + ffd = socket(AF_INET,SOCK_STREAM,0); + if (ffd == -1) { + qldap_errno = ERRNO; + auth_error(); + } + + if (timeoutconn(ffd, &ip.ix[0].ip, auth_port, ctimeout) != 0) { + qldap_errno = ERRNO; + auth_error(); + } + + /* We have a connection, first send user and pass */ + auth_forward(ffd, name, passwd); + copyloop(0, ffd, timeout); + + _exit(0); /* all went ok, exit normaly */ + +} +#endif /* QLDAP_CLUSTER */ + +static int make_filter(stralloc *value, stralloc *filter) +/* create a searchfilter, "(uid=VALUE)" */ +{ + stralloc tmp = {0}; + + + if ( !stralloc_copy(&tmp, value) ) { + qldap_errno = ERRNO; + return 0; + } + if ( !escape_forldap(&tmp) ) { + qldap_errno = ERRNO; + return 0; + } + if ( !stralloc_copys(filter,"(" ) ) { + qldap_errno = ERRNO; + return 0; + } + if ( qldap_objectclass.len && ( + !stralloc_cats(filter,"&(" ) || + !stralloc_cats(filter,LDAP_OBJECTCLASS) || + !stralloc_cats(filter,"=") || + !stralloc_cat(filter,&qldap_objectclass) || + !stralloc_cats(filter,")(") ) ) { + qldap_errno = ERRNO; + return 0; + } + if ( !stralloc_cats(filter, LDAP_UID) || + !stralloc_cats(filter, "=") || + !stralloc_cat(filter, &tmp) || + !stralloc_cats(filter, ")") ) { + qldap_errno = ERRNO; + return 0; + } + if ( qldap_objectclass.len && + !stralloc_cats(filter,")") ) { + qldap_errno = ERRNO; + return 0; + } + if ( !stralloc_0(filter) ) { + qldap_errno = ERRNO; + return 0; + } + free_stralloc(&tmp); + return 1; +} + +static void free_stralloc(stralloc* sa) +{ + alloc_free(sa->s); + sa->s = 0; + return; +} + diff -u -N qmail-1.03-patch20010301-orig/conf-cc inst2/conf-cc --- qmail-1.03-patch20010301-orig/conf-cc Mon Jun 15 12:53:16 1998 +++ inst2/conf-cc Fri Mar 9 02:16:31 2001 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DFD_SETSIZE=4096 This will be used to compile .c files. diff -u -N qmail-1.03-patch20010301-orig/hier.c inst2/hier.c --- qmail-1.03-patch20010301-orig/hier.c Thu Mar 1 23:33:39 2001 +++ inst2/hier.c Fri Mar 9 02:20:11 2001 @@ -152,6 +152,7 @@ c(auto_qmail,"bin","qmail-quotawarn",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","auth_pop",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","auth_imap",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","auth_smtp",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-ldaplookup",auto_uido,auto_gidq,0700); c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); diff -u -N qmail-1.03-patch20010301-orig/qmail-smtpd.c inst2/qmail-smtpd.c --- qmail-1.03-patch20010301-orig/qmail-smtpd.c Thu Mar 1 23:33:40 2001 +++ inst2/qmail-smtpd.c Fri Mar 9 02:35:55 2001 @@ -1,4 +1,7 @@ #include "sig.h" +#include +#include +#include #include "readwrite.h" #include "stralloc.h" #include "substdio.h" @@ -17,6 +20,7 @@ #include "scan.h" #include "byte.h" #include "case.h" +#include "wait.h" #include "env.h" #include "now.h" #include "exit.h" @@ -32,6 +36,9 @@ SSL *ssl = NULL; stralloc clientcert = {0}; #endif +#ifdef USE_SMTPAUTH +#include "base64.h" +#endif #define MAXHOPS 100 unsigned int databytes = 0; @@ -130,6 +137,8 @@ void die_read() { logline(1,"read error, connection closed"); _exit(1); } void die_alarm() { out("451 timeout (#4.4.2)\r\n"); logline(1,"connection timed out, closing connection"); flush(); _exit(1); } void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); logline(1,"out of memory, closing connection"); flush(); _exit(1); } +void die_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); } +void die_fork() { out("421 unable to start checkpassword.\r\n"); flush(); _exit(1); } void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); logline(1,"unable to real controls, closing connection"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); logline(1,"unable to figure out my IP address, closing connection"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); logline(1,"stray new line detected, closing connection"); flush(); _exit(1); } @@ -562,23 +571,21 @@ { smtp_greet("250-"); smtpsize[fmt_ulong(smtpsize,(unsigned long) databytes)] = 0; -#ifdef TLS - if (ssl) { - out("\r\n250-PIPELINING\r\n"); - out("250-SIZE "); out(smtpsize); out("\r\n"); - out("250 8BITMIME\r\n"); - } - else { -#endif out("\r\n250-PIPELINING\r\n"); +#ifdef USE_SMTPAUTH +#ifdef USE_OLD_GREETING + out("250-AUTH=LOGIN CRAM-MD5 PLAIN\r\n"); +#endif +#ifdef USE_NEW_GREETING + out("250-AUTH LOGIN CRAM-MD5 PLAIN\r\n"); +#endif +#endif #ifdef TLS - out("250-STARTTLS\r\n"); + if (!ssl) + out("250-STARTTLS\r\n"); #endif out("250-SIZE "); out(smtpsize); out("\r\n"); out("250 8BITMIME\r\n"); -#ifdef TLS - } -#endif seenmail = 0; dohelo(arg); logpid(3); logstring(3,"remote ehlo ="); logstring(3,arg); logflush(3); logpid(3); logstring(3,"max msg size ="); logstring(3,smtpsize); logflush(3); @@ -1087,10 +1094,300 @@ } #endif +#ifdef USE_SMTPAUTH +static unsigned char authenticated=0; +static char **smtpauth_argv; +static char *auth_argv[4]; +static stralloc smtpauth = {0}; +static char smtpauthlogin[65]; +static char smtpauthpass[65]; +static char smtpauthtimestamp[65]; +char unique[FMT_ULONG + FMT_ULONG + 3]; + +static int smtpauth_getl(void) { + int i; + if (!stralloc_copys(&smtpauth, "")) return -1; + for (;;) { + if (!stralloc_readyplus(&smtpauth,1)) return -1; + i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1); + if (i != 1) return i; + if (smtpauth.s[smtpauth.len] == '\n') break; + ++smtpauth.len; + } + if (smtpauth.len > 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len; + smtpauth.s[smtpauth.len] = 0; + return smtpauth.len; +} + +static void smtpauth_authenticate(void) +{ + int st, pid, fds[2]; + + if (pipe(fds)) { + out("535 pipe failure\r\n"); + flush(); + _exit(0); + } + /* spawn external program + + external program should return '0' if it was successful, + + submit: /bin/checkpassword /bin/true + + */ + switch ((pid=fork())) { + case -1: die_fork(); + case 0: close(fds[1]); + fd_copy(3,fds[0]); + flush(); + execvp(auth_argv[1], auth_argv+1); + die_nomem(); + }; + close(fds[0]); + write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1); + write(fds[1], smtpauthpass, str_len(smtpauthpass)+1); + if (str_len(smtpauthtimestamp)) + { + write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1); + } + close(fds[1]); + wait_pid(&st, pid); + + if (wait_crashed(st)) + die_crash(); + if (wait_exitcode(st) == 0) { + out("235 go ahead\r\n"); + flush(); + relayok=relayclient=""; + authenticated=1; + remoteinfo=smtpauthlogin; + return; + } + sleep(2); + out("535 auth failure\r\n"); + flush(); + return; +} + +void smtp_auth(arg) char *arg; { + int ret; + /* netscape 4.5 sends AUTH LOGIN + microsoft outlook express sends AUTH LOGIN + + idea is simple + + use an external program to test authority + if success, set 'RELAYCLIENT' + otherwise, let them know nicely (hangup) + + note, i really don't like djb's coding style even though i'm using it here. + i think using spaces for tabs is bad. + -mrs.brisby@nimh.org + */ + /* Here i've added support for other auth types. + + -brush@elysium.pl */ + if (!authenticated) + { + if ((ret=strncasecmp(arg,"login",5))==0) + { + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) { + /* here's the base64 encoded login */ + b64_pton(arg, smtpauthlogin, sizeof(smtpauthlogin)); + } else { + out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */ + flush(); + if (smtpauth_getl() > 0) + b64_pton(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin)); + else + die_read(); + } + out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */ + flush(); + if (smtpauth_getl() > 0) + b64_pton(smtpauth.s, smtpauthpass, sizeof(smtpauthpass)); + else + die_read(); + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"plain",5))==0) + { + int start; + static char smtpauthloginpass[200]; + + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) + { + if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } + /* here's the base64 encoded login/password */ + b64_pton(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } else { + int i; + out("334 ok. go on.\r\n"); + flush(); + i=smtpauth_getl(); + if(i <= 0) + die_read(); + else if(i*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } else b64_pton(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } + smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0; + start=strlen(smtpauthloginpass)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthlogin,smtpauthloginpass+start); + + start+=strlen(smtpauthlogin)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthpass,smtpauthloginpass+start); + + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"cram-md5",8))==0) + { + char *helper; + char *s; + int i; + static stralloc me = {0}; + static stralloc greet = {0}; + static stralloc greetenc = {0}; + i = control_readline(&me,"control/me"); + if (i != 1) + { + out("535 internal server error\r\n"); flush(); _exit(0); + } + for (i=0;i <= me.len;i++) + { + if (me.s[i]=='\n') + { + me.s[i]=0; + break; + } + } + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (greet.len) + { + for (i=0;i"); + greet.s[greet.len]=0; // obscure fix but it works + stralloc_readyplus(&greet,3); + if (greetenc.len) + { + for (i=0;i 0) + { + s=calloc((size_t) strlen(smtpauth.s),(size_t)1); + b64_pton(smtpauth.s, s, strlen(smtpauth.s)); + } + helper=strtok(s," "); + if(helper!=NULL) + { + strncpy (smtpauthlogin,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + helper=strtok(NULL," "); + if(helper!=NULL) + { + strncpy (smtpauthtimestamp,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + strncpy (smtpauthpass,greet.s,64); + auth_argv[1]=smtpauth_argv[3]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[4]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else + { + out("504 auth type not supported\r\n"); + flush(); + return; + } + } + else + { + out("503 you are already authenticated\r\n"); + flush(); + return; + } +} +#endif + struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +#ifdef USE_SMTPAUTH +, { "auth", smtp_auth, flush } +#endif , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } @@ -1104,8 +1401,11 @@ , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) int argc; char **argv; { +#ifdef USE_SMTPAUTH + smtpauth_argv = argv; +#endif #ifdef TLS sig_alarmcatch(sigalrm); #endif