Fallback to hostnames if we don't have ldap_url_parse, ldap_is_ldap_url, ldap_urldesc...
[freeradius.git] / src / modules / rlm_ldap / rlm_ldap.c
1 /*
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 as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file rlm_ldap.c
20  * @brief LDAP authorization and authentication module.
21  *
22  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23  * @author Alan DeKok <aland@freeradius.org>
24  *
25  * @copyright 2012,2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
26  * @copyright 2013,2015 Network RADIUS SARL <info@networkradius.com>
27  * @copyright 2012 Alan DeKok <aland@freeradius.org>
28  * @copyright 1999-2013 The FreeRADIUS Server Project.
29  */
30 RCSID("$Id$")
31
32 #include        <freeradius-devel/rad_assert.h>
33
34 #include        <stdarg.h>
35 #include        <ctype.h>
36
37 #include        "ldap.h"
38
39 /*
40  *      Scopes
41  */
42 FR_NAME_NUMBER const ldap_scope[] = {
43         { "sub",        LDAP_SCOPE_SUB  },
44         { "one",        LDAP_SCOPE_ONE  },
45         { "base",       LDAP_SCOPE_BASE },
46 #ifdef LDAP_SCOPE_CHILDREN
47         { "children",   LDAP_SCOPE_CHILDREN },
48 #endif
49         {  NULL , -1 }
50 };
51
52 #ifdef LDAP_OPT_X_TLS_NEVER
53 FR_NAME_NUMBER const ldap_tls_require_cert[] = {
54         { "never",      LDAP_OPT_X_TLS_NEVER    },
55         { "demand",     LDAP_OPT_X_TLS_DEMAND   },
56         { "allow",      LDAP_OPT_X_TLS_ALLOW    },
57         { "try",        LDAP_OPT_X_TLS_TRY      },
58         { "hard",       LDAP_OPT_X_TLS_HARD     },      /* oh yes, just like that */
59
60         {  NULL , -1 }
61 };
62 #endif
63
64 static FR_NAME_NUMBER const ldap_dereference[] = {
65         { "never",      LDAP_DEREF_NEVER        },
66         { "searching",  LDAP_DEREF_SEARCHING    },
67         { "finding",    LDAP_DEREF_FINDING      },
68         { "always",     LDAP_DEREF_ALWAYS       },
69
70         {  NULL , -1 }
71 };
72
73 static CONF_PARSER sasl_mech_dynamic[] = {
74         { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL | PW_TYPE_NOT_EMPTY, ldap_sasl_dynamic, mech), NULL },
75         { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, proxy), NULL },
76         { "realm", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, realm), NULL }
77 };
78
79 static CONF_PARSER sasl_mech_static[] = {
80         { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_NOT_EMPTY, ldap_sasl, mech), NULL },
81         { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, proxy), NULL },
82         { "realm", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, realm), NULL }
83 };
84
85 /*
86  *      TLS Configuration
87  */
88 static CONF_PARSER tls_config[] = {
89         /*
90          *      Deprecated attributes
91          */
92         { "cacertfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_ca_file), NULL },
93         { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_ca_file), NULL },
94
95         { "cacertdir", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_ca_path), NULL },
96         { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_ca_path), NULL },
97
98         { "certfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_certificate_file), NULL },
99         { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_certificate_file), NULL },
100
101         { "keyfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_private_key_file), NULL }, // OK if it changes on HUP
102         { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_private_key_file), NULL }, // OK if it changes on HUP
103
104         { "randfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_random_file), NULL },
105         { "random_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_random_file), NULL },
106
107         /*
108          *      LDAP Specific TLS attributes
109          */
110         { "start_tls", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, start_tls), "no" },
111         { "require_cert", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, tls_require_cert_str), NULL },
112
113         { NULL, -1, 0, NULL, NULL }
114 };
115
116
117 static CONF_PARSER profile_config[] = {
118         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, profile_filter), "(&)" }, //!< Correct filter for when the DN is known.
119         { "attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, profile_attr), NULL },
120         { "default", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, default_profile), NULL },
121
122         { NULL, -1, 0, NULL, NULL }
123 };
124
125 /*
126  *      User configuration
127  */
128 static CONF_PARSER user_config[] = {
129         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, userobj_filter), NULL },
130         { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_scope_str), "sub" },
131         { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, userobj_base_dn), "" },
132         { "sort_by", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_sort_by), NULL },
133
134         { "access_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_access_attr), NULL },
135         { "access_positive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, access_positive), "yes" },
136
137         /* Should be deprecated */
138         { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, user_sasl), (void const *) sasl_mech_dynamic },
139
140         { NULL, -1, 0, NULL, NULL }
141 };
142
143 /*
144  *      Group configuration
145  */
146 static CONF_PARSER group_config[] = {
147         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_filter), NULL },
148         { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_scope_str), "sub" },
149         { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, groupobj_base_dn), "" },
150
151         { "name_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_name_attr), "cn" },
152         { "membership_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_membership_attr), NULL },
153         { "membership_filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_ldap_t, groupobj_membership_filter), NULL },
154         { "cacheable_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, cacheable_group_name), "no" },
155         { "cacheable_dn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, cacheable_group_dn), "no" },
156         { "cache_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, cache_attribute), NULL },
157
158         { NULL, -1, 0, NULL, NULL }
159 };
160
161 static CONF_PARSER client_config[] = {
162         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_filter), NULL },
163         { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_scope_str), "sub" },
164         { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_base_dn), "" },
165
166         { NULL, -1, 0, NULL, NULL }
167 };
168
169 /*
170  *      Reference for accounting updates
171  */
172 static const CONF_PARSER acct_section_config[] = {
173         { "reference", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, ldap_acct_section_t, reference), "." },
174
175         {NULL, -1, 0, NULL, NULL}
176 };
177
178 /*
179  *      Various options that don't belong in the main configuration.
180  *
181  *      Note that these overlap a bit with the connection pool code!
182  */
183 static CONF_PARSER option_config[] = {
184         /*
185          *      Debugging flags to the server
186          */
187         { "ldap_debug", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, ldap_debug), "0x0000" },
188
189         { "dereference", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, dereference_str), NULL },
190
191         { "chase_referrals", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, chase_referrals), NULL },
192
193         { "rebind", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, rebind), NULL },
194
195 #ifdef LDAP_OPT_NETWORK_TIMEOUT
196         /* timeout on network activity */
197         { "net_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, net_timeout), "10" },
198 #endif
199
200         /* timeout for search results */
201         { "res_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, res_timeout), "20" },
202
203         /* allow server unlimited time for search (server-side limit) */
204         { "srv_timelimit", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, srv_timelimit), "20" },
205
206 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
207         { "idle", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_idle), "60" },
208 #endif
209 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
210         { "probes", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_probes), "3" },
211 #endif
212 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
213         { "interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_interval), "30" },
214 #endif
215
216         { NULL, -1, 0, NULL, NULL }
217 };
218
219
220 static const CONF_PARSER module_config[] = {
221         { "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, config_server), NULL },  /* Do not set to required */
222         { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, rlm_ldap_t, port), NULL },
223
224         { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, admin_identity), NULL },
225         { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, admin_password), NULL },
226
227         { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, admin_sasl), (void const *) sasl_mech_static },
228
229         { "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, valuepair_attr), NULL },
230
231 #ifdef WITH_EDIR
232         /* support for eDirectory Universal Password */
233         { "edir", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, edir), NULL }, /* NULL defaults to "no" */
234
235         /*
236          *      Attempt to bind with the cleartext password we got from eDirectory
237          *      Universal password for additional authorization checks.
238          */
239         { "edir_autz", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, edir_autz), NULL }, /* NULL defaults to "no" */
240 #endif
241
242         { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, do_clients), NULL }, /* NULL defaults to "no" */
243
244         { "user", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) user_config },
245
246         { "group", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) group_config },
247
248         { "client", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_config },
249
250         { "profile", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) profile_config },
251
252         { "options", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) option_config },
253
254         { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
255
256         {NULL, -1, 0, NULL, NULL}
257 };
258
259 static ssize_t ldapquote_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
260 {
261         return rlm_ldap_escape_func(request, out, freespace, fmt, NULL);
262 }
263
264 /** Expand an LDAP URL into a query, and return a string result from that query.
265  *
266  */
267 static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
268 {
269         ldap_rcode_t            status;
270         size_t                  len = 0;
271         rlm_ldap_t              *inst = instance;
272
273         LDAPURLDesc             *ldap_url;
274         LDAPMessage             *result = NULL;
275         LDAPMessage             *entry = NULL;
276
277         struct berval           **values;
278
279         ldap_handle_t           *conn;
280         int                     ldap_errno;
281
282         char const              *url;
283         char const              **attrs;
284
285         url = fmt;
286
287         if (!ldap_is_ldap_url(url)) {
288                 REDEBUG("String passed does not look like an LDAP URL");
289                 return -1;
290         }
291
292         if (ldap_url_parse(url, &ldap_url)){
293                 REDEBUG("Parsing LDAP URL failed");
294                 return -1;
295         }
296
297         /*
298          *      Nothing, empty string, "*" string, or got 2 things, die.
299          */
300         if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
301             !*ldap_url->lud_attrs[0] ||
302             (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
303             ldap_url->lud_attrs[1]) {
304                 REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
305
306                 goto free_urldesc;
307         }
308
309         conn = mod_conn_get(inst, request);
310         if (!conn) goto free_urldesc;
311
312         memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
313
314         status = rlm_ldap_search(&result, inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope,
315                                  ldap_url->lud_filter, attrs, NULL, NULL);
316         switch (status) {
317         case LDAP_PROC_SUCCESS:
318                 break;
319
320         default:
321                 goto free_socket;
322         }
323
324         rad_assert(conn);
325         rad_assert(result);
326
327         entry = ldap_first_entry(conn->handle, result);
328         if (!entry) {
329                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
330                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
331                 len = -1;
332                 goto free_result;
333         }
334
335         values = ldap_get_values_len(conn->handle, entry, ldap_url->lud_attrs[0]);
336         if (!values) {
337                 RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
338                 goto free_result;
339         }
340
341         if (values[0]->bv_len >= freespace) goto free_values;
342
343         memcpy(out, values[0]->bv_val, values[0]->bv_len + 1);  /* +1 as strlcpy expects buffer size */
344         len = values[0]->bv_len;
345
346 free_values:
347         ldap_value_free_len(values);
348 free_result:
349         ldap_msgfree(result);
350 free_socket:
351         mod_conn_release(inst, conn);
352 free_urldesc:
353         ldap_free_urldesc(ldap_url);
354
355         return len;
356 }
357
358 /** Perform LDAP-Group comparison checking
359  *
360  * Attempts to match users to groups using a variety of methods.
361  *
362  * @param instance of the rlm_ldap module.
363  * @param request Current request.
364  * @param thing Unknown.
365  * @param check Which group to check for user membership.
366  * @param check_pairs Unknown.
367  * @param reply_pairs Unknown.
368  * @return
369  *      - 1 on failure (or if the user is not a member).
370  *      - 0 on success.
371  */
372 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
373                              UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
374 {
375         rlm_ldap_t      *inst = instance;
376         rlm_rcode_t     rcode;
377
378         bool            found = false;
379         bool            check_is_dn;
380
381         ldap_handle_t   *conn = NULL;
382         char const      *user_dn;
383
384         rad_assert(inst->groupobj_base_dn);
385
386         RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
387
388         if (check->vp_length == 0) {
389                 REDEBUG("Cannot do comparison (group name is empty)");
390                 return 1;
391         }
392
393         /*
394          *      Check if we can do cached membership verification
395          */
396         check_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length);
397         if (check_is_dn) {
398                 char *norm;
399
400                 MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue)));
401                 rlm_ldap_normalise_dn(norm, check->vp_strvalue);
402                 pairstrsteal(check, norm);
403         }
404         if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
405                 switch (rlm_ldap_check_cached(inst, request, check)) {
406                 case RLM_MODULE_NOTFOUND:
407                         found = false;
408                         goto finish;
409
410                 case RLM_MODULE_OK:
411                         found = true;
412                         goto finish;
413                 /*
414                  *      Fallback to dynamic search on failure
415                  */
416                 case RLM_MODULE_FAIL:
417                 case RLM_MODULE_INVALID:
418                 default:
419                         break;
420                 }
421         }
422
423         conn = mod_conn_get(inst, request);
424         if (!conn) return 1;
425
426         /*
427          *      This is used in the default membership filter.
428          */
429         user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
430         if (!user_dn) {
431                 mod_conn_release(inst, conn);
432                 return 1;
433         }
434
435         rad_assert(conn);
436
437         /*
438          *      Check groupobj user membership
439          */
440         if (inst->groupobj_membership_filter) {
441                 switch (rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) {
442                 case RLM_MODULE_NOTFOUND:
443                         break;
444
445                 case RLM_MODULE_OK:
446                         found = true;
447
448                 default:
449                         goto finish;
450                 }
451         }
452
453         rad_assert(conn);
454
455         /*
456          *      Check userobj group membership
457          */
458         if (inst->userobj_membership_attr) {
459                 switch (rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) {
460                 case RLM_MODULE_NOTFOUND:
461                         break;
462
463                 case RLM_MODULE_OK:
464                         found = true;
465
466                 default:
467                         goto finish;
468                 }
469         }
470
471         rad_assert(conn);
472
473 finish:
474         if (conn) mod_conn_release(inst, conn);
475
476         if (!found) {
477                 RDEBUG("User is not a member of \"%s\"", check->vp_strvalue);
478
479                 return 1;
480         }
481
482         return 0;
483 }
484
485 /** Detach from the LDAP server and cleanup internal state.
486  *
487  */
488 static int mod_detach(void *instance)
489 {
490         rlm_ldap_t *inst = instance;
491
492         fr_connection_pool_free(inst->pool);
493
494         if (inst->user_map) {
495                 talloc_free(inst->user_map);
496         }
497
498         /*
499          *      Keeping the dummy ld around for the lifetime
500          *      of the module should always work,
501          *      irrespective of what changes happen in libldap.
502          */
503         if (inst->handle) {
504 #ifdef HAVE_LDAP_UNBIND_EXT_S
505                 ldap_unbind_ext_s(inst->handle, NULL, NULL);
506 #else
507                 ldap_unbind_s(inst->handle);
508 #endif
509         }
510
511 #ifdef HAVE_LDAP_CREATE_SORT_CONTROL
512         if (inst->userobj_sort_ctrl) ldap_control_free(inst->userobj_sort_ctrl);
513 #endif
514
515         return 0;
516 }
517
518 /** Parse an accounting sub section.
519  *
520  * Allocate a new ldap_acct_section_t and write the config data into it.
521  *
522  * @param[in] inst rlm_ldap configuration.
523  * @param[in] parent of the config section.
524  * @param[out] config to write the sub section parameters to.
525  * @param[in] comp The section name were parsing the config for.
526  * @return
527  *      - 0 on success.
528  *      - < 0 on failure.
529  */
530 static int parse_sub_section(rlm_ldap_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
531                              rlm_components_t comp)
532 {
533         CONF_SECTION *cs;
534
535         char const *name = section_type_value[comp].section;
536
537         cs = cf_section_sub_find(parent, name);
538         if (!cs) {
539                 DEBUG2("rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
540                        "from this section", inst->name, name);
541
542                 return 0;
543         }
544
545         *config = talloc_zero(inst, ldap_acct_section_t);
546         if (cf_section_parse(cs, *config, acct_section_config) < 0) {
547                 LDAP_ERR("Failed parsing configuration for section %s", name);
548
549                 return -1;
550         }
551
552         (*config)->cs = cs;
553
554         return 0;
555 }
556
557 /** Bootstrap the module
558  *
559  * Define attributes.
560  *
561  * @param conf to parse.
562  * @param instance configuration data.
563  * @return
564  *      - 0 on success.
565  *      - < 0 on failure.
566  */
567 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
568 {
569         rlm_ldap_t *inst = instance;
570
571         inst->name = cf_section_name2(conf);
572         if (!inst->name) {
573                 inst->name = cf_section_name1(conf);
574         }
575
576         /*
577          *      Group comparison checks.
578          */
579         if (cf_section_name2(conf)) {
580                 char buffer[256];
581
582                 snprintf(buffer, sizeof(buffer), "%s-LDAP-Group", inst->name);
583
584                 if (paircompare_register_byname(buffer, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst) < 0) {
585                         LDAP_ERR("Error registering group comparison: %s", fr_strerror());
586                         goto error;
587                 }
588
589                 inst->group_da = dict_attrbyname(buffer);
590
591                 /*
592                  *      We're the default instance
593                  */
594         } else {
595                 if (paircompare_register_byname("LDAP-Group", dict_attrbyvalue(PW_USER_NAME, 0),
596                                                 false, rlm_ldap_groupcmp, inst) < 0) {
597                         LDAP_ERR("Error registering group comparison: %s", fr_strerror());
598                         goto error;
599                 }
600
601                 inst->group_da = dict_attrbyname("LDAP-Group");
602         }
603
604         /*
605          *      Setup the cache attribute
606          */
607         if (inst->cache_attribute) {
608                 ATTR_FLAGS flags;
609
610                 memset(&flags, 0, sizeof(flags));
611                 if (dict_addattr(inst->cache_attribute, -1, 0, PW_TYPE_STRING, flags) < 0) {
612                         LDAP_ERR("Error creating cache attribute: %s", fr_strerror());
613                 error:
614                         return -1;
615
616                 }
617                 inst->cache_da = dict_attrbyname(inst->cache_attribute);
618         } else {
619                 inst->cache_da = inst->group_da;        /* Default to the group_da */
620         }
621
622         xlat_register(inst->name, ldap_xlat, rlm_ldap_escape_func, inst);
623         xlat_register("ldapquote", ldapquote_xlat, NULL, inst);
624
625         return 0;
626 }
627
628
629 /** Instantiate the module
630  *
631  * Creates a new instance of the module reading parameters from a configuration section.
632  *
633  * @param conf to parse.
634  * @param instance configuration data.
635  * @return
636  *      - 0 on success.
637  *      - < 0 on failure.
638  */
639 static int mod_instantiate(CONF_SECTION *conf, void *instance)
640 {
641         static bool     version_done;
642
643         CONF_PAIR       *cp;
644         CONF_ITEM       *ci;
645
646         CONF_SECTION *options, *update;
647         rlm_ldap_t *inst = instance;
648
649         inst->cs = conf;
650
651         options = cf_section_sub_find(conf, "options");
652         if (!options || !cf_pair_find(options, "chase_referrals")) {
653                 inst->chase_referrals_unset = true;      /* use OpenLDAP defaults */
654         }
655
656         /*
657          *      Only needs to be done once, prevents races in environment
658          *      initialisation within libldap.
659          *
660          *      See: https://github.com/arr2036/ldapperf/issues/2
661          */
662 #ifdef HAVE_LDAP_INITIALIZE
663         ldap_initialize(&inst->handle, "");
664 #else
665         inst->handle = ldap_init("", 0);
666 #endif
667
668         /*
669          *      Get version info from the LDAP API.
670          */
671         if (!version_done) {
672                 static LDAPAPIInfo info = { .ldapai_info_version = LDAP_API_INFO_VERSION };     /* static to quiet valgrind about this being uninitialised */
673                 int ldap_errno;
674
675                 version_done = true;
676
677                 ldap_errno = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
678                 if (ldap_errno == LDAP_OPT_SUCCESS) {
679                         /*
680                          *      Don't generate warnings if the compile type vendor name
681                          *      is found within the link time vendor name.
682                          *
683                          *      This allows the server to be built against OpenLDAP but
684                          *      run with Symas OpenLDAP.
685                          */
686                         if (strcasestr(info.ldapai_vendor_name, LDAP_VENDOR_NAME) == NULL) {
687                                 WARN("rlm_ldap: libldap vendor changed since the server was built");
688                                 WARN("rlm_ldap: linked: %s, built: %s", info.ldapai_vendor_name, LDAP_VENDOR_NAME);
689                         }
690
691                         if (info.ldapai_vendor_version < LDAP_VENDOR_VERSION) {
692                                 WARN("rlm_ldap: libldap older than the version the server was built against");
693                                 WARN("rlm_ldap: linked: %i, built: %i",
694                                      info.ldapai_vendor_version, LDAP_VENDOR_VERSION);
695                         }
696
697                         INFO("rlm_ldap: libldap vendor: %s, version: %i", info.ldapai_vendor_name,
698                              info.ldapai_vendor_version);
699
700                         ldap_memfree(info.ldapai_vendor_name);
701                         ldap_memfree(info.ldapai_extensions);
702                 } else {
703                         DEBUG("rlm_ldap: Falling back to build time libldap version info.  Query for LDAP_OPT_API_INFO "
704                               "returned: %i", ldap_errno);
705                         INFO("rlm_ldap: libldap vendor: %s, version: %i.%i.%i", LDAP_VENDOR_NAME,
706                              LDAP_VENDOR_VERSION_MAJOR, LDAP_VENDOR_VERSION_MINOR, LDAP_VENDOR_VERSION_PATCH);
707                 }
708         }
709
710         /*
711          *      If the configuration parameters can't be parsed, then fail.
712          */
713         if ((parse_sub_section(inst, conf, &inst->accounting, MOD_ACCOUNTING) < 0) ||
714             (parse_sub_section(inst, conf, &inst->postauth, MOD_POST_AUTH) < 0)) {
715                 cf_log_err_cs(conf, "Failed parsing configuration");
716
717                 goto error;
718         }
719
720         /*
721          *      Sanity checks for cacheable groups code.
722          */
723         if (inst->cacheable_group_name && inst->groupobj_membership_filter) {
724                 if (!inst->groupobj_name_attr) {
725                         cf_log_err_cs(conf, "Configuration item 'group.name_attribute' must be set if cacheable "
726                                       "group names are enabled");
727
728                         goto error;
729                 }
730         }
731
732         /*
733          *      If we have a *pair* as opposed to a *section*
734          *      then the module is referencing another ldap module's
735          *      connection pool.
736          */
737         if (!cf_pair_find(conf, "pool")) {
738                 if (!inst->config_server) {
739                         cf_log_err_cs(conf, "Configuration item 'server' must have a value");
740                         goto error;
741                 }
742         }
743
744 #ifndef HAVE_LDAP_SASL_INTERACTIVE_BIND
745         if (inst->user_sasl.mech) {
746                 cf_log_err_cs(conf, "Configuration item 'user.sasl.mech' not supported.  "
747                               "Linked libldap does not provide ldap_sasl_bind function");
748                 goto error;
749         }
750
751         if (inst->admin_sasl.mech) {
752                 cf_log_err_cs(conf, "Configuration item 'sasl.mech' not supported.  "
753                               "Linked libldap does not provide ldap_sasl_interactive_bind function");
754                 goto error;
755         }
756 #endif
757
758 #ifndef HAVE_LDAP_CREATE_SORT_CONTROL
759         if (inst->userobj_sort_by) {
760                 cf_log_err_cs(conf, "Configuration item 'sort_by' not supported.  "
761                               "Linked libldap does not provide ldap_create_sort_control function");
762                 goto error;
763         }
764 #endif
765
766         /*
767          *      For backwards compatibility hack up the first 'server'
768          *      CONF_ITEM into chunks, and add them back into the config.
769          *
770          *      @fixme this should be removed at some point.
771          */
772         if (inst->config_server) {
773                 char const      *value;
774                 char const      *p;
775                 char const      *q;
776                 char            *buff;
777
778                 bool            done = false;
779                 bool            first = true;
780
781                 cp = cf_pair_find(conf, "server");
782                 if (!cp) {
783                         cf_log_err_cs(conf, "Configuration item 'server' must have a value");
784                         return -1;
785                 }
786
787                 value = cf_pair_value(cp);
788
789                 p = value;
790                 q = p;
791                 while (!done) {
792                         switch (*q) {
793                         case '\0':
794                                 done = true;
795                                 if (p == value) break;  /* string contained no separators */
796
797                                 /* FALL-THROUGH */
798
799                         case ',':
800                         case ';':
801                         case ' ':
802                                 while (isspace((int) *p)) p++;
803                                 if (p == q) continue;
804
805                                 buff = talloc_array(inst, char, (q - p) + 1);
806                                 strlcpy(buff, p, talloc_array_length(buff));
807                                 p = ++q;
808
809                                 if (first) {
810                                         WARN("Listing multiple LDAP servers in the 'server' configuration item "
811                                              "is deprecated and will be removed in a future release.  "
812                                              "Use multiple 'server' configuration items instead");
813                                         WARN("- server = '%s'", value);
814                                 }
815                                 WARN("+ server = '%s'", buff);
816
817                                 /*
818                                  *      For the first instance of server we find, just replace
819                                  *      the existing "server" config item.
820                                  */
821                                 if (first) {
822                                         cf_pair_replace(conf, cp, buff);
823                                         first = false;
824                                         continue;
825                                 }
826
827                                 /*
828                                  *      For subsequent instances we need to add new conf pairs.
829                                  */
830                                 cp = cf_pair_alloc(conf, "server", buff, T_OP_EQ, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
831                                 if (!cp) return -1;
832
833                                 ci = cf_pair_to_item(cp);
834                                 cf_item_add(conf, ci);
835
836                                 break;
837
838                         default:
839                                 q++;
840                                 continue;
841                         }
842                 }
843         }
844
845         /*
846          *      Now iterate over all the 'server' config items
847          */
848         for (cp = cf_pair_find(conf, "server");
849              cp;
850              cp = cf_pair_find_next(conf, cp, "server")) {
851                 char const *value;
852
853                 value = cf_pair_value(cp);
854
855 #if defined(HAVE_LDAP_URL_PARSE) && defined(HAVE_LDAP_IS_LDAP_URL) && defined(LDAP_URL_DESC2STR)
856                 /*
857                  *      Split original server value out into URI, server and port
858                  *      so whatever initialization function we use later will have
859                  *      the server information in the format it needs.
860                  */
861                 if (ldap_is_ldap_url(value)) {
862                         LDAPURLDesc     *ldap_url;
863                         int             default_port = LDAP_PORT;
864
865                         if (ldap_url_parse(value, &ldap_url)){
866                                 cf_log_err_cs(conf, "Parsing LDAP URL \"%s\" failed", value);
867                         ldap_url_error:
868                                 ldap_free_urldesc(ldap_url);
869                                 return -1;
870                         }
871
872                         if (ldap_url->lud_dn) {
873                                 cf_log_err_cs(conf, "Base DN cannot be specified via server URL");
874                                 goto ldap_url_error;
875                         }
876
877                         if (ldap_url->lud_attrs && ldap_url->lud_attrs[0]) {
878                                 cf_log_err_cs(conf, "Attribute list cannot be specified via server URL");
879                                 goto ldap_url_error;
880                         }
881
882                         if (ldap_url->lud_scope >= 0) {
883                                 cf_log_err_cs(conf, "Scope cannot be specified via server URL");
884                                 goto ldap_url_error;
885                         }
886
887                         /* We allow extensions */
888
889 #  ifdef HAVE_LDAP_INITIALIZE
890                         {
891                                 char *url;
892
893                                 /*
894                                  *      Figure out the default port from the URL
895                                  */
896                                 if (ldap_url->lud_scheme && (strcmp(ldap_url->lud_scheme, "ldaps") == 0)) {
897                                         if (inst->start_tls == true) {
898                                                 cf_log_err_cs(conf, "ldaps:// scheme is not compatible "
899                                                               "with 'start_tls'");
900                                                 goto ldap_url_error;
901                                         }
902                                         default_port = LDAPS_PORT;
903                                 }
904
905                                 /*
906                                  *      Configured port overrides URL port
907                                  */
908                                 if (inst->port) ldap_url->lud_port = inst->port;
909
910                                 /*
911                                  *      If there's no URL port, then set it to the default
912                                  *      this is so debugging messages show explicitly
913                                  *      the port we're connecting to.
914                                  */
915                                 if (!ldap_url->lud_port) ldap_url->lud_port = default_port;
916
917                                 url = ldap_url_desc2str(ldap_url);
918                                 if (!url) {
919                                         cf_log_err_cs(conf, "Failed recombining URL components");
920                                         goto ldap_url_error;
921                                 }
922                                 inst->server = talloc_asprintf_append(inst->server, "%s ", url);
923                                 LDAP_FREE(url);
924                         }
925 #  else
926                         /*
927                          *      No LDAP initialize function.  Can't specify a scheme.
928                          */
929                         if (ldap_url->lud_scheme &&
930                             ((strcmp(ldap_url->lud_scheme, "ldaps") == 0) ||
931                             (strcmp(ldap_url->lud_scheme, "ldapi") == 0) ||
932                             (strcmp(ldap_url->lud_scheme, "cldap") == 0))) {
933                                 cf_log_err_cs(conf, "%s is not supported by linked libldap",
934                                               ldap_url->lud_scheme);
935                                 return -1;
936                         }
937
938                         default_port = inst->port ? inst->port : LDAP_PORT;
939                         inst->server = talloc_asprintf_append(inst->server, "%s",
940                                                               ldap_url->lud_host ? ldap_url->lud_host : "localhost");
941                         if (default_port) inst->server = talloc_asprintf_append(inst->server, ":%i", default_port);
942                         inst->server = talloc_strdup_append(inst->server, " ");
943 #  endif
944                         /*
945                          *      @todo We could set a few other top level
946                          *      directives using the URL, like base_dn
947                          *      and scope.
948                          */
949                         ldap_free_urldesc(ldap_url);
950                 /*
951                  *      We need to construct an LDAP URI
952                  */
953                 } else
954 #endif  /* HAVE_LDAP_URL_PARSE && HAVE_LDAP_IS_LDAP_URL && LDAP_URL_DESC2STR */
955                 /*
956                  *      If it's not an URL, or we don't have the functions necessary
957                  *      to break apart the URL and recombine it, then just treat
958                  *      server as a hostname.
959                  */
960                 {
961 #ifdef HAVE_LDAP_INITIALIZE
962                         char    const *p;
963                         char    *q;
964                         int     port = 0;
965                         size_t  len;
966
967                         port = inst->port;
968
969                         p = strrchr(value, ':');
970                         if (p) {
971                                 port = (int)strtol((p + 1), &q, 10);
972                                 if ((p == value) || ((p + 1) == q) || (*q != '\0')) {
973                                         cf_log_err_cp(cp, "Invalid server, must be in <server>[:<port>] format");
974                                         return -1;
975                                 }
976                                 len = p - value;
977                         } else {
978                                 len = strlen(value);
979                         }
980
981                         if (port == 0) port = LDAP_PORT;
982
983                         inst->server = talloc_asprintf_append(inst->server, "ldap://%.*s:%i ",
984                                                               (int) len, value, port);
985 #else
986                         /*
987                          *      ldap_init takes port, which can be overridden by :port so
988                          *      we don't need to do any parsing here.
989                          */
990                         inst->server = talloc_asprintf_append(inst->server, "%s ", value);
991 #endif
992                 }
993         }
994         if (inst->server) inst->server[talloc_array_length(inst->server) - 2] = '\0';
995
996         DEBUG4("LDAP server string: %s", inst->server);
997
998 #ifdef LDAP_OPT_X_TLS_NEVER
999         /*
1000          *      Workaround for servers which support LDAPS but not START TLS
1001          */
1002         if (inst->port == LDAPS_PORT || inst->tls_mode) {
1003                 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
1004         } else {
1005                 inst->tls_mode = 0;
1006         }
1007 #endif
1008
1009         /*
1010          *      Convert dereference strings to enumerated constants
1011          */
1012         if (inst->dereference_str) {
1013                 inst->dereference = fr_str2int(ldap_dereference, inst->dereference_str, -1);
1014                 if (inst->dereference < 0) {
1015                         cf_log_err_cs(conf, "Invalid 'dereference' value \"%s\", expected 'never', 'searching', "
1016                                       "'finding' or 'always'", inst->dereference_str);
1017                         goto error;
1018                 }
1019         }
1020
1021 #if LDAP_SET_REBIND_PROC_ARGS != 3
1022         /*
1023          *      The 2-argument rebind doesn't take an instance variable.  Our rebind function needs the instance
1024          *      variable for the username, password, etc.
1025          */
1026         if (inst->rebind == true) {
1027                 cf_log_err_cs(conf, "Cannot use 'rebind' configuration item as this version of libldap "
1028                               "does not support the API that we need");
1029
1030                 goto error;
1031         }
1032 #endif
1033
1034         /*
1035          *      Convert scope strings to enumerated constants
1036          */
1037         inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1);
1038         if (inst->userobj_scope < 0) {
1039                 cf_log_err_cs(conf, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'"
1040 #ifdef LDAP_SCOPE_CHILDREN
1041                               ", 'base' or 'children'"
1042 #else
1043                               " or 'base'"
1044 #endif
1045                          , inst->userobj_scope_str);
1046                 goto error;
1047         }
1048
1049         inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1);
1050         if (inst->groupobj_scope < 0) {
1051                 cf_log_err_cs(conf, "Invalid 'group.scope' value \"%s\", expected 'sub', 'one'"
1052 #ifdef LDAP_SCOPE_CHILDREN
1053                               ", 'base' or 'children'"
1054 #else
1055                               " or 'base'"
1056 #endif
1057                          , inst->groupobj_scope_str);
1058                 goto error;
1059         }
1060
1061         inst->clientobj_scope = fr_str2int(ldap_scope, inst->clientobj_scope_str, -1);
1062         if (inst->clientobj_scope < 0) {
1063                 cf_log_err_cs(conf, "Invalid 'client.scope' value \"%s\", expected 'sub', 'one'"
1064 #ifdef LDAP_SCOPE_CHILDREN
1065                               ", 'base' or 'children'"
1066 #else
1067                               " or 'base'"
1068 #endif
1069                          , inst->clientobj_scope_str);
1070                 goto error;
1071         }
1072
1073 #ifdef HAVE_LDAP_CREATE_SORT_CONTROL
1074         /*
1075          *      Build the server side sort control for user objects
1076          */
1077         if (inst->userobj_sort_by) {
1078                 LDAPSortKey     **keys;
1079                 int             ret;
1080                 char            *p;
1081
1082                 memcpy(&p, &inst->userobj_sort_by, sizeof(p));
1083
1084                 ret = ldap_create_sort_keylist(&keys, p);
1085                 if (ret != LDAP_SUCCESS) {
1086                         cf_log_err_cs(conf, "Invalid user.sort_by value \"%s\": %s",
1087                                       inst->userobj_sort_by, ldap_err2string(ret));
1088                         goto error;
1089                 }
1090
1091                 /*
1092                  *      Always set the control as critical, if it's not needed
1093                  *      the user can comment it out...
1094                  */
1095                 ret = ldap_create_sort_control(inst->handle, keys, 1, &inst->userobj_sort_ctrl);
1096                 ldap_free_sort_keylist(keys);
1097                 if (ret != LDAP_SUCCESS) {
1098                         LDAP_ERR("Failed creating server sort control: %s", ldap_err2string(ret));
1099                         goto error;
1100                 }
1101         }
1102 #endif
1103
1104         if (inst->tls_require_cert_str) {
1105 #ifdef LDAP_OPT_X_TLS_NEVER
1106                 /*
1107                  *      Convert cert strictness to enumerated constants
1108                  */
1109                 inst->tls_require_cert = fr_str2int(ldap_tls_require_cert, inst->tls_require_cert_str, -1);
1110                 if (inst->tls_require_cert < 0) {
1111                         cf_log_err_cs(conf, "Invalid 'tls.require_cert' value \"%s\", expected 'never', "
1112                                       "'demand', 'allow', 'try' or 'hard'", inst->tls_require_cert_str);
1113                         goto error;
1114                 }
1115 #else
1116                 cf_log_err_cs(conf, "Modifying 'tls.require_cert' is not supported by current "
1117                               "version of libldap. Please upgrade or substitute current libldap and "
1118                               "rebuild this module");
1119
1120                 goto error;
1121 #endif
1122         }
1123
1124         /*
1125          *      Build the attribute map
1126          */
1127         update = cf_section_sub_find(inst->cs, "update");
1128         if (update && (map_afrom_cs(&inst->user_map, update,
1129                                     PAIR_LIST_REPLY, PAIR_LIST_REQUEST, rlm_ldap_map_verify, inst,
1130                                     LDAP_MAX_ATTRMAP) < 0)) {
1131                 return -1;
1132         }
1133
1134         /*
1135          *      Initialize the socket pool.
1136          */
1137         inst->pool = fr_connection_pool_module_init(inst->cs, inst, mod_conn_create, NULL, NULL);
1138         if (!inst->pool) goto error;
1139
1140         /*
1141          *      Bulk load dynamic clients.
1142          */
1143         if (inst->do_clients) {
1144                 CONF_SECTION *cs, *map, *tmpl;
1145
1146                 cs = cf_section_sub_find(inst->cs, "client");
1147                 if (!cs) {
1148                         cf_log_err_cs(conf, "Told to load clients but no client section found");
1149                         goto error;
1150                 }
1151
1152                 map = cf_section_sub_find(cs, "attribute");
1153                 if (!map) {
1154                         cf_log_err_cs(cs, "Told to load clients but no attribute section found");
1155                         goto error;
1156                 }
1157
1158                 tmpl = cf_section_sub_find(cs, "template");
1159
1160                 if (rlm_ldap_client_load(inst, tmpl, map) < 0) {
1161                         cf_log_err_cs(cs, "Error loading clients");
1162
1163                         return -1;
1164                 }
1165         }
1166
1167         return 0;
1168
1169 error:
1170         return -1;
1171 }
1172
1173 static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull);
1174 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
1175 {
1176         rlm_rcode_t     rcode;
1177         ldap_rcode_t    status;
1178         char const      *dn;
1179         rlm_ldap_t      *inst = instance;
1180         ldap_handle_t   *conn;
1181
1182         char            sasl_mech_buff[LDAP_MAX_DN_STR_LEN];
1183         char            sasl_proxy_buff[LDAP_MAX_DN_STR_LEN];
1184         char            sasl_realm_buff[LDAP_MAX_DN_STR_LEN];
1185         ldap_sasl       sasl;
1186
1187         /*
1188          * Ensure that we're being passed a plain-text password, and not
1189          * anything else.
1190          */
1191
1192         if (!request->username) {
1193                 REDEBUG("Attribute \"User-Name\" is required for authentication");
1194
1195                 return RLM_MODULE_INVALID;
1196         }
1197
1198         if (!request->password ||
1199             (request->password->da->attr != PW_USER_PASSWORD)) {
1200                 RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
1201                 RWDEBUG("*********************************************");
1202                 RWDEBUG("* THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
1203                 RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
1204                 RWDEBUG("*********************************************");
1205
1206                 REDEBUG("Attribute \"User-Password\" is required for authentication");
1207
1208                 return RLM_MODULE_INVALID;
1209         }
1210
1211         if (request->password->vp_length == 0) {
1212                 REDEBUG("Empty password supplied");
1213
1214                 return RLM_MODULE_INVALID;
1215         }
1216
1217         conn = mod_conn_get(inst, request);
1218         if (!conn) return RLM_MODULE_FAIL;
1219
1220         /*
1221          *      Expand dynamic SASL fields
1222          */
1223         if (conn->inst->user_sasl.mech) {
1224                 memset(&sasl, 0, sizeof(sasl));
1225
1226                 if (tmpl_expand(&sasl.mech, sasl_mech_buff, sizeof(sasl_mech_buff), request,
1227                                 conn->inst->user_sasl.mech, rlm_ldap_escape_func, inst) < 0) {
1228                         REDEBUG("Failed expanding user.sasl.mech: %s", fr_strerror());
1229                         rcode = RLM_MODULE_FAIL;
1230                         goto finish;
1231                 }
1232
1233                 if (conn->inst->user_sasl.proxy) {
1234                         if (tmpl_expand(&sasl.proxy, sasl_proxy_buff, sizeof(sasl_proxy_buff), request,
1235                                         conn->inst->user_sasl.proxy, rlm_ldap_escape_func, inst) < 0) {
1236                                 REDEBUG("Failed expanding user.sasl.proxy: %s", fr_strerror());
1237                                 rcode = RLM_MODULE_FAIL;
1238                                 goto finish;
1239                         }
1240                 }
1241
1242                 if (conn->inst->user_sasl.realm) {
1243                         if (tmpl_expand(&sasl.realm, sasl_realm_buff, sizeof(sasl_realm_buff), request,
1244                                         conn->inst->user_sasl.realm, rlm_ldap_escape_func, inst) < 0) {
1245                                 REDEBUG("Failed expanding user.sasl.realm: %s", fr_strerror());
1246                                 rcode = RLM_MODULE_FAIL;
1247                                 goto finish;
1248                         }
1249                 }
1250         }
1251
1252         RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
1253
1254         /*
1255          *      Get the DN by doing a search.
1256          */
1257         dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1258         if (!dn) {
1259                 mod_conn_release(inst, conn);
1260
1261                 return rcode;
1262         }
1263         conn->rebound = true;
1264         status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue,
1265                                conn->inst->user_sasl.mech ? &sasl : NULL, true);
1266         switch (status) {
1267         case LDAP_PROC_SUCCESS:
1268                 rcode = RLM_MODULE_OK;
1269                 RDEBUG("Bind as user \"%s\" was successful", dn);
1270                 break;
1271
1272         case LDAP_PROC_NOT_PERMITTED:
1273                 rcode = RLM_MODULE_USERLOCK;
1274                 break;
1275
1276         case LDAP_PROC_REJECT:
1277                 rcode = RLM_MODULE_REJECT;
1278                 break;
1279
1280         case LDAP_PROC_BAD_DN:
1281                 rcode = RLM_MODULE_INVALID;
1282                 break;
1283
1284         case LDAP_PROC_NO_RESULT:
1285                 rcode = RLM_MODULE_NOTFOUND;
1286                 break;
1287
1288         default:
1289                 rcode = RLM_MODULE_FAIL;
1290                 break;
1291         };
1292
1293 finish:
1294         mod_conn_release(inst, conn);
1295
1296         return rcode;
1297 }
1298
1299 /** Search for and apply an LDAP profile
1300  *
1301  * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common sets of attributes
1302  * to the request.
1303  *
1304  * @param[in] inst rlm_ldap configuration.
1305  * @param[in] request Current request.
1306  * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
1307  * @param[in] dn of profile object to apply.
1308  * @param[in] expanded Structure containing a list of xlat expanded attribute names and mapping information.
1309  * @return One of the RLM_MODULE_* values.
1310  */
1311 static rlm_rcode_t rlm_ldap_map_profile(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
1312                                         char const *dn, rlm_ldap_map_exp_t const *expanded)
1313 {
1314         rlm_rcode_t     rcode = RLM_MODULE_OK;
1315         ldap_rcode_t    status;
1316         LDAPMessage     *result = NULL, *entry = NULL;
1317         int             ldap_errno;
1318         LDAP            *handle = (*pconn)->handle;
1319         char const      *filter;
1320         char            filter_buff[LDAP_MAX_FILTER_STR_LEN];
1321
1322         rad_assert(inst->profile_filter);       /* We always have a default filter set */
1323
1324         if (!dn || !*dn) return RLM_MODULE_OK;
1325
1326         if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request,
1327                         inst->profile_filter, rlm_ldap_escape_func, NULL) < 0) {
1328                 REDEBUG("Failed creating profile filter");
1329
1330                 return RLM_MODULE_INVALID;
1331         }
1332
1333         status = rlm_ldap_search(&result, inst, request, pconn, dn,
1334                                  LDAP_SCOPE_BASE, filter, expanded->attrs, NULL, NULL);
1335         switch (status) {
1336         case LDAP_PROC_SUCCESS:
1337                 break;
1338
1339         case LDAP_PROC_BAD_DN:
1340         case LDAP_PROC_NO_RESULT:
1341                 RDEBUG("Profile object \"%s\" not found", dn);
1342                 return RLM_MODULE_NOTFOUND;
1343
1344         default:
1345                 return RLM_MODULE_FAIL;
1346         }
1347
1348         rad_assert(*pconn);
1349         rad_assert(result);
1350
1351         entry = ldap_first_entry(handle, result);
1352         if (!entry) {
1353                 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1354                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1355
1356                 rcode = RLM_MODULE_NOTFOUND;
1357
1358                 goto free_result;
1359         }
1360
1361         RDEBUG("Processing profile attributes");
1362         RINDENT();
1363         if (rlm_ldap_map_do(inst, request, handle, expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
1364         REXDENT();
1365
1366 free_result:
1367         ldap_msgfree(result);
1368
1369         return rcode;
1370 }
1371
1372 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) CC_HINT(nonnull);
1373 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
1374 {
1375         rlm_rcode_t             rcode = RLM_MODULE_OK;
1376         ldap_rcode_t            status;
1377         int                     ldap_errno;
1378         int                     i;
1379         rlm_ldap_t              *inst = instance;
1380         struct berval           **values;
1381         VALUE_PAIR              *vp;
1382         ldap_handle_t           *conn;
1383         LDAPMessage             *result, *entry;
1384         char const              *dn = NULL;
1385         rlm_ldap_map_exp_t      expanded; /* faster than mallocing every time */
1386
1387         /*
1388          *      Don't be tempted to add a check for request->username
1389          *      or request->password here. rlm_ldap.authorize can be used for
1390          *      many things besides searching for users.
1391          */
1392
1393         if (rlm_ldap_map_expand(&expanded, request, inst->user_map) < 0) return RLM_MODULE_FAIL;
1394
1395         conn = mod_conn_get(inst, request);
1396         if (!conn) return RLM_MODULE_FAIL;
1397
1398         /*
1399          *      Add any additional attributes we need for checking access, memberships, and profiles
1400          */
1401         if (inst->userobj_access_attr) {
1402                 expanded.attrs[expanded.count++] = inst->userobj_access_attr;
1403         }
1404
1405         if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) {
1406                 expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
1407         }
1408
1409         if (inst->profile_attr) {
1410                 expanded.attrs[expanded.count++] = inst->profile_attr;
1411         }
1412
1413         if (inst->valuepair_attr) {
1414                 expanded.attrs[expanded.count++] = inst->valuepair_attr;
1415         }
1416
1417         expanded.attrs[expanded.count] = NULL;
1418
1419         dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode);
1420         if (!dn) {
1421                 goto finish;
1422         }
1423
1424         entry = ldap_first_entry(conn->handle, result);
1425         if (!entry) {
1426                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
1427                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
1428
1429                 goto finish;
1430         }
1431
1432         /*
1433          *      Check for access.
1434          */
1435         if (inst->userobj_access_attr) {
1436                 rcode = rlm_ldap_check_access(inst, request, conn, entry);
1437                 if (rcode != RLM_MODULE_OK) {
1438                         goto finish;
1439                 }
1440         }
1441
1442         /*
1443          *      Check if we need to cache group memberships
1444          */
1445         if (inst->cacheable_group_dn || inst->cacheable_group_name) {
1446                 if (inst->userobj_membership_attr) {
1447                         rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr);
1448                         if (rcode != RLM_MODULE_OK) {
1449                                 goto finish;
1450                         }
1451                 }
1452
1453                 rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
1454                 if (rcode != RLM_MODULE_OK) {
1455                         goto finish;
1456                 }
1457         }
1458
1459 #ifdef WITH_EDIR
1460         /*
1461          *      We already have a Cleartext-Password.  Skip edir.
1462          */
1463         if (pairfind(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
1464                 goto skip_edir;
1465         }
1466
1467         /*
1468          *      Retrieve Universal Password if we use eDirectory
1469          */
1470         if (inst->edir) {
1471                 int res = 0;
1472                 char password[256];
1473                 size_t pass_size = sizeof(password);
1474
1475                 /*
1476                  *      Retrive universal password
1477                  */
1478                 res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
1479                 if (res != 0) {
1480                         REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res));
1481                         rcode = RLM_MODULE_FAIL;
1482
1483                         goto finish;
1484                 }
1485
1486                 /*
1487                  *      Add Cleartext-Password attribute to the request
1488                  */
1489                 vp = radius_paircreate(request, &request->config, PW_CLEARTEXT_PASSWORD, 0);
1490                 pairstrcpy(vp, password);
1491                 vp->vp_length = pass_size;
1492
1493                 if (RDEBUG_ENABLED3) {
1494                         RDEBUG3("Added eDirectory password.  control:%s += '%s'", vp->da->name, vp->vp_strvalue);
1495                 } else {
1496                         RDEBUG2("Added eDirectory password");
1497                 }
1498
1499                 if (inst->edir_autz) {
1500                         RDEBUG2("Binding as user for eDirectory authorization checks");
1501                         /*
1502                          *      Bind as the user
1503                          */
1504                         conn->rebound = true;
1505                         status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, NULL, true);
1506                         switch (status) {
1507                         case LDAP_PROC_SUCCESS:
1508                                 rcode = RLM_MODULE_OK;
1509                                 RDEBUG("Bind as user '%s' was successful", dn);
1510                                 break;
1511
1512                         case LDAP_PROC_NOT_PERMITTED:
1513                                 rcode = RLM_MODULE_USERLOCK;
1514                                 goto finish;
1515
1516                         case LDAP_PROC_REJECT:
1517                                 rcode = RLM_MODULE_REJECT;
1518                                 goto finish;
1519
1520                         case LDAP_PROC_BAD_DN:
1521                                 rcode = RLM_MODULE_INVALID;
1522                                 goto finish;
1523
1524                         case LDAP_PROC_NO_RESULT:
1525                                 rcode = RLM_MODULE_NOTFOUND;
1526                                 goto finish;
1527
1528                         default:
1529                                 rcode = RLM_MODULE_FAIL;
1530                                 goto finish;
1531                         };
1532                 }
1533         }
1534
1535 skip_edir:
1536 #endif
1537
1538         /*
1539          *      Apply ONE user profile, or a default user profile.
1540          */
1541         if (inst->default_profile) {
1542                 char const *profile;
1543                 char profile_buff[1024];
1544
1545                 if (tmpl_expand(&profile, profile_buff, sizeof(profile_buff),
1546                                 request, inst->default_profile, NULL, NULL) < 0) {
1547                         REDEBUG("Failed creating default profile string");
1548
1549                         rcode = RLM_MODULE_INVALID;
1550                         goto finish;
1551                 }
1552
1553                 switch (rlm_ldap_map_profile(inst, request, &conn, profile, &expanded)) {
1554                 case RLM_MODULE_INVALID:
1555                         rcode = RLM_MODULE_INVALID;
1556                         goto finish;
1557
1558                 case RLM_MODULE_FAIL:
1559                         rcode = RLM_MODULE_FAIL;
1560                         goto finish;
1561
1562                 case RLM_MODULE_UPDATED:
1563                         rcode = RLM_MODULE_UPDATED;
1564                         /* FALL-THROUGH */
1565                 default:
1566                         break;
1567                 }
1568         }
1569
1570         /*
1571          *      Apply a SET of user profiles.
1572          */
1573         if (inst->profile_attr) {
1574                 values = ldap_get_values_len(conn->handle, entry, inst->profile_attr);
1575                 if (values != NULL) {
1576                         for (i = 0; values[i] != NULL; i++) {
1577                                 char *value;
1578
1579                                 value = rlm_ldap_berval_to_string(request, values[i]);
1580                                 rlm_ldap_map_profile(inst, request, &conn, value, &expanded);
1581                                 talloc_free(value);
1582                         }
1583                         ldap_value_free_len(values);
1584                 }
1585         }
1586
1587         if (inst->user_map || inst->valuepair_attr) {
1588                 RDEBUG("Processing user attributes");
1589                 RINDENT();
1590                 if (rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
1591                 REXDENT();
1592                 rlm_ldap_check_reply(inst, request);
1593         }
1594
1595 finish:
1596         talloc_free(expanded.ctx);
1597         if (result) ldap_msgfree(result);
1598         mod_conn_release(inst, conn);
1599
1600         return rcode;
1601 }
1602
1603 /** Modify user's object in LDAP
1604  *
1605  * Process a modifcation map to update a user object in the LDAP directory.
1606  *
1607  * @param inst rlm_ldap instance.
1608  * @param request Current request.
1609  * @param section that holds the map to process.
1610  * @return one of the RLM_MODULE_* values.
1611  */
1612 static rlm_rcode_t user_modify(rlm_ldap_t *inst, REQUEST *request, ldap_acct_section_t *section)
1613 {
1614         rlm_rcode_t     rcode = RLM_MODULE_OK;
1615         ldap_rcode_t    status;
1616
1617         ldap_handle_t   *conn = NULL;
1618
1619         LDAPMod         *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
1620         LDAPMod         **modify = mod_p;
1621
1622         char            *passed[LDAP_MAX_ATTRMAP * 2];
1623         int             i, total = 0, last_pass = 0;
1624
1625         char            *expanded[LDAP_MAX_ATTRMAP];
1626         int             last_exp = 0;
1627
1628         char const      *attr;
1629         char const      *value;
1630
1631         char const      *dn;
1632         /*
1633          *      Build our set of modifications using the update sections in
1634          *      the config.
1635          */
1636         CONF_ITEM       *ci;
1637         CONF_PAIR       *cp;
1638         CONF_SECTION    *cs;
1639         FR_TOKEN        op;
1640         char            path[MAX_STRING_LEN];
1641
1642         char            *p = path;
1643
1644         rad_assert(section);
1645
1646         /*
1647          *      Locate the update section were going to be using
1648          */
1649         if (section->reference[0] != '.') {
1650                 *p++ = '.';
1651         }
1652
1653         if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) {
1654                 goto error;
1655         }
1656
1657         ci = cf_reference_item(NULL, section->cs, path);
1658         if (!ci) {
1659                 goto error;
1660         }
1661
1662         if (!cf_item_is_section(ci)){
1663                 REDEBUG("Reference must resolve to a section");
1664
1665                 goto error;
1666         }
1667
1668         cs = cf_section_sub_find(cf_item_to_section(ci), "update");
1669         if (!cs) {
1670                 REDEBUG("Section must contain 'update' subsection");
1671
1672                 goto error;
1673         }
1674
1675         /*
1676          *      Iterate over all the pairs, building our mods array
1677          */
1678         for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
1679                 bool do_xlat = false;
1680
1681                 if (total == LDAP_MAX_ATTRMAP) {
1682                         REDEBUG("Modify map size exceeded");
1683
1684                         goto error;
1685                 }
1686
1687                 if (!cf_item_is_pair(ci)) {
1688                         REDEBUG("Entry is not in \"ldap-attribute = value\" format");
1689
1690                         goto error;
1691                 }
1692
1693                 /*
1694                  *      Retrieve all the information we need about the pair
1695                  */
1696                 cp = cf_item_to_pair(ci);
1697                 value = cf_pair_value(cp);
1698                 attr = cf_pair_attr(cp);
1699                 op = cf_pair_operator(cp);
1700
1701                 if (!value || (*value == '\0')) {
1702                         RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
1703
1704                         continue;
1705                 }
1706
1707                 switch (cf_pair_value_type(cp)) {
1708                 case T_BARE_WORD:
1709                 case T_SINGLE_QUOTED_STRING:
1710                         break;
1711
1712                 case T_BACK_QUOTED_STRING:
1713                 case T_DOUBLE_QUOTED_STRING:
1714                         do_xlat = true;
1715                         break;
1716
1717                 default:
1718                         rad_assert(0);
1719                         goto error;
1720                 }
1721
1722                 if (op == T_OP_CMP_FALSE) {
1723                         passed[last_pass] = NULL;
1724                 } else if (do_xlat) {
1725                         char *exp = NULL;
1726
1727                         if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) {
1728                                 RDEBUG("Skipping attribute \"%s\"", attr);
1729
1730                                 talloc_free(exp);
1731
1732                                 continue;
1733                         }
1734
1735                         expanded[last_exp++] = exp;
1736                         passed[last_pass] = exp;
1737                 /*
1738                  *      Static strings
1739                  */
1740                 } else {
1741                         memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
1742                 }
1743
1744                 passed[last_pass + 1] = NULL;
1745
1746                 mod_s[total].mod_values = &(passed[last_pass]);
1747
1748                 last_pass += 2;
1749
1750                 switch (op) {
1751                 /*
1752                  *  T_OP_EQ is *NOT* supported, it is impossible to
1753                  *  support because of the lack of transactions in LDAP
1754                  */
1755                 case T_OP_ADD:
1756                         mod_s[total].mod_op = LDAP_MOD_ADD;
1757                         break;
1758
1759                 case T_OP_SET:
1760                         mod_s[total].mod_op = LDAP_MOD_REPLACE;
1761                         break;
1762
1763                 case T_OP_SUB:
1764                 case T_OP_CMP_FALSE:
1765                         mod_s[total].mod_op = LDAP_MOD_DELETE;
1766                         break;
1767
1768 #ifdef LDAP_MOD_INCREMENT
1769                 case T_OP_INCRM:
1770                         mod_s[total].mod_op = LDAP_MOD_INCREMENT;
1771                         break;
1772 #endif
1773                 default:
1774                         REDEBUG("Operator '%s' is not supported for LDAP modify operations",
1775                                 fr_int2str(fr_tokens, op, "<INVALID>"));
1776
1777                         goto error;
1778                 }
1779
1780                 /*
1781                  *      Now we know the value is ok, copy the pointers into
1782                  *      the ldapmod struct.
1783                  */
1784                 memcpy(&(mod_s[total].mod_type), &attr, sizeof(mod_s[total].mod_type));
1785
1786                 mod_p[total] = &(mod_s[total]);
1787                 total++;
1788         }
1789
1790         if (total == 0) {
1791                 rcode = RLM_MODULE_NOOP;
1792                 goto release;
1793         }
1794
1795         mod_p[total] = NULL;
1796
1797         conn = mod_conn_get(inst, request);
1798         if (!conn) return RLM_MODULE_FAIL;
1799
1800
1801         dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1802         if (!dn || (rcode != RLM_MODULE_OK)) {
1803                 goto error;
1804         }
1805
1806         status = rlm_ldap_modify(inst, request, &conn, dn, modify);
1807         switch (status) {
1808         case LDAP_PROC_SUCCESS:
1809                 break;
1810
1811         case LDAP_PROC_REJECT:
1812         case LDAP_PROC_BAD_DN:
1813                 rcode = RLM_MODULE_INVALID;
1814                 break;
1815
1816         default:
1817                 rcode = RLM_MODULE_FAIL;
1818                 break;
1819         };
1820
1821         release:
1822         error:
1823         /*
1824          *      Free up any buffers we allocated for xlat expansion
1825          */
1826         for (i = 0; i < last_exp; i++) {
1827                 talloc_free(expanded[i]);
1828         }
1829
1830         mod_conn_release(inst, conn);
1831
1832         return rcode;
1833 }
1834
1835 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) CC_HINT(nonnull);
1836 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
1837 {
1838         rlm_ldap_t *inst = instance;
1839
1840         if (inst->accounting) return user_modify(inst, request, inst->accounting);
1841
1842         return RLM_MODULE_NOOP;
1843 }
1844
1845 static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull);
1846 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
1847 {
1848         rlm_ldap_t *inst = instance;
1849
1850         if (inst->postauth) {
1851                 return user_modify(inst, request, inst->postauth);
1852         }
1853
1854         return RLM_MODULE_NOOP;
1855 }
1856
1857
1858 /* globally exported name */
1859 extern module_t rlm_ldap;
1860 module_t rlm_ldap = {
1861         .magic          = RLM_MODULE_INIT,
1862         .name           = "ldap",
1863         .inst_size      = sizeof(rlm_ldap_t),
1864         .config         = module_config,
1865         .bootstrap      = mod_bootstrap,
1866         .instantiate    = mod_instantiate,
1867         .detach         = mod_detach,
1868         .methods = {
1869                 [MOD_AUTHENTICATE]      = mod_authenticate,
1870                 [MOD_AUTHORIZE]         = mod_authorize,
1871                 [MOD_ACCOUNTING]        = mod_accounting,
1872                 [MOD_POST_AUTH]         = mod_post_auth
1873         },
1874 };