2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief LDAP module library functions.
21 * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
23 * @copyright 2013 The FreeRADIUS Server Project.
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27 #include <freeradius-devel/rad_assert.h>
38 /** Converts "bad" strings into ones which are safe for LDAP
40 * This is a callback for xlat operations.
42 * Will escape any characters in input strings that would cause the string to be interpreted as part of a DN and or
43 * filter. Escape sequence is @verbatim \<hex><hex> @endverbatim
45 * @param request The current request.
46 * @param out Pointer to output buffer.
47 * @param outlen Size of the output buffer.
48 * @param in Raw unescaped string.
49 * @param arg Any additional arguments (unused).
51 size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, const char *in, UNUSED void *arg)
53 static const char encode[] = ",+\"\\<>;*=()";
54 static const char hextab[] = "0123456789abcdef";
57 if (*in && ((*in == ' ') || (*in == '#'))) {
63 * Encode unsafe characters.
65 if (memchr(encode, *in, sizeof(encode) - 1)) {
69 * Only 3 or less bytes available.
74 *out++ = hextab[(*in >> 4) & 0x0f];
75 *out++ = hextab[*in & 0x0f];
85 * Doesn't need encoding
96 /** Check whether a string is a DN
98 * @param str to check.
99 * @return true if string is a DN, else false.
101 int rlm_ldap_is_dn(const char *str)
103 return strrchr(str, ',') == NULL ? FALSE : TRUE;
106 /** Find the place at which the two DN strings diverge
108 * Returns the length of the non matching string in full.
111 * @param part Partial DN as returned by ldap_parse_result.
112 * @return the length of the portion of full which wasn't matched or -1 on error.
114 static size_t rlm_ldap_common_dn(const char *full, const char *part)
116 size_t f_len, p_len, i;
122 f_len = strlen(full);
128 p_len = strlen(part);
133 if ((f_len < p_len) || !f_len) {
138 for (i = 0; i < p_len; i++) {
139 if (part[p_len - i] != full[f_len - i]) {
144 return f_len - p_len;
147 /** Parse response from LDAP server dealing with any errors
149 * Should be called after an LDAP operation. Will check result of operation and if it was successful, then attempt
150 * to retrieve and parse the result.
152 * Will also produce extended error output including any messages the server sent, and information about partial
155 * @param[in] inst of LDAP module.
156 * @param[in] conn Current connection.
157 * @param[in] msgid returned from last operation.
158 * @param[in] dn Last search or bind DN.
159 * @param[out] result Where to write result, if NULL result will be freed.
160 * @param[out] error Where to write the error string, may be NULL, must not be freed.
161 * @param[out] extra Where to write additional error string to, may be NULL (faster) or must be freed
162 * (with talloc_free).
163 * @return One of the LDAP_PROC_* codes.
165 static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_handle_t *conn, int msgid, const char *dn,
166 LDAPMessage **result, const char **error, char **extra)
168 ldap_rcode_t status = LDAP_PROC_SUCCESS;
170 int lib_errno = LDAP_SUCCESS; //!< errno returned by the library.
171 int srv_errno = LDAP_SUCCESS; //!< errno in the result message.
173 char *part_dn = NULL; //!< Partial DN match.
174 char *our_err = NULL; //!< Our extended error message.
175 char *srv_err = NULL; //!< Server's extended error message.
178 int freeit = FALSE; //!< Whether the message should
179 //!< be freed after being processed.
182 struct timeval tv; //!< Holds timeout values.
184 LDAPMessage *tmp_msg; //!< Temporary message pointer storage
185 //!< if we weren't provided with one.
187 const char *tmp_err; //!< Temporary error pointer storage
188 //!< if we weren't provided with one.
200 * We always need the result, but our caller may not
208 * Check if there was an error sending the request
210 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
212 if (lib_errno != LDAP_SUCCESS) {
216 tv.tv_sec = inst->timeout;
220 * Now retrieve the result and check for errors
221 * ldap_result returns -1 on error, and 0 on timeout
223 lib_errno = ldap_result(conn->handle, msgid, 1, &tv, result);
224 if (lib_errno == 0) {
225 lib_errno = LDAP_TIMEOUT;
230 if (lib_errno == -1) {
231 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
237 * Parse the result and check for errors sent by the server
239 lib_errno = ldap_parse_result(conn->handle, *result,
241 extra ? &part_dn : NULL,
242 extra ? &srv_err : NULL,
245 if (lib_errno != LDAP_SUCCESS) {
246 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
253 if ((lib_errno == LDAP_SUCCESS) && (srv_errno != LDAP_SUCCESS)) {
254 lib_errno = srv_errno;
255 } else if ((lib_errno != LDAP_SUCCESS) && (srv_errno == LDAP_SUCCESS)) {
256 srv_errno = lib_errno;
265 case LDAP_NO_SUCH_OBJECT:
266 *error = "The specified object wasn't found, check basedn and admin dn";
268 status = LDAP_PROC_BAD_DN;
273 * Build our own internal diagnostic string
275 len = rlm_ldap_common_dn(dn, part_dn);
278 our_err = talloc_asprintf(conn, "Match stopped here: [%.*s]%s", len, part_dn, part_dn ? part_dn : "");
282 case LDAP_INSUFFICIENT_ACCESS:
283 *error = "Insufficient access. Check the identity and password configuration directives";
285 status = LDAP_PROC_NOT_PERMITTED;
288 case LDAP_UNWILLING_TO_PERFORM:
289 *error = "Server was unwilling to perform";
291 status = LDAP_PROC_NOT_PERMITTED;
295 exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
297 *error = "Timed out while waiting for server to respond";
299 status = LDAP_PROC_ERROR;
302 case LDAP_FILTER_ERROR:
303 *error = "Bad search filter";
305 status = LDAP_PROC_ERROR;
308 case LDAP_TIMELIMIT_EXCEEDED:
309 exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
311 *error = "Time limit exceeded";
315 case LDAP_UNAVAILABLE:
316 case LDAP_SERVER_DOWN:
317 status = LDAP_PROC_RETRY;
321 case LDAP_INVALID_CREDENTIALS:
322 case LDAP_CONSTRAINT_VIOLATION:
323 status = LDAP_PROC_REJECT;
327 case LDAP_OPERATIONS_ERROR:
328 *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. See the ldap module configuration "
333 status = LDAP_PROC_ERROR;
338 *error = ldap_err2string(lib_errno);
341 if (!extra || ((lib_errno == srv_errno) && !our_err && !srv_err)) {
346 * Output the error codes from the library and server
348 p = talloc_strdup(conn, "");
351 if (lib_errno != srv_errno) {
352 a = talloc_asprintf_append(p, "LDAP lib error: %s (%u), srv error: %s (%u)",
353 ldap_err2string(lib_errno), lib_errno,
354 ldap_err2string(srv_errno), srv_errno);
364 a = talloc_asprintf_append_buffer(p,". %s", our_err);
374 a = talloc_asprintf_append_buffer(p, ". Server said: %s", srv_err);
392 ldap_memfree(srv_err);
396 ldap_memfree(part_dn);
400 talloc_free(our_err);
403 if ((lib_errno || srv_errno) && *result) {
404 ldap_msgfree(*result);
411 /** Bind to the LDAP directory as a user
413 * Performs a simple bind to the LDAP directory, and handles any errors that
416 * @param inst rlm_ldap configuration.
417 * @param request Current request, this may be NULL, in which case all debug logging is done with radlog.
418 * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL after
419 * calling this function.
420 * @param dn The DN of the user, may be NULL to bind anonymously.
421 * @param password May be NULL if no password is specified.
422 * @param retry if the server is down.
423 * @return one of the LDAP_PROC_* values.
425 ldap_rcode_t rlm_ldap_bind(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn, const char *dn,
426 const char *password, int retry)
432 const char *error = NULL;
435 rad_assert(*pconn && (*pconn)->handle);
438 * Bind as anonymous user
443 msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
444 /* We got a valid message ID */
447 RDEBUG2("Waiting for bind result...");
449 DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
453 status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
455 case LDAP_PROC_SUCCESS:
457 case LDAP_PROC_NOT_PERMITTED:
458 LDAP_ERR_REQ("Bind was not permitted: %s", error);
463 case LDAP_PROC_REJECT:
464 LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
469 case LDAP_PROC_RETRY:
471 *pconn = fr_connection_reconnect(inst->pool, *pconn);
473 LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
474 dn, inst->server, inst->port, error);
476 talloc_free(extra); /* don't leak debug info */
482 status = LDAP_PROC_ERROR;
485 * Were not allowed to retry, or there are no more
486 * sockets, treat this as a hard failure.
490 #ifdef HAVE_LDAP_INITIALIZE
492 LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
496 LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
504 if (extra) talloc_free(extra);
506 return status; /* caller closes the connection */
510 /** Search for something in the LDAP directory
512 * Binds as the administrative user and performs a search, dealing with any
515 * @param inst rlm_ldap configuration.
516 * @param request Current request.
517 * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL
518 * after calling this function.
520 * @param scope to use.
521 * @param filter to use.
522 * @param attrs to retrieve.
523 * @param result Where to store the result. Must be freed with ldap_msgfree.
524 * may be NULL in which case result will be automatically freed after use.
525 * @return One of the LDAP_PROC_* values.
527 ldap_rcode_t rlm_ldap_search(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
528 const char *dn, int scope, const char *filter, const char * const *attrs,
529 LDAPMessage **result)
533 int msgid; //!< Message id returned by
534 //!< ldap_search_ext.
536 int count = 0; //!< Number of results we got.
538 struct timeval tv; //!< Holds timeout values.
540 const char *error = NULL;
543 rad_assert(*pconn && (*pconn)->handle);
546 * OpenLDAP library doesn't declare attrs array as const, but
547 * it really should be *sigh*.
550 memcpy(&search_attrs, &attrs, sizeof(attrs));
553 * Do all searches as the admin user.
555 if ((*pconn)->rebound) {
556 status = rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE);
557 if (status != LDAP_PROC_SUCCESS) {
558 return LDAP_PROC_ERROR;
563 (*pconn)->rebound = FALSE;
566 RDEBUG2("Performing search in '%s' with filter '%s'", dn, filter);
569 * If LDAP search produced an error it should also be logged
570 * to the ld. result should pick it up without us
571 * having to pass it explicitly.
573 tv.tv_sec = inst->timeout;
576 (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, 0, NULL, NULL, &tv, 0, &msgid);
578 RDEBUG2("Waiting for search result...");
579 status = rlm_ldap_result(inst, *pconn, msgid, dn, result, &error, &extra);
581 case LDAP_PROC_SUCCESS:
583 case LDAP_PROC_RETRY:
584 *pconn = fr_connection_reconnect(inst->pool, *pconn);
586 RDEBUGW("Search failed: %s. Got new socket, retrying...", error);
588 talloc_free(extra); /* don't leak debug info */
593 status = LDAP_PROC_ERROR;
597 RDEBUGE("Failed performing search: %s", error);
598 RDEBUGE("%s", extra);
604 count = ldap_count_entries((*pconn)->handle, *result);
606 ldap_msgfree(*result);
609 RDEBUG("Search returned no results");
611 status = LDAP_PROC_NO_RESULT;
616 if (extra) talloc_free(extra);
621 /** Modify something in the LDAP directory
623 * Binds as the administrative user and attempts to modify an LDAP object.
625 * @param inst rlm_ldap configuration.
626 * @param request Current request.
627 * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL after
628 * calling this function.
629 * @param dn to modify.
630 * @param mods to make.
631 * @return One of the LDAP_PROC_* values.
633 ldap_rcode_t rlm_ldap_modify(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
634 const char *dn, LDAPMod *mods[])
638 int msgid; //!< Message id returned by
639 //!< ldap_search_ext.
641 const char *error = NULL;
644 rad_assert(*pconn && (*pconn)->handle);
647 * Perform all modifications as the admin user.
649 if ((*pconn)->rebound) {
650 status = rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE);
651 if (status != LDAP_PROC_SUCCESS) {
652 return LDAP_PROC_ERROR;
657 (*pconn)->rebound = FALSE;
660 RDEBUG2("Modifying object with DN \"%s\"", dn);
662 (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
664 RDEBUG2("Waiting for modify result...");
665 status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
667 case LDAP_PROC_SUCCESS:
669 case LDAP_PROC_RETRY:
670 *pconn = fr_connection_reconnect(inst->pool, *pconn);
672 RDEBUGW("Modify failed: %s. Got new socket, retrying...", error);
674 talloc_free(extra); /* don't leak debug info */
679 status = LDAP_PROC_ERROR;
683 RDEBUGE("Failed modifying object: %s", error);
684 RDEBUGE("%s", extra);
690 if (extra) talloc_free(extra);
695 /** Retrieve the DN of a user object
697 * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any attributes
698 * passed and return the result in *result.
700 * This potentially allows for all authorization and authentication checks to be performed in one ldap search
701 * operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* LDAP directory servers out there.
703 * @param[in] inst rlm_ldap configuration.
704 * @param[in] request Current request.
705 * @param[in,out] pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL
706 * after calling this function.
707 * @param[in] attrs Additional attributes to retrieve, may be NULL.
708 * @param[in] force Query even if the User-DN already exists.
709 * @param[out] result Where to write the result, may be NULL in which case result is discarded.
710 * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes.
711 * @return The user's DN or NULL on error.
713 const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
714 const char *attrs[], int force, LDAPMessage **result, rlm_rcode_t *rcode)
716 static const char *tmp_attrs[] = { NULL };
719 VALUE_PAIR *vp = NULL;
720 LDAPMessage *tmp_msg = NULL, *entry = NULL;
723 char filter[LDAP_MAX_FILTER_STR_LEN];
724 char basedn[LDAP_MAX_FILTER_STR_LEN];
726 int freeit = FALSE; //!< Whether the message should
727 //!< be freed after being processed.
729 *rcode = RLM_MODULE_FAIL;
738 memset(&attrs, 0, sizeof(tmp_attrs));
742 * If the caller isn't looking for the result we can just return the current userdn value.
745 vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
747 RDEBUG("Using user DN from request \"%s\"", vp->vp_strvalue);
748 *rcode = RLM_MODULE_OK;
749 return vp->vp_strvalue;
754 * Perform all searches as the admin user.
756 if ((*pconn)->rebound) {
757 status = rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE);
758 if (status != LDAP_PROC_SUCCESS) {
759 *rcode = RLM_MODULE_FAIL;
765 (*pconn)->rebound = FALSE;
769 if (!radius_xlat(filter, sizeof(filter), inst->userobj_filter, request, rlm_ldap_escape_func, NULL)) {
770 RDEBUGE("Unable to create filter");
772 *rcode = RLM_MODULE_INVALID;
776 if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
777 RDEBUGE("Unable to create basedn");
779 *rcode = RLM_MODULE_INVALID;
783 status = rlm_ldap_search(inst, request, pconn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, result);
785 case LDAP_PROC_SUCCESS:
787 case LDAP_PROC_NO_RESULT:
788 *rcode = RLM_MODULE_NOTFOUND;
791 *rcode = RLM_MODULE_FAIL;
797 entry = ldap_first_entry((*pconn)->handle, *result);
799 ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
800 RDEBUGE("Failed retrieving entry: %s",
801 ldap_err2string(ldap_errno));
806 dn = ldap_get_dn((*pconn)->handle, entry);
808 ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
810 RDEBUGE("Retrieving object DN from entry failed: %s",
811 ldap_err2string(ldap_errno));
816 RDEBUG("User object found at DN \"%s\"", dn);
817 vp = pairmake(request, &request->config_items, "LDAP-UserDN", dn, T_OP_EQ);
819 *rcode = RLM_MODULE_OK;
825 if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) {
826 ldap_msgfree(*result);
830 return vp ? vp->vp_strvalue : NULL;
833 rlm_rcode_t rlm_ldap_apply_profile(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
834 const char *profile, const rlm_ldap_map_xlat_t *expanded)
836 rlm_rcode_t rcode = RLM_MODULE_OK;
838 LDAPMessage *result = NULL, *entry = NULL;
840 LDAP *handle = (*pconn)->handle;
841 char filter[LDAP_MAX_FILTER_STR_LEN];
843 if (!profile || !*profile) {
844 return RLM_MODULE_NOOP;
846 strlcpy(filter, inst->base_filter, sizeof(filter));
848 status = rlm_ldap_search(inst, request, pconn, profile, LDAP_SCOPE_BASE, filter, expanded->attrs, &result);
850 case LDAP_PROC_SUCCESS:
852 case LDAP_PROC_NO_RESULT:
853 RDEBUG("Profile \"%s\" not found", profile);
854 return RLM_MODULE_NOTFOUND;
856 return RLM_MODULE_FAIL;
862 entry = ldap_first_entry(handle, result);
864 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
865 RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
867 rcode = RLM_MODULE_NOTFOUND;
872 rlm_ldap_map_do(inst, request, handle, expanded, entry);
875 ldap_msgfree(result);
880 /** Convert multiple group names into a DNs
882 * Given an array of group names, builds a filter matching all names, then retrieves all group objects
883 * and stores the DN associated with each group object.
885 * @param inst rlm_ldap configuration.
886 * @param request Current request.
887 * @param conn Current connection.
888 * @param names to covert to DNs (NULL terminated).
889 * @param out Where to write the DNs. DNs must be freed with ldap_memfree(). Will be NULL terminated.
890 * @param outlen Size of out.
891 * @return One of the RLM_MODULE_* values.
893 rlm_rcode_t rlm_ldap_group_name2dn(const ldap_instance_t *inst, REQUEST *request,
894 ldap_handle_t **pconn, char **names, char **out,
901 unsigned int name_cnt, entry_cnt;
902 const char *attrs[] = { NULL };
904 LDAPMessage *result = NULL, *entry;
908 char buffer[LDAP_MAX_GROUP_NAME_LEN + 1];
915 return RLM_MODULE_OK;
918 if (!inst->groupobj_name_attr) {
919 RDEBUGE("Told to convert group names to DNs but missing 'group.name_attribute' directive");
921 return RLM_MODULE_INVALID;
925 * It'll probably only save a few ms in network latency, but it means we can send a query
926 * for the entire group list at once.
928 filter = talloc_asprintf(request, "(&(%s)(|(", inst->base_filter);
930 rlm_ldap_escape_func(request, buffer, sizeof(buffer), *++name, NULL);
931 filter = talloc_asprintf_append_buffer(filter, "(%s=%s)", inst->groupobj_name_attr, buffer);
935 filter = talloc_strdup_append_buffer(filter, "))");
937 status = rlm_ldap_search(inst, request, pconn, inst->basedn, LDAP_SCOPE_SUB, filter, attrs, &result);
939 case LDAP_PROC_SUCCESS:
941 case LDAP_PROC_NO_RESULT:
942 rcode = RLM_MODULE_INVALID;
945 rcode = RLM_MODULE_FAIL;
949 entry_cnt = ldap_count_entries((*pconn)->handle, result);
950 if (entry_cnt > name_cnt) {
951 RDEBUGE("Number of DNs exceeds number of names, base_dn or base_filter should be more restrictive");
952 rcode = RLM_MODULE_INVALID;
957 if (entry_cnt > (outlen - 1)) {
958 RDEBUGE("Number of DNs exceeds limit (%i)", outlen - 1);
959 rcode = RLM_MODULE_INVALID;
964 if (entry_cnt < name_cnt) {
965 RDEBUGW("Got partial mapping of group names to DNs, membership information may be incomplete");
968 entry = ldap_first_entry((*pconn)->handle, result);
970 ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
971 RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
973 rcode = RLM_MODULE_INVALID;
978 *dn = ldap_get_dn((*pconn)->handle, entry);
979 } while((entry = ldap_next_entry((*pconn)->handle, entry)));
986 ldap_msgfree(result);
990 * Be nice and cleanup the output array if we error out.
992 if (status != RLM_MODULE_OK) {
994 while(*dn) ldap_memfree(*dn++);
1001 /** Convert a single group name into a DN
1003 * Unlike the inverse conversion of a name to a DN, most LDAP directories don't allow filtering by DN,
1004 * so we need to search for each DN individually.
1005 * @param inst rlm_ldap configuration.
1006 * @param request Current request.
1007 * @param conn Current connection.
1008 * @param dn to resolve.
1009 * @param out Where to write group name (must be freed with ldap_memfree()).
1010 * @return One of the RLM_MODULE_* values.
1012 rlm_rcode_t rlm_ldap_group_dn2name(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
1013 const char *dn, char **out)
1016 ldap_rcode_t status;
1020 const char *attrs[] = { inst->groupobj_name_attr, NULL };
1021 LDAPMessage *result = NULL, *entry;
1025 if (!inst->groupobj_name_attr) {
1026 RDEBUGE("Told to convert group DN to name but missing 'group.name_attribute' directive");
1028 return RLM_MODULE_INVALID;
1031 status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, inst->base_filter, attrs,
1034 case LDAP_PROC_SUCCESS:
1036 case LDAP_PROC_NO_RESULT:
1037 return RLM_MODULE_INVALID;
1039 return RLM_MODULE_FAIL;
1042 entry = ldap_first_entry((*pconn)->handle, result);
1044 ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1045 RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1047 rcode = RLM_MODULE_INVALID;
1051 vals = ldap_get_values((*pconn)->handle, entry, inst->groupobj_name_attr);
1053 rcode = RLM_MODULE_INVALID;
1061 ldap_msgfree(result);
1065 ldap_value_free(vals);
1071 /** Convert group membership information into attributes
1073 * @param inst rlm_ldap configuration.
1074 * @param request Current request.
1075 * @param conn used to retrieve entry.
1076 * @param entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
1077 * @return One of the RLM_MODULE_* values.
1079 rlm_rcode_t rlm_ldap_cacheable_membership(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
1085 char *group_name[LDAP_MAX_CACHEABLE + 1];
1086 char **name_p = group_name;
1088 char *group_dn[LDAP_MAX_CACHEABLE + 1];
1096 if (!inst->cacheable_group_dn && !inst->cacheable_group_name) {
1097 return RLM_MODULE_OK;
1101 * Group membership apparently isn't stored in user objects, so jump straight to resolving groups
1102 * with the group membership filter.
1104 if (!inst->userobj_membership_attr) {
1109 * Parse the membership information we got in the initial user query.
1111 vals = ldap_get_values((*pconn)->handle, entry, inst->userobj_membership_attr);
1116 for (i = 0; (vals[i] != NULL) && (i < LDAP_MAX_CACHEABLE); i++) {
1117 is_dn = rlm_ldap_is_dn(vals[i]);
1119 if (inst->cacheable_group_dn) {
1121 * The easy case, were caching DNs and we got a DN.
1124 pairmake(request, &request->config_items, "LDAP-GroupDN", vals[i], T_OP_ADD);
1125 RDEBUG3("Added LDAP-GroupDN with value \"%s\" to control list", vals[i]);
1128 * We were told to cache DNs but we got a name, we now need to resolve this to a DN.
1129 * Store all the group names in an array so we can do one query.
1132 *name_p++ = vals[i];
1136 if (inst->cacheable_group_name) {
1138 * The easy case, were caching names and we got a name.
1141 pairmake(request, &request->config_items, "LDAP-Group", vals[i], T_OP_ADD);
1142 RDEBUG3("Added LDAP-Group with value \"%s\" to control list", vals[i]);
1144 * We were told to cache names but we got a DN, we now need to resolve this to a name.
1145 * Only Active Directory supports filtering on DN, so we have to search for each
1149 rcode = rlm_ldap_group_dn2name(inst, request, pconn, vals[i], &name);
1150 if (rcode != RLM_MODULE_OK) {
1151 ldap_value_free(vals);
1156 pairmake(request, &request->config_items, "LDAP-Group", name, T_OP_ADD);
1157 RDEBUG3("Added LDAP-Group with value \"%s\" to control list", name);
1164 rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn));
1166 ldap_value_free(vals);
1168 if (rcode != RLM_MODULE_OK) {
1174 pairmake(request, &request->config_items, "LDAP-GroupDN", *dn_p, T_OP_ADD);
1175 RDEBUG3("Added LDAP-GroupDN with value \"%s\" to control list", *dn_p);
1176 ldap_memfree(*dn_p);
1183 /* @todo add code to search for groups with this user as a member and add them to control list */
1188 /** Check for presence of access attribute in result
1190 * @param inst rlm_ldap configuration.
1191 * @param request Current request.
1192 * @param conn used to retrieve entry.
1193 * @param entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
1194 * @return RLM_MODULE_USERLOCK if the user was denied access, else RLM_MODULE_OK.
1196 rlm_rcode_t rlm_ldap_check_access(const ldap_instance_t *inst, REQUEST *request,
1197 const ldap_handle_t *conn, LDAPMessage *entry)
1199 rlm_rcode_t rcode = RLM_MODULE_OK;
1202 vals = ldap_get_values(conn->handle, entry, inst->userobj_access_attr);
1204 if (inst->access_positive && (strncmp(vals[0], "FALSE", 5) == 0)) {
1205 RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out");
1206 rcode = RLM_MODULE_USERLOCK;
1208 RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr);
1209 rcode = RLM_MODULE_USERLOCK;
1212 ldap_value_free(vals);
1213 } else if (inst->access_positive) {
1214 RDEBUG("No \"%s\" attribute - user locked out", inst->userobj_access_attr);
1215 rcode = RLM_MODULE_USERLOCK;
1221 /** Verify we got a password from the search
1223 * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password.
1225 * @param inst rlm_ldap configuration.
1226 * @param request Current request.
1228 void rlm_ldap_check_reply(const ldap_instance_t *inst, REQUEST *request)
1231 * More warning messages for people who can't be bothered to read the documentation.
1233 * Expect_password is set when we process the mapping, and is only true if there was a mapping between
1234 * an LDAP attribute and a password reference attribute in the control list.
1236 if (inst->expect_password && (debug_flag > 1)) {
1237 if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
1238 !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
1239 !pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) &&
1240 !pairfind(request->config_items, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
1241 !pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
1242 RDEBUGW("No \"reference\" password added. Ensure the admin user has permission to "
1243 "read the password attribute");
1244 RDEBUGW("PAP authentication will *NOT* work with Active Directory (if that is what you "
1245 "were trying to configure)");
1250 #if LDAP_SET_REBIND_PROC_ARGS == 3
1251 /** Callback for OpenLDAP to rebind and chase referrals
1253 * Called by OpenLDAP when it receives a referral and has to rebind.
1255 * @param handle to rebind.
1256 * @param url to bind to.
1257 * @param request that triggered the rebind.
1258 * @param msgid that triggered the rebind.
1259 * @param ctx rlm_ldap configuration.
1261 static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
1264 ldap_rcode_t status;
1265 ldap_handle_t *conn = ctx;
1269 conn->referred = TRUE;
1270 conn->rebound = TRUE; /* not really, but oh well... */
1271 rad_assert(handle == conn->handle);
1273 DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
1275 status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->login, conn->inst->password, FALSE);
1276 if (status != LDAP_PROC_SUCCESS) {
1277 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1283 return LDAP_SUCCESS;
1287 /** Create and return a new connection
1289 * Create a new ldap connection and allocate memory for a new rlm_handle_t
1291 * @param ctx rlm_ldap instance.
1292 * @return A new connection handle or NULL on error.
1294 void *rlm_ldap_conn_create(void *ctx)
1296 ldap_rcode_t status;
1298 int ldap_errno, ldap_version;
1301 ldap_instance_t *inst = ctx;
1302 LDAP *handle = NULL;
1303 ldap_handle_t *conn = NULL;
1305 #ifdef HAVE_LDAP_INITIALIZE
1307 DEBUG("rlm_ldap (%s): Connecting to %s", inst->xlat_name, inst->server);
1309 ldap_errno = ldap_initialize(&handle, inst->server);
1310 if (ldap_errno != LDAP_SUCCESS) {
1311 LDAP_ERR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
1317 DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name, inst->server, inst->port);
1319 handle = ldap_init(inst->server, inst->port);
1321 LDAP_ERR("ldap_init() failed");
1327 * We now have a connection structure, but no actual TCP connection.
1329 * Set a bunch of LDAP options, using common code.
1331 #define do_ldap_option(_option, _name, _value) \
1332 if (ldap_set_option(handle, _option, _value) != LDAP_OPT_SUCCESS) { \
1333 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
1334 LDAP_ERR("Could not set %s: %s", _name, ldap_err2string(ldap_errno)); \
1337 if (inst->ldap_debug) {
1338 do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &(inst->ldap_debug));
1342 * Leave "chase_referrals" unset to use the OpenLDAP default.
1344 if (inst->chase_referrals != 2) {
1345 if (inst->chase_referrals) {
1346 do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_ON);
1348 if (inst->rebind == 1) {
1349 #if LDAP_SET_REBIND_PROC_ARGS == 3
1350 ldap_set_rebind_proc(handle, rlm_ldap_rebind, inst);
1352 DEBUGW("The flag 'rebind = yes' is not supported by the system LDAP library. "
1357 do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_OFF);
1361 tv.tv_sec = inst->net_timeout;
1363 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
1365 do_ldap_option(LDAP_OPT_TIMELIMIT, "timelimit", &(inst->timelimit));
1367 ldap_version = LDAP_VERSION3;
1368 do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version);
1370 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
1371 do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle", &(inst->keepalive_idle));
1374 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
1375 do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes", &(inst->keepalive_probes));
1378 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
1379 do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval", &(inst->keepalive_interval));
1382 #ifdef HAVE_LDAP_START_TLS
1384 * Set all of the TLS options
1386 if (inst->tls_mode) {
1387 do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
1390 # define maybe_ldap_option(_option, _name, _value) \
1391 if (_value) do_ldap_option(_option, _name, _value)
1393 maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "cacertfile", inst->tls_cacertfile);
1394 maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "cacertdir", inst->tls_cacertdir);
1396 # ifdef HAVE_LDAP_INT_TLS_CONFIG
1397 if (ldap_int_tls_config(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, inst->tls_require_cert) != LDAP_OPT_SUCCESS) {
1398 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1400 LDAP_ERR("Could not set LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s", inst->tls_require_cert,
1401 ldap_err2string(ldap_errno));
1406 * Set certificate options
1408 maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certfile", inst->tls_certfile);
1409 maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "keyfile", inst->tls_keyfile);
1410 maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE, "randfile", inst->tls_randfile);
1413 * And finally start the TLS code.
1415 if (inst->start_tls) {
1416 if (inst->port == 636) {
1417 DEBUGW("Told to Start TLS on LDAPS port this will probably fail, please correct the "
1421 if (ldap_start_tls_s(handle, NULL, NULL) != LDAP_SUCCESS) {
1422 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1424 LDAP_ERR("Could not start TLS: %s", ldap_err2string(ldap_errno));
1428 #endif /* HAVE_LDAP_START_TLS */
1431 * Allocate memory for the handle.
1433 conn = talloc_zero(ctx, ldap_handle_t);
1435 conn->handle = handle;
1436 conn->rebound = FALSE;
1437 conn->referred = FALSE;
1439 status = rlm_ldap_bind(inst, NULL, &conn, inst->login, inst->password, FALSE);
1440 if (status != LDAP_PROC_SUCCESS) {
1447 if (handle) ldap_unbind_s(handle);
1448 if (conn) talloc_free(conn);
1454 /** Close and delete a connection
1456 * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
1457 * connection handle.
1459 * @param ctx unused.
1460 * @param connection to destroy.
1461 * @return always indicates success.
1463 int rlm_ldap_conn_delete(UNUSED void *ctx, void *connection)
1465 ldap_handle_t *conn = connection;
1467 ldap_unbind_s(conn->handle);
1474 /** Gets an LDAP socket from the connection pool
1476 * Retrieve a socket from the connection pool, or NULL on error (of if no sockets are available).
1478 * @param inst rlm_ldap configuration.
1479 * @param request Current request.
1481 ldap_handle_t *rlm_ldap_get_socket(const ldap_instance_t *inst, REQUEST *request)
1483 ldap_handle_t *conn;
1485 conn = fr_connection_get(inst->pool);
1487 RDEBUGE("All ldap connections are in use");
1495 /** Frees an LDAP socket back to the connection pool
1497 * If the socket was rebound chasing a referral onto another server then we destroy it.
1498 * If the socket was rebound to another user on the same server, we let the next caller rebind it.
1500 * @param inst rlm_ldap configuration.
1501 * @param conn to release.
1503 void rlm_ldap_release_socket(const ldap_instance_t *inst, ldap_handle_t *conn)
1506 * Could have already been free'd due to a previous error.
1511 * We chased a referral to another server.
1513 * This connection is no longer part of the pool which is connected to and bound to the configured server.
1516 * Note that we do NOT close it if it was bound to another user. Instead, we let the next caller do the
1519 if (conn->referred) {
1520 fr_connection_del(inst->pool, conn);
1524 fr_connection_release(inst->pool, conn);