diff -upN qmail-ldap.20060201/checkpassword.c qmail-1.03/checkpassword.c --- qmail-ldap.20060201/checkpassword.c 2006-02-08 13:20:12.128765080 +0100 +++ qmail-1.03/checkpassword.c 2006-02-08 13:23:33.000000000 +0100 @@ -128,9 +128,18 @@ check_ldap(stralloc *login, stralloc *au attrs[10] = 0; } +#ifdef NAMED_VIRTUAL + r = virtual_login(login); /* sets login & (global) virtual_basedn */ + if (r < 0) goto fail; +#endif filter = filter_uid(login->s); if (filter == 0) { r = ERRNO; goto fail; } +#ifdef IP_VIRTUAL + r = virtual_ip(env_get("TCPLOCALIP")); /* sets (global) virtual_basedn*/ + if (r < 0) goto fail; +#endif + r = qldap_lookup(q, filter, attrs); if (r != OK) goto fail; diff -upN qmail-ldap.20060201/Makefile qmail-1.03/Makefile --- qmail-ldap.20060201/Makefile 2006-02-08 13:20:12.054776328 +0100 +++ qmail-1.03/Makefile 2006-02-08 13:28:12.000000000 +0100 @@ -52,6 +52,11 @@ LDAPINCLUDES=-I/usr/local/include #OPENSSLBIN=/usr/local/bin/openssl #OPENSSLBIN=openssl +# Virtual domain support: see VIRTUAL.readme +# use -DIP_VIRTUAL to enable IP-based virtual domain support +# use -DNAMED_VIRTUAL to enable username-based virtual domain support +#VIRTUAL=-DIP_VIRTUAL -DNAMED_VIRTUAL + # to make the Netscape download progress bar work with qmail-pop3d # uncomment the next line (allready done) MNW=-DMAKE_NETSCAPE_WORK @@ -140,7 +145,7 @@ auth_mod.o: \ compile auth_mod.c auth_mod.h checkpassword.h byte.h localdelivery.h \ locallookup.h output.h qldap.h qldap-debug.h qldap-errno.h stralloc.h \ read-ctrl.h dirmaker.h qldap-cluster.h select.h alloc.h - ./compile $(LDAPFLAGS) $(DEBUG) $(HDIRMAKE) $(MDIRMAKE) auth_mod.c + ./compile $(LDAPFLAGS) $(DEBUG) $(HDIRMAKE) $(MDIRMAKE) $(VIRTUAL) auth_mod.c auth_pop: \ load auth_pop.o auth_mod.o checkpassword.o passwd.o digest_md4.o \ @@ -457,7 +462,7 @@ error.h fmt.h localdelivery.h passwd.h p qldap.h qldap-debug.h qldap-errno.h qmail-ldap.h scan.h str.h stralloc.h \ dns.h ipalloc.h ipme.h ndelay.h qldap-cluster.h readwrite.h select.h \ timeoutconn.h dirmaker.h mailmaker.h - ./compile $(LDAPFLAGS) $(LDAPINCLUDES) $(DEBUG) checkpassword.c + ./compile $(LDAPFLAGS) $(LDAPINCLUDES) $(DEBUG) $(VIRTUAL) checkpassword.c chkshsgr: \ load chkshsgr.o @@ -1404,8 +1409,9 @@ qldap-debug.o qldap-errno.o auto_break.o qldap.o: \ compile qldap.c qldap.h alloc.h byte.h case.h check.h control.h error.h \ -fmt.h qldap-debug.h qldap-errno.h qmail-ldap.h scan.h str.h stralloc.h - ./compile $(LDAPFLAGS) $(LDAPINCLUDES) $(DEBUG) qldap.c +fmt.h qldap-debug.h qldap-errno.h qmail-ldap.h scan.h str.h stralloc.h \ +constmap.h + ./compile $(LDAPFLAGS) $(LDAPINCLUDES) $(DEBUG) $(VIRTUAL) qldap.c qldap-cluster.o: \ compile qldap-cluster.c qldap-cluster.h constmap.h control.h qldap-debug.h \ @@ -1515,10 +1521,11 @@ qlx.h qmail-group: \ load qmail-group.o qmail.o now.o control.o case.a getln.a sig.a open.a \ seek.a fd.a wait.a env.a qldap.a read-ctrl.o stralloc.a alloc.a strerr.a \ -substdio.a error.a fs.a str.a coe.o auto_qmail.o +substdio.a error.a fs.a str.a coe.o auto_qmail.o constmap.o case_diffb.o ./load qmail-group qmail.o now.o control.o case.a getln.a sig.a \ open.a seek.a fd.a wait.a env.a qldap.a read-ctrl.o stralloc.a \ alloc.a fs.a strerr.a substdio.a error.a str.a coe.o auto_qmail.o \ + constmap.o case_diffb.o \ $(LDAPLIBS) qmail-group.o: \ @@ -2180,10 +2187,11 @@ qmail-users.9 conf-break conf-spawn qmail-verify: \ load qmail-verify.o qldap.a read-ctrl.o control.o getln.a substdio.a \ stralloc.a env.a alloc.a error.a open.a fs.a case.a cdb.a str.a timeoutread.o \ -localdelivery.o auto_qmail.o +localdelivery.o auto_qmail.o constmap.o case_diffb.o ./load qmail-verify qldap.a read-ctrl.o control.o getln.a \ substdio.a stralloc.a env.a alloc.a error.a open.a fs.a case.a \ cdb.a str.a seek.a timeoutread.o localdelivery.o auto_qmail.o \ + constmap.o case_diffb.o \ $(LDAPLIBS) qmail-verify.o: \ diff -upN qmail-ldap.20060201/qldap.c qmail-1.03/qldap.c --- qmail-ldap.20060201/qldap.c 2006-02-08 13:20:12.269743648 +0100 +++ qmail-1.03/qldap.c 2006-02-08 13:23:33.000000000 +0100 @@ -49,6 +49,7 @@ #include "scan.h" #include "str.h" #include "stralloc.h" +#include "constmap.h" #include "qldap.h" @@ -70,6 +71,17 @@ struct qldap { stralloc ldap_server = {0}; stralloc basedn = {0}; +#ifdef VIRTUAL_DN +stralloc virtual_basedn = {0}; +#endif +#ifdef IP_VIRTUAL +struct constmap map_ip_dn; +stralloc vdn_ip_file = {0}; +#endif +#ifdef NAMED_VIRTUAL +struct constmap map_domain_dn; +stralloc vdn_domain_file = {0}; +#endif stralloc objectclass = {0}; stralloc ldap_login = {0}; stralloc ldap_password = {0}; @@ -174,7 +186,27 @@ qldap_ctrl_generic(void) if (control_readint(&rebind, "control/ldaprebind") == -1) return -1; logit(64, "init_ldap: control/ldaprebind: %i\n", rebind); - + +#ifdef IP_VIRTUAL + /* control/ipvirtualbasedn */ + if (control_readfile(&vdn_ip_file, "control/ipvirtualbasedn", 0) == -1) + return -1; + if (!constmap_init(&map_ip_dn, vdn_ip_file.s, vdn_ip_file.len, 1)) + /* it doesn't hurt when vdn_ip_file is empty (no file) */ + return -1; + /* Logging? How to log a constmap? */ +#endif +#ifdef NAMED_VIRTUAL + /* control/namedvirtualbasedn */ + if (control_readfile(&vdn_domain_file, "control/namedvirtualbasedn",0) + == -1) + return -1; + if (!constmap_init(&map_domain_dn, + vdn_domain_file.s, + vdn_domain_file.len, 1)) + return -1; + /* Logging? How to log a constmap? */ +#endif /* defaults */ if (control_readint(&default_uid, "control/ldapuid") == -1) @@ -223,7 +255,12 @@ qldap_need_rebind(void) char * qldap_basedn(void) { - return basedn.s; +#ifdef VIRTUAL_DN + if(virtual_basedn.s && *(virtual_basedn.s)) + return virtual_basedn.s; + else +#endif + return basedn.s; } qldap * @@ -237,6 +274,114 @@ qldap_new(void) return q; } +#ifdef NAMED_VIRTUAL +int +virtual_login(stralloc* login) +/* Return value: -1 on error + * 0 if login -> virtual_basedn mapping not found + * 1 if login -> virtual_basedn mapping found */ +{ + stralloc domain = {0}; + unsigned int sep_idx; + const char* vbdn; + /* splitting to user and domain part */ + if((sep_idx = str_rchr(login->s, VIRTUAL_SEPARATOR)) && + (login->s[sep_idx] != '\0') && (login->s[sep_idx+1] != '\0')) + { + /* setting domain */ + if(!stralloc_copyb(&domain, + (char*)(login->s + sep_idx + 1), + login->len - sep_idx - 1)) + return -1; + vbdn = constmap(&map_domain_dn,domain.s,domain.len-1); + if(vbdn && *vbdn) + { + /* setting virtual_basedn */ + if(!stralloc_copys(&virtual_basedn,vbdn) || + !stralloc_0(&virtual_basedn)) + { + /* clearing up */ + constmap_free(&map_domain_dn); + stralloc_copys(&virtual_basedn, ""); + return -1; + } + logit(64, + "init_ldap: control/namedvirtualbasedn: %s for login %s\n", + vbdn, login->s); + /* truncate login */ + login->s[sep_idx] = '\0'; + login->len = sep_idx+1; + constmap_free(&map_domain_dn); + return 1; + } + else + { + logit(64, + "init_ldap: control/namedvirtualbasedn: no basedn for %s\n", + login->s); + return 0; + } + } + else + { + /* no virtual login, so leave it untouched */ + return 0; + } +} +#endif +#ifdef IP_VIRTUAL +int +virtual_ip(const char* local_ip) +/* Return value: -1 on error + * 0 if local_ip not found + * 1 if local_ip -> virtual_basedn mapping found */ +{ + const char* vbdn; + unsigned int ip_size; + + if(!local_ip) + return 0; /* no local IP */ + /* Maybe constmap_free here ?! */ + if(virtual_basedn.len > 1) + return 0; /* virtual domain was extracted from login name */ + + ip_size = str_len(local_ip); + if(ip_size > 128) + ip_size = 128; /* env contains fake data (not even IPv6!) + * Maybe we should terminate here */ + + /* searching for local ip */ + vbdn = constmap(&map_ip_dn, local_ip, ip_size); + if(vbdn && *vbdn) + { + /* setting virtual_basedn */ + if(!stralloc_copys(&virtual_basedn,vbdn) || + !stralloc_0(&virtual_basedn)) + { + /* Clearing up on error */ + constmap_free(&map_ip_dn); + stralloc_copys(&virtual_basedn,""); + return -1; + } + logit(64, + "init_ldap: control/ipvirtualbasedn: %s for local ip %s\n", + vbdn, local_ip); + constmap_free(&map_ip_dn); + return 1; + + } + else + { + logit(64, + "init_ldap: control/ipvirtualbasedn: %s not found\n", + local_ip); + + constmap_free(&map_ip_dn); + return 0; + } +} +#endif + /****** LDAP OPEN, BIND & CLOSE *********************************************/ int @@ -383,7 +528,7 @@ qldap_lookup(qldap *q, const char *filte tv.tv_sec = ldap_timeout; tv.tv_usec = 0; - rc = ldap_search_st(q->ld, basedn.s, LDAP_SCOPE_SUBTREE, + rc = ldap_search_st(q->ld, qldap_basedn(), LDAP_SCOPE_SUBTREE, filter, (char **)attrs, 0, &tv, &q->res); switch (rc) { diff -upN qmail-ldap.20060201/qldap.h qmail-1.03/qldap.h --- qmail-ldap.20060201/qldap.h 2006-02-08 13:20:12.270743496 +0100 +++ qmail-1.03/qldap.h 2006-02-08 13:23:33.000000000 +0100 @@ -52,6 +52,12 @@ int qldap_ctrl_generic(void); int qldap_need_rebind(void); char *qldap_basedn(void); qldap *qldap_new(void); +#ifdef NAMED_VIRTUAL +int virtual_login(stralloc*); +#endif +#ifdef IP_VIRTUAL +int virtual_ip(const char*); +#endif /* possible errors: * init: FAILED, ERRNO diff -upN qmail-ldap.20060201/qmail-ldap.h qmail-1.03/qmail-ldap.h --- qmail-ldap.20060201/qmail-ldap.h 2006-02-08 13:20:12.289740608 +0100 +++ qmail-1.03/qmail-ldap.h 2006-02-08 13:23:33.000000000 +0100 @@ -187,4 +187,13 @@ #define DO_DOT 0x02 #define DO_BOTH (DO_LDAP | DO_DOT) +#if defined(IP_VIRTUAL) || defined (NAMED_VIRTUAL) +#define VIRTUAL_DN +/* Separator between username and domain part. Needed only when using + * username-based virtual domains. + * NOTE: if you change this value, you should probably check testvektor + * in check.c !!! */ +#define VIRTUAL_SEPARATOR '@' +#endif + #endif diff -upN qmail-ldap.20060201/VIRTUAL.readme qmail-1.03/VIRTUAL.readme --- qmail-ldap.20060201/VIRTUAL.readme 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/VIRTUAL.readme 2006-02-08 13:23:33.000000000 +0100 @@ -0,0 +1,68 @@ +Virtual domain support for qmail-ldap +===================================== + +Author: Kristof Bajnok (bajnokk@sztaki.hu) +9 January 2004 + +-------- + +The term "virtual domain support" means that you can define seperate ldap +brunches (base DN's) for your different domains used in authentication in +order to have them being administered independently. + +There are basically two types of virtual domain support + - IP based virtual domains + - username based virtual domains + +1. IP based virtual domains +--------------------------- +You should use IP based virtual domains, if you have to integrate several +different user databases, coming from non-virtual environment. In this case, +you can rarely guarantee that usernames are unique. +If you don't want your users to change their usernames from 'user' to +'user@domain' form, you should use different IP addresses for each +virtual domain, so that you can define a base DN for each address. +The whole concept of this implementation assumes that you are using a tcp +wrapper in front of your [smtp|pop3|imap] servers, which sets the TCPLOCALIP +environment variable, containing the IP address (of the server) to which the +client is bound. Tcpserver (included in ucspi-tcp package) is OK. +When a connection comes in, the base DN being used for authenticating the +user is selected according to TCPLOCALIP. +You can specify 'local ip' -> 'ldap' basedn mappings in control/ipvirtualbasedn. +The form of this file is as follows: ip_address:ldap_basedn +You can specify more than one mapping, the file is multiline. Only one mapping +per a single IP address is allowed. +Unless a mapping is defined for TCPLOCALIP, control/ldapbasedn will be used. + +2. Username based virtual domains +--------------------------------- +If your users have usernames that have a "virtual domain part" (something +that can identify the virtual domain, ie: john@domain.com), you can assign +different ldap basedn's for each domain. +By default the separator between user and domain parts is '@'. You can change +it by editing qldap.h (VIRTUAL_SEPARATOR), but then you should take a look at +check.c! +You can define 'domain' -> 'ldap basedn' mappings in control/namedvirtualbasedn +The form of this file is as follows: domain:ldap_basedn +You can specify more than one mapping, the file is multiline. Only one mapping +per domain is allowed. +In the LDAP database the uid field of the users in the virtual domain brunch +should contain only the "username" part, and not "@domain". (Users still have +to use username@domain form in their mail client.) +If the domain is not found in control/namedvirtualbasedn, control/ldapbasedn +will be used and authentication will go as usual (searching for +uid=username@domain). + +Mixing IP-based and username based virtual domains +-------------------------------------------------- +The procedure is as follows: + - if the username has a virtual domain part, then the basedn will be set + according to control/namedvirtualbasedn + - control/ipvirtualbasedn is only checked if username doesn't contain + virtual domain + - if neither approach succeeds, ldap search for authentication will have a + basedn defined in control/ldapbasedn (and username unchanged). + + +Please send your questions and comments to bajnokk@sztaki.hu +