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