diff options
Diffstat (limited to 'src/qmail-ldapam.c')
-rw-r--r-- | src/qmail-ldapam.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/src/qmail-ldapam.c b/src/qmail-ldapam.c new file mode 100644 index 0000000..2d5b78f --- /dev/null +++ b/src/qmail-ldapam.c @@ -0,0 +1,369 @@ +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> +#include <pwd.h> +#include <ldap.h> +#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); +} |