Hi,
attached is a patch to cyrus/saslauthd/lak.c to allow it to expand '%d' macro in ldap_search_base option to the domain context derived from the realm '%r'. eg. ldap_search_base: ou=people, %d in saslauthd.conf if realm is 'domain.tld', the ldap search base will expand to 'ou=people,dc=domain,dc=tld' Does imapd always provide the realm in a virtual server environment when authenticating users? Is it safe to assume that the realm is 'domain.tld' or more specifically the domain given to mkimap script? Is this correct/incorrect/unnecessary? --Kervin
--- lak.c.orig Mon Oct 14 01:15:25 2002 +++ lak.c Mon Oct 14 14:37:23 2002 @@ -68,11 +68,11 @@ static int lak_config_getswitch(const char *, int ); static int lak_config(const char *, LAK_CONF **); static int lak_escape(const char *, char **); -static int lak_filter(LAK *, const char *, const char *, char **); +static int lak_filter(LAK *, const char *, const char *, const char *, char **); static int lak_connect(LAK *); static int lak_bind(LAK *, char, const char *, const char *); static int lak_init(const char *, LAK **); -static int lak_search(LAK *, const char *, const char **, LDAPMessage **); +static int lak_search(LAK *, const char *, const char *, const char **, LDAPMessage +**); static int lak_retrieve(LAK *, const char *, const char *, const char **, LAK_RESULT **); static int lak_auth_custom(LAK *, const char *, const char *, const char *); static int lak_auth_bind(LAK *, const char *, const char *, const char *); @@ -327,45 +327,77 @@ * %% = % * %u = user * %r = realm + * %d = realm as a domain context * Note: calling function must free memory. */ -static int lak_filter(LAK *lak, const char *username, const char *realm, char **result) +static int lak_filter(LAK *lak, const char *filter, const char *username, const char +*realm, char **result) { - char *buf; - char *end, *ptr, *temp; + char *buf, *domain_str = NULL; + char *temp; + const char *ptr, *end; char *ebuf; - int rc; + int rc, i,j; /* to permit multiple occurences of username and/or realm in filter */ /* and avoid memory overflow in filter build [eg: (|(uid=%u)(userid=%u)) ] */ - int percents, realm_len, user_len, maxparamlength; + int percents, realm_len, user_len, domain_len, maxparamlength; - if (lak->conf->filter == NULL) { + if (filter == NULL) { syslog(LOG_WARNING|LOG_AUTH, "filter not setup"); return LAK_FAIL; } - /* find the longest param of username and realm */ + /* expand domain only if we have a realm */ + if( realm!=NULL && strlen(realm)>3) + { + /* find the size of the domain string */ + domain_len = 0; + for(i=0;i<strlen(realm);i++) + if(realm[i]=='.') + domain_len++; + /* strlen(",dc=") is equal to 4 */ + /* strlen("dc=") is equal to 3 */ + domain_len = domain_len*4 + strlen(realm) + 3; + + domain_str=calloc(1,domain_len+1); + if(domain_str == NULL) { + syslog(LOG_ERR|LOG_AUTH, "Cannot allocate memory"); + return LAK_NOMEM; + } + + strncat(domain_str, "dc=", 3); + for(i=0,j=3;i<strlen(realm);i++) { + if(realm[i]=='.') { + strncat(&domain_str[j], ",dc=", 4); + j += 4; + } + else domain_str[j++] = realm[i]; + } + } + + /* find the longest param of username, realm or domain context*/ user_len=strlen(username); realm_len=strlen(realm); if( user_len > realm_len ) maxparamlength = user_len; else maxparamlength = realm_len; + if( maxparamlength < domain_len ) + maxparamlength = domain_len; /* find the number of occurences of percent sign in filter */ - for( percents=0, buf=lak->conf->filter; *buf; buf++ ) { - if( *buf == '%' ) percents++; + for( i=0,percents=0; i<strlen(filter); i++ ) { + if( filter[i] == '%' ) percents++; } - buf=malloc(strlen(lak->conf->filter) + (percents * maxparamlength) +1); + buf=malloc(strlen(filter) + (percents * maxparamlength) +1); if(buf == NULL) { syslog(LOG_ERR|LOG_AUTH, "Cannot allocate memory"); return LAK_NOMEM; } buf[0] = '\0'; - ptr=lak->conf->filter; + ptr=filter; end = ptr + strlen(ptr); while ((temp=strchr(ptr,'%'))!=NULL ) { @@ -404,6 +436,18 @@ syslog(LOG_WARNING|LOG_AUTH, "Realm not available."); } break; + case 'd': + if (domain_str!=NULL) { + rc = lak_escape(domain_str, &ebuf); + if (rc == LAK_OK) { + strcat(buf,ebuf); + free(ebuf); + } + } else if (realm) { + /* complain only if we have a realm and no +domain */ + syslog(LOG_WARNING|LOG_AUTH, "Domain not +available."); + } + break; default: break; } @@ -413,6 +457,8 @@ strcat(buf, ptr); *result = buf; + if(domain_str) + free(domain_str); return LAK_OK; } @@ -610,7 +656,7 @@ } -static int lak_search(LAK *lak, const char *filter, const char **attrs, LDAPMessage **res) +static int lak_search(LAK *lak, const char *search_base, const char *filter, const +char **attrs, LDAPMessage **res) { int rc = 0; int retry = 1; @@ -624,7 +670,7 @@ return LAK_FAIL; } - rc = ldap_search_st(lak->ld, lak->conf->search_base, lak->conf->scope, filter, (char **) attrs, 0, &(lak->conf->timeout), res); + rc = ldap_search_st(lak->ld, search_base, lak->conf->scope, filter, (char **) +attrs, 0, &(lak->conf->timeout), res); switch (rc) { case LDAP_SUCCESS: case LDAP_SIZELIMIT_EXCEEDED: @@ -661,6 +707,7 @@ { int rc = 0; char *filter = NULL; + char *search_base = NULL; LDAPMessage *res; LDAPMessage *entry; BerElement *ber; @@ -668,12 +715,20 @@ *ret = NULL; - rc = lak_filter(lak, user, realm, &filter); + rc = lak_filter(lak, lak->conf->filter, user, realm, &filter); if (rc != LAK_OK) { + syslog(LOG_WARNING|LOG_AUTH, "lak_filter failed for search filter."); return LAK_FAIL; } - rc = lak_search(lak, filter, attrs, &res); + /* expand the search base filter */ + rc = lak_filter(lak, lak->conf->search_base, user, realm, &search_base); + if (rc != LAK_OK) { + syslog(LOG_WARNING|LOG_AUTH, "lak_filter failed for search base."); + return LAK_FAIL; + } + + rc = lak_search(lak, search_base, filter, attrs, &res); if (rc != LAK_OK) { free(filter); return LAK_FAIL; @@ -749,18 +804,25 @@ static int lak_auth_bind(LAK *lak, const char *user, const char *realm, const char *password) { - char *filter; + char *filter, *search_base; int rc; char *dn; LDAPMessage *res, *entry; - rc = lak_filter(lak, user, realm, &filter); + rc = lak_filter(lak, lak->conf->filter, user, realm, &filter); + if (rc != LAK_OK) { + syslog(LOG_WARNING|LOG_AUTH, "lak_filter failed for search filter."); + return LAK_FAIL; + } + + /* expand the search base filter */ + rc = lak_filter(lak, lak->conf->search_base, user, realm, &search_base); if (rc != LAK_OK) { - syslog(LOG_WARNING|LOG_AUTH, "lak_filter failed."); + syslog(LOG_WARNING|LOG_AUTH, "lak_filter failed for search base."); return LAK_FAIL; } - rc = lak_search(lak, filter, NULL, &res); + rc = lak_search(lak, search_base, filter, NULL, &res); if (rc != LAK_OK) { free(filter); return LAK_FAIL;