--- lib/imapoptions 27 Aug 2008 08:28:47 -0000 1.55 +++ lib/imapoptions 18 Sep 2008 23:53:44 -0000 @@ -376,7 +376,7 @@ %D = user dn. (use when ldap_member_method: filter) %1-9 = domain tokens (%1 = tld, %2 = domain when %d = domain.tld) - ldap_filter is not used when ldap_sasl is enabled. */ + ldap_filter is not used when ldap_proxy_authz is enabled. */ { "ldap_group_base", "", STRING } /* LDAP base dn for ldap_group_filter. */ @@ -385,6 +385,16 @@ /* Specify a filter that searches for group identifiers. See ldap_filter for more options. */ +{ "ldap_group_method", "filter", STRINGLIST("filter", "attribute", "none") } +/* Specify a group validation method. The "attribute" method + uses a filter, specified by ldap_group_filter, to find (person) + entries which contain the group. + + The "filter" method uses a filter, specified by ldap_group_filter, + to find a group. + + The "none" method allows any group name which can be canonicalized. */ + { "ldap_group_scope", "sub", STRINGLIST("sub", "one", "base") } /* Specify search scope for ldap_group_filter. */ @@ -405,8 +415,9 @@ See ldap_filter for more options. */ { "ldap_member_method", "attribute", STRINGLIST("attribute", "filter") } -/* Specify a group method. The "attribute" method retrieves groups from - a multi-valued attribute specified in ldap_member_attribute. +/* Specify a group membership population method. The "attribute" method + retrieves groups from a multi-valued attribute specified in + ldap_member_attribute. The "filter" method uses a filter, specified by ldap_member_filter, to find groups; ldap_member_attribute is a single-value attribute group name. */ @@ -418,6 +429,11 @@ /* Password for the connection to the LDAP server (SASL and simple bind). Do not use for anonymous simple binds */ +{ "ldap_proxy_authz", "legacy", STRINGLIST("legacy", "on", "off") } +/* Use LDAP proxy authz control to determine user DN. A value of "legacy" + causes ldap_sasl to control this behavior. A value of "off" means use + ldap_filter to determine the user DN. */ + { "ldap_realm", NULL, STRING } /* SASL realm for LDAP authentication */ @@ -453,7 +469,8 @@ /* Deprecated. Use ldap_uri */ { "ldap_size_limit", 1, INT } -/* Specify a number of entries for a search request to return. */ +/* Deprecated. This value is ignored. Instead, a size limit of 1 is used + where appropriate and LDAP_NO_LIMIT otherwise. */ { "ldap_start_tls", 0, SWITCH } /* Use StartTLS extended operation. Do not use ldaps: ldap_uri when @@ -491,7 +508,7 @@ { "ldap_version", 3, INT } /* Specify the LDAP protocol version. If ldap_start_tls and/or - ldap_use_sasl are enabled, ldap_version will be automatically + ldap_sasl are enabled, ldap_version will be automatically set to 3. */ { "lmtp_downcase_rcpt", 0, SWITCH } --- ptclient/ldap.c 10 Apr 2008 18:03:47 -0000 1.15 +++ ptclient/ldap.c 18 Sep 2008 23:53:44 -0000 @@ -133,7 +133,6 @@ const char *uri; int version; struct timeval timeout; - int size_limit; int time_limit; int deref; int referrals; @@ -141,6 +140,7 @@ int scope; const char *base; int sasl; + int proxy_authz; const char *id; const char *bind_dn; const char *password; @@ -161,6 +161,7 @@ const char *member_filter; const char *member_base; int member_scope; + int group_method; const char *group_filter; const char *group_base; int group_scope; @@ -175,6 +176,10 @@ #define PTSM_MEMBER_METHOD_ATTRIBUTE 0 #define PTSM_MEMBER_METHOD_FILTER 1 +#define PTSM_GROUP_METHOD_ATTRIBUTE 0 +#define PTSM_GROUP_METHOD_FILTER 1 +#define PTSM_GROUP_METHOD_NONE 2 + #define ISSET(x) ((x != NULL) && (*(x) != '\0')) #define EMPTY(x) ((x == NULL) || (*(x) == '\0')) @@ -368,10 +373,6 @@ syslog(LOG_WARNING, "Unable to set LDAP_OPT_REFERRALS."); } - rc = ldap_set_option(ptsm->ld, LDAP_OPT_SIZELIMIT, &(ptsm->size_limit)); - if (rc != LDAP_OPT_SUCCESS) - syslog(LOG_WARNING, "Unable to set LDAP_OPT_SIZELIMIT %d.", ptsm->size_limit); - rc = ldap_set_option(ptsm->ld, LDAP_OPT_RESTART, ptsm->restart ? LDAP_OPT_ON : LDAP_OPT_OFF); if (rc != LDAP_OPT_SUCCESS) { syslog(LOG_WARNING, "Unable to set LDAP_OPT_RESTART."); @@ -460,7 +461,6 @@ ptsm->deref = LDAP_DEREF_NEVER; } ptsm->referrals = config_getswitch(IMAPOPT_LDAP_REFERRALS); - ptsm->size_limit = config_getint(IMAPOPT_LDAP_SIZE_LIMIT); ptsm->time_limit = config_getint(IMAPOPT_LDAP_TIME_LIMIT); p = config_getstring(IMAPOPT_LDAP_SCOPE); if (!strcasecmp(p, "one")) { @@ -472,6 +472,14 @@ } ptsm->bind_dn = config_getstring(IMAPOPT_LDAP_BIND_DN); ptsm->sasl = config_getswitch(IMAPOPT_LDAP_SASL); + p = config_getswitch(IMAPOPT_LDAP_PROXY_AUTHZ); + if (!strcasecmp(p, "legacy")) { + ptsm->proxy_authz = ptsm->sasl; + } else if (!strcasecmp(p, "on")) { + ptsm->proxy_authz = 1; + } else { + ptsm->proxy_authz = 0; + } ptsm->id = (config_getstring(IMAPOPT_LDAP_ID) ? config_getstring(IMAPOPT_LDAP_ID) : config_getstring(IMAPOPT_LDAP_SASL_AUTHC)); ptsm->authz = (config_getstring(IMAPOPT_LDAP_AUTHZ) ? @@ -507,6 +515,14 @@ ptsm->member_base = config_getstring(IMAPOPT_LDAP_MEMBER_BASE); ptsm->member_attribute = (config_getstring(IMAPOPT_LDAP_MEMBER_ATTRIBUTE) ? config_getstring(IMAPOPT_LDAP_MEMBER_ATTRIBUTE) : config_getstring(IMAPOPT_LDAP_MEMBER_ATTRIBUTE)); + p = config_getstring(IMAPOPT_LDAP_GROUP_METHOD); + if (!strcasecmp(p, "filter")) { + ptsm->group_method = PTSM_GROUP_METHOD_FILTER; + } else if (!strcasecmp(p, "attribute")) { + ptsm->group_method = PTSM_GROUP_METHOD_ATTRIBUTE; + } else { + ptsm->group_method = PTSM_GROUP_METHOD_NONE; + } p = config_getstring(IMAPOPT_LDAP_GROUP_SCOPE); if (!strcasecmp(p, "one")) { ptsm->group_scope = LDAP_SCOPE_ONELEVEL; @@ -781,6 +797,7 @@ char **ret) { int rc; + int size_limit; #if LDAP_VENDOR_VERSION >= 20125 struct berval *dn = NULL; @@ -789,7 +806,7 @@ char *authzid; #endif char *base = NULL, *filter = NULL; - char *attrs[] = {NULL}; + char *attrs[] = {LDAP_NO_ATTRS, NULL}; LDAPMessage *res; LDAPMessage *entry; char *attr, **vals; @@ -802,7 +819,7 @@ #if LDAP_VENDOR_VERSION >= 20125 - if (ptsm->sasl) { + if (ptsm->proxy_authz) { authzid = xmalloc(size + sizeof("u:")); if (authzid == NULL) @@ -846,6 +863,11 @@ if (rc != PTSM_OK) return rc; + size_limit = 1; + rc = ldap_set_option(ptsm->ld, LDAP_OPT_SIZELIMIT, &size_limit); + if (rc != LDAP_OPT_SUCCESS) + syslog(LOG_WARNING, "Unable to set LDAP_OPT_SIZELIMIT %d.", size_limit); + rc = ldap_search_st(ptsm->ld, base, ptsm->scope, filter, attrs, 0, &(ptsm->timeout), &res); free(filter); free(base); @@ -986,6 +1008,7 @@ { char *base = NULL, *filter = NULL; int rc; + int size_limit; int i; int n; LDAPMessage *res = NULL; LDAPMessage *entry = NULL; @@ -1017,6 +1040,11 @@ goto done; } + size_limit = LDAP_NO_LIMIT; + rc = ldap_set_option(ptsm->ld, LDAP_OPT_SIZELIMIT, &size_limit); + if (rc != LDAP_OPT_SUCCESS) + syslog(LOG_WARNING, "Unable to set LDAP_OPT_SIZELIMIT %d.", size_limit); + rc = ldap_search_st(ptsm->ld, base, ptsm->member_scope, filter, attrs, 0, &(ptsm->timeout), &res); if (rc != LDAP_SUCCESS) { *reply = "ldap_search(filter) failed"; @@ -1024,8 +1052,15 @@ ldap_unbind(ptsm->ld); ptsm->ld = NULL; rc = PTSM_RETRY; - } else + } else { + /* + * We do have a (truncated) result in this case. Should we use it? + */ + if (rc == LDAP_SIZELIMIT_EXCEEDED) { + *reply = "ldap_search(filter) size limit exceeded"; + } rc = PTSM_FAIL; + } goto done; } @@ -1044,7 +1079,7 @@ rc = PTSM_FAIL; goto done; } - (*newstate)->ngroups = n; + strcpy((*newstate)->userid.id, canon_id); (*newstate)->userid.hash = strhash(canon_id); (*newstate)->mark = time(0); @@ -1105,17 +1140,20 @@ { char *base = NULL, *filter = NULL; int rc; + int size_limit; int i; int n; LDAPMessage *res = NULL; - LDAPMessage *entry = NULL; - char **vals = NULL; - char *attrs[] = {NULL}; + char *attrs[] = {LDAP_NO_ATTRS, NULL}; if (strncmp(canon_id, "group:", 6)) { // Sanity check *reply = "not a group identifier"; return PTSM_FAIL; } + if (ptsm->group_method == PTSM_GROUP_METHOD_NONE) { + goto none; + } + rc = ptsmodule_connect(); if (rc != PTSM_OK) { *reply = "ptsmodule_connect() failed"; @@ -1134,6 +1172,16 @@ goto done; } + + if (ptsm->group_method == PTSM_GROUP_METHOD_FILTER) { + size_limit = 1; + } else { + size_limit = LDAP_NO_LIMIT; + } + rc = ldap_set_option(ptsm->ld, LDAP_OPT_SIZELIMIT, &size_limit); + if (rc != LDAP_OPT_SUCCESS) + syslog(LOG_WARNING, "Unable to set LDAP_OPT_SIZELIMIT %d.", size_limit); + rc = ldap_search_st(ptsm->ld, base, ptsm->group_scope, filter, attrs, 0, &(ptsm->timeout), &res); if (rc != LDAP_SUCCESS) { *reply = "ldap_search(group) failed"; @@ -1141,20 +1189,39 @@ ldap_unbind(ptsm->ld); ptsm->ld = NULL; rc = PTSM_RETRY; - } else + goto done; + } else if (rc == LDAP_SIZELIMIT_EXCEEDED) { + /* + * If the method is "attribute", size limit doesn't + * indicate a problem. + */ + if (ptsm->group_method == PTSM_GROUP_METHOD_FILTER) { + *reply = "ambiguous group identifier found (size limit)"; + rc = PTSM_FAIL; + goto done; + } + } else { rc = PTSM_FAIL; - goto done; + goto done; + } } n = ldap_count_entries(ptsm->ld, res); - if (n != 1) { + if (n == 0) { *reply = "group identifier not found"; rc = PTSM_FAIL; goto done; } - *dsize = sizeof(struct auth_state) + - (n * sizeof(struct auth_ident)); + if (n > 1 && ptsm->group_method == PTSM_GROUP_METHOD_FILTER) { + *reply = "ambiguous group identifier found"; + rc = PTSM_FAIL; + goto done; + } + +none:; + + *dsize = sizeof(struct auth_state); *newstate = xmalloc(*dsize); if (*newstate == NULL) { *reply = "no memory";