summaryrefslogtreecommitdiff
path: root/gnu/usr.sbin/sendmail/libsm/ldap.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.sbin/sendmail/libsm/ldap.c')
-rw-r--r--gnu/usr.sbin/sendmail/libsm/ldap.c569
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
**