summaryrefslogtreecommitdiff
path: root/src/qmail-ldapam.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmail-ldapam.c')
-rw-r--r--src/qmail-ldapam.c369
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);
+}