--- lib/imapoptions 22 Apr 2008 11:06:03 -0000 1.53 +++ lib/imapoptions 29 May 2008 19:29:12 -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,9 @@ /* Password for the connection to the LDAP server (SASL and simple bind). Do not use for anonymous simple binds */ +{ "ldap_proxy_authz", 0, SWITCH } +/* Use LDAP proxy authz control to determine user DN. */ + { "ldap_realm", NULL, STRING } /* SASL realm for LDAP authentication */ @@ -452,7 +466,7 @@ { "ldap_servers", "ldap://localhost/", STRING } /* Deprecated. Use ldap_uri */ -{ "ldap_size_limit", 1, INT } +{ "ldap_size_limit", 2, INT } /* Specify a number of entries for a search request to return. */ { "ldap_start_tls", 0, SWITCH } @@ -491,7 +505,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 } Index: ptclient/ldap.c =================================================================== RCS file: /afs/andrew/system/cvs/src/cyrus/ptclient/ldap.c,v retrieving revision 1.15 diff -u -r1.15 ldap.c --- ptclient/ldap.c 10 Apr 2008 18:03:47 -0000 1.15 +++ ptclient/ldap.c 29 May 2008 19:29:13 -0000 @@ -141,6 +141,7 @@ int scope; const char *base; int sasl; + int proxy_authz; const char *id; const char *bind_dn; const char *password; @@ -161,6 +162,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 +177,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')) @@ -472,6 +478,7 @@ } ptsm->bind_dn = config_getstring(IMAPOPT_LDAP_BIND_DN); ptsm->sasl = config_getswitch(IMAPOPT_LDAP_SASL); + ptsm->proxy_authz = config_getswitch(IMAPOPT_LDAP_PROXY_AUTHZ); 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 +514,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; @@ -789,7 +804,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 +817,7 @@ #if LDAP_VENDOR_VERSION >= 20125 - if (ptsm->sasl) { + if (ptsm->proxy_authz) { authzid = xmalloc(size + sizeof("u:")); if (authzid == NULL) @@ -1024,6 +1039,12 @@ ldap_unbind(ptsm->ld); ptsm->ld = NULL; rc = PTSM_RETRY; + } else if (rc == LDAP_SIZELIMIT_EXCEEDED) { + /* + * We do have a (truncated) result in this case. Should we use it? + */ + *reply = "ldap_search(filter) size limit exceeded"; + rc = PTSM_FAIL; } else rc = PTSM_FAIL; goto done; @@ -1107,15 +1128,17 @@ int rc; 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"; @@ -1143,18 +1166,32 @@ rc = PTSM_RETRY; } else rc = PTSM_FAIL; - goto done; + + /* + * If the method is "attribute", size limit doesn't indicate a problem. + */ + if (rc == LDAP_SIZELIMIT_EXCEEDED && + ptsm->group_method == PTSM_GROUP_METHOD_FILTER) { + 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"; Index: ptclient/ptloader.c =================================================================== RCS file: /afs/andrew/system/cvs/src/cyrus/ptclient/ptloader.c,v retrieving revision 1.48 diff -u -r1.48 ptloader.c --- ptclient/ptloader.c 24 Mar 2008 18:34:22 -0000 1.48 +++ ptclient/ptloader.c 29 May 2008 19:29:13 -0000 @@ -203,7 +203,6 @@ char **argv __attribute__((unused)), char **envp __attribute__((unused))) { - char keyinhex[512]; const char *reply = NULL; char user[PTS_DB_KEYSIZE]; int rc, dsize; @@ -232,17 +231,15 @@ memset(&user, 0, sizeof(user)); if (read(c, &user, size) < 0) { - syslog(LOG_ERR, "socket(user; size = %d; key = %s): %m", - size, keyinhex); + syslog(LOG_ERR, "socket(user; size = %d): %m", size); reply = "Error reading request (user)"; goto sendreply; } if (ptclient_debug) { - syslog(LOG_DEBUG, "user %s, cacheid %s", user, keyinhex); + syslog(LOG_DEBUG, "user %s", user); } - newstate = ptsmodule_make_authstate(user, size, &reply, &dsize); if(newstate) { @@ -260,7 +257,7 @@ } sendreply: - if (retry_write(c, reply, strlen(reply)) <0) { + if (retry_write(c, reply, strlen(reply) + 1) <0) { syslog(LOG_WARNING, "retry_write: %m"); } close(c);