--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 if the
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file ldap.c
+ * @brief LDAP module library functions.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
+ * @copyright 2013 The FreeRADIUS Server Project.
+ */
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <stdarg.h>
+#include <ctype.h>
+
+#include <lber.h>
+#include <ldap.h>
+#include "ldap.h"
+
+
+
+/** Converts "bad" strings into ones which are safe for LDAP
+ *
+ * This is a callback for xlat operations.
+ *
+ * Will escape any characters in input strings that would cause the string to be interpreted as part of a DN and or
+ * filter. Escape sequence is \<hex><hex>.
+ *
+ * @param request The current request.
+ * @param out Pointer to output buffer.
+ * @param outlen Size of the output buffer.
+ * @param in Raw unescaped string.
+ * @param arg Any additional arguments (unused).
+ */
+size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, const char *in, UNUSED void *arg)
+{
+ static const char encode[] = ",+\"\\<>;*=()";
+ static const char hextab[] = "0123456789abcdef";
+ size_t left = outlen;
+
+ if (*in && ((*in == ' ') || (*in == '#'))) {
+ goto encode;
+ }
+
+ while (*in) {
+ /*
+ * Encode unsafe characters.
+ */
+ if (memchr(encode, *in, sizeof(encode) - 1)) {
+ encode:
+
+ /*
+ * Only 3 or less bytes available.
+ */
+ if (left <= 3) break;
+
+ *out++ = '\\';
+ *out++ = hextab[(*in >> 4) & 0x0f];
+ *out++ = hextab[*in & 0x0f];
+ in++;
+ left -= 3;
+
+ continue;
+ }
+
+ if (left <= 1) break;
+
+ /*
+ * Doesn't need encoding
+ */
+ *out++ = *in++;
+ left--;
+ }
+
+ *out = '\0';
+
+ return outlen - left;
+}
+
+/** Check whether a string is a DN
+ *
+ * @param str to check.
+ * @return true if string is a DN, else false.
+ */
+int rlm_ldap_is_dn(const char *str)
+{
+ return strrchr(str, ',') == NULL ? FALSE : TRUE;
+}
+
+/** Find the place at which the two DN strings diverge
+ *
+ * Returns the length of the non matching string in full.
+ *
+ * @param full DN.
+ * @param part Partial DN as returned by ldap_parse_result.
+ * @param the length of the portion of full which wasn't matched or -1 on error.
+ */
+static size_t rlm_ldap_common_dn(const char *full, const char *part)
+{
+ size_t f_len, p_len, i;
+
+ if (!full) {
+ return -1;
+ }
+
+ f_len = strlen(full);
+
+ if (!part) {
+ return f_len;
+ }
+
+ p_len = strlen(part);
+ if (!p_len) {
+ return f_len;
+ }
+
+ if ((f_len < p_len) || !f_len) {
+ return -1;
+ }
+
+
+ for (i = 0; i < p_len; i++) {
+ if (part[p_len - i] != full[f_len - i]) {
+ return -1;
+ }
+ }
+
+ return f_len - p_len;
+}
+
+/** Parse response from LDAP server dealing with any errors
+ *
+ * Should be called after an LDAP operation. Will check result of operation and if it was successful, then attempt
+ * to retrieve and parse the result.
+ *
+ * Will also produce extended error output including any messages the server sent, and information about partial
+ * DN matches.
+ *
+ * @param[in] inst of LDAP module.
+ * @param[in] conn Current connection.
+ * @param[in] msgid returned from last operation.
+ * @param[in] dn Last search or bind DN.
+ * @param[out] result Where to write result, if NULL result will be freed.
+ * @param[out] error Where to write the error string, may be NULL, must not be freed.
+ * @param[out] extra Where to write additional error string to, may be NULL (faster) or must be freed
+ * (with talloc_free).
+ * @return One of the LDAP_PROC_* codes.
+ */
+static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_handle_t *conn, int msgid, const char *dn,
+ LDAPMessage **result, const char **error, char **extra)
+{
+ ldap_rcode_t status = LDAP_PROC_SUCCESS;
+
+ int lib_errno = LDAP_SUCCESS; //!< errno returned by the library.
+ int srv_errno = LDAP_SUCCESS; //!< errno in the result message.
+
+ char *part_dn = NULL; //!< Partial DN match.
+ char *our_err = NULL; //!< Our extended error message.
+ char *srv_err = NULL; //!< Server's extended error message.
+ char *p, *a;
+
+ int freeit = FALSE; //!< Whether the message should
+ //!< be freed after being processed.
+ int len;
+
+ struct timeval tv; //!< Holds timeout values.
+
+ LDAPMessage *tmp_msg; //!< Temporary message pointer storage
+ //!< if we weren't provided with one.
+
+ const char *tmp_err; //!< Temporary error pointer storage
+ //!< if we weren't provided with one.
+
+ if (!error) {
+ error = &tmp_err;
+ }
+ *error = NULL;
+
+ if (extra) {
+ *extra = NULL;
+ }
+
+ /*
+ * We always need the result, but our caller may not
+ */
+ if (!result) {
+ result = &tmp_msg;
+ freeit = TRUE;
+ }
+
+ /*
+ * Check if there was an error sending the request
+ */
+ ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+ &lib_errno);
+ if (lib_errno != LDAP_SUCCESS) {
+ goto process_error;
+ }
+
+ tv.tv_sec = inst->timeout;
+ tv.tv_usec = 0;
+
+ /*
+ * Now retrieve the result and check for errors
+ * ldap_result returns -1 on error, and 0 on timeout
+ */
+ lib_errno = ldap_result(conn->handle, msgid, 1, &tv, result);
+ if (lib_errno == 0) {
+ lib_errno = LDAP_TIMEOUT;
+
+ goto process_error;
+ }
+
+ if (lib_errno == -1) {
+ ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+ &lib_errno);
+ goto process_error;
+ }
+
+ /*
+ * Parse the result and check for errors sent by the server
+ */
+ lib_errno = ldap_parse_result(conn->handle, *result,
+ &srv_errno,
+ extra ? &part_dn : NULL,
+ extra ? extra : NULL,
+ NULL, NULL, freeit);
+
+ if (lib_errno != LDAP_SUCCESS) {
+ ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+ &lib_errno);
+ goto process_error;
+ }
+
+ process_error:
+
+ if ((lib_errno == LDAP_SUCCESS) && (srv_errno != LDAP_SUCCESS)) {
+ lib_errno = srv_errno;
+ } else if ((lib_errno != LDAP_SUCCESS) && (srv_errno == LDAP_SUCCESS)) {
+ srv_errno = lib_errno;
+ }
+
+ switch (lib_errno) {
+ case LDAP_SUCCESS:
+ *error = "Success";
+
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ *error = "The specified object wasn't found, check basedn and admin dn";
+
+ status = LDAP_PROC_BAD_DN;
+
+ if (!extra) break;
+
+ /*
+ * Build our own internal diagnostic string
+ */
+ len = rlm_ldap_common_dn(dn, part_dn);
+ if (len < 0) break;
+
+ our_err = talloc_asprintf(conn, "Match stopped here: [%.*s]%s", len, part_dn, part_dn ? part_dn : "");
+
+ break;
+
+ case LDAP_INSUFFICIENT_ACCESS:
+ *error = "Insufficient access. Check the identity and password configuration directives";
+
+ status = LDAP_PROC_NOT_PERMITTED;
+ break;
+
+ case LDAP_UNWILLING_TO_PERFORM:
+ *error = "Server was unwilling to perform";
+
+ status = LDAP_PROC_NOT_PERMITTED;
+ break;
+
+ case LDAP_TIMEOUT:
+ exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
+
+ *error = "Timed out while waiting for server to respond";
+
+ status = LDAP_PROC_ERROR;
+ break;
+
+ case LDAP_FILTER_ERROR:
+ *error = "Bad search filter";
+
+ status = LDAP_PROC_ERROR;
+ break;
+
+ case LDAP_TIMELIMIT_EXCEEDED:
+ exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
+
+ *error = "Time limit exceeded";
+ /* FALL-THROUGH */
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ case LDAP_SERVER_DOWN:
+ status = LDAP_PROC_RETRY;
+
+ goto error_string;
+
+ case LDAP_INVALID_CREDENTIALS:
+ case LDAP_CONSTRAINT_VIOLATION:
+ status = LDAP_PROC_REJECT;
+
+ goto error_string;
+
+ case LDAP_OPERATIONS_ERROR:
+ *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. See the ldap module configuration "
+ "for details.";
+ /* FALL-THROUGH */
+
+ default:
+ status = LDAP_PROC_ERROR;
+
+ error_string:
+
+ if (!*error) {
+ *error = ldap_err2string(lib_errno);
+ }
+
+ if (!extra || ((lib_errno == srv_errno) && !our_err && !srv_err)) {
+ break;
+ }
+
+ /*
+ * Output the error codes from the library and server
+ */
+ p = talloc_strdup(conn, "");
+ if (!p) break;
+
+ if (lib_errno != srv_errno) {
+ a = talloc_asprintf_append(p, "LDAP lib error: %s (%u), srv error: %s (%u)",
+ ldap_err2string(lib_errno), lib_errno,
+ ldap_err2string(srv_errno), srv_errno);
+ if (!a) {
+ talloc_free(p);
+ break;
+ }
+
+ p = a;
+ }
+
+ if (our_err) {
+ a = talloc_asprintf_append_buffer(p,". %s", our_err);
+ if (!a) {
+ talloc_free(p);
+ break;
+ }
+
+ p = a;
+ }
+
+ if (srv_err) {
+ a = talloc_asprintf_append_buffer(p, ". Server said: %s", srv_err);
+ if (!a) {
+ talloc_free(p);
+ break;
+ }
+
+ p = a;
+ }
+
+ *extra = p;
+
+ break;
+ }
+
+ /*
+ * Cleanup memory
+ */
+ if (srv_err) {
+ ldap_memfree(srv_err);
+ }
+
+ if (part_dn) {
+ ldap_memfree(part_dn);
+ }
+
+ if (our_err) {
+ talloc_free(our_err);
+ }
+
+ if ((lib_errno || srv_errno) && *result) {
+ ldap_msgfree(*result);
+ *result = NULL;
+ }
+
+ return status;
+}
+
+/** Bind to the LDAP directory as a user
+ *
+ * Performs a simple bind to the LDAP directory, and handles any errors that
+ * occur.
+ *
+ * @param request Current request, this may be NULL, in which case all debug logging is done with radlog.
+ * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL after
+ * calling this function.
+ * @param dn The DN of the user, may be NULL to bind anonymously.
+ * @param password May be NULL if no password is specified.
+ * @param retry if the server is down.
+ * @return one of the RLM_MODULE_* values.
+ */
+rlm_rcode_t rlm_ldap_bind(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn, const char *dn,
+ const char *password, int retry)
+{
+ rlm_rcode_t rcode = RLM_MODULE_OK;
+ ldap_rcode_t status;
+
+ int msgid;
+
+ const char *error = NULL;
+ char *extra = NULL;
+
+ /*
+ * Bind as anonymous user
+ */
+ if (!dn) dn = "";
+
+retry:
+ msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
+ /* We got a valid message ID */
+ if (msgid >= 0) {
+ if (request) {
+ RDEBUG2("Waiting for bind result...");
+ } else {
+ DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
+ }
+ }
+
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_NOT_PERMITTED:
+ rcode = RLM_MODULE_USERLOCK;
+
+ LDAP_ERR_REQ("Bind was not permitted: %s", error);
+ LDAP_EXT_REQ();
+
+ break;
+
+ case LDAP_PROC_REJECT:
+ rcode = RLM_MODULE_REJECT;
+
+ LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
+ LDAP_EXT_REQ();
+
+ break;
+
+ case LDAP_PROC_RETRY:
+ if (retry) {
+ *pconn = fr_connection_reconnect(inst->pool, *pconn);
+ if (*pconn) {
+ LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
+ dn, inst->server, inst->port, error);
+
+ talloc_free(extra); /* don't leak debug info */
+
+ goto retry;
+ }
+ };
+
+ /*
+ * Were not allowed to retry, or there are no more
+ * sockets, treat this as a hard failure.
+ */
+ goto error;
+ default:
+error:
+ rcode = RLM_MODULE_FAIL;
+#ifdef HAVE_LDAP_INITIALIZE
+ if (inst->is_url) {
+ LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
+ } else
+#endif
+ {
+ LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
+ inst->port, error);
+ }
+ LDAP_EXT_REQ();
+
+ break;
+ }
+
+ if (extra) talloc_free(extra);
+
+ return rcode; /* caller closes the connection */
+}
+
+
+/** Search for something in the LDAP directory
+ *
+ * Binds as the administrative user and performs a search, dealing with any
+ * errors.
+ *
+ * @param request Current request.
+ * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL
+ * after calling this function.
+ * @param dn to use.
+ * @param scope to use.
+ * @param filter to use.
+ * @param attrs to retrieve.
+ * @param result Where to store the result. Must be freed with ldap_msgfree.
+ * may be NULL in which case result will be automatically freed after use.
+ * @return One of the LDAP_PROC_* values.
+ */
+ldap_rcode_t rlm_ldap_search(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+ const char *dn, int scope, const char *filter, const char * const *attrs,
+ LDAPMessage **result)
+{
+ ldap_rcode_t status;
+
+ int msgid; //!< Message id returned by
+ //!< ldap_search_ext.
+
+ int count = 0; //!< Number of results we got.
+
+ struct timeval tv; //!< Holds timeout values.
+
+ const char *error = NULL;
+ char *extra = NULL;
+
+ /*
+ * OpenLDAP library doesn't declare attrs array as const, but
+ * it really should be *sigh*.
+ */
+ char **search_attrs;
+ memcpy(&search_attrs, &attrs, sizeof(attrs));
+
+ /*
+ * Do all searches as the admin user.
+ */
+ if ((*pconn)->rebound) {
+ if (rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE) != RLM_MODULE_OK) {
+ return LDAP_PROC_ERROR;
+ }
+
+ rad_assert(*pconn);
+
+ (*pconn)->rebound = FALSE;
+ }
+
+ RDEBUG2("Performing search in '%s' with filter '%s'", dn, filter);
+
+ /*
+ * If LDAP search produced an error it should also be logged
+ * to the ld. result should pick it up without us
+ * having to pass it explicitly.
+ */
+ tv.tv_sec = inst->timeout;
+ tv.tv_usec = 0;
+retry:
+ (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, 0, NULL, NULL, &tv, 0, &msgid);
+
+ RDEBUG2("Waiting for search result...");
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, result, &error, &extra);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_RETRY:
+ *pconn = fr_connection_reconnect(inst->pool, *pconn);
+ if (*pconn) {
+ RDEBUGW("Search failed: %s. Got new socket, retrying...", error);
+
+ talloc_free(extra); /* don't leak debug info */
+
+ goto retry;
+ }
+
+ status = LDAP_PROC_ERROR;
+
+ /* FALL-THROUGH */
+ default:
+ RDEBUGE("Failed performing search: %s", error);
+ RDEBUGE("%s", extra);
+
+ goto finish;
+ }
+
+ if (result) {
+ count = ldap_count_entries((*pconn)->handle, *result);
+ if (count == 0) {
+ ldap_msgfree(*result);
+ *result = NULL;
+
+ RDEBUG("Search returned no results");
+
+ status = LDAP_PROC_NO_RESULT;
+ }
+ }
+
+ finish:
+ if (extra) talloc_free(extra);
+
+ return status;
+}
+
+/** Modify something in the LDAP directory
+ *
+ * Binds as the administrative user and attempts to modify an LDAP object.
+ *
+ * @param request Current request.
+ * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL after
+ * calling this function.
+ * @param dn to modify.
+ * @param mods to make.
+ * @return One of the LDAP_PROC_* values.
+ */
+ldap_rcode_t rlm_ldap_modify(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+ const char *dn, LDAPMod *mods[])
+{
+ ldap_rcode_t status;
+
+ int msgid; //!< Message id returned by
+ //!< ldap_search_ext.
+
+ const char *error = NULL;
+ char *extra = NULL;
+
+ /*
+ * Perform all modifications as the admin user.
+ */
+ if ((*pconn)->rebound) {
+ if (rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE) != RLM_MODULE_OK) {
+ return LDAP_PROC_ERROR;
+ }
+
+ rad_assert(*pconn);
+
+ (*pconn)->rebound = FALSE;
+ }
+
+ RDEBUG2("Modifying object with DN \"%s\"", dn);
+ retry:
+ (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
+
+ RDEBUG2("Waiting for modify result...");
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_RETRY:
+ *pconn = fr_connection_reconnect(inst->pool, *pconn);
+ if (*pconn) {
+ RDEBUGW("Modify failed: %s. Got new socket, retrying...", error);
+
+ talloc_free(extra); /* don't leak debug info */
+
+ goto retry;
+ }
+
+ status = LDAP_PROC_ERROR;
+
+ /* FALL-THROUGH */
+ default:
+ RDEBUGE("Failed modifying object: %s", error);
+ RDEBUGE("%s", extra);
+
+ goto finish;
+ }
+
+ finish:
+ if (extra) talloc_free(extra);
+
+ return status;
+}
+
+/** Retrieve the DN of a user object
+ *
+ * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any attributes
+ * passed and return the result in *result.
+ *
+ * This potentially allows for all authorization and authentication checks to be performed in one ldap search
+ * operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* LDAP directory servers out there.
+ *
+ * @param[in] request Current request.
+ * @param[in,out] pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL
+ * after calling this function.
+ * @param[in] attrs Additional attributes to retrieve, may be NULL.
+ * @param[out] result Where to write the result, may be NULL in which case result is discarded.
+ * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes.
+ * @return The user's DN or NULL on error.
+ */
+const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+ const char *attrs[], LDAPMessage **result, rlm_rcode_t *rcode)
+{
+ static const char *tmp_attrs[] = { NULL };
+
+ ldap_rcode_t status;
+ VALUE_PAIR *vp = NULL;
+ LDAPMessage *tmp_msg = NULL, *entry = NULL;
+ int ldap_errno;
+ char *dn;
+ char filter[LDAP_MAX_FILTER_STR_LEN];
+ char basedn[LDAP_MAX_FILTER_STR_LEN];
+
+ int freeit = FALSE; //!< Whether the message should
+ //!< be freed after being processed.
+
+ *rcode = RLM_MODULE_FAIL;
+
+ if (!result) {
+ result = &tmp_msg;
+ freeit = TRUE;
+ }
+ *result = NULL;
+
+ if (!attrs) {
+ memset(&attrs, 0, sizeof(tmp_attrs));
+ }
+
+ /*
+ * If the caller isn't looking for the result we can just return the current userdn value.
+ */
+ if (!result) {
+ vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
+ if (vp) {
+ *rcode = RLM_MODULE_OK;
+ return vp->vp_strvalue;
+ }
+ }
+
+ /*
+ * Perform all searches as the admin user.
+ */
+ if ((*pconn)->rebound) {
+ if (rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE) != RLM_MODULE_OK) {
+ *rcode = RLM_MODULE_FAIL;
+ return NULL;
+ }
+
+ rad_assert(*pconn);
+
+ (*pconn)->rebound = FALSE;
+ }
+
+
+ if (!radius_xlat(filter, sizeof(filter), inst->filter, request, rlm_ldap_escape_func, NULL)) {
+ RDEBUGE("Unable to create filter");
+
+ *rcode = RLM_MODULE_INVALID;
+ return NULL;
+ }
+
+ if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
+ RDEBUGE("Unable to create basedn");
+
+ *rcode = RLM_MODULE_INVALID;
+ return NULL;
+ }
+
+ status = rlm_ldap_search(inst, request, pconn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, result);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_NO_RESULT:
+ *rcode = RLM_MODULE_NOTFOUND;
+ return NULL;
+ default:
+ return NULL;
+ }
+
+ rad_assert(*pconn);
+
+ entry = ldap_first_entry((*pconn)->handle, *result);
+ if (!entry) {
+ ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+ RDEBUGE("Failed retrieving entry: %s",
+ ldap_err2string(ldap_errno));
+
+ goto finish;
+ }
+
+ dn = ldap_get_dn((*pconn)->handle, entry);
+ if (!dn) {
+ ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+
+ RDEBUGE("Retrieving object DN from entry failed: %s",
+ ldap_err2string(ldap_errno));
+
+ goto finish;
+ }
+
+ vp = pairmake("LDAP-UserDn", dn, T_OP_EQ);
+ if (vp) {
+ pairadd(&request->config_items, vp);
+ *rcode = RLM_MODULE_OK;
+ }
+
+ finish:
+ ldap_memfree(dn);
+
+ if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) {
+ ldap_msgfree(*result);
+ *result = NULL;
+ }
+
+ return vp ? vp->vp_strvalue : NULL;
+}
+
+rlm_rcode_t rlm_ldap_apply_profile(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+ const char *profile, const rlm_ldap_map_xlat_t *expanded)
+{
+ rlm_rcode_t rcode = RLM_MODULE_OK;
+ ldap_rcode_t status;
+ LDAPMessage *result = NULL, *entry = NULL;
+ int ldap_errno;
+ LDAP *handle = (*pconn)->handle;
+ char filter[LDAP_MAX_FILTER_STR_LEN];
+
+ if (!profile || !*profile) {
+ return RLM_MODULE_NOOP;
+ }
+ strlcpy(filter, inst->base_filter, sizeof(filter));
+
+ status = rlm_ldap_search(inst, request, pconn, profile, LDAP_SCOPE_BASE, filter, expanded->attrs, &result);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_NO_RESULT:
+ RDEBUG("Profile \"%s\" not found", profile);
+ return RLM_MODULE_NOTFOUND;
+ default:
+ return RLM_MODULE_FAIL;
+ }
+
+ rad_assert(*pconn);
+ rad_assert(result);
+
+ entry = ldap_first_entry(handle, result);
+ if (!entry) {
+ ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+ RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
+
+ rcode = RLM_MODULE_NOTFOUND;
+
+ goto free_result;
+ }
+
+ rlm_ldap_map_do(inst, request, handle, expanded, entry);
+
+free_result:
+ ldap_msgfree(result);
+
+ return rcode;
+}
+
+/** Check for presence of access attribute in result
+ *
+ * @param inst rlm_ldap configuration.
+ * @param request Current request.
+ * @param conn used to retrieve entry.
+ * @param entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
+ * @return RLM_MODULE_USERLOCK if the user was denied access, else RLM_MODULE_OK.
+ */
+rlm_rcode_t rlm_ldap_check_access(const ldap_instance_t *inst, REQUEST *request, const ldap_handle_t *conn,
+ LDAPMessage *entry)
+{
+ rlm_rcode_t rcode = RLM_MODULE_OK;
+ char **vals = NULL;
+
+ vals = ldap_get_values(conn->handle, entry, inst->userobj_access_attr);
+ if (vals) {
+ if (inst->access_positive && (strncmp(vals[0], "FALSE", 5) == 0)) {
+ RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out");
+ rcode = RLM_MODULE_USERLOCK;
+ } else {
+ RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr);
+ rcode = RLM_MODULE_USERLOCK;
+ }
+
+ ldap_value_free(vals);
+ } else if (inst->access_positive) {
+ RDEBUG("No \"%s\" attribute - user locked out", inst->userobj_access_attr);
+ rcode = RLM_MODULE_USERLOCK;
+ }
+
+ return rcode;
+}
+
+/** Verify we got a password from the search
+ *
+ * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password.
+ *
+ * @param inst rlm_ldap configuration.
+ * @param request Current request.
+ */
+void rlm_ldap_check_reply(const ldap_instance_t *inst, REQUEST *request)
+{
+ /*
+ * More warning messages for people who can't be bothered to read the documentation.
+ *
+ * Expect_password is set when we process the mapping, and is only true if there was a mapping between
+ * an LDAP attribute and a password reference attribute in the control list.
+ */
+ if (inst->expect_password && (debug_flag > 1)) {
+ if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
+ !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
+ !pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) &&
+ !pairfind(request->config_items, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
+ !pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
+ RDEBUGW("No \"reference\" password added. Ensure the admin user has permission to "
+ "read the password attribute");
+ RDEBUGW("PAP authentication will *NOT* work with Active Directory (if that is what you "
+ "were trying to configure)");
+ }
+ }
+}
+
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+/** Callback for OpenLDAP to rebind and chase referrals
+ *
+ * Called by OpenLDAP when it receives a referral and has to rebind.
+ *
+ * @param handle to rebind.
+ * @param url to bind to.
+ * @param request that triggered the rebind.
+ * @param msgid that triggered the rebind.
+ * @param ctx rlm_ldap configuration.
+ */
+static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
+ void *ctx)
+{
+ rlm_rcode_t rcode;
+ ldap_handle_t *conn = ctx;
+
+ int ldap_errno;
+
+ conn->referred = TRUE;
+ conn->rebound = TRUE; /* not really, but oh well... */
+ rad_assert(handle == conn->handle);
+
+ DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
+
+ rcode = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->login, conn->inst->password, FALSE);
+
+ if (rcode == RLM_MODULE_OK) {
+ return LDAP_SUCCESS;
+ }
+
+ ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+
+ return ldap_errno;
+}
+#endif
+
+/** Create and return a new connection
+ *
+ * Create a new ldap connection and allocate memory for a new rlm_handle_t
+ *
+ * @param ctx rlm_ldap instance.
+ * @return A new connection handle or NULL on error.
+ */
+void *rlm_ldap_conn_create(void *ctx)
+{
+ rlm_rcode_t rcode;
+
+ int ldap_errno, ldap_version;
+ struct timeval tv;
+
+ ldap_instance_t *inst = ctx;
+ LDAP *handle = NULL;
+ ldap_handle_t *conn = NULL;
+
+#ifdef HAVE_LDAP_INITIALIZE
+ if (inst->is_url) {
+ DEBUG("rlm_ldap (%s): Connecting to %s", inst->xlat_name, inst->server);
+
+ ldap_errno = ldap_initialize(&handle, inst->server);
+ if (ldap_errno != LDAP_SUCCESS) {
+ LDAP_ERR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
+ goto error;
+ }
+ } else
+#endif
+ {
+ DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name, inst->server, inst->port);
+
+ handle = ldap_init(inst->server, inst->port);
+ if (!handle) {
+ LDAP_ERR("ldap_init() failed");
+ goto error;
+ }
+ }
+
+ /*
+ * We now have a connection structure, but no actual TCP connection.
+ *
+ * Set a bunch of LDAP options, using common code.
+ */
+#define do_ldap_option(_option, _name, _value) \
+ if (ldap_set_option(handle, _option, _value) != LDAP_OPT_SUCCESS) { \
+ ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
+ LDAP_ERR("Could not set %s: %s", _name, ldap_err2string(ldap_errno)); \
+ }
+
+ if (inst->ldap_debug) {
+ do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &(inst->ldap_debug));
+ }
+
+ /*
+ * Leave "chase_referrals" unset to use the OpenLDAP default.
+ */
+ if (inst->chase_referrals != 2) {
+ if (inst->chase_referrals) {
+ do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_ON);
+
+ if (inst->rebind == 1) {
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc(handle, rlm_ldap_rebind, inst);
+#else
+ DEBUGW("The flag 'rebind = yes' is not supported by the system LDAP library. "
+ "Ignoring.");
+#endif
+ }
+ } else {
+ do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_OFF);
+ }
+ }
+
+ tv.tv_sec = inst->net_timeout;
+ tv.tv_usec = 0;
+ do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
+
+ do_ldap_option(LDAP_OPT_TIMELIMIT, "timelimit", &(inst->timelimit));
+
+ ldap_version = LDAP_VERSION3;
+ do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version);
+
+#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
+ do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle", &(inst->keepalive_idle));
+#endif
+
+#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
+ do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes", &(inst->keepalive_probes));
+#endif
+
+#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
+ do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval", &(inst->keepalive_interval));
+#endif
+
+#ifdef HAVE_LDAP_START_TLS
+ /*
+ * Set all of the TLS options
+ */
+ if (inst->tls_mode) {
+ do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
+ }
+
+# define maybe_ldap_option(_option, _name, _value) \
+ if (_value) do_ldap_option(_option, _name, _value)
+
+ maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "cacertfile", inst->tls_cacertfile);
+ maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "cacertdir", inst->tls_cacertdir);
+
+# ifdef HAVE_LDAP_INT_TLS_CONFIG
+ if (ldap_int_tls_config(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, inst->tls_require_cert) != LDAP_OPT_SUCCESS) {
+ ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+
+ LDAP_ERR("Could not set LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s", inst->tls_require_cert,
+ ldap_err2string(ldap_errno));
+ }
+# endif
+
+ /*
+ * Set certificate options
+ */
+ maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certfile", inst->tls_certfile);
+ maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "keyfile", inst->tls_keyfile);
+ maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE, "randfile", inst->tls_randfile);
+
+ /*
+ * And finally start the TLS code.
+ */
+ if (inst->start_tls) {
+ if (inst->port == 636) {
+ DEBUGW("Told to Start TLS on LDAPS port this will probably fail, please correct the "
+ "configuration");
+ }
+
+ if (ldap_start_tls_s(handle, NULL, NULL) != LDAP_SUCCESS) {
+ ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+
+ LDAP_ERR("Could not start TLS: %s", ldap_err2string(ldap_errno));
+ goto error;
+ }
+ }
+#endif /* HAVE_LDAP_START_TLS */
+
+ /*
+ * Allocate memory for the handle.
+ */
+ conn = talloc_zero(ctx, ldap_handle_t);
+ conn->inst = inst;
+ conn->handle = handle;
+ conn->rebound = FALSE;
+ conn->referred = FALSE;
+
+ rcode = rlm_ldap_bind(inst, NULL, &conn, inst->login, inst->password, FALSE);
+ if (rcode != RLM_MODULE_OK) {
+ goto error;
+ }
+
+ return conn;
+
+ error:
+ if (handle) ldap_unbind_s(handle);
+ if (conn) talloc_free(conn);
+
+ return NULL;
+}
+
+
+/** Close and delete a connection
+ *
+ * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
+ * connection handle.
+ *
+ * @param ctx unused.
+ * @param connection to destroy.
+ * @return always indicates success.
+ */
+int rlm_ldap_conn_delete(UNUSED void *ctx, void *connection)
+{
+ ldap_handle_t *conn = connection;
+
+ ldap_unbind_s(conn->handle);
+ talloc_free(conn);
+
+ return 0;
+}
+
+
+/** Gets an LDAP socket from the connection pool
+ *
+ * Retrieve a socket from the connection pool, or NULL on error (of if no sockets are available).
+ *
+ * @param inst rlm_ldap configuration.
+ * @param request Current request.
+ */
+ldap_handle_t *rlm_ldap_get_socket(const ldap_instance_t *inst, REQUEST *request)
+{
+ ldap_handle_t *conn;
+
+ conn = fr_connection_get(inst->pool);
+ if (!conn) {
+ RDEBUGE("All ldap connections are in use");
+
+ return NULL;
+ }
+
+ return conn;
+}
+
+/** Frees an LDAP socket back to the connection pool
+ *
+ * If the socket was rebound chasing a referral onto another server then we destroy it.
+ * If the socket was rebound to another user on the same server, we let the next caller rebind it.
+ *
+ * @param inst rlm_ldap configuration.
+ * @param conn to release.
+ */
+void rlm_ldap_release_socket(const ldap_instance_t *inst, ldap_handle_t *conn)
+{
+ /*
+ * Could have already been free'd due to a previous error.
+ */
+ if (!conn) return;
+
+ /*
+ * We chased a referral to another server.
+ *
+ * This connection is no longer part of the pool which is connected to and bound to the configured server.
+ * Close it.
+ *
+ * Note that we do NOT close it if it was bound to another user. Instead, we let the next caller do the
+ * rebind.
+ */
+ if (conn->referred) {
+ fr_connection_del(inst->pool, conn);
+ return;
+ }
+
+ fr_connection_release(inst->pool, conn);
+ return;
+}
* @file rlm_ldap.c
* @brief LDAP authorization and authentication module.
*
- * @copyright 1999-2013 The FreeRADIUS Server Project.
- * @copyright 2012 Alan DeKok <aland@freeradius.org>
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @author Alan DeKok <aland@freeradius.org>
+ *
+ * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
* @copyright 2012-2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2012 Alan DeKok <aland@freeradius.org>
+ * @copyright 1999-2013 The FreeRADIUS Server Project.
*/
#include <freeradius-devel/ident.h>
RCSID("$Id$")
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
#include <stdarg.h>
#include <ctype.h>
-#include <lber.h>
-#include <ldap.h>
-
-#define MAX_ATTRMAP 128
-#define MAX_ATTR_STR_LEN 256
-#define MAX_FILTER_STR_LEN 1024
-
-#ifdef WITH_EDIR
-extern int nmasldap_get_password(LDAP *ld,char *objectDN, char *pwd, size_t *pwdSize);
-
-#endif
-
-typedef struct ldap_acct_section {
- CONF_SECTION *cs;
-
- const char *reference;
-} ldap_acct_section_t;
-
-
-typedef struct {
- CONF_SECTION *cs;
- fr_connection_pool_t *pool;
-
- char *server;
- int port;
-
- char *login;
- char *password;
-
- char *filter;
- char *basedn;
-
- int chase_referrals;
- int rebind;
-
- int ldap_debug; //!< Debug flag for the SDK.
-
- const char *xlat_name; //!< Instance name.
-
- int expect_password;
-
- /*
- * RADIUS attribute to LDAP attribute maps
- */
- value_pair_map_t *user_map; //!< Attribute map applied to users and
- //!< profiles.
-
- /*
- * Access related configuration
- */
- char *access_attr;
- int positive_access_attr;
-
- /*
- * Profiles
- */
- char *base_filter;
- char *default_profile;
- char *profile_attr;
-
- /*
- * Group checking.
- */
- char *groupname_attr;
- char *groupmemb_filter;
- char *groupmemb_attr;
-
- /*
- * Accounting
- */
- ldap_acct_section_t *postauth;
- ldap_acct_section_t *accounting;
-
- /*
- * TLS items. We should really normalize these with the
- * TLS code in 3.0.
- */
- int tls_mode;
- int start_tls;
- char *tls_cacertfile;
- char *tls_cacertdir;
- char *tls_certfile;
- char *tls_keyfile;
- char *tls_randfile;
- char *tls_require_cert;
-
- /*
- * Options
- */
- int timelimit;
- int net_timeout;
- int timeout;
- int is_url;
+#include "ldap.h"
#ifdef WITH_EDIR
- /*
- * eDir support
- */
- int edir;
- int edir_autz;
-#endif
- /*
- * For keep-alives.
- */
-#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
- int keepalive_idle;
-#endif
-#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
- int keepalive_probes;
+extern int nmasldap_get_password(LDAP *ld, char *objectDN, char *pwd,
+ size_t *pwdSize);
#endif
-#ifdef LDAP_OPT_ERROR_NUMBER
- int keepalive_interval;
-#endif
-
-} ldap_instance;
-
-/* The default setting for TLS Certificate Verification */
-#define TLS_DEFAULT_VERIFY "allow"
/*
* TLS Configuration
*/
static CONF_PARSER tls_config[] = {
- {"start_tls", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,start_tls), NULL, "no"},
- {"cacertfile", PW_TYPE_FILENAME,
- offsetof(ldap_instance,tls_cacertfile), NULL, NULL},
- {"cacertdir", PW_TYPE_FILENAME,
- offsetof(ldap_instance,tls_cacertdir), NULL, NULL},
- {"certfile", PW_TYPE_FILENAME,
- offsetof(ldap_instance,tls_certfile), NULL, NULL},
- {"keyfile", PW_TYPE_FILENAME,
- offsetof(ldap_instance,tls_keyfile), NULL, NULL},
- {"randfile", PW_TYPE_STRING_PTR, /* OK if it changes on HUP */
- offsetof(ldap_instance,tls_randfile), NULL, NULL},
- {"require_cert", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
+ {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, start_tls), NULL, "no"},
+ {"cacertfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertfile), NULL, NULL},
+ {"cacertdir", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertdir), NULL, NULL},
+ {"certfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_certfile), NULL, NULL},
+ {"keyfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_keyfile), NULL, NULL}, // OK if it changes on HUP
+ {"randfile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_randfile), NULL, NULL},
+ {"require_cert",PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
{ NULL, -1, 0, NULL, NULL }
};
-
+/*
+ * Access limitations
+ */
static CONF_PARSER attr_config[] = {
- /*
- * Access limitations
- */
/* LDAP attribute name that controls remote access */
- {"access_attr", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,access_attr), NULL, NULL},
- {"positive_access_attr", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,positive_access_attr), NULL, "yes"},
-
- {"base_filter", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,base_filter), NULL,
- "(objectclass=radiusprofile)"},
- {"default_profile", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,default_profile), NULL, NULL},
- {"profile_attribute", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,profile_attr), NULL, NULL},
+ {"userobj_access_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_access_attr), NULL, NULL},
+ {"access_positive", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,access_positive), NULL, "yes"},
+ {"base_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,base_filter), NULL, "(objectclass=radiusprofile)"},
+ {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,default_profile), NULL, NULL},
+ {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,profile_attr), NULL, NULL},
{ NULL, -1, 0, NULL, NULL }
};
-
/*
* Group configuration
*/
static CONF_PARSER group_config[] = {
/*
- * Group checks. These could probably be done
- * via dynamic xlat's.
+ * Group checks. These could probably be done via dynamic xlat's.
*/
- {"name_attribute", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,groupname_attr), NULL, "cn"},
- {"membership_filter", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,groupmemb_filter), NULL,
- "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))"
- "(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"},
- {"membership_attribute", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,groupmemb_attr), NULL, NULL},
-
-
+ {"name_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,groupobj_name_attr), NULL, "cn"},
+ {"membership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,groupobj_membership_filter), NULL,
+ "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)"
+ "(uniquemember=%{Ldap-UserDn})))"},
+ {"membership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_membership_attr), NULL, NULL},
{ NULL, -1, 0, NULL, NULL }
};
* Reference for accounting updates
*/
static const CONF_PARSER acct_section_config[] = {
- {"reference", PW_TYPE_STRING_PTR,
- offsetof(ldap_acct_section_t, reference), NULL, "."},
+ {"reference", PW_TYPE_STRING_PTR, offsetof(ldap_acct_section_t, reference), NULL, "."},
{NULL, -1, 0, NULL, NULL}
};
/*
* Debugging flags to the server
*/
- {"ldap_debug", PW_TYPE_INTEGER,
- offsetof(ldap_instance,ldap_debug), NULL, "0x0000"},
+ {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance_t,ldap_debug), NULL, "0x0000"},
- {"chase_referrals", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,chase_referrals), NULL, NULL},
+ {"chase_referrals", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,chase_referrals), NULL, NULL},
- {"rebind", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,rebind), NULL, NULL},
+ {"rebind", PW_TYPE_BOOLEAN,offsetof(ldap_instance_t,rebind), NULL, NULL},
/* timeout on network activity */
- {"net_timeout", PW_TYPE_INTEGER,
- offsetof(ldap_instance,net_timeout), NULL, "10"},
+ {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,net_timeout), NULL, "10"},
/* timeout for search results */
- {"timeout", PW_TYPE_INTEGER,
- offsetof(ldap_instance,timeout), NULL, "20"},
+ {"timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,timeout), NULL, "20"},
/* allow server unlimited time for search (server-side limit) */
- {"timelimit", PW_TYPE_INTEGER,
- offsetof(ldap_instance,timelimit), NULL, "20"},
+ {"timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance_t,timelimit), NULL, "20"},
#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
- {"idle", PW_TYPE_INTEGER,
- offsetof(ldap_instance,keepalive_idle), NULL, "60"},
+ {"idle", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_idle), NULL, "60"},
#endif
#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
- {"probes", PW_TYPE_INTEGER,
- offsetof(ldap_instance,keepalive_probes), NULL, "3"},
+ {"probes", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_probes), NULL, "3"},
#endif
#ifdef LDAP_OPT_ERROR_NUMBER
- {"interval", PW_TYPE_INTEGER,
- offsetof(ldap_instance,keepalive_interval), NULL, "30"},
+ {"interval", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_interval), NULL, "30"},
#endif
{ NULL, -1, 0, NULL, NULL }
};
static const CONF_PARSER module_config[] = {
- {"server", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,server), NULL, "localhost"},
- {"port", PW_TYPE_INTEGER,
- offsetof(ldap_instance,port), NULL, "389"},
+ {"server", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,server), NULL, "localhost"},
+ {"port", PW_TYPE_INTEGER, offsetof(ldap_instance_t,port), NULL, "389"},
- {"password", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,password), NULL, ""},
- {"identity", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,login), NULL, ""},
+ {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,password), NULL, ""},
+ {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,login), NULL, ""},
/*
* DN's and filters.
*/
- {"basedn", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,basedn), NULL, "o=notexist"},
+ {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,basedn), NULL, "o=notexist"},
- {"filter", PW_TYPE_STRING_PTR,
- offsetof(ldap_instance,filter), NULL, "(uid=%u)"},
+ {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_filter), NULL, "(uid=%u)"},
- /* turn off the annoying warning if we don't expect a password */
- {"expect_password", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,expect_password), NULL, "yes"},
-
#ifdef WITH_EDIR
/* support for eDirectory Universal Password */
{"edir", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,edir), NULL, NULL}, /* NULL defaults to "no" */
+ offsetof(ldap_instance_t,edir), NULL, NULL}, /* NULL defaults to "no" */
/*
* Attempt to bind with the Cleartext password we got from eDirectory
* Universal password for additional authorization checks.
*/
- {"edir_autz", PW_TYPE_BOOLEAN,
- offsetof(ldap_instance,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
+ {"edir_autz", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
#endif
/*
{NULL, -1, 0, NULL, NULL}
};
-typedef struct ldap_conn {
- LDAP *handle;
- int rebound;
- int referred;
- ldap_instance *inst;
-} LDAP_CONN;
-
-typedef struct xlat_attrs {
- const value_pair_map_t *maps;
- const char *attrs[MAX_ATTRMAP];
-} xlat_attrs_t;
-
-typedef struct rlm_ldap_result {
- char **values;
- int count;
-} rlm_ldap_result_t;
-
-typedef enum {
- LDAP_PROC_SUCCESS = 0,
- LDAP_PROC_ERROR = -1,
- LDAP_PROC_RETRY = -2,
- LDAP_PROC_NOTPERMITTED = -3,
- LDAP_PROC_REJECT = -4
-} ldap_rcode_t;
-
-static ldap_rcode_t process_ldap_errno(ldap_instance *inst,
- const LDAP_CONN *conn,
- const char **error)
-{
- int ldap_errno;
-
- *error = NULL;
-
- ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
- &ldap_errno);
- switch (ldap_errno) {
- case LDAP_SUCCESS:
- case LDAP_NO_SUCH_OBJECT:
- return LDAP_PROC_SUCCESS;
-
- case LDAP_INSUFFICIENT_ACCESS:
- *error = "Insufficient access. Check the identity and password "
- "configuration directive";
-
- return LDAP_PROC_NOTPERMITTED;
-
- case LDAP_UNWILLING_TO_PERFORM:
- *error = "Server was unwilling to perform";
-
- return LDAP_PROC_NOTPERMITTED;
-
- case LDAP_TIMEOUT:
- exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
-
- *error = "Timed out while waiting for server to respond";
-
- return LDAP_PROC_ERROR;
-
- case LDAP_FILTER_ERROR:
- *error = "Bad search filter";
-
- return LDAP_PROC_ERROR;
-
- case LDAP_TIMELIMIT_EXCEEDED:
- exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
-
- *error = "Time limit exceeded";
- /* FALL-THROUGH */
-
- case LDAP_BUSY:
- case LDAP_UNAVAILABLE:
- /*
- * Reconnect. There's an issue with the socket
- * or LDAP server.
- */
- *error = ldap_err2string(ldap_errno);
-
- case LDAP_SERVER_DOWN:
- return LDAP_PROC_RETRY;
-
- case LDAP_INVALID_CREDENTIALS:
- case LDAP_CONSTRAINT_VIOLATION:
- *error = ldap_err2string(ldap_errno);
-
- return LDAP_PROC_REJECT;
-
- case LDAP_OPERATIONS_ERROR:
- *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. "
- "See the ldap module configuration for details.";
- /* FALL-THROUGH */
-
- default:
- *error = ldap_err2string(ldap_errno);
-
- return LDAP_PROC_ERROR;
- }
-}
-
-
-static int ldap_bind_wrapper(REQUEST *request, LDAP_CONN **pconn,
- const char *user, const char *password,
- int retry)
-{
- int rcode, msg_id;
- int module_rcode = RLM_MODULE_OK;
- LDAP_CONN *conn = *pconn;
- ldap_instance *inst = conn->inst;
- LDAPMessage *result = NULL;
- const char *error = NULL;
- char *ext_error = NULL;
- struct timeval tv;
-
-retry:
- msg_id = ldap_bind(conn->handle, user, password, LDAP_AUTH_SIMPLE);
- if (msg_id < 0) goto get_error;
-
- DEBUG3("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
-
- tv.tv_sec = inst->timeout;
- tv.tv_usec = 0;
-
- rcode = ldap_result(conn->handle, msg_id, 1, &tv, &result);
- if (rcode > 0) {
- rcode = ldap_parse_result(conn->handle, result, NULL, NULL,
- &ext_error, NULL, NULL, 1);
- }
-
-get_error:
- switch (process_ldap_errno(inst, conn, &error))
- {
- case LDAP_PROC_SUCCESS:
- break;
- case LDAP_PROC_NOTPERMITTED:
- if (request) {
- RDEBUGE("Bind was not permitted (%s): %s", error,
- ext_error ? ext_error :
- "no additional information");
- } else {
- radlog(L_ERR, "rlm_ldap (%s): Bind was not permitted "
- "(%s): %s", inst->xlat_name,
- error, ext_error ? ext_error :
- "no additional information");
- }
-
- module_rcode = RLM_MODULE_USERLOCK;
-
- break;
- case LDAP_PROC_REJECT:
- if (request) {
- RDEBUGE("Bind credentials incorrect (%s): %s", error,
- ext_error ? ext_error :
- "no additional information");
- } else {
- radlog(L_ERR, "rlm_ldap (%s): Bind credentials "
- "incorrect (%s): %s", inst->xlat_name,
- error, ext_error ? ext_error :
- "no additional information");
- }
-
- module_rcode = RLM_MODULE_REJECT;
-
- break;
- case LDAP_PROC_ERROR:
- module_rcode = RLM_MODULE_FAIL;
-error:
-#ifdef HAVE_LDAP_INITIALIZE
- if (inst->is_url) {
- radlog(L_ERR, "rlm_ldap (%s): bind "
- "with %s to %s failed: %s",
- inst->xlat_name, user,
- inst->server, error);
- } else
-#endif
- {
- radlog(L_ERR, "rlm_ldap (%s): bind "
- "with %s to %s:%d failed: %s",
- inst->xlat_name, user,
- inst->server, inst->port, error);
- }
-
- break;
- case LDAP_PROC_RETRY:
- if (retry) {
- radlog(L_ERR, "rlm_ldap (%s): bind "
- "with %s to %s:%d failed, reconnecting: %s",
- inst->xlat_name, user,
- inst->server, inst->port, error);
- ldap_memfree(ext_error);
- ext_error = NULL;
-
- *pconn = fr_connection_reconnect(inst->pool, *pconn);
- if (*pconn) goto retry;
- } else goto error;
-
- module_rcode = RLM_MODULE_FAIL;
- break;
- }
-
- ldap_memfree(ext_error);
-
- return module_rcode; /* caller closes the connection */
-}
-
-#if LDAP_SET_REBIND_PROC_ARGS == 3
-/*
- * Rebind && chase referral stuff
- */
-static int ldap_rebind(LDAP *handle, LDAP_CONST char *url,
- UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
- void *ctx )
-{
- int rcode, ldap_errno;
- LDAP_CONN *conn = ctx;
-
- conn->referred = TRUE;
- conn->rebound = TRUE; /* not really, but oh well... */
- rad_assert(handle == conn->handle);
-
- DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
-
-
- rcode = ldap_bind_wrapper(NULL, &conn, conn->inst->login,
- conn->inst->password, FALSE);
-
- if (rcode == RLM_MODULE_OK) {
- return LDAP_SUCCESS;
- }
-
- ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
-
- return ldap_errno;
-}
-#endif
-
-/** Create and return a new connection
- * This function is probably too big.
- */
-static void *ldap_conn_create(void *ctx)
-{
- int module_rcode;
- int ldap_errno, ldap_version;
- struct timeval tv;
- ldap_instance *inst = ctx;
- LDAP *handle = NULL;
- LDAP_CONN *conn = NULL;
-
-#ifdef HAVE_LDAP_INITIALIZE
- if (inst->is_url) {
- DEBUG("rlm_ldap (%s): Connecting to %s", inst->xlat_name,
- inst->server);
-
- ldap_errno = ldap_initialize(&handle, inst->server);
- if (ldap_errno != LDAP_SUCCESS) {
- radlog(L_ERR, "rlm_ldap (%s): ldap_initialize() "
- "failed: %s",
- inst->xlat_name, ldap_err2string(ldap_errno));
- goto conn_fail;
- }
- } else
-#endif
- {
- DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name,
- inst->server, inst->port);
-
- handle = ldap_init(inst->server, inst->port);
- if (!handle) {
- radlog(L_ERR, "rlm_ldap (%s): ldap_init() failed",
- inst->xlat_name);
- conn_fail:
- if (handle) ldap_unbind_s(handle);
- return NULL;
- }
- }
-
- /*
- * We now have a connection structure, but no actual TCP connection.
- *
- * Set a bunch of LDAP options, using common code.
- */
-#define do_ldap_option(_option, _name, _value) \
- if (ldap_set_option(handle, _option, _value) != LDAP_OPT_SUCCESS) { \
- ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
- radlog(L_ERR, "rlm_ldap (%s): Could not set %s: %s", \
- inst->xlat_name, _name, ldap_err2string(ldap_errno)); \
- }
-
- if (inst->ldap_debug) {
- do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug",
- &(inst->ldap_debug));
- }
-
- /*
- * Leave "chase_referrals" unset to use the OpenLDAP
- * default.
- */
- if (inst->chase_referrals != 2) {
- if (inst->chase_referrals) {
- do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals",
- LDAP_OPT_ON);
-
- if (inst->rebind == 1) {
-#if LDAP_SET_REBIND_PROC_ARGS == 3
- ldap_set_rebind_proc(handle, ldap_rebind, inst);
-#else
- DEBUGW("The flag 'rebind = yes' is not supported by the system LDAP library. Ignoring.");
-#endif
- }
- } else {
- do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals",
- LDAP_OPT_OFF);
- }
- }
-
- tv.tv_sec = inst->net_timeout;
- tv.tv_usec = 0;
- do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
-
- do_ldap_option(LDAP_OPT_TIMELIMIT, "timelimit", &(inst->timelimit));
-
- ldap_version = LDAP_VERSION3;
- do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version",
- &ldap_version);
-
-#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
- do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle",
- &(inst->keepalive_idle));
-#endif
-
-#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
- do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes",
- &(inst->keepalive_probes));
-#endif
-
-#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
- do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval",
- &(inst->keepalive_interval));
-#endif
-
-#ifdef HAVE_LDAP_START_TLS
- /*
- * Set all of the TLS options
- */
- if (inst->tls_mode) {
- do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
- }
-
-#define maybe_ldap_option(_option, _name, _value) \
- if (_value) do_ldap_option(_option, _name, _value)
-
- maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE,
- "cacertfile", inst->tls_cacertfile);
- maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR,
- "cacertdir", inst->tls_cacertdir);
-
-#ifdef HAVE_LDAP_INT_TLS_CONFIG
- if (ldap_int_tls_config(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
- (inst->tls_require_cert)) != LDAP_OPT_SUCCESS) {
- ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): could not set "
- "LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s",
- inst->xlat_name,
- inst->tls_require_cert,
- ldap_err2string(ldap_errno));
- }
-#endif
-
- maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE,
- "certfile", inst->tls_certfile);
- maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE,
- "keyfile", inst->tls_keyfile);
- maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE,
- "randfile", inst->tls_randfile);
-
- /*
- * And finally start the TLS code.
- */
- if (inst->start_tls && (inst->port != 636)) {
- ldap_errno = ldap_start_tls_s(handle, NULL, NULL);
- if (ldap_errno != LDAP_SUCCESS) {
- ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): could not start TLS: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
- goto conn_fail;
- }
- }
-#endif /* HAVE_LDAP_START_TLS */
-
- conn = talloc_zero(ctx, LDAP_CONN);
- conn->inst = inst;
- conn->handle = handle;
- conn->rebound = FALSE;
- conn->referred = FALSE;
-
- module_rcode = ldap_bind_wrapper(NULL, &conn, inst->login,
- inst->password, FALSE);
- if (module_rcode != RLM_MODULE_OK) {
- goto conn_fail;
- }
-
- return conn;
-}
-
-
-/** Close and delete a connection
- *
- */
-static int ldap_conn_delete(UNUSED void *ctx, void *connection)
-{
- LDAP_CONN *conn = connection;
-
- ldap_unbind_s(conn->handle);
- talloc_free(conn);
-
- return 0;
-}
-
-
-/** Gets an LDAP socket from the connection pool
- *
- */
-static LDAP_CONN *ldap_get_socket(ldap_instance *inst)
-{
- LDAP_CONN *conn;
-
- conn = fr_connection_get(inst->pool);
- if (!conn) {
- radlog(L_ERR, "rlm_ldap (%s): all ldap connections are in use",
- inst->xlat_name);
- return NULL;
- }
-
- return conn;
-}
-
-/** Frees an LDAP socket back to the connection pool
- *
- */
-static void ldap_release_socket(ldap_instance *inst, LDAP_CONN *conn)
-{
- /*
- * Could have already been free'd due to a previous error.
- */
- if (!conn) return;
-
- /*
- * We chased a referral to another server.
- *
- * This connection is no longer part of the pool which is
- * connected to and bound to the configured server.
- * Close it.
- *
- * Note that we do NOT close it if it was bound to
- * another user. Instead, we let the next caller do the
- * rebind.
- */
- if (conn->referred) {
- fr_connection_del(inst->pool, conn);
- return;
- }
-
- fr_connection_release(inst->pool, conn);
- return;
-}
-
-
-/* Converts "bad" strings into ones which are safe for LDAP
- *
- */
-static size_t ldap_escape_func(UNUSED REQUEST *request, char *out,
- size_t outlen, const char *in, UNUSED void *arg)
-{
- static const char encode[] = ",+\"\\<>;*=()";
- static const char hextab[] = "0123456789abcdef";
- size_t left = outlen;
-
- if (*in && ((*in == ' ') || (*in == '#'))) {
- goto encode;
- }
-
- while (*in) {
- /*
- * Encode unsafe characters.
- */
- if (memchr(encode, *in, sizeof(encode) - 1)) {
- encode:
-
- /*
- * Only 3 or less bytes available.
- */
- if (left <= 3) break;
-
- *out++ = '\\';
- *out++ = hextab[(*in >> 4) & 0x0f];
- *out++ = hextab[*in & 0x0f];
- in++;
- left -= 3;
-
- continue;
- }
-
- if (left <= 1) break;
-
- /*
- * Doesn't need encoding
- */
- *out++ = *in++;
- left--;
- }
-
- *out = '\0';
-
- return outlen - left;
-}
-
-/** Do a search and get a response
- *
- */
-static int perform_search(ldap_instance *inst, REQUEST *request,
- LDAP_CONN **pconn, const char *search_basedn,
- int scope, const char *filter,
- const char * const *attrs, LDAPMessage **presult)
-{
- int ldap_errno;
- int count = 0;
- struct timeval tv;
- const char *error = NULL;
-
- /*
- * OpenLDAP library doesn't declare attrs array as const, but
- * it really should be *sigh*.
- */
- char **search_attrs;
- memcpy(&search_attrs, &attrs, sizeof(attrs));
-
- *presult = NULL;
-
- /*
- * Do all searches as the default admin user.
- */
- if ((*pconn)->rebound) {
- ldap_errno = ldap_bind_wrapper(request, pconn, inst->login,
- inst->password, TRUE);
- if (ldap_errno != RLM_MODULE_OK) {
- return -1;
- }
-
- rad_assert(*pconn);
- (*pconn)->rebound = FALSE;
- }
-
- tv.tv_sec = inst->timeout;
- tv.tv_usec = 0;
- RDEBUG2("Performing search in '%s' with filter '%s'",
- search_basedn ? search_basedn : "(null)" ,
- filter);
-
-retry:
- ldap_errno = ldap_search_ext_s((*pconn)->handle, search_basedn, scope,
- filter, search_attrs, 0, NULL, NULL,
- &tv, 0, presult);
- if (ldap_errno != LDAP_SUCCESS) {
- ldap_msgfree(*presult);
- switch (process_ldap_errno(inst, *pconn, &error))
- {
- case LDAP_PROC_SUCCESS:
- break;
- case LDAP_PROC_RETRY:
- radlog(L_ERR, "rlm_ldap (%s): Failed "
- "performing search, reconnecting: %s",
- inst->xlat_name, error);
-
- *pconn = fr_connection_reconnect(inst->pool,
- *pconn);
- if (*pconn) goto retry;
-
- return -1;
- default:
- radlog(L_ERR, "rlm_ldap (%s): Failed "
- "performing search: %s",
- inst->xlat_name, error);
-
- return -1;
- }
- }
-
- count = ldap_count_entries((*pconn)->handle, *presult);
- if (count == 0) {
- ldap_msgfree(*presult);
- RDEBUGE("Search returned no results");
-
- return -2;
- }
-
- if (count != 1) {
- ldap_msgfree(*presult);
- RDEBUGE("Got ambiguous search result (%d results)", count);
-
- return -2;
- }
-
- return 0;
-}
-
/** Expand an LDAP URL into a query, and return a string result from that query.
*
*/
static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
char *out, size_t freespace)
{
- int rcode;
+ ldap_rcode_t status;
size_t length = 0;
- ldap_instance *inst = instance;
+ ldap_instance_t *inst = instance;
LDAPURLDesc *ldap_url;
LDAPMessage *result = NULL;
LDAPMessage *entry = NULL;
char **vals;
- LDAP_CONN *conn;
+ ldap_handle_t *conn;
int ldap_errno;
const char *url;
const char **attrs;
- char buffer[MAX_FILTER_STR_LEN];
+ char buffer[LDAP_MAX_FILTER_STR_LEN];
if (strchr(fmt, '%') != NULL) {
- if (!radius_xlat(buffer, sizeof(buffer), fmt, request,
- ldap_escape_func, NULL)) {
- radlog(L_ERR,
- "rlm_ldap (%s): Unable to create LDAP URL",
- inst->xlat_name);
+ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, rlm_ldap_escape_func, NULL)) {
+ RDEBUGE("Unable to create LDAP URL");
return 0;
}
url = buffer;
}
if (!ldap_is_ldap_url(url)) {
- radlog(L_ERR, "rlm_ldap (%s): String passed does not look "
- "like an LDAP URL", inst->xlat_name);
+ RDEBUGE("String passed does not look like an LDAP URL");
return 0;
}
if (ldap_url_parse(url, &ldap_url)){
- radlog(L_ERR, "rlm_ldap (%s): Parsing LDAP URL failed",
- inst->xlat_name);
+ RDEBUGE("Parsing LDAP URL failed");
return 0;
}
!*ldap_url->lud_attrs[0] ||
(strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
ldap_url->lud_attrs[1]) {
- radlog(L_ERR, "rlm_ldap (%s): Bad attributes list in LDAP "
- "URL. URL must specify exactly one attribute to "
- "retrieve",
- inst->xlat_name);
+ RDEBUGE("Bad attributes list in LDAP URL. "
+ "URL must specify exactly one attribute to "
+ "retrieve");
goto free_urldesc;
}
- if (ldap_url->lud_host &&
- ((strncmp(inst->server, ldap_url->lud_host,
- strlen(inst->server)) != 0) ||
+ if (ldap_url->lud_host &&
+ ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
(ldap_url->lud_port != inst->port))) {
- RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host,
- inst->port);
+ RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
goto free_urldesc;
}
- conn = ldap_get_socket(inst);
+ conn = rlm_ldap_get_socket(inst, request);
if (!conn) goto free_urldesc;
memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
- rcode = perform_search(inst, request, &conn, ldap_url->lud_dn,
- ldap_url->lud_scope, ldap_url->lud_filter, attrs,
- &result);
- if (rcode < 0) {
- if (rcode == -2) {
- RDEBUG("Search returned not found", inst->xlat_name);
+ status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
+ attrs, &result);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_NO_RESULT:
+ RDEBUG("Search returned not found");
+ default:
goto free_socket;
- }
-
- goto free_socket;
}
+ rad_assert(conn);
+ rad_assert(result);
+
entry = ldap_first_entry(conn->handle, result);
if (!entry) {
- ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
+ ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+ RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
goto free_result;
}
vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
if (!vals) {
- RDEBUG("No \"%s\" attributes found in specified object",
- inst->xlat_name, ldap_url->lud_attrs[0]);
+ RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
goto free_result;
}
free_result:
ldap_msgfree(result);
free_socket:
- ldap_release_socket(inst, conn);
+ rlm_ldap_release_socket(inst, conn);
free_urldesc:
ldap_free_urldesc(ldap_url);
return length;
}
-
-static char *get_userdn(LDAP_CONN **pconn, REQUEST *request,
- rlm_rcode_t *module_rcode)
-{
- int rcode;
- VALUE_PAIR *vp;
- ldap_instance *inst = (*pconn)->inst;
- LDAPMessage *result, *entry;
- int ldap_errno;
- static char firstattr[] = "uid";
- char *user_dn;
- const char *attrs[] = {firstattr, NULL};
- char filter[MAX_FILTER_STR_LEN];
- char basedn[MAX_FILTER_STR_LEN];
-
- *module_rcode = RLM_MODULE_FAIL;
-
- vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
- if (vp) {
- *module_rcode = RLM_MODULE_OK;
- return vp->vp_strvalue;
- }
-
- if (!radius_xlat(filter, sizeof(filter), inst->filter,
- request, ldap_escape_func, NULL)) {
- radlog(L_ERR, "rlm_ldap (%s): Unable to create filter",
- inst->xlat_name);
- *module_rcode = RLM_MODULE_INVALID;
- return NULL;
- }
-
- if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
- request, ldap_escape_func, NULL)) {
- radlog(L_ERR, "rlm_ldap (%s): Unable to create basedn",
- inst->xlat_name);
- *module_rcode = RLM_MODULE_INVALID;
- return NULL;
- }
-
- rcode = perform_search(inst, request, pconn, basedn, LDAP_SCOPE_SUBTREE,
- filter, attrs, &result);
- if (rcode < 0) {
- if (rcode == -2) {
- *module_rcode = RLM_MODULE_NOTFOUND;
- }
-
- return NULL;
- }
-
- if ((entry = ldap_first_entry((*pconn)->handle, result)) == NULL) {
- ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
- ldap_msgfree(result);
- return NULL;
- }
-
- if ((user_dn = ldap_get_dn((*pconn)->handle, entry)) == NULL) {
- ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): ldap_get_dn() failed: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
-
- ldap_msgfree(result);
- return NULL;
- }
-
- vp = pairmake("LDAP-UserDn", user_dn, T_OP_EQ);
- if (!vp) {
- ldap_memfree(user_dn);
- ldap_msgfree(result);
- return NULL;
- }
-
- *module_rcode = RLM_MODULE_OK;
-
- pairadd(&request->config_items, vp);
- ldap_memfree(user_dn);
- ldap_msgfree(result);
-
- return vp->vp_strvalue;
-}
-
-
/** Perform LDAP-Group comparison checking
*
+ * Attempts to match users to groups using a variety of methods.
+ *
+ * @param instance of the rlm_ldap module.
+ * @param request Current request.
+ * @param thing Unknown.
+ * @param check Which group to check for user membership.
+ * @param check_pairs Unknown.
+ * @param reply_pairs Unknown.
+ * @return 1 on failure (or if the user is not a member), else 0.
*/
-static int ldap_groupcmp(void *instance, REQUEST *request,
- UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
- UNUSED VALUE_PAIR *check_pairs,
- UNUSED VALUE_PAIR **reply_pairs)
+static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
+ UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
{
- ldap_instance *inst = instance;
- int i, rcode, found;
- rlm_rcode_t module_rcode;
+ ldap_instance_t *inst = instance;
+ rlm_rcode_t rcode;
+ ldap_rcode_t status;
+ int i, found;
LDAPMessage *result = NULL;
LDAPMessage *entry = NULL;
int ldap_errno;
int check_is_dn = FALSE, value_is_dn = FALSE;
- static char firstattr[] = "dn";
- const char *attrs[] = {firstattr, NULL};
char **vals;
- const char *group_attrs[] = {inst->groupmemb_attr, NULL};
- LDAP_CONN *conn;
- char *user_dn;
+ const char *group_attrs[] = {inst->userobj_membership_attr, NULL};
+ ldap_handle_t *conn;
+ const char *user_dn;
- char gr_filter[MAX_FILTER_STR_LEN];
- char filter[MAX_FILTER_STR_LEN];
- char basedn[MAX_FILTER_STR_LEN];
+ char gr_filter[LDAP_MAX_FILTER_STR_LEN];
+ char filter[LDAP_MAX_FILTER_STR_LEN];
+ char basedn[LDAP_MAX_FILTER_STR_LEN];
RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
return 1;
}
- conn = ldap_get_socket(inst);
+ conn = rlm_ldap_get_socket(inst, request);
if (!conn) return 1;
/*
* This is used in the default membership filter.
*/
- user_dn = get_userdn(&conn, request, &module_rcode);
+ user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, NULL, &rcode);
if (!user_dn) {
- ldap_release_socket(inst, conn);
+ rlm_ldap_release_socket(inst, conn);
return 1;
}
-
- if (!inst->groupmemb_filter) goto check_attr;
+
+ rad_assert(conn);
+
+ if (!inst->groupobj_membership_filter) goto check_attr;
if (!radius_xlat(gr_filter, sizeof(gr_filter),
- inst->groupmemb_filter, request, ldap_escape_func,
+ inst->groupobj_membership_filter, request, rlm_ldap_escape_func,
NULL)) {
- radlog(L_ERR, "rlm_ldap (%s): Failed creating group filter",
- inst->xlat_name);
+ RDEBUGE("Failed creating group filter");
+
return 1;
}
/*
* If it's a DN, use that.
*/
- check_is_dn = strchr(check->vp_strvalue,',') == NULL ? FALSE : TRUE;
-
+ check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
if (check_is_dn) {
strlcpy(filter, gr_filter, sizeof(filter));
strlcpy(basedn, check->vp_strvalue, sizeof(basedn));
} else {
snprintf(filter, sizeof(filter), "(&(%s=%s)%s)",
- inst->groupname_attr,
+ inst->groupobj_name_attr,
check->vp_strvalue, gr_filter);
/*
- * get_userdn does this, too. Oh well.
+ * rlm_ldap_find_user does this, too. Oh well.
*/
- if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
- request, ldap_escape_func, NULL)) {
- radlog(L_ERR, "rlm_ldap (%s): Failed creating basedn",
- inst->xlat_name);
+ if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
+ RDEBUGE("Failed creating basedn");
+
return 1;
}
}
- rcode = perform_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE,
- filter, attrs, &result);
- if (rcode == 0) {
- ldap_release_socket(inst, conn);
- ldap_msgfree(result);
-
- RDEBUG("User found in group object");
-
- return 0;
- }
-
- if (rcode == -1) {
- ldap_release_socket(inst, conn);
- return 1;
+ status = rlm_ldap_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE, filter, NULL, NULL);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ RDEBUG("User found in group object");
+ found = TRUE;
+ goto finish;
+ case LDAP_PROC_NO_RESULT:
+ RDEBUG("Search returned not found");
+ goto check_attr;
+ default:
+ goto finish;
}
-
- /* else the search returned -2, for "not found" */
+
+ rad_assert(conn);
+ rad_assert(result);
/*
* Else the search returned NOTFOUND. See if we're
* configured to search for group membership using user
* object attribute.
*/
- if (!inst->groupmemb_attr) {
- ldap_release_socket(inst, conn);
- RDEBUG("Group object \"%s\" not found, or user is not a member",
- check->vp_strvalue);
+ if (!inst->userobj_membership_attr) {
+ rlm_ldap_release_socket(inst, conn);
+ RDEBUG("Group object \"%s\" not found, or user is not a member", check->vp_strvalue);
return 1;
}
check_attr:
- RDEBUG2("Checking user object membership (%s) attributes",
- inst->groupmemb_attr);
+ RDEBUG2("Checking user object membership (%s) attributes", inst->userobj_membership_attr);
snprintf(filter ,sizeof(filter), "(objectclass=*)");
- rcode = perform_search(inst, request, &conn, user_dn, LDAP_SCOPE_BASE,
- filter, group_attrs, &result);
- if (rcode < 0) {
- if (rcode == -2) {
- RDEBUG("Can't check membership attributes, user object "
- "not found");
- }
- ldap_release_socket(inst, conn);
- return 1;
+ status = rlm_ldap_search(inst, request, &conn, user_dn, LDAP_SCOPE_BASE, filter, group_attrs, &result);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_NO_RESULT:
+ RDEBUG("Can't check membership attributes, user object not found");
+ default:
+ rlm_ldap_release_socket(inst, conn);
+ return 1;
}
entry = ldap_first_entry(conn->handle, result);
if (!entry) {
- ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
+ ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+ RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
- ldap_release_socket(inst, conn);
+ rlm_ldap_release_socket(inst, conn);
ldap_msgfree(result);
return 1;
}
- vals = ldap_get_values(conn->handle, entry, inst->groupmemb_attr);
+ vals = ldap_get_values(conn->handle, entry, inst->userobj_membership_attr);
if (!vals) {
RDEBUG("No group membership attribute(s) found in user object");
- ldap_release_socket(inst, conn);
+ rlm_ldap_release_socket(inst, conn);
ldap_msgfree(result);
return 1;
}
*/
found = FALSE;
for (i = 0; i < ldap_count_values(vals); i++) {
- LDAPMessage *gr_result = NULL;
-
- value_is_dn = strchr(vals[i], ',') == NULL ? FALSE : TRUE;
+ value_is_dn = rlm_ldap_is_dn(vals[i]);
RDEBUG2("Processing group membership value \"%s\"", vals[i]);
*/
if (!check_is_dn && !value_is_dn) {
if (strcmp(vals[i], check->vp_strvalue) == 0){
- RDEBUG("User found (membership value matches "
- "check value)");
+ RDEBUG("User found (membership value matches check value)");
found = TRUE;
break;
*/
if (check_is_dn && value_is_dn) {
if (strcasecmp(vals[i], check->vp_strvalue) == 0){
- RDEBUG("User found (membership DN matches "
- "check DN)");
+ RDEBUG("User found (membership DN matches check DN)");
found = TRUE;
break;
/*
* We have a value which is a DN, and a check item which
* specifies the name of a group, search using the value
- * DN for the group, and see if it has a groupname_attr
+ * DN for the group, and see if it has a groupobj_name_attr
* which matches our check val.
*/
RDEBUG2("Searching with membership DN and group name");
- snprintf(filter,sizeof(filter), "(%s=%s)",
- inst->groupname_attr, check->vp_strvalue);
+ snprintf(filter,sizeof(filter), "(%s=%s)", inst->groupobj_name_attr, check->vp_strvalue);
- rcode = perform_search(inst, request, &conn, vals[i],
- LDAP_SCOPE_BASE, filter, attrs,
- &gr_result);
-
- ldap_msgfree(gr_result);
-
- /* Error occurred */
- if (rcode == -1) {
- ldap_value_free(vals);
- ldap_msgfree(result);
- ldap_release_socket(inst, conn);
- return 1;
- }
-
- /*
- * Either the group DN wasn't found, or it didn't have the
- * correct name. Continue looping over the attributes.
- */
- if (rcode == -2) {
- ldap_msgfree(gr_result);
- continue;
+ status = rlm_ldap_search(inst, request, &conn, vals[i], LDAP_SCOPE_BASE, filter, NULL, NULL);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ found = TRUE;
+ RDEBUG("User found (group name in membership DN matches check value)");
+
+ break;
+ case LDAP_PROC_NO_RESULT:
+ continue;
+ default:
+ goto finish;
}
- found = TRUE;
-
- RDEBUG("User found (group name in membership DN matches check "
- "value)");
-
break;
}
+ finish:
+
ldap_value_free(vals);
ldap_msgfree(result);
- ldap_release_socket(inst, conn);
+ rlm_ldap_release_socket(inst, conn);
if (!found) {
RDEBUG("User is not a member of specified group");
*/
static int ldap_detach(void *instance)
{
- ldap_instance *inst = instance;
+ ldap_instance_t *inst = instance;
fr_connection_pool_delete(inst->pool);
return 0;
}
-static int parse_sub_section(CONF_SECTION *parent,
- ldap_instance *inst,
- ldap_acct_section_t **config,
+/** Parse an accounting sub section.
+ *
+ * Allocate a new ldap_acct_section_t and write the config data into it.
+ *
+ * @param[in] parent of the config section.
+ * @param[out]
+ */
+static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
rlm_components_t comp)
{
CONF_SECTION *cs;
cs = cf_section_sub_find(parent, name);
if (!cs) {
- radlog(L_INFO, "rlm_ldap (%s): Couldn't find configuration for "
- "%s, will return NOOP for calls from this section",
- inst->xlat_name, name);
+ radlog(L_INFO, "rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
+ "from this section", inst->xlat_name, name);
return 0;
- }
-
- *config = talloc_zero(inst, ldap_acct_section_t);
- if (cf_section_parse(cs, *config, acct_section_config) < 0) {
- radlog(L_ERR, "rlm_ldap (%s): Failed parsing configuration for "
- "section %s", inst->xlat_name, name);
- return -1;
- }
-
- (*config)->cs = cs;
-
- return 0;
-}
-
-static int ldap_map_verify(ldap_instance *inst, value_pair_map_t **head)
-{
- value_pair_map_t *map;
+ }
- if (radius_attrmap(inst->cs, head, PAIR_LIST_REPLY,
- PAIR_LIST_REQUEST, MAX_ATTRMAP) < 0) {
+ *config = talloc_zero(inst, ldap_acct_section_t);
+ if (cf_section_parse(cs, *config, acct_section_config) < 0) {
+ LDAP_ERR("Failed parsing configuration for section %s", name);
+
return -1;
}
- /*
- * Attrmap only performs some basic validation checks, we need
- * to do rlm_ldap specific checks here.
- */
- for (map = *head; map != NULL; map = map->next) {
- if (map->dst->type != VPT_TYPE_ATTR) {
- cf_log_err(map->ci, "Left operand must be an "
- "attribute ref");
-
- return -1;
- }
-
- if (map->src->type == VPT_TYPE_LIST) {
- cf_log_err(map->ci, "Right operand must not be "
- "a list");
-
- return -1;
- }
-
- switch (map->src->type)
- {
- /*
- * Only =, :=, += and -= operators are supported for
- * cache entries.
- */
- case VPT_TYPE_LITERAL:
- case VPT_TYPE_XLAT:
- case VPT_TYPE_ATTR:
- switch (map->op) {
- case T_OP_SET:
- case T_OP_EQ:
- case T_OP_SUB:
- case T_OP_ADD:
- break;
- default:
- cf_log_err(map->ci, "Operator \"%s\" not "
- "allowed for %s values",
- fr_int2str(fr_tokens, map->op,
- "¿unknown?"),
- fr_int2str(vpt_types, map->src->type,
- "¿unknown?"));
- return -1;
- }
- default:
- break;
- }
- }
+ (*config)->cs = cs;
+
return 0;
}
*/
static int ldap_instantiate(CONF_SECTION * conf, void **instance)
{
- ldap_instance *inst;
+ ldap_instance_t *inst;
- *instance = inst = talloc_zero(conf, ldap_instance);
+ *instance = inst = talloc_zero(conf, ldap_instance_t);
if (!inst) return -1;
inst->cs = conf;
* If the configuration parameters can't be parsed, then fail.
*/
if ((cf_section_parse(conf, inst, module_config) < 0) ||
- (parse_sub_section(conf, inst,
- &inst->accounting,
- RLM_COMPONENT_ACCT) < 0) ||
- (parse_sub_section(conf, inst,
- &inst->postauth,
- RLM_COMPONENT_POST_AUTH) < 0)) {
- radlog(L_ERR, "rlm_ldap (%s): Failed parsing configuration",
- inst->xlat_name);
+ (parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
+ (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
+ LDAP_ERR("Failed parsing configuration");
+
goto error;
}
- if (inst->server == NULL) {
- radlog(L_ERR, "rlm_ldap (%s): Missing 'server' directive",
- inst->xlat_name);
+ if (!inst->server) {
+ LDAP_ERR("Missing 'server' directive");
+
goto error;
}
/*
- * Check for URLs. If they're used and the library doesn't
- * support them, then complain.
+ * Check for URLs. If they're used and the library doesn't support them, then complain.
*/
inst->is_url = 0;
if (ldap_is_ldap_url(inst->server)) {
inst->is_url = 1;
inst->port = 0;
#else
- radlog(L_ERR, "rlm_ldap (%s): 'server' directive is in URL "
- "form but ldap_initialize() is not available",
- inst->xlat_name);
+ LDAP_ERR("'server' directive is in URL form but ldap_initialize() is not available");
goto error;
#endif
}
- /* workaround for servers which support LDAPS but not START TLS */
+ /*
+ * Workaround for servers which support LDAPS but not START TLS
+ */
if (inst->port == LDAPS_PORT || inst->tls_mode) {
inst->tls_mode = LDAP_OPT_X_TLS_HARD;
} else {
#if LDAP_SET_REBIND_PROC_ARGS != 3
/*
- * The 2-argument rebind doesn't take an instance
- * variable. Our rebind function needs the instance
+ * The 2-argument rebind doesn't take an instance variable. Our rebind function needs the instance
* variable for the username, password, etc.
*/
if (inst->rebind == 1) {
- radlog(L_ERR, "rlm_ldap (%s): Cannot use 'rebind' directive "
- "as this version of libldap does not support the API "
- "that we need", inst->xlat-name);
+ LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API "
+ "that we need");
+
goto error;
}
#endif
/*
* Build the attribute map
*/
- if (ldap_map_verify(inst, &(inst->user_map)) < 0) {
+ if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
goto error;
}
/*
* Group comparison checks.
*/
- paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst);
+ paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, rlm_ldap_groupcmp, inst);
if (cf_section_name2(conf)) {
const DICT_ATTR *da;
ATTR_FLAGS flags;
dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
da = dict_attrbyname(buffer);
if (!da) {
- radlog(L_ERR, "rlm_ldap (%s): Failed creating "
- "attribute %s", inst->xlat_name, buffer);
+ LDAP_ERR("Failed creating attribute %s", buffer);
+
goto error;
}
- paircompare_register(da->attr, PW_USER_NAME, ldap_groupcmp,
- inst);
+ paircompare_register(da->attr, PW_USER_NAME, rlm_ldap_groupcmp, inst);
}
xlat_register(inst->xlat_name, ldap_xlat, inst);
/*
* Initialize the socket pool.
*/
- inst->pool = fr_connection_pool_init(inst->cs, inst,
- ldap_conn_create,
- NULL,
- ldap_conn_delete);
+ inst->pool = fr_connection_pool_init(inst->cs, inst, rlm_ldap_conn_create, NULL, rlm_ldap_conn_delete);
if (!inst->pool) {
ldap_detach(inst);
return -1;
return -1;
}
-static int check_access(ldap_instance *inst, REQUEST* request, LDAP_CONN *conn,
- LDAPMessage *entry)
+/** Check the user's password against ldap database
+ *
+ */
+static rlm_rcode_t ldap_authenticate(void *instance, REQUEST *request)
{
- int rcode = -1;
- char **vals = NULL;
-
- vals = ldap_get_values(conn->handle, entry, inst->access_attr);
- if (vals) {
- if (inst->positive_access_attr) {
- if (strncmp(vals[0], "FALSE", 5) == 0) {
- RDEBUG("Dialup access disabled");
-
- } else {
- rcode = 0;
- }
-
- } else {
- RDEBUG("\"%s\" attribute exists - access denied by"
- " default", inst->access_attr);
- }
-
- ldap_value_free(vals);
-
- } else if (inst->positive_access_attr) {
- RDEBUG("No %s attribute - access denied by default",
- inst->access_attr);
-
- } else {
- rcode = 0;
- }
-
- return rcode;
-}
-
+ rlm_rcode_t rcode;
+ const char *user_dn;
+ ldap_instance_t *inst = instance;
+ ldap_handle_t *conn;
-static VALUE_PAIR *ldap_getvalue(REQUEST *request, const value_pair_map_t *map,
- void *ctx)
-{
- rlm_ldap_result_t *self = ctx;
- VALUE_PAIR *head, **tail, *vp;
- int i;
-
- request = request;
-
- head = NULL;
- tail = &head;
-
/*
- * Iterate over all the retrieved values,
- * don't try and be clever about changing operators
- * just use whatever was set in the attribute map.
+ * Ensure that we're being passed a plain-text password, and not
+ * anything else.
*/
- for (i = 0; i < self->count; i++) {
- vp = pairalloc(NULL, map->dst->da);
- rad_assert(vp);
-
- pairparsevalue(vp, self->values[i]);
-
- *tail = vp;
- tail = &(vp->next);
- }
-
- return head;
-}
-
-
-static void xlat_attrsfree(const xlat_attrs_t *expanded)
-{
- const value_pair_map_t *map;
- unsigned int total = 0;
-
- const char *name;
-
- for (map = expanded->maps; map != NULL; map = map->next)
- {
- name = expanded->attrs[total++];
- if (!name) return;
-
- switch (map->src->type)
- {
- case VPT_TYPE_XLAT:
- case VPT_TYPE_ATTR:
- rad_cfree(name);
- break;
- default:
- break;
- }
- }
-}
-
-
-static int xlat_attrs(REQUEST *request, const value_pair_map_t *maps,
- xlat_attrs_t *expanded)
-{
- const value_pair_map_t *map;
- unsigned int total = 0;
-
- size_t len;
- char *buffer;
- VALUE_PAIR *found, **from = NULL;
- REQUEST *context;
-
- for (map = maps; map != NULL; map = map->next)
- {
- switch (map->src->type)
- {
- case VPT_TYPE_XLAT:
- buffer = rad_malloc(MAX_ATTR_STR_LEN);
- len = radius_xlat(buffer, MAX_ATTR_STR_LEN,
- map->src->name, request, NULL, NULL);
-
- if (len <= 0) {
- RDEBUG("Expansion of LDAP attribute "
- "\"%s\" failed", map->src->name);
-
- goto error;
- }
-
- expanded->attrs[total++] = buffer;
- break;
+ if (!request->username) {
+ RDEBUGE("Attribute \"User-Name\" is required for authentication");
- case VPT_TYPE_ATTR:
- context = request;
-
- if (radius_request(&context, map->src->request) == 0) {
- from = radius_list(context, map->src->list);
- }
- if (!from) continue;
-
- found = pairfind(*from, map->src->da->attr,
- map->src->da->vendor, TAG_ANY);
- if (!found) continue;
-
- buffer = rad_malloc(MAX_ATTR_STR_LEN);
- strlcpy(buffer, found->vp_strvalue, MAX_ATTR_STR_LEN);
-
- expanded->attrs[total++] = buffer;
- break;
-
- case VPT_TYPE_LITERAL:
- expanded->attrs[total++] = map->src->name;
- break;
- default:
- rad_assert(0);
- error:
- expanded->attrs[total] = NULL;
-
- xlat_attrsfree(expanded);
-
- return -1;
- }
-
+ return RLM_MODULE_INVALID;
}
-
- expanded->attrs[total] = NULL;
- expanded->maps = maps;
-
- return 0;
-}
-
-/** Convert attribute map into valuepairs
- *
- * Use the attribute map built earlier to convert LDAP values into valuepairs
- * and insert them into whichever list they need to go into.
- *
- * This is *NOT* atomic, but there's no condition in which we should error
- * out...
- */
-static void do_attrmap(UNUSED ldap_instance *inst, REQUEST *request,
- LDAP *handle, const xlat_attrs_t *expanded,
- LDAPMessage *entry)
-{
- const value_pair_map_t *map;
- unsigned int total = 0;
-
- rlm_ldap_result_t result;
- const char *name;
-
- for (map = expanded->maps; map != NULL; map = map->next)
- {
- name = expanded->attrs[total++];
+ if (!request->password ||
+ (request->password->da->attr != PW_USER_PASSWORD)) {
+ RDEBUGW(" You have set \"Auth-Type := LDAP\" somewhere.");
+ RDEBUGW(" *********************************************");
+ RDEBUGW(" * THAT CONFIGURATION IS WRONG. DELETE IT. ");
+ RDEBUGW(" * YOU ARE PREVENTING THE SERVER FROM WORKING.");
+ RDEBUGW(" *********************************************");
- result.values = ldap_get_values(handle, entry, name);
- if (!result.values) {
- RDEBUG2("Attribute \"%s\" not found in LDAP object",
- name);
-
- goto next;
- }
-
- /*
- * Find out how many values there are for the
- * attribute and extract all of them.
- */
- result.count = ldap_count_values(result.values);
-
- /*
- * If something bad happened, just skip, this is probably
- * a case of the dst being incorrect for the current
- * request context
- */
- if (radius_map2request(request, map, name, ldap_getvalue,
- &result) < 0) {
- goto next;
- }
-
- next:
+ RDEBUGE("Attribute \"User-Password\" is required for authentication.");
- ldap_value_free(result.values);
+ return RLM_MODULE_INVALID;
}
-}
-
-
-static void do_check_reply(ldap_instance *inst, REQUEST *request)
-{
- /*
- * More warning messages for people who can't be bothered
- * to read the documentation.
- */
- if (inst->expect_password && (debug_flag > 1)) {
- if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
- !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
- !pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) &&
- !pairfind(request->config_items, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
- !pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
- RDEBUGW("No \"known good\" password "
- "was found in LDAP. Are you sure that "
- "the user is configured correctly?");
- }
- }
-}
-
-static void apply_profile(ldap_instance *inst, REQUEST *request,
- LDAP_CONN **pconn, const char *profile,
- const xlat_attrs_t *expanded)
-{
- int rcode;
- LDAPMessage *result, *entry;
- int ldap_errno;
- LDAP *handle = (*pconn)->handle;
- char filter[MAX_FILTER_STR_LEN];
+ if (request->password->length == 0) {
+ RDEBUGE("Empty password supplied");
+
+ return RLM_MODULE_INVALID;
+ }
- if (!profile || !*profile) return;
+ RDEBUG("Login attempt by \"%s\" with password \"%s\"", request->username->vp_strvalue,
+ request->password->vp_strvalue);
- strlcpy(filter, inst->base_filter, sizeof(filter));
+ conn = rlm_ldap_get_socket(inst, request);
+ if (!conn) return RLM_MODULE_FAIL;
- rcode = perform_search(inst, request, pconn, profile, LDAP_SCOPE_BASE,
- filter, expanded->attrs, &result);
+ /*
+ * Get the DN by doing a search.
+ */
+ user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, NULL, &rcode);
+ if (!user_dn) {
+ rlm_ldap_release_socket(inst, conn);
- if (rcode < 0) {
- if (rcode == -2) {
- RDEBUG("Profile \"%s\" not found", profile);
- }
- goto free_result;
+ return rcode;
}
- entry = ldap_first_entry(handle, result);
- if (!entry) {
- ldap_get_option(handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
-
- goto free_result;
+ /*
+ * Bind as the user
+ */
+ conn->rebound = TRUE;
+ rcode = rlm_ldap_bind(inst, request, &conn, user_dn, request->password->vp_strvalue, TRUE);
+ if (rcode == RLM_MODULE_OK) {
+ RDEBUG("Bind as user \"%s\" was successful", user_dn);
}
-
- do_attrmap(inst, request, handle, expanded, entry);
-free_result:
- ldap_msgfree(result);
+ rlm_ldap_release_socket(inst, conn);
+
+ return rcode;
}
-
/** Check if user is authorized for remote access
*
*/
static rlm_rcode_t ldap_authorize(void *instance, REQUEST *request)
{
- int rcode;
- int module_rcode = RLM_MODULE_OK;
- ldap_instance *inst = instance;
+ ldap_rcode_t status;
+ rlm_rcode_t rcode = RLM_MODULE_OK;
+ ldap_instance_t *inst = instance;
char *user_dn = NULL;
char **vals;
VALUE_PAIR *vp;
- LDAP_CONN *conn;
+ ldap_handle_t *conn;
LDAPMessage *result, *entry;
int ldap_errno;
- char filter[MAX_FILTER_STR_LEN];
- char basedn[MAX_FILTER_STR_LEN];
- xlat_attrs_t expanded; /* faster that mallocing every time */
+ char filter[LDAP_MAX_FILTER_STR_LEN];
+ char basedn[LDAP_MAX_FILTER_STR_LEN];
+ rlm_ldap_map_xlat_t expanded; /* faster that mallocing every time */
if (!request->username) {
- RDEBUG2("Attribute \"User-Name\" is required for "
- "authorization.");
+ RDEBUG2("Attribute \"User-Name\" is required for authorization.");
+
return RLM_MODULE_NOOP;
}
*/
if (request->username->length == 0) {
RDEBUG2("Zero length username not permitted");
+
return RLM_MODULE_INVALID;
}
- if (!radius_xlat(filter, sizeof(filter), inst->filter,
- request, ldap_escape_func, NULL)) {
- radlog(L_ERR, "rlm_ldap (%s): Failed creating filter",
- inst->xlat_name);
+ if (!radius_xlat(filter, sizeof(filter), inst->filter, request, rlm_ldap_escape_func, NULL)) {
+ RDEBUGE("Failed creating filter");
+
return RLM_MODULE_INVALID;
}
- if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
- request, ldap_escape_func, NULL)) {
- radlog(L_ERR, "rlm_ldap (%s): Failed creating basedn",
- inst->xlat_name);
+ if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
+ RDEBUGE("Failed creating basedn");
+
return RLM_MODULE_INVALID;
}
- if (xlat_attrs(request, inst->user_map, &expanded) < 0) {
+ if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
return RLM_MODULE_FAIL;
}
-
- conn = ldap_get_socket(inst);
+ conn = rlm_ldap_get_socket(inst, request);
if (!conn) return RLM_MODULE_FAIL;
- rcode = perform_search(inst, request, &conn, basedn,
- LDAP_SCOPE_SUBTREE, filter, expanded.attrs,
- &result);
-
- if (rcode < 0) {
- if (rcode == -2) {
+ status = rlm_ldap_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE, filter, expanded.attrs, &result);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
+ case LDAP_PROC_NO_RESULT:
+ rcode = RLM_MODULE_NOTFOUND;
RDEBUGE("User object not found");
- module_rcode = RLM_MODULE_NOTFOUND;
-
+ default:
goto free_socket;
- }
-
- goto free_socket;
}
+
+ rad_assert(conn);
entry = ldap_first_entry(conn->handle, result);
if (!entry) {
- ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
+ ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+ RDEBUGE("Failed retrieving entry: %s",
+ ldap_err2string(ldap_errno));
goto free_result;
}
user_dn = ldap_get_dn(conn->handle, entry);
if (!user_dn) {
- ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
- &ldap_errno);
- radlog(L_ERR, "rlm_ldap (%s): ldap_get_dn() failed: %s",
- inst->xlat_name,
- ldap_err2string(ldap_errno));
+ ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+ RDEBUGE("ldap_get_dn() failed: %s", ldap_err2string(ldap_errno));
+
goto free_result;
}
RDEBUG2("User found at DN \"%s\"", user_dn);
- /*
- * Adding attribute containing the Users' DN.
- */
- pairadd(&request->config_items,
- pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
#ifdef WITH_EDIR
/*
bufsize = sizeof(buffer);
/* retrive universal password */
- res = nmasldap_get_password(conn->handle, user_dn,
- buffer, &bufsize);
+ res = nmasldap_get_password(conn->handle, user_dn, buffer, &bufsize);
if (res != 0) {
RDEBUGW("Failed to retrieve eDirectory password");
module_rcode = RLM_MODULE_NOOP;
}
/* Add Cleartext-Password attribute to the request */
- vp = radius_paircreate(request, &request->config_items,
- PW_CLEARTEXT_PASSWORD, 0);
+ vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
strlcpy(vp->vp_strvalue, buffer, sizeof(vp->vp_strvalue));
vp->length = strlen(vp->vp_strvalue);
- RDEBUG2("Added eDirectory password in check items as %s = %s",
- vp->da->name, vp->vp_strvalue);
+ RDEBUG2("Added eDirectory password in check items as %s = %s", vp->da->name, vp->vp_strvalue);
if (inst->edir_autz) {
- RDEBUG2("Binding as user for eDirectory authorization "
- "checks");
+ RDEBUG2("Binding as user for eDirectory authorization checks");
/*
* Bind as the user
*/
conn->rebound = TRUE;
- module_rcode = ldap_bind_wrapper(request, &conn,
- user_dn,
- vp->vp_strvalue,
- TRUE);
+ module_rcode = ldap_bind_user(request, &conn, user_dn, vp->vp_strvalue, TRUE);
if (module_rcode != RLM_MODULE_OK) {
goto free_result;
}
/*
* Check for access.
*/
- if (inst->access_attr) {
- if (check_access(inst, request, conn, entry) < 0) {
- module_rcode = RLM_MODULE_USERLOCK;
+ if (inst->userobj_access_attr) {
+ rcode = rlm_ldap_check_access(inst, request, conn, entry);
+ if (rcode != RLM_MODULE_OK) {
goto free_result;
}
}
*/
vp = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
if (vp || inst->default_profile) {
- char *profile = inst->default_profile;
+ const char *profile = inst->default_profile;
if (vp) profile = vp->vp_strvalue;
- apply_profile(inst, request, &conn, profile, &expanded);
+ rlm_ldap_apply_profile(inst, request, &conn, profile, &expanded);
}
/*
if (vals != NULL) {
int i;
- for (i = 0; (vals[i] != NULL) && (*vals[i] != '\0');
- i++) {
- apply_profile(inst, request, &conn, vals[i],
- &expanded);
+ for (i = 0; (vals[i] != NULL) && (*vals[i] != '\0'); i++) {
+ rlm_ldap_apply_profile(inst, request, &conn, vals[i], &expanded);
}
ldap_value_free(vals);
}
if (inst->user_map) {
- do_attrmap(inst, request, conn->handle, &expanded, entry);
- do_check_reply(inst, request);
+ rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
+ rlm_ldap_check_reply(inst, request);
}
free_result:
if (user_dn) ldap_memfree(user_dn);
- xlat_attrsfree(&expanded);
+ rlm_ldap_map_xlat_free(&expanded);
ldap_msgfree(result);
free_socket:
- ldap_release_socket(inst, conn);
-
- return module_rcode;
-}
-
-
-/** Check the user's password against ldap database
- *
- */
-static rlm_rcode_t ldap_authenticate(void *instance, REQUEST *request)
-{
- rlm_rcode_t module_rcode;
- const char *user_dn;
- ldap_instance *inst = instance;
- LDAP_CONN *conn;
-
- /*
- * Ensure that we're being passed a plain-text password, and not
- * anything else.
- */
-
- if (!request->username) {
- radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Name\" is "
- "required for authentication", inst->xlat_name);
- return RLM_MODULE_INVALID;
- }
-
- if (!request->password) {
- radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Password\" "
- "is required for authentication.", inst->xlat_name);
- RDEBUG2(" You have set \"Auth-Type := LDAP\" somewhere.");
- RDEBUG2(" *********************************************");
- RDEBUG2(" * THAT CONFIGURATION IS WRONG. DELETE IT. ");
- RDEBUG2(" * YOU ARE PREVENTING THE SERVER FROM WORKING.");
- RDEBUG2(" *********************************************");
- return RLM_MODULE_INVALID;
- }
-
- if (request->password->da->attr != PW_USER_PASSWORD) {
- radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Password\" "
- "is required for authentication. Cannot use \"%s\".",
- inst->xlat_name, request->password->da->name);
- return RLM_MODULE_INVALID;
- }
-
- if (request->password->length == 0) {
- RDEBUGE("Empty password supplied");
-
- return RLM_MODULE_INVALID;
- }
-
- RDEBUG("Login attempt by \"%s\" with password \"%s\"",
- request->username->vp_strvalue, request->password->vp_strvalue);
-
- conn = ldap_get_socket(inst);
- if (!conn) return RLM_MODULE_FAIL;
+ rlm_ldap_release_socket(inst, conn);
- /*
- * Get the DN by doing a search.
- */
- user_dn = get_userdn(&conn, request, &module_rcode);
- if (!user_dn) {
- ldap_release_socket(inst, conn);
- return module_rcode;
- }
-
- /*
- * Bind as the user
- */
- conn->rebound = TRUE;
- module_rcode = ldap_bind_wrapper(request, &conn, user_dn,
- request->password->vp_strvalue,
- TRUE);
- if (module_rcode == RLM_MODULE_OK) {
- RDEBUG("Bind as user \"%s\" was successful", user_dn);
- }
-
- ldap_release_socket(inst, conn);
- return module_rcode;
+ return rcode;
}
/** Modify user's object in LDAP
*
+ * Process a modifcation map to update a user object in the LDAP directory.
+ *
+ * @param inst rlm_ldap instance.
+ * @param request Current request.
+ * @param section that holds the map to process.
+ * @return one of the RLM_MODULE_* values.
*/
-static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
- ldap_acct_section_t *section)
+static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
{
- rlm_rcode_t module_rcode = RLM_MODULE_OK;
- int ldap_errno, rcode, msg_id;
- LDAPMessage *result = NULL;
+ rlm_rcode_t rcode = RLM_MODULE_OK;
- LDAP_CONN *conn = NULL;
+ ldap_handle_t *conn = NULL;
- LDAPMod *mod_p[MAX_ATTRMAP + 1], mod_s[MAX_ATTRMAP];
+ LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
LDAPMod **modify = mod_p;
- char *passed[MAX_ATTRMAP * 2];
+ char *passed[LDAP_MAX_ATTRMAP * 2];
int i, total = 0, last_pass = 0;
- char *expanded[MAX_ATTRMAP];
+ char *expanded[LDAP_MAX_ATTRMAP];
int last_exp = 0;
- struct timeval tv;
-
const char *attr;
const char *value;
- const char *user_dn;
-
- const char *error = NULL;
-
+ const char *dn;
/*
* Build our set of modifications using the update sections in
* the config.
*p++ = '.';
}
- if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1,
- section->reference, request, NULL, NULL)) {
+ if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1, section->reference, request, NULL, NULL)) {
goto error;
}
}
if (!cf_item_is_section(ci)){
- radlog(L_ERR, "rlm_ldap (%s): Reference must resolve to a "
- "section", inst->xlat_name);
+ RDEBUGE("Reference must resolve to a section");
goto error;
}
cs = cf_section_sub_find(cf_itemtosection(ci), "update");
if (!cs) {
- radlog(L_ERR, "rlm_ldap (%s): Section must contain 'update' "
- "subsection",
- inst->xlat_name);
+ RDEBUGE("Section must contain 'update' subsection");
goto error;
}
/*
* Iterate over all the pairs, building our mods array
*/
- for (ci = cf_item_find_next(cs, NULL);
- ci != NULL;
- ci = cf_item_find_next(cs, ci)) {
+ for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
int do_xlat = FALSE;
- if (total == MAX_ATTRMAP) {
- radlog(L_ERR, "rlm_ldap (%s): Modify map size exceeded",
- inst->xlat_name);
+ if (total == LDAP_MAX_ATTRMAP) {
+ RDEBUGE("Modify map size exceeded");
goto error;
}
if (!cf_item_is_pair(ci)) {
- radlog(L_ERR, "rlm_ldap (%s): Entry is not in "
- "\"ldap-attribute = value\" format",
- inst->xlat_name);
+ RDEBUGE("Entry is not in \"ldap-attribute = value\" format");
goto error;
}
attr = cf_pair_attr(cp);
op = cf_pair_operator(cp);
- if ((value == NULL) || (*value == '\0')) {
- RDEBUG("empty value string, "
- "skipping attribute \"%s\"", attr);
+ if (!value || (*value == '\0')) {
+ RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
continue;
}
} else if (do_xlat) {
p = rad_malloc(1024);
if (radius_xlat(p, 1024, value, request, NULL, NULL) <= 0) {
- RDEBUG("xlat failed or empty value string, "
- "skipping attribute \"%s\"", attr);
+ RDEBUG("xlat failed or empty value string, skipping attribute \"%s\"", attr);
free(p);
* Static strings
*/
} else {
- memcpy(&(passed[last_pass]), &value,
- sizeof(passed[last_pass]));
+ memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
}
passed[last_pass + 1] = NULL;
break;
#endif
default:
- radlog(L_ERR, "rlm_ldap (%s): Operator '%s' "
- "is not supported for LDAP modify "
- "operations", inst->xlat_name,
- fr_int2str(fr_tokens, op, "¿unknown?"));
+ RDEBUGE("Operator '%s' is not supported for LDAP modify operations",
+ fr_int2str(fr_tokens, op, "¿unknown?"));
goto error;
}
* Now we know the value is ok, copy the pointers into
* the ldapmod struct.
*/
- memcpy(&(mod_s[total].mod_type), &(attr),
- sizeof(mod_s[total].mod_type));
+ memcpy(&(mod_s[total].mod_type), &(attr), sizeof(mod_s[total].mod_type));
mod_p[total] = &(mod_s[total]);
total++;
}
if (total == 0) {
- module_rcode = RLM_MODULE_NOOP;
+ rcode = RLM_MODULE_NOOP;
goto release;
}
mod_p[total] = NULL;
- conn = ldap_get_socket(inst);
+ conn = rlm_ldap_get_socket(inst, request);
if (!conn) return RLM_MODULE_FAIL;
-
- /*
- * Perform all modifications as the default admin user.
- */
- if (conn->rebound) {
- ldap_errno = ldap_bind_wrapper(request, &conn, inst->login,
- inst->password, TRUE);
- if (ldap_errno != RLM_MODULE_OK) {
- goto error;
- }
- rad_assert(conn != NULL);
- conn->rebound = FALSE;
- }
- user_dn = get_userdn(&conn, request, &module_rcode);
- if (!user_dn) {
- module_rcode = RLM_MODULE_NOTFOUND;
- goto release;
- }
-
- RDEBUG2("Modifying user object with DN \"%s\"", user_dn);
- retry:
- ldap_errno = ldap_modify_ext(conn->handle, user_dn, modify, NULL, NULL,
- &msg_id);
- if (ldap_errno != LDAP_SUCCESS) {
- switch (process_ldap_errno(inst, conn, &error))
- {
- case LDAP_PROC_SUCCESS:
- break;
- case LDAP_PROC_RETRY:
- radlog(L_ERR, "rlm_ldap (%s): Failed "
- "modifying object, reconnecting: %s",
- inst->xlat_name, error);
- goto retry;
- default:
- radlog(L_ERR, "rlm_ldap (%s): Failed "
- "modifying object: %s",
- inst->xlat_name, error);
- goto error;
- }
+ dn = rlm_ldap_find_user(inst, request, &conn, NULL, NULL, &rcode);
+ if (!dn || (rcode = RLM_MODULE_OK)) {
+ goto error;
}
-
- DEBUG3("rlm_ldap (%s): Waiting for modify result...", inst->xlat_name);
-
- tv.tv_sec = inst->timeout;
- tv.tv_usec = 0;
- result:
- rcode = ldap_result(conn->handle, msg_id, 1, &tv, &result);
- ldap_msgfree(result);
- if (rcode <= 0) {
- switch (process_ldap_errno(inst, conn, &error))
- {
- case LDAP_PROC_SUCCESS:
- break;
- case LDAP_PROC_RETRY:
- radlog(L_ERR, "rlm_ldap (%s): Failed "
- "modifying object, reconnecting: %s",
- inst->xlat_name, error);
- goto result;
- default:
- error:
- radlog(L_ERR, "rlm_ldap (%s): Failed "
- "modifying object: %s",
- inst->xlat_name, error);
- module_rcode = RLM_MODULE_FAIL;
- goto release;
- }
- }
-
- RDEBUG2("Modification successful!");
+ rcode = rlm_ldap_modify(inst, request, &conn, dn, modify);
release:
+ error:
/*
* Free up any buffers we allocated for xlat expansion
*/
for (i = 0; i < last_exp; i++) {
free(expanded[i]);
}
-
- ldap_release_socket(inst, conn);
+ rlm_ldap_release_socket(inst, conn);
- return module_rcode;
+ return rcode;
}
-
static rlm_rcode_t ldap_accounting(void *instance, REQUEST * request) {
- ldap_instance *inst = instance;
+ ldap_instance_t *inst = instance;
if (inst->accounting) {
return user_modify(inst, request, inst->accounting);
*/
static rlm_rcode_t ldap_postauth(void *instance, REQUEST * request)
{
- ldap_instance *inst = instance;
+ ldap_instance_t *inst = instance;
if (inst->postauth) {
return user_modify(inst, request, inst->postauth);