diff options
Diffstat (limited to 'gnu/usr.sbin/sendmail/libsm/ldap.c')
-rw-r--r-- | gnu/usr.sbin/sendmail/libsm/ldap.c | 569 |
1 files changed, 567 insertions, 2 deletions
diff --git a/gnu/usr.sbin/sendmail/libsm/ldap.c b/gnu/usr.sbin/sendmail/libsm/ldap.c index 2f7af53c3cd..f1eaa76d2ae 100644 --- a/gnu/usr.sbin/sendmail/libsm/ldap.c +++ b/gnu/usr.sbin/sendmail/libsm/ldap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 Sendmail, Inc. and its suppliers. + * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -8,7 +8,7 @@ */ #include <sm/gen.h> -SM_RCSID("@(#)$Sendmail: ldap.c,v 1.10 2001/09/11 04:04:48 gshapiro Exp $") +SM_RCSID("@(#)$Sendmail: ldap.c,v 1.18 2002/01/11 22:06:51 gshapiro Exp $") #if LDAPMAP # include <sys/types.h> @@ -24,6 +24,7 @@ SM_RCSID("@(#)$Sendmail: ldap.c,v 1.10 2001/09/11 04:04:48 gshapiro Exp $") # include <sm/errstring.h> # include <sm/ldap.h> # include <sm/string.h> +# include <sm/sysexits.h> SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap", "@(#)$Debug: sm_trace_ldap - trace LDAP operations $"); @@ -70,6 +71,10 @@ sm_ldap_clear(lmap) lmap->ldap_ld = NULL; lmap->ldap_filter = NULL; lmap->ldap_attr[0] = NULL; +#if _FFR_LDAP_RECURSION + lmap->ldap_attr_type[0] = LDAPMAP_ATTR_NORMAL; + lmap->ldap_attr_final[0] = NULL; +#endif /* _FFR_LDAP_RECURSION */ lmap->ldap_res = NULL; lmap->ldap_next = NULL; lmap->ldap_pid = 0; @@ -300,6 +305,566 @@ sm_ldap_search(lmap, key) return msgid; } +# if _FFR_LDAP_RECURSION +/* +** SM_LDAP_RESULTS -- return results from an LDAP lookup in result +** +** Parameters: +** lmap -- pointer to SM_LDAP_STRUCT in use +** msgid -- msgid returned by sm_ldap_search() +** flags -- flags for the lookup +** delim -- delimiter for result concatenation +** rpool -- memory pool for storage +** result -- return string +** recurse -- recursion list +** +** Returns: +** status (sysexit) +*/ + +# define LDAPMAP_ERROR_CLEANUP() \ +{ \ + if (lmap->ldap_res != NULL) \ + { \ + ldap_msgfree(lmap->ldap_res); \ + lmap->ldap_res = NULL; \ + } \ + (void) ldap_abandon(lmap->ldap_ld, msgid); \ +} + +static int +ldapmap_add_recurse(top, item, type, rpool) + SM_LDAP_RECURSE_LIST **top; + char *item; + int type; + SM_RPOOL_T *rpool; +{ + SM_LDAP_RECURSE_LIST *p; + SM_LDAP_RECURSE_LIST *last; + + last = NULL; + for (p = *top; p != NULL; p = p->lr_next) + { + if (strcasecmp(item, p->lr_search) == 0 && + type == p->lr_type) + { + /* already on list */ + return 1; + } + last = p; + } + + /* not on list, add it */ + p = sm_rpool_malloc_x(rpool, sizeof *p); + p->lr_search = sm_rpool_strdup_x(rpool, item); + p->lr_type = type; + p->lr_next = NULL; + if (last == NULL) + *top = p; + else + last->lr_next = p; + return 0; +} + +int +sm_ldap_results(lmap, msgid, flags, delim, rpool, result, recurse) + SM_LDAP_STRUCT *lmap; + int msgid; + int flags; + char delim; + SM_RPOOL_T *rpool; + char **result; + SM_LDAP_RECURSE_LIST *recurse; +{ + bool toplevel; + int i; + int entries = 0; + int statp; + int vsize; + int ret; + int save_errno; + char *p; + + /* Are we the top top level of the search? */ + toplevel = (recurse == NULL); + + /* Get results */ + statp = EX_NOTFOUND; + while ((ret = ldap_result(lmap->ldap_ld, msgid, 0, + (lmap->ldap_timeout.tv_sec == 0 ? NULL : + &(lmap->ldap_timeout)), + &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY) + { + LDAPMessage *entry; + + if (bitset(SM_LDAP_SINGLEMATCH, flags)) + { + entries += ldap_count_entries(lmap->ldap_ld, + lmap->ldap_res); + if (entries > 1) + { + LDAPMAP_ERROR_CLEANUP(); + errno = ENOENT; + return EX_NOTFOUND; + } + } + + /* If we don't want multiple values and we have one, break */ + if (delim == '\0' && *result != NULL) + break; + + /* Cycle through all entries */ + for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); + entry != NULL; + entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) + { + BerElement *ber; + char *attr; + char **vals = NULL; + char *dn; + + /* + ** If matching only and found an entry, + ** no need to spin through attributes + */ + + if (statp == EX_OK && + bitset(SM_LDAP_MATCHONLY, flags)) + continue; + + /* record completed DN's to prevent loops */ + dn = ldap_get_dn(lmap->ldap_ld, entry); + if (dn == NULL) + { + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + save_errno += E_LDAPBASE; + LDAPMAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_OSERR; + } + + switch (ldapmap_add_recurse(&recurse, dn, + LDAPMAP_ATTR_NORMAL, + rpool)) + { + case -1: + /* error adding */ + ldap_memfree(dn); + LDAPMAP_ERROR_CLEANUP(); + errno = ENOMEM; + return EX_OSERR; + + case 1: + /* already on list, skip it */ + ldap_memfree(dn); + continue; + } + ldap_memfree(dn); + +# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) + /* + ** Reset value to prevent lingering + ** LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see below) + */ + + lmap->ldap_ld->ld_errno = LDAP_SUCCESS; +# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ + + for (attr = ldap_first_attribute(lmap->ldap_ld, entry, + &ber); + attr != NULL; + attr = ldap_next_attribute(lmap->ldap_ld, entry, + ber)) + { + char *tmp, *vp_tmp; + int type; + + for (i = 0; lmap->ldap_attr[i] != NULL; i++) + { + if (sm_strcasecmp(lmap->ldap_attr[i], + attr) == 0) + { + type = lmap->ldap_attr_type[i]; + break; + } + } + if (lmap->ldap_attr[i] == NULL) + { + /* attribute not requested */ +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + LDAPMAP_ERROR_CLEANUP(); + errno = EFAULT; + return EX_SOFTWARE; + } + + if (lmap->ldap_attrsonly == LDAPMAP_FALSE) + { + vals = ldap_get_values(lmap->ldap_ld, + entry, + attr); + if (vals == NULL) + { + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + if (save_errno == LDAP_SUCCESS) + { +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + continue; + } + + /* Must be an error */ + save_errno += E_LDAPBASE; +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + LDAPMAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + } + + statp = EX_OK; + +# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) + /* + ** Reset value to prevent lingering + ** LDAP_DECODING_ERROR due to + ** OpenLDAP 1.X's hack (see below) + */ + + lmap->ldap_ld->ld_errno = LDAP_SUCCESS; +# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ + + /* + ** If matching only, + ** no need to spin through entries + */ + + if (bitset(SM_LDAP_MATCHONLY, flags)) + { + if (lmap->ldap_attrsonly == LDAPMAP_FALSE) + ldap_value_free(vals); + +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + continue; + } + + /* + ** If we don't want multiple values, + ** return first found. + */ + + if (delim == '\0') + { + if (lmap->ldap_attrsonly == LDAPMAP_TRUE) + { + *result = sm_rpool_strdup_x(rpool, + attr); +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + break; + } + + if (vals[0] == NULL) + { + ldap_value_free(vals); +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + continue; + } + + vsize = strlen(vals[0]) + 1; + if (lmap->ldap_attrsep != '\0') + vsize += strlen(attr) + 1; + *result = sm_rpool_malloc_x(rpool, + vsize); + if (lmap->ldap_attrsep != '\0') + sm_snprintf(*result, vsize, + "%s%c%s", + attr, + lmap->ldap_attrsep, + vals[0]); + else + sm_strlcpy(*result, vals[0], + vsize); + ldap_value_free(vals); +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + break; + } + + /* attributes only */ + if (lmap->ldap_attrsonly == LDAPMAP_TRUE) + { + if (*result == NULL) + *result = sm_rpool_strdup_x(rpool, + attr); + else + { + vsize = strlen(*result) + + strlen(attr) + 2; + tmp = sm_rpool_malloc_x(rpool, + vsize); + (void) sm_snprintf(tmp, + vsize, "%s%c%s", + *result, delim, + attr); + *result = tmp; + } +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + continue; + } + + /* + ** If there is more than one, + ** munge then into a map_coldelim + ** separated string + */ + + vsize = 0; + for (i = 0; vals[i] != NULL; i++) + { + if (type == LDAPMAP_ATTR_DN || + type == LDAPMAP_ATTR_FILTER || + type == LDAPMAP_ATTR_URL) + { + if (ldapmap_add_recurse(&recurse, + vals[i], + type) < 0) + { + LDAPMAP_ERROR_CLEANUP(); + errno = ENOMEM; + return EX_OSERR; + } + } + if (type != LDAPMAP_ATTR_NORMAL) + { +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + continue; + } + vsize += strlen(vals[i]) + 1; + if (lmap->ldap_attrsep != '\0') + vsize += strlen(attr) + 1; + } + vp_tmp = sm_rpool_malloc_x(rpool, vsize); + *vp_tmp = '\0'; + + p = vp_tmp; + for (i = 0; vals[i] != NULL; i++) + { + if (lmap->ldap_attrsep != '\0') + { + p += sm_strlcpy(p, attr, + vsize - (p - vp_tmp)); + *p++ = lmap->ldap_attrsep; + } + p += sm_strlcpy(p, vals[i], + vsize - (p - vp_tmp)); + if (p >= vp_tmp + vsize) + { + /* Internal error: buffer too small for LDAP values */ + LDAPMAP_ERROR_CLEANUP(); + errno = ENOMEM; + return EX_OSERR; + } + if (vals[i + 1] != NULL) + *p++ = delim; + } + + ldap_value_free(vals); +# if USING_NETSCAPE_LDAP + ldap_memfree(attr); +# endif /* USING_NETSCAPE_LDAP */ + if (*result == NULL) + { + *result = vp_tmp; + continue; + } + vsize = strlen(*result) + strlen(vp_tmp) + 2; + tmp = sm_rpool_malloc_x(rpool, vsize); + (void) sm_snprintf(tmp, vsize, "%s%c%s", + *result, delim, vp_tmp); + *result = tmp; + } + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + + /* + ** We check save_errno != LDAP_DECODING_ERROR since + ** OpenLDAP 1.X has a very ugly *undocumented* + ** hack of returning this error code from + ** ldap_next_attribute() if the library freed the + ** ber attribute. See: + ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html + */ + + if (save_errno != LDAP_SUCCESS && + save_errno != LDAP_DECODING_ERROR) + { + /* Must be an error */ + save_errno += E_LDAPBASE; + LDAPMAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + + /* We don't want multiple values and we have one */ + if (delim == '\0' && *result != NULL) + break; + } + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + if (save_errno != LDAP_SUCCESS && + save_errno != LDAP_DECODING_ERROR) + { + /* Must be an error */ + save_errno += E_LDAPBASE; + LDAPMAP_ERROR_CLEANUP(); + errno = save_errno; + return EX_TEMPFAIL; + } + ldap_msgfree(lmap->ldap_res); + lmap->ldap_res = NULL; + } + + if (ret == 0) + save_errno = ETIMEDOUT; + else + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + if (save_errno != LDAP_SUCCESS) + { + statp = EX_TEMPFAIL; + if (ret != 0) + { + switch (save_errno) + { +#ifdef LDAP_SERVER_DOWN + case LDAP_SERVER_DOWN: +#endif /* LDAP_SERVER_DOWN */ + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + /* server disappeared, try reopen on next search */ + statp = EX_RESTART; + break; + } + save_errno += E_LDAPBASE; + } + LDAPMAP_ERROR_CLEANUP(); + errno = save_errno; + return statp; + } + + if (lmap->ldap_res != NULL) + { + ldap_msgfree(lmap->ldap_res); + lmap->ldap_res = NULL; + } + + if (toplevel) + { + SM_LDAP_RECURSE_LIST *rl; + + /* + ** Spin through the built-up recurse list at the top + ** of the recursion. Since new items are added at the + ** end of the shared list, we actually only ever get + ** one level of recursion before things pop back to the + ** top. Any items added to the list during that recursion + ** will be expanded by the top level. + */ + + for (rl = recurse; rl != NULL; rl = rl->lr_next) + { + int sid; + int status; + + if (rl->lr_type == LDAPMAP_ATTR_NORMAL) + { + /* already expanded */ + continue; + } + else if (rl->lr_type == LDAPMAP_ATTR_DN) + { + /* do DN search */ + sid = ldap_search(lmap->ldap_ld, + rl->lr_search, + lmap->ldap_scope, + "(objectClass=*)", + lmap->ldap_attr_final, + lmap->ldap_attrsonly); + } + else if (rl->lr_type == LDAPMAP_ATTR_FILTER) + { + /* do new search */ + sid = ldap_search(lmap->ldap_ld, + lmap->ldap_base, + lmap->ldap_scope, + rl->lr_search, + lmap->ldap_attr_final, + lmap->ldap_attrsonly); + } + else if (rl->lr_type == LDAPMAP_ATTR_URL) + { + /* do new URL search */ + sid = ldap_url_search(lmap->ldap_ld, + rl->lr_search, + lmap->ldap_attrsonly); + } + else + { + /* unknown or illegal attribute type */ + errno = EFAULT; + return EX_SOFTWARE; + } + + /* Collect results */ + if (sid == -1) + { + save_errno = sm_ldap_geterrno(lmap->ldap_ld); + statp = EX_TEMPFAIL; + switch (save_errno) + { +#ifdef LDAP_SERVER_DOWN + case LDAP_SERVER_DOWN: +#endif /* LDAP_SERVER_DOWN */ + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + /* server disappeared, try reopen on next search */ + statp = EX_RESTART; + break; + } + errno = save_errno + E_LDAPBASE; + return statp; + } + + status = sm_ldap_results(lmap, sid, flags, delim, + rpool, result, recurse); + save_errno = errno; + if (status != EX_OK && status != EX_NOTFOUND) + { + errno = save_errno; + return status; + } + + /* Mark as done */ + rl->lr_type = LDAPMAP_ATTR_NORMAL; + } + } + return statp; +} +#endif /* _FFR_LDAP_RECURSION */ + /* ** SM_LDAP_CLOSE -- close LDAP connection ** |