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, char const *in, UNUSED void *arg)
53 static char const encode[] = ",+\"\\<>;*=()";
54 static char const 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(char const *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(char const *full, char const *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) {
137 for (i = 0; i < p_len; i++) {
138 if (part[p_len - i] != full[f_len - i]) {
143 return f_len - p_len;
146 /** Combine and expand filters
148 * @param request Current request.
149 * @param out Where to write the expanded string.
150 * @param outlen Length of output buffer.
151 * @param sub Array of subfilters (may contain NULLs).
152 * @param sublen Number of potential subfilters in array.
153 * @return length of expanded data.
155 ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen)
157 char buffer[LDAP_MAX_FILTER_STR_LEN + 1];
158 char const *in = NULL;
167 * Figure out how many filter elements we need to integrate
169 for (i = 0; i < sublen; i++) {
170 if (sub[i] && *sub[i]) {
189 for (i = 0; i < sublen; i++) {
190 if (sub[i] && (*sub[i] != '\0')) {
191 len += strlcpy(p + len, sub[i], outlen - len);
193 if ((size_t) len >= outlen) {
195 REDEBUG("Out of buffer space creating filter");
202 if ((outlen - len) < 2) {
212 len = radius_xlat(out, outlen, request, in, rlm_ldap_escape_func, NULL);
214 REDEBUG("Failed creating filter");
222 /** Return the error string associated with a handle
224 * @param conn to retrieve error from.
225 * @return error string.
227 char const *rlm_ldap_error_str(ldap_handle_t const *conn)
230 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &lib_errno);
231 if (lib_errno == LDAP_SUCCESS) {
235 return ldap_err2string(lib_errno);
238 /** Parse response from LDAP server dealing with any errors
240 * Should be called after an LDAP operation. Will check result of operation and if it was successful, then attempt
241 * to retrieve and parse the result.
243 * Will also produce extended error output including any messages the server sent, and information about partial
246 * @param[in] inst of LDAP module.
247 * @param[in] conn Current connection.
248 * @param[in] msgid returned from last operation.
249 * @param[in] dn Last search or bind DN.
250 * @param[out] result Where to write result, if NULL result will be freed.
251 * @param[out] error Where to write the error string, may be NULL, must not be freed.
252 * @param[out] extra Where to write additional error string to, may be NULL (faster) or must be freed
253 * (with talloc_free).
254 * @return One of the LDAP_PROC_* codes.
256 static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
257 LDAPMessage **result, char const **error, char **extra)
259 ldap_rcode_t status = LDAP_PROC_SUCCESS;
261 int lib_errno = LDAP_SUCCESS; // errno returned by the library.
262 int srv_errno = LDAP_SUCCESS; // errno in the result message.
264 char *part_dn = NULL; // Partial DN match.
265 char *our_err = NULL; // Our extended error message.
266 char *srv_err = NULL; // Server's extended error message.
269 int freeit = false; // Whether the message should be freed after being processed.
272 struct timeval tv; // Holds timeout values.
274 LDAPMessage *tmp_msg; // Temporary message pointer storage if we weren't provided with one.
276 char const *tmp_err; // Temporary error pointer storage if we weren't provided with one.
288 * We always need the result, but our caller may not
298 * Check if there was an error sending the request
300 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
302 if (lib_errno != LDAP_SUCCESS) {
306 memset(&tv, 0, sizeof(tv));
307 tv.tv_sec = inst->res_timeout;
310 * Now retrieve the result and check for errors
311 * ldap_result returns -1 on error, and 0 on timeout
313 lib_errno = ldap_result(conn->handle, msgid, 1, &tv, result);
314 if (lib_errno == 0) {
315 lib_errno = LDAP_TIMEOUT;
320 if (lib_errno == -1) {
321 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
327 * Parse the result and check for errors sent by the server
329 lib_errno = ldap_parse_result(conn->handle, *result,
331 extra ? &part_dn : NULL,
332 extra ? &srv_err : NULL,
338 if (lib_errno != LDAP_SUCCESS) {
339 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
346 if ((lib_errno == LDAP_SUCCESS) && (srv_errno != LDAP_SUCCESS)) {
347 lib_errno = srv_errno;
348 } else if ((lib_errno != LDAP_SUCCESS) && (srv_errno == LDAP_SUCCESS)) {
349 srv_errno = lib_errno;
358 case LDAP_NO_SUCH_OBJECT:
359 *error = "The specified DN wasn't found, check base_dn and identity";
361 status = LDAP_PROC_BAD_DN;
366 * Build our own internal diagnostic string
368 len = rlm_ldap_common_dn(dn, part_dn);
371 our_err = talloc_asprintf(conn, "Match stopped here: [%.*s]%s", len, dn, part_dn ? part_dn : "");
375 case LDAP_INSUFFICIENT_ACCESS:
376 *error = "Insufficient access. Check the identity and password configuration directives";
378 status = LDAP_PROC_NOT_PERMITTED;
381 case LDAP_UNWILLING_TO_PERFORM:
382 *error = "Server was unwilling to perform";
384 status = LDAP_PROC_NOT_PERMITTED;
388 exec_trigger(NULL, inst->cs, "modules.ldap.timeout", true);
390 *error = "Timed out while waiting for server to respond";
392 status = LDAP_PROC_ERROR;
395 case LDAP_FILTER_ERROR:
396 *error = "Bad search filter";
398 status = LDAP_PROC_ERROR;
401 case LDAP_TIMELIMIT_EXCEEDED:
402 exec_trigger(NULL, inst->cs, "modules.ldap.timeout", true);
404 *error = "Time limit exceeded";
408 case LDAP_UNAVAILABLE:
409 case LDAP_SERVER_DOWN:
410 status = LDAP_PROC_RETRY;
414 case LDAP_INVALID_CREDENTIALS:
415 case LDAP_CONSTRAINT_VIOLATION:
416 status = LDAP_PROC_REJECT;
420 case LDAP_OPERATIONS_ERROR:
421 *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. See the ldap module configuration "
426 status = LDAP_PROC_ERROR;
431 *error = ldap_err2string(lib_errno);
434 if (!extra || ((lib_errno == srv_errno) && !our_err && !srv_err)) {
439 * Output the error codes from the library and server
441 p = talloc_zero_array(conn, char, 1);
444 if (lib_errno != srv_errno) {
445 a = talloc_asprintf_append(p, "LDAP lib error: %s (%u), srv error: %s (%u). ",
446 ldap_err2string(lib_errno), lib_errno,
447 ldap_err2string(srv_errno), srv_errno);
457 a = talloc_asprintf_append_buffer(p, "%s. ", our_err);
467 a = talloc_asprintf_append_buffer(p, "Server said: %s. ", srv_err);
485 ldap_memfree(srv_err);
489 ldap_memfree(part_dn);
493 talloc_free(our_err);
496 if ((lib_errno || srv_errno) && *result) {
497 ldap_msgfree(*result);
504 /** Bind to the LDAP directory as a user
506 * Performs a simple bind to the LDAP directory, and handles any errors that occur.
508 * @param[in] inst rlm_ldap configuration.
509 * @param[in] request Current request, this may be NULL, in which case all debug logging is done with radlog.
510 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
511 * @param[in] dn of the user, may be NULL to bind anonymously.
512 * @param[in] password of the user, may be NULL if no password is specified.
513 * @param[in] retry if the server is down.
514 * @return one of the LDAP_PROC_* values.
516 ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
517 char const *password, int retry)
523 char const *error = NULL;
526 rad_assert(*pconn && (*pconn)->handle);
529 * Bind as anonymous user
534 msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
535 /* We got a valid message ID */
538 RDEBUG2("Waiting for bind result...");
540 DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
544 status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
546 case LDAP_PROC_SUCCESS:
547 LDAP_DBG_REQ("Bind successful");
549 case LDAP_PROC_NOT_PERMITTED:
550 LDAP_ERR_REQ("Bind was not permitted: %s", error);
555 case LDAP_PROC_REJECT:
556 LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
561 case LDAP_PROC_RETRY:
563 *pconn = fr_connection_reconnect(inst->pool, *pconn);
565 LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
566 dn, inst->server, inst->port, error);
568 talloc_free(extra); /* don't leak debug info */
574 status = LDAP_PROC_ERROR;
577 * Were not allowed to retry, or there are no more
578 * sockets, treat this as a hard failure.
582 #ifdef HAVE_LDAP_INITIALIZE
584 LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
588 LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
600 return status; /* caller closes the connection */
604 /** Search for something in the LDAP directory
606 * Binds as the administrative user and performs a search, dealing with any errors.
608 * @param[in] inst rlm_ldap configuration.
609 * @param[in] request Current request.
610 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
611 * @param[in] dn to use as base for the search.
612 * @param[in] scope to use (LDAP_SCOPE_BASE, LDAP_SCOPE_ONE, LDAP_SCOPE_SUB).
613 * @param[in] filter to use, should be pre-escaped.
614 * @param[in] attrs to retrieve.
615 * @param[out] result Where to store the result. Must be freed with ldap_msgfree if LDAP_PROC_SUCCESS is returned.
616 * May be NULL in which case result will be automatically freed after use.
617 * @return One of the LDAP_PROC_* values.
619 ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
620 char const *dn, int scope, char const *filter, char const * const *attrs,
621 LDAPMessage **result)
624 LDAPMessage *our_result = NULL;
626 int msgid; // Message id returned by
629 int count = 0; // Number of results we got.
631 struct timeval tv; // Holds timeout values.
633 char const *error = NULL;
636 rad_assert(*pconn && (*pconn)->handle);
639 * OpenLDAP library doesn't declare attrs array as const, but
640 * it really should be *sigh*.
643 memcpy(&search_attrs, &attrs, sizeof(attrs));
646 * Do all searches as the admin user.
648 if ((*pconn)->rebound) {
649 status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, true);
650 if (status != LDAP_PROC_SUCCESS) {
651 return LDAP_PROC_ERROR;
656 (*pconn)->rebound = false;
659 LDAP_DBG_REQ("Performing search in '%s' with filter '%s'", dn, filter);
662 * If LDAP search produced an error it should also be logged
663 * to the ld. result should pick it up without us
664 * having to pass it explicitly.
666 memset(&tv, 0, sizeof(tv));
667 tv.tv_sec = inst->res_timeout;
669 (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, 0, NULL, NULL, &tv, 0, &msgid);
671 LDAP_DBG_REQ("Waiting for search result...");
672 status = rlm_ldap_result(inst, *pconn, msgid, dn, &our_result, &error, &extra);
674 case LDAP_PROC_SUCCESS:
676 case LDAP_PROC_RETRY:
677 *pconn = fr_connection_reconnect(inst->pool, *pconn);
679 LDAP_DBGW_REQ("Search failed: %s. Got new socket, retrying...", error);
681 talloc_free(extra); /* don't leak debug info */
686 status = LDAP_PROC_ERROR;
690 LDAP_ERR_REQ("Failed performing search: %s", error);
691 if (extra) LDAP_ERR_REQ("%s", extra);
696 count = ldap_count_entries((*pconn)->handle, our_result);
698 LDAP_ERR_REQ("Error counting results: %s", rlm_ldap_error_str(*pconn));
699 status = LDAP_PROC_ERROR;
701 ldap_msgfree(our_result);
703 } else if (count == 0) {
704 LDAP_DBG_REQ("Search returned no results");
705 status = LDAP_PROC_NO_RESULT;
707 ldap_msgfree(our_result);
717 * We always need to get the result to count entries, but the caller
718 * may not of requested one. If that's the case, free it, else write
719 * it to where our caller said.
723 ldap_msgfree(our_result);
727 *result = our_result;
733 /** Modify something in the LDAP directory
735 * Binds as the administrative user and attempts to modify an LDAP object.
737 * @param[in] inst rlm_ldap configuration.
738 * @param[in] request Current request.
739 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
740 * @param[in] dn of the object to modify.
741 * @param[in] mods to make, see 'man ldap_modify' for more information.
742 * @return One of the LDAP_PROC_* values.
744 ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
745 char const *dn, LDAPMod *mods[])
749 int msgid; // Message id returned by ldap_search_ext.
751 char const *error = NULL;
754 rad_assert(*pconn && (*pconn)->handle);
757 * Perform all modifications as the admin user.
759 if ((*pconn)->rebound) {
760 status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, true);
761 if (status != LDAP_PROC_SUCCESS) {
762 return LDAP_PROC_ERROR;
767 (*pconn)->rebound = false;
770 RDEBUG2("Modifying object with DN \"%s\"", dn);
772 (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
774 RDEBUG2("Waiting for modify result...");
775 status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
777 case LDAP_PROC_SUCCESS:
779 case LDAP_PROC_RETRY:
780 *pconn = fr_connection_reconnect(inst->pool, *pconn);
782 RWDEBUG("Modify failed: %s. Got new socket, retrying...", error);
784 talloc_free(extra); /* don't leak debug info */
789 status = LDAP_PROC_ERROR;
793 REDEBUG("Failed modifying object: %s", error);
794 REDEBUG("%s", extra);
807 /** Retrieve the DN of a user object
809 * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any attributes
810 * passed and return the result in *result.
812 * This potentially allows for all authorization and authentication checks to be performed in one ldap search
813 * operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* LDAP directory servers out there.
815 * @param[in] inst rlm_ldap configuration.
816 * @param[in] request Current request.
817 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
818 * @param[in] attrs Additional attributes to retrieve, may be NULL.
819 * @param[in] force Query even if the User-DN already exists.
820 * @param[out] result Where to write the result, may be NULL in which case result is discarded.
821 * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes.
822 * @return The user's DN or NULL on error.
824 char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
825 char const *attrs[], int force, LDAPMessage **result, rlm_rcode_t *rcode)
827 static char const *tmp_attrs[] = { NULL };
830 VALUE_PAIR *vp = NULL;
831 LDAPMessage *tmp_msg = NULL, *entry = NULL;
834 char filter[LDAP_MAX_FILTER_STR_LEN];
835 char base_dn[LDAP_MAX_DN_STR_LEN];
837 int freeit = false; //!< Whether the message should
838 //!< be freed after being processed.
840 *rcode = RLM_MODULE_FAIL;
849 memset(&attrs, 0, sizeof(tmp_attrs));
853 * If the caller isn't looking for the result we can just return the current userdn value.
856 vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
858 RDEBUG("Using user DN from request \"%s\"", vp->vp_strvalue);
859 *rcode = RLM_MODULE_OK;
860 return vp->vp_strvalue;
865 * Perform all searches as the admin user.
867 if ((*pconn)->rebound) {
868 status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, true);
869 if (status != LDAP_PROC_SUCCESS) {
870 *rcode = RLM_MODULE_FAIL;
876 (*pconn)->rebound = false;
879 if (radius_xlat(filter, sizeof(filter), request, inst->userobj_filter, rlm_ldap_escape_func, NULL) < 0) {
880 REDEBUG("Unable to create filter");
882 *rcode = RLM_MODULE_INVALID;
886 if (radius_xlat(base_dn, sizeof(base_dn), request, inst->userobj_base_dn, rlm_ldap_escape_func, NULL) < 0) {
887 REDEBUG("Unable to create base_dn");
889 *rcode = RLM_MODULE_INVALID;
893 status = rlm_ldap_search(inst, request, pconn, base_dn, inst->userobj_scope, filter, attrs, result);
895 case LDAP_PROC_SUCCESS:
897 case LDAP_PROC_NO_RESULT:
898 *rcode = RLM_MODULE_NOTFOUND;
901 *rcode = RLM_MODULE_FAIL;
907 entry = ldap_first_entry((*pconn)->handle, *result);
909 ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
910 REDEBUG("Failed retrieving entry: %s",
911 ldap_err2string(ldap_errno));
916 dn = ldap_get_dn((*pconn)->handle, entry);
918 ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
920 REDEBUG("Retrieving object DN from entry failed: %s",
921 ldap_err2string(ldap_errno));
926 RDEBUG("User object found at DN \"%s\"", dn);
927 vp = pairmake(request, &request->config_items, "LDAP-UserDN", dn, T_OP_EQ);
929 *rcode = RLM_MODULE_OK;
935 if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) {
936 ldap_msgfree(*result);
940 return vp ? vp->vp_strvalue : NULL;
943 /** Check for presence of access attribute in result
945 * @param[in] inst rlm_ldap configuration.
946 * @param[in] request Current request.
947 * @param[in] conn used to retrieve access attributes.
948 * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
949 * @return RLM_MODULE_USERLOCK if the user was denied access, else RLM_MODULE_OK.
951 rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request,
952 ldap_handle_t const *conn, LDAPMessage *entry)
954 rlm_rcode_t rcode = RLM_MODULE_OK;
957 vals = ldap_get_values(conn->handle, entry, inst->userobj_access_attr);
959 if (inst->access_positive && (strncmp(vals[0], "false", 5) == 0)) {
960 RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out");
961 rcode = RLM_MODULE_USERLOCK;
963 RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr);
964 rcode = RLM_MODULE_USERLOCK;
967 ldap_value_free(vals);
968 } else if (inst->access_positive) {
969 RDEBUG("No \"%s\" attribute - user locked out", inst->userobj_access_attr);
970 rcode = RLM_MODULE_USERLOCK;
976 /** Verify we got a password from the search
978 * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password.
980 * @param inst rlm_ldap configuration.
981 * @param request Current request.
983 void rlm_ldap_check_reply(ldap_instance_t const *inst, REQUEST *request)
986 * More warning messages for people who can't be bothered to read the documentation.
988 * Expect_password is set when we process the mapping, and is only true if there was a mapping between
989 * an LDAP attribute and a password reference attribute in the control list.
991 if (inst->expect_password && (debug_flag > 1)) {
992 if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
993 !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
994 !pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) &&
995 !pairfind(request->config_items, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
996 !pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
997 RWDEBUG("No \"reference\" password added. Ensure the admin user has permission to "
998 "read the password attribute");
999 RWDEBUG("PAP authentication will *NOT* work with Active Directory (if that is what you "
1000 "were trying to configure)");
1005 #if LDAP_SET_REBIND_PROC_ARGS == 3
1006 /** Callback for OpenLDAP to rebind and chase referrals
1008 * Called by OpenLDAP when it receives a referral and has to rebind.
1010 * @param handle to rebind.
1011 * @param url to bind to.
1012 * @param request that triggered the rebind.
1013 * @param msgid that triggered the rebind.
1014 * @param ctx rlm_ldap configuration.
1016 static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
1019 ldap_rcode_t status;
1020 ldap_handle_t *conn = ctx;
1024 conn->referred = true;
1025 conn->rebound = true; /* not really, but oh well... */
1026 rad_assert(handle == conn->handle);
1028 DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
1030 status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password, false);
1031 if (status != LDAP_PROC_SUCCESS) {
1032 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1038 return LDAP_SUCCESS;
1042 /** Create and return a new connection
1044 * Create a new ldap connection and allocate memory for a new rlm_handle_t
1046 * @param instance rlm_ldap instance.
1047 * @return A new connection handle or NULL on error.
1049 void *mod_conn_create(void *instance)
1051 ldap_rcode_t status;
1053 int ldap_errno, ldap_version;
1056 ldap_instance_t *inst = instance;
1057 LDAP *handle = NULL;
1058 ldap_handle_t *conn = NULL;
1060 #ifdef HAVE_LDAP_INITIALIZE
1062 DEBUG("rlm_ldap (%s): Connecting to %s", inst->xlat_name, inst->server);
1064 ldap_errno = ldap_initialize(&handle, inst->server);
1065 if (ldap_errno != LDAP_SUCCESS) {
1066 LDAP_ERR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
1072 DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name, inst->server, inst->port);
1074 handle = ldap_init(inst->server, inst->port);
1076 LDAP_ERR("ldap_init() failed");
1082 * We now have a connection structure, but no actual TCP connection.
1084 * Set a bunch of LDAP options, using common code.
1086 #define do_ldap_option(_option, _name, _value) \
1087 if (ldap_set_option(handle, _option, _value) != LDAP_OPT_SUCCESS) { \
1088 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
1089 LDAP_ERR("Could not set %s: %s", _name, ldap_err2string(ldap_errno)); \
1092 if (inst->ldap_debug) {
1093 do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &(inst->ldap_debug));
1097 * Leave "chase_referrals" unset to use the OpenLDAP default.
1099 if (inst->chase_referrals != 2) {
1100 if (inst->chase_referrals) {
1101 do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_ON);
1103 if (inst->rebind == true) {
1104 #if LDAP_SET_REBIND_PROC_ARGS == 3
1105 ldap_set_rebind_proc(handle, rlm_ldap_rebind, inst);
1109 do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_OFF);
1113 memset(&tv, 0, sizeof(tv));
1114 tv.tv_sec = inst->net_timeout;
1116 do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
1118 do_ldap_option(LDAP_OPT_TIMELIMIT, "srv_timelimit", &(inst->srv_timelimit));
1120 ldap_version = LDAP_VERSION3;
1121 do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version);
1123 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
1124 do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle", &(inst->keepalive_idle));
1127 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
1128 do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes", &(inst->keepalive_probes));
1131 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
1132 do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval", &(inst->keepalive_interval));
1135 #ifdef HAVE_LDAP_START_TLS
1137 * Set all of the TLS options
1140 # ifdef LDAP_OPT_X_TLS_NEWCTX
1142 /* Always use the new TLS configuration context */
1144 do_ldap_option(LDAP_OPT_X_TLS_NEWCTX, "new TLS context", &is_server);
1149 if (inst->tls_mode) {
1150 do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
1153 # define maybe_ldap_option(_option, _name, _value) \
1154 if (_value) do_ldap_option(_option, _name, _value)
1156 maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "ca_file", inst->tls_ca_file);
1157 maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "ca_path", inst->tls_ca_path);
1161 * Set certificate options
1163 maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certificate_file", inst->tls_certificate_file);
1164 maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "private_key_file", inst->tls_private_key_file);
1165 maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE, "random_file", inst->tls_random_file);
1167 # ifdef LDAP_OPT_X_TLS_NEVER
1168 if (inst->tls_require_cert_str) {
1169 do_ldap_option(LDAP_OPT_X_TLS_REQUIRE_CERT, "require_cert", &inst->tls_require_cert);
1174 * And finally start the TLS code.
1176 if (inst->start_tls) {
1177 if (inst->port == 636) {
1178 WDEBUG("Told to Start TLS on LDAPS port this will probably fail, please correct the "
1182 if (ldap_start_tls_s(handle, NULL, NULL) != LDAP_SUCCESS) {
1183 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1185 LDAP_ERR("Could not start TLS: %s", ldap_err2string(ldap_errno));
1189 #endif /* HAVE_LDAP_START_TLS */
1192 * Allocate memory for the handle.
1194 conn = talloc_zero(instance, ldap_handle_t);
1196 conn->handle = handle;
1197 conn->rebound = false;
1198 conn->referred = false;
1200 status = rlm_ldap_bind(inst, NULL, &conn, inst->admin_dn, inst->password, false);
1201 if (status != LDAP_PROC_SUCCESS) {
1208 if (handle) ldap_unbind_s(handle);
1209 if (conn) talloc_free(conn);
1215 /** Close and delete a connection
1217 * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
1218 * connection handle.
1220 * @param instance rlm_ldap instance.
1221 * @param handle to destroy.
1222 * @return always indicates success.
1224 int mod_conn_delete(UNUSED void *instance, void *handle)
1226 ldap_handle_t *conn = handle;
1228 ldap_unbind_s(conn->handle);
1235 /** Gets an LDAP socket from the connection pool
1237 * Retrieve a socket from the connection pool, or NULL on error (of if no sockets are available).
1239 * @param inst rlm_ldap configuration.
1240 * @param request Current request (may be NULL).
1242 ldap_handle_t *rlm_ldap_get_socket(ldap_instance_t const *inst, REQUEST *request)
1244 ldap_handle_t *conn;
1246 conn = fr_connection_get(inst->pool);
1248 REDEBUG("All ldap connections are in use");
1257 /** Frees an LDAP socket back to the connection pool
1259 * If the socket was rebound chasing a referral onto another server then we destroy it.
1260 * If the socket was rebound to another user on the same server, we let the next caller rebind it.
1262 * @param inst rlm_ldap configuration.
1263 * @param conn to release.
1265 void rlm_ldap_release_socket(ldap_instance_t const *inst, ldap_handle_t *conn)
1268 * Could have already been free'd due to a previous error.
1273 * We chased a referral to another server.
1275 * This connection is no longer part of the pool which is connected to and bound to the configured server.
1278 * Note that we do NOT close it if it was bound to another user. Instead, we let the next caller do the
1281 if (conn->referred) {
1282 fr_connection_del(inst->pool, conn);
1286 fr_connection_release(inst->pool, conn);