#include #include #include #include #include #include "auto_qmail.h" #include "qmail.h" #include "case.h" #include "control.h" #include "constmap.h" #include "readwrite.h" #include "buffer.h" #include "fd.h" #include "byte.h" #include "case.h" #include "str.h" #include "stralloc.h" #include "exit.h" #include "logmsg.h" #include "pathexec.h" #include "getln.h" #include "scan.h" #define WHO "qmail-ldapam" #define LDAP_SCOPE LDAP_SCOPE_SUBTREE #define MAIL_ACCOUNT_NAME "mail" #define MAIL_ACCOUNT_UID 8 #define MAIL_ACCOUNT_GID 12 #define FDAUTH 3 #define FDPWD 5 #define FLAG_DIR "-d" #define FLAG_MAIL "-m" #define PORT_LDAP 389 #define PORT_LDAPS 636 char authbuf[BUFSIZE_AUTH]; buffer ba = BUFFER_INIT(write,FDAUTH,authbuf,sizeof(authbuf)); char bspace[512]; buffer bp; struct constmap mapldapauth; stralloc ldapcntl = {0}; stralloc disabled = {0}; /* LDAP binding params */ stralloc binddn = {0}; stralloc bindpw = {0}; stralloc bindpwds = {0}; stralloc bindbase = {0}; stralloc bindhost = {0}; stralloc bindmbox = {0}; stralloc filter = {0}; stralloc user = {0}; // user w/o domain appended stralloc homeparam = {0}; unsigned long port = PORT_LDAP; void temp_nomem() { logmsg(WHO,110,FATAL,"out of memory"); } void exit(int fail) { int i; for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0; _exit(fail); } int match = 0; void read_passwd(void) { if (!bindpwds.len) { buffer_init(&bp,buffer_unixread,FDPWD,bspace,sizeof(bspace)); if (getln(&bp,&bindpwds,&match,'\0') == -1) logmsg(WHO,111,ERROR,"unable to read password"); close(5); if (match) --bindpwds.len; } } static int ldap_lookup(char *host,int port,char *user,char *pwd) { char *attrs[] = { NULL }; char *dn = 0; LDAP *ld; LDAPMessage *res, *entry; int r; if ((ld = ldap_init(host,port)) == 0) logmsg(WHO,110,ERROR,"Unable to initialise LDAP connection"); // if (starttls) // ldap_start_tls_s(LDAP *ld, LDAPControl **serverctrls, LDAPControl **clientctrls); r = ldap_simple_bind_s(ld,binddn.s,bindpw.s); if (r) logmsg(WHO,110,ERROR,"can't bind with LDAP server"); r = ldap_search_s(ld,bindbase.s,LDAP_SCOPE,filter.s,attrs,0,&res); if (r) logmsg(WHO,1,ERROR,B("search failed:",ldap_err2string(r))); entry = ldap_first_entry(ld,res); if (!entry) return 1; dn = ldap_get_dn(ld,res); ldap_msgfree(res); r = ldap_simple_bind_s(ld,dn,pwd); if (r) return 1; ldap_memfree(dn); ldap_unbind(ld); return 0; } static int ldap_userhome(char *host,int port,char *user,char *pwd,char *mbox) { char *attrs[] = { NULL }; char **values; LDAP *ld; LDAPMessage *res, *entry; int r; if ((ld = ldap_init(host,port) == 0)) logmsg(WHO,110,ERROR,"Unable to setup connection"); r = ldap_simple_bind_s(ld,binddn.s,bindpw.s); if (r) logmsg(WHO,110,ERROR,"can't bind to LDAP server"); r = ldap_search_s(ld,bindbase.s,LDAP_SCOPE,filter.s,attrs,0,&res); if (r) logmsg(WHO,1,ERROR,B("search failed: ",ldap_err2string(r))); entry = ldap_first_entry(ld,res); if (!entry) return 1; values = ldap_get_values(ld,entry,mbox); if (values && values[0]) { if (!stralloc_copys(&homeparam,values[0])) temp_nomem(); if (!stralloc_cats(&homeparam,"../../")) temp_nomem(); if (!stralloc_0(&homeparam)) temp_nomem(); } ldap_msgfree(res); ldap_unbind(ld); return 0; } static stralloc cafile = {0}; static stralloc cadir = {0}; static stralloc certfile = {0}; static stralloc keyfile = {0}; static stralloc certpwd = {0}; int main (int argc, char **argv) { char *authuser = 0; char *ldaparam = 0; char *domain = 0; char *password = 0; char *host = 0; int authlen = 0; int buflen = 0; int domlen = 0; int flaghome = 0; int flagmail = 0; int i = 0; int f, h, j, k, p, c, r, t, w; int rc; if (!argv[1]) logmsg(WHO,100,USAGE,"qmail-ldapam [-d|-m] prog"); if (!case_diffs(argv[1],FLAG_DIR)) { if (!argv[2]) logmsg(WHO,100,USAGE,"qmail-ldapam [-d|-m] prog"); flaghome = 1; } else { if (!case_diffs(argv[1],FLAG_MAIL)) { if (!argv[2]) logmsg(WHO,100,USAGE,"qmail-ldapam [-d|-m] prog"); flagmail = 1; } } for (;;) { do rc = read(FDAUTH,authbuf + buflen,sizeof(authbuf) - buflen); while ((rc == -1) && (errno == EINTR)); if (r == -1) exit(111); if (rc == 0) break; buflen += rc; if (buflen >= sizeof(authbuf)) exit(2); } close(FDAUTH); authuser = authbuf + i; /* username */ if (i == buflen) exit(2); while (authbuf[i++]) /* password */ if (i == buflen) exit(2); password= authbuf + i; if (i == buflen) exit(2); authlen = str_len(authuser); if (!stralloc_copyb(&user,authuser,authlen)) temp_nomem(); if ((i = byte_rchr(authuser,authlen,'@'))) /* @domain */ if (i < authlen && authuser[i] == '@') { domain = authuser + i; domlen = str_len(domain); case_lowerb(domain,domlen); user.len = 0; if (!stralloc_copyb(&user,authuser,i)) temp_nomem(); } if (!stralloc_0(&user)) exit(111); /* Read control file users/ldapauth and go for checks */ if (chdir(auto_qmail) == -1) exit(110); switch (control_readfile(&ldapcntl,"control/ldapauth",0)) { case -1: exit(110); case 0: if (!constmap_init(&mapldapauth,"",0,1)) temp_nomem(); case 1: if (!constmap_init(&mapldapauth,ldapcntl.s,ldapcntl.len,1)) temp_nomem(); } /* Check for disabled authuser/domains */ if (!stralloc_copys(&disabled,"!")) temp_nomem(); if (!stralloc_catb(&disabled,authuser,authlen)) temp_nomem(); if (constmap(&mapldapauth,disabled.s,disabled.len)) temp_nomem(); if (domlen) { disabled.len = 0; if (!stralloc_copys(&disabled,"!")) temp_nomem(); if (!stralloc_catb(&disabled,domain,domlen)) temp_nomem(); if (constmap(&mapldapauth,disabled.s,disabled.len)) temp_nomem(); } if (!ldaparam && domlen) ldaparam = constmap(&mapldapauth,domain,domlen); // 1. ldap server by domain if (!ldaparam) ldaparam = constmap(&mapldapauth,"*",1); // 2. one ldap for all if (!ldaparam) exit(1); /* Evaluate LDAP lookup params: i j h p t c w f h Host:Bind_DN|Bind_PW|Base|Host;Port|CA|Cert:Pwd|Filter:Homedir */ if (!stralloc_copys(&bindhost,"localhost")) temp_nomem(); /* Default LDAP host */ if (!stralloc_copys(&bindmbox,"homeDirectory")) temp_nomem(); /* Default POSIX name*/ i = str_chr(ldaparam,'|'); /* Bind DN */ if (ldaparam[i] == '|') { ldaparam[i] = 0; j = str_chr(ldaparam + i,'|'); /* Bind PWD */ if (ldaparam[i + j + 1] == '|') { ldaparam[i + j + 1] = 0; k = str_chr(ldaparam + i + j + 2,'|'); /* Base */ if (ldaparam[i + j + k + 2] == '|') { ldaparam[i + j + k + 2] = 0; if (!stralloc_copys(&bindbase,ldaparam + i + j + 2)) temp_nomem(); p = str_chr(ldaparam + i + j + k + 3,';'); /* Host;Port */ if (ldaparam[i + j + k + p + 3] == ';') { ldaparam[i + j + k + p + 2] = 0; if (p > 0) scan_ulong(ldaparam + i + j + k + p + 4,&port); if (!stralloc_copys(&bindhost,ldaparam + i + j + k + 3)) temp_nomem(); t = str_chr(ldaparam + i + j + k + 3,'|'); /* Trust Cert */ if (ldaparam[i + j + k + t + 3] == '|') { ldaparam[i + j + k + t + 3] = 0; if (ldaparam[i + j + k + t + 2] == '/') { if (!stralloc_copys(&cadir,ldaparam + i + j + k + t + 3)) temp_nomem(); if (!stralloc_0(&cadir)) temp_nomem(); } else { if (!stralloc_copys(&cafile,ldaparam + i + j + k + t + 3)) temp_nomem(); if (!stralloc_0(&cafile)) temp_nomem(); } w = str_chr(ldaparam + i + j + k + t + 4,':'); /* Cert:Pwd */ if (ldaparam[i + j + k + t + w + 4] == ':') { ldaparam[i + j + k + t + w + 4] = 0; if (!stralloc_copys(&certfile,ldaparam + i + j + k + t + 4)) temp_nomem(); f = str_chr(ldaparam + i + j + k + t + w + 5,':'); /* Filter */ if (ldaparam[i + j + k + t + w + 4] == '|') { ldaparam[i + j + k + t + w + 4] = 0; if (!stralloc_copys(&certpwd,ldaparam + i + j + k + f + 4)) temp_nomem(); h = str_chr(ldaparam + i + j + k + 3,'|'); /* Homedir */ if (ldaparam[i + j + k + h + 3] == '|') { ldaparam[i + j + k + t + 3] = 0; if (!stralloc_copys(&bindmbox,ldaparam + i + j + k + 3)) temp_nomem(); if (!stralloc_0(&bindmbox)) temp_nomem(); } } // f } // c } // t } // k } // j if (!stralloc_copys(&bindpw,ldaparam + i + 1)) temp_nomem(); if (!stralloc_0(&bindpw)) temp_nomem(); } // i if (!stralloc_copys(&binddn,ldaparam)) temp_nomem(); if (!stralloc_0(&binddn)) temp_nomem(); } if (flagmail) { /* LDAP filter */ if (!stralloc_copys(&filter,"(&(mail=")) temp_nomem(); if (!stralloc_cats(&filter,authuser)) temp_nomem(); if (!stralloc_cats(&filter,"))")) temp_nomem(); if (!stralloc_0(&filter)) temp_nomem(); } else { if (!stralloc_copys(&filter,"(&uid=")) temp_nomem(); if (!stralloc_cat(&filter,&user)) temp_nomem(); if (!stralloc_cats(&filter,")(dc=")) temp_nomem(); if (!stralloc_cats(&filter,host)) temp_nomem(); if (!stralloc_cats(&filter,"))")) temp_nomem(); if (!stralloc_0(&filter)) temp_nomem(); } if (!str_diff(bindpw.s,"*")) { read_passwd(); for (i = 0; i < bindpwds.len; i++) { if (bindpwds.s[i] == ' ') { if (!stralloc_copyb(&bindpw,bindpwds.s,i - 1)) temp_nomem(); if (!stralloc_0(&bindpw)) temp_nomem(); host = bindhost.s; if (!ldap_lookup(host,port,authuser,password)) break; bindpwds.s = bindpwds.s + i; ++i; } } logmsg(WHO,110,ERROR,B("can't bind to LDAP host: ",host)); } else if (ldap_lookup(host,port,authuser,password)) logmsg(WHO,110,ERROR,B("can't bind to LDAP host: ",host)); /* Now we check the user's mailbox for POP3 and IMAP4 capabilities */ if (flaghome) { if (ldap_userhome(host,port,authuser,password,bindmbox.s)) exit(1); if (initgroups(MAIL_ACCOUNT_NAME, MAIL_ACCOUNT_GID)) logmsg(WHO,107,ERROR,B("Unable to set supplementary groups: ",strerror(errno))); if (setgid(MAIL_ACCOUNT_GID)) logmsg(WHO,106,ERROR,B("Unable to set gid: ",strerror(errno))); if (setuid(MAIL_ACCOUNT_UID)) logmsg(WHO,105,ERROR,B("Unable to set uid: ",strerror(errno))); if (chdir(homeparam.s)) logmsg(WHO,108,ERROR,B("Unable to change to home dir: ",homeparam.s,strerror(errno))); } for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0; if (flaghome || flagmail) pathexec(argv + 2); else pathexec(argv + 1); exit(111); }