Pass a threadsafe ctx into fr_connection_pool create callback
[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, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_ldap.c
19  * @brief LDAP authorization and authentication module.
20  *
21  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22  * @author Alan DeKok <aland@freeradius.org>
23  *
24  * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
25  * @copyright 2012-2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
26  * @copyright 2012 Alan DeKok <aland@freeradius.org>
27  * @copyright 1999-2013 The FreeRADIUS Server Project.
28  */
29 RCSID("$Id$")
30
31 #include        <freeradius-devel/rad_assert.h>
32
33 #include        <stdarg.h>
34 #include        <ctype.h>
35
36 #include        "ldap.h"
37
38 /*
39  *      Scopes
40  */
41 FR_NAME_NUMBER const ldap_scope[] = {
42         { "sub",        LDAP_SCOPE_SUB  },
43         { "one",        LDAP_SCOPE_ONE  },
44         { "base",       LDAP_SCOPE_BASE },
45 #ifdef LDAP_SCOPE_CHILDREN
46         { "children",   LDAP_SCOPE_CHILDREN },
47 #endif
48         {  NULL , -1 }
49 };
50
51 #ifdef LDAP_OPT_X_TLS_NEVER
52 FR_NAME_NUMBER const ldap_tls_require_cert[] = {
53         { "never",      LDAP_OPT_X_TLS_NEVER    },
54         { "demand",     LDAP_OPT_X_TLS_DEMAND   },
55         { "allow",      LDAP_OPT_X_TLS_ALLOW    },
56         { "try",        LDAP_OPT_X_TLS_TRY      },
57         { "hard",       LDAP_OPT_X_TLS_HARD     },      /* oh yes, just like that */
58
59         {  NULL , -1 }
60 };
61 #endif
62
63 FR_NAME_NUMBER const ldap_dereference[] = {
64         { "never",      LDAP_DEREF_NEVER        },
65         { "searching",  LDAP_DEREF_SEARCHING    },
66         { "finding",    LDAP_DEREF_FINDING      },
67         { "always",     LDAP_DEREF_ALWAYS       },
68
69         {  NULL , -1 }
70 };
71
72 /*
73  *      TLS Configuration
74  */
75 static CONF_PARSER tls_config[] = {
76         /*
77          *      Deprecated attributes
78          */
79         { "cacertfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_ca_file), NULL },
80         { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_ca_file), NULL },
81
82         { "cacertdir", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_ca_path), NULL },
83         { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_ca_path), NULL },
84
85         { "certfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_certificate_file), NULL },
86         { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_certificate_file), NULL },
87
88         { "keyfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_private_key_file), NULL }, // OK if it changes on HUP
89         { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_private_key_file), NULL }, // OK if it changes on HUP
90
91         { "randfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_random_file), NULL },
92         { "random_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_random_file), NULL },
93
94         /*
95          *      LDAP Specific TLS attributes
96          */
97         { "start_tls", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, start_tls), "no" },
98         { "require_cert", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, tls_require_cert_str), NULL },
99
100         { NULL, -1, 0, NULL, NULL }
101 };
102
103
104 static CONF_PARSER profile_config[] = {
105         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, profile_filter), "(&)" },   //!< Correct filter for
106                                                                                                 //!< when the DN is
107                                                                                                 //!< known.
108         { "attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, profile_attr), NULL },
109         { "default", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, default_profile), NULL },
110
111         { NULL, -1, 0, NULL, NULL }
112 };
113
114 /*
115  *      User configuration
116  */
117 static CONF_PARSER user_config[] = {
118         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_filter), "(uid=%u)" },
119         { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_scope_str), "sub" },
120         { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_base_dn), "" },
121
122         { "access_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_access_attr), NULL },
123         { "access_positive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, access_positive), "yes" },
124
125         { NULL, -1, 0, NULL, NULL }
126 };
127
128 /*
129  *      Group configuration
130  */
131 static CONF_PARSER group_config[] = {
132         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_filter), NULL },
133         { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_scope_str), "sub" },
134         { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_base_dn), "" },
135
136         { "name_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_name_attr), "cn" },
137         { "membership_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_membership_attr), NULL },
138         { "membership_filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_membership_filter), NULL },
139         { "cacheable_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, cacheable_group_name), "no" },
140         { "cacheable_dn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, cacheable_group_dn), "no" },
141         { "cache_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, cache_attribute), NULL },
142
143         { NULL, -1, 0, NULL, NULL }
144 };
145
146 /*
147  *      Client configuration
148  */
149 static CONF_PARSER client_attribute[] = {
150         { "identifier", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_identifier), "host" },
151         { "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_shortname), "cn" },
152         { "nas_type", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_type), NULL },
153         { "secret", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_secret), NULL },
154         { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_server), NULL },
155         { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_require_ma), NULL },
156
157         { NULL, -1, 0, NULL, NULL }
158 };
159
160 static CONF_PARSER client_config[] = {
161         { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_filter), NULL },
162         { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_scope_str), "sub" },
163         { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_base_dn), "" },
164         { "attribute", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_attribute },
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, 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, ldap_instance_t, ldap_debug), "0x0000" },
188
189         { "dereference", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, dereference_str), NULL },
190
191         { "chase_referrals", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, chase_referrals), NULL },
192
193         { "rebind", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, rebind), NULL },
194
195 #ifdef LDAP_OPT_NETWORK_TIMEOUT
196         /* timeout on network activity */
197         { "net_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, net_timeout), "10" },
198 #endif
199
200         /* timeout for search results */
201         { "res_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, res_timeout), "20" },
202
203         /* allow server unlimited time for search (server-side limit) */
204         { "srv_timelimit", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, srv_timelimit), "20" },
205
206 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
207         { "idle", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_idle), "60" },
208 #endif
209 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
210         { "probes", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_probes), "3" },
211 #endif
212 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
213         { "interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_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 | PW_TYPE_REQUIRED, ldap_instance_t, server), "localhost" },
222         { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, ldap_instance_t, port), "389" },
223
224         { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, ldap_instance_t, password), "" },
225         { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, admin_dn), "" },
226
227         { "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, valuepair_attr), NULL },
228
229 #ifdef WITH_EDIR
230         /* support for eDirectory Universal Password */
231         { "edir", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, edir), NULL }, /* NULL defaults to "no" */
232
233         /*
234          *      Attempt to bind with the cleartext password we got from eDirectory
235          *      Universal password for additional authorization checks.
236          */
237         { "edir_autz", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, edir_autz), NULL }, /* NULL defaults to "no" */
238 #endif
239
240         { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, do_clients), NULL }, /* NULL defaults to "no" */
241
242         { "user", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) user_config },
243
244         { "group", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) group_config },
245
246         { "client", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_config },
247
248         { "profile", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) profile_config },
249
250         { "options", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) option_config },
251
252         { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
253
254         {NULL, -1, 0, NULL, NULL}
255 };
256
257 /** Expand an LDAP URL into a query, and return a string result from that query.
258  *
259  */
260 static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
261 {
262         ldap_rcode_t status;
263         size_t len = 0;
264         ldap_instance_t *inst = instance;
265         LDAPURLDesc *ldap_url;
266         LDAPMessage *result = NULL;
267         LDAPMessage *entry = NULL;
268         char **vals;
269         ldap_handle_t *conn;
270         int ldap_errno;
271         char const *url;
272         char const **attrs;
273
274         url = fmt;
275
276         if (!ldap_is_ldap_url(url)) {
277                 REDEBUG("String passed does not look like an LDAP URL");
278                 return -1;
279         }
280
281         if (ldap_url_parse(url, &ldap_url)){
282                 REDEBUG("Parsing LDAP URL failed");
283                 return -1;
284         }
285
286         /*
287          *      Nothing, empty string, "*" string, or got 2 things, die.
288          */
289         if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
290             !*ldap_url->lud_attrs[0] ||
291             (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
292             ldap_url->lud_attrs[1]) {
293                 REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
294
295                 goto free_urldesc;
296         }
297
298         if (ldap_url->lud_host &&
299             ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
300              ((uint32_t) ldap_url->lud_port != inst->port))) {
301                 RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
302
303                 goto free_urldesc;
304         }
305
306         conn = rlm_ldap_get_socket(inst, request);
307         if (!conn) goto free_urldesc;
308
309         memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
310
311         status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
312                                  attrs, &result);
313         switch (status) {
314                 case LDAP_PROC_SUCCESS:
315                         break;
316                 case LDAP_PROC_NO_RESULT:
317                         RDEBUG("Search returned not found");
318                 default:
319                         goto free_socket;
320         }
321
322         rad_assert(conn);
323         rad_assert(result);
324
325         entry = ldap_first_entry(conn->handle, result);
326         if (!entry) {
327                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
328                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
329                 len = -1;
330                 goto free_result;
331         }
332
333         vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
334         if (!vals) {
335                 RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
336                 goto free_result;
337         }
338
339         len = strlen(vals[0]);
340         if (len >= freespace){
341                 goto free_vals;
342         }
343
344         strlcpy(out, vals[0], freespace);
345
346 free_vals:
347         ldap_value_free(vals);
348 free_result:
349         ldap_msgfree(result);
350 free_socket:
351         rlm_ldap_release_socket(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 1 on failure (or if the user is not a member), else 0.
369  */
370 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
371                              UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
372 {
373         ldap_instance_t *inst = instance;
374         rlm_rcode_t     rcode;
375
376         bool            found = false;
377         bool            check_is_dn;
378
379         ldap_handle_t   *conn = NULL;
380         char const      *user_dn;
381
382         rad_assert(inst->groupobj_base_dn);
383
384         RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
385
386         if (check->length == 0) {
387                 RDEBUG("Cannot do comparison (group name is empty)");
388                 return 1;
389         }
390
391         /*
392          *      Check if we can do cached membership verification
393          */
394         check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
395         if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
396                 switch(rlm_ldap_check_cached(inst, request, check)) {
397                         case RLM_MODULE_NOTFOUND:
398                                 found = false;
399                                 goto finish;
400                         case RLM_MODULE_OK:
401                                 found = true;
402                                 goto finish;
403                         /*
404                          *      Fallback to dynamic search on failure
405                          */
406                         case RLM_MODULE_FAIL:
407                         case RLM_MODULE_INVALID:
408                         default:
409                                 break;
410                 }
411         }
412
413         conn = rlm_ldap_get_socket(inst, request);
414         if (!conn) return 1;
415
416         /*
417          *      This is used in the default membership filter.
418          */
419         user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
420         if (!user_dn) {
421                 rlm_ldap_release_socket(inst, conn);
422                 return 1;
423         }
424
425         rad_assert(conn);
426
427         /*
428          *      Check groupobj user membership
429          */
430         if (inst->groupobj_membership_filter) {
431                 switch(rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) {
432                         case RLM_MODULE_NOTFOUND:
433                                 break;
434                         case RLM_MODULE_OK:
435                                 found = true;
436                         default:
437                                 goto finish;
438                 }
439         }
440
441         rad_assert(conn);
442
443         /*
444          *      Check userobj group membership
445          */
446         if (inst->userobj_membership_attr) {
447                 switch(rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) {
448                         case RLM_MODULE_NOTFOUND:
449                                 break;
450                         case RLM_MODULE_OK:
451                                 found = true;
452                         default:
453                                 goto finish;
454                 }
455         }
456
457         rad_assert(conn);
458
459         finish:
460         if (conn) {
461                 rlm_ldap_release_socket(inst, conn);
462         }
463
464         if (!found) {
465                 RDEBUG("User is not a member of specified group");
466
467                 return 1;
468         }
469
470         return 0;
471 }
472
473 /** Detach from the LDAP server and cleanup internal state.
474  *
475  */
476 static int mod_detach(void *instance)
477 {
478         ldap_instance_t *inst = instance;
479
480         fr_connection_pool_delete(inst->pool);
481
482         if (inst->user_map) {
483                 talloc_free(inst->user_map);
484         }
485
486         return 0;
487 }
488
489 /** Parse an accounting sub section.
490  *
491  * Allocate a new ldap_acct_section_t and write the config data into it.
492  *
493  * @param[in] inst rlm_ldap configuration.
494  * @param[in] parent of the config section.
495  * @param[out] config to write the sub section parameters to.
496  * @param[in] comp The section name were parsing the config for.
497  * @return 0 on success, else < 0 on failure.
498  */
499 static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
500                              rlm_components_t comp)
501 {
502         CONF_SECTION *cs;
503
504         char const *name = section_type_value[comp].section;
505
506         cs = cf_section_sub_find(parent, name);
507         if (!cs) {
508                 INFO("rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
509                        "from this section", inst->xlat_name, name);
510
511                 return 0;
512         }
513
514         *config = talloc_zero(inst, ldap_acct_section_t);
515         if (cf_section_parse(cs, *config, acct_section_config) < 0) {
516                 LDAP_ERR("Failed parsing configuration for section %s", name);
517
518                 return -1;
519         }
520
521         (*config)->cs = cs;
522
523         return 0;
524 }
525
526 /** Instantiate the module
527  *
528  * Creates a new instance of the module reading parameters from a configuration section.
529  *
530  * @param conf to parse.
531  * @param instance Where to write pointer to configuration data.
532  * @return 0 on success < 0 on failure.
533  */
534 static int mod_instantiate(CONF_SECTION *conf, void *instance)
535 {
536         static bool version_done;
537
538         CONF_SECTION *options;
539         ldap_instance_t *inst = instance;
540
541         inst->cs = conf;
542
543         options = cf_section_sub_find(conf, "options");
544         if (!options || !cf_pair_find(options, "chase_referrals")) {
545                 inst->chase_referrals_unset = true;      /* use OpenLDAP defaults */
546         }
547
548         inst->xlat_name = cf_section_name2(conf);
549         if (!inst->xlat_name) {
550                 inst->xlat_name = cf_section_name1(conf);
551         }
552
553         /*
554          *      Get version info from the LDAP API.
555          */
556         if (!version_done) {
557                 int ldap_errno;
558                 LDAPAPIInfo info;
559
560                 version_done = true;
561
562                 ldap_errno = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
563                 if (ldap_errno == LDAP_OPT_SUCCESS) {
564                         if (strcmp(info.ldapai_vendor_name, LDAP_VENDOR_NAME) != 0) {
565                                 WARN("rlm_ldap: libldap vendor changed since the server was built");
566                                 WARN("rlm_ldap: linked: %s built: %s", info.ldapai_vendor_name, LDAP_VENDOR_NAME);
567                         }
568
569                         if (info.ldapai_vendor_version != LDAP_VENDOR_VERSION) {
570                                 WARN("rlm_ldap: libldap version changed since the server was built");
571                                 WARN("rlm_ldap: linked: %i built: %i",
572                                      info.ldapai_vendor_version, LDAP_VENDOR_VERSION);
573                         }
574
575                         INFO("rlm_ldap: libldap vendor: %s version: %i", info.ldapai_vendor_name,
576                              info.ldapai_vendor_version);
577                         ldap_memfree(info.ldapai_vendor_name);
578                         ldap_memfree(info.ldapai_extensions);
579                 } else {
580                         WARN("rlm_ldap: Falling back to build time libldap version info.  Query for LDAP_OPT_API_INFO "
581                              "returned: %i", ldap_errno);
582                         INFO("rlm_ldap: libldap vendor: %s version: %i", LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
583                 }
584         }
585
586         /*
587          *      If the configuration parameters can't be parsed, then fail.
588          */
589         if ((parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
590             (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
591                 LDAP_ERR("Failed parsing configuration");
592
593                 goto error;
594         }
595
596         /*
597          *      Sanity checks for cacheable groups code.
598          */
599         if (inst->cacheable_group_name && inst->groupobj_membership_filter) {
600                 if (!inst->groupobj_name_attr) {
601                         LDAP_ERR("Directive 'group.name_attribute' must be set if cacheable group names are enabled");
602
603                         goto error;
604                 }
605         }
606
607         /*
608          *      Check for URLs.  If they're used and the library doesn't support them, then complain.
609          */
610         inst->is_url = 0;
611         if (ldap_is_ldap_url(inst->server)) {
612 #ifdef HAVE_LDAP_INITIALIZE
613                 inst->is_url = 1;
614                 inst->port = 0;
615 #else
616                 LDAP_ERR("Directive 'server' is in URL form but ldap_initialize() is not available");
617                 goto error;
618 #endif
619         }
620
621 #ifdef LDAP_OPT_X_TLS_NEVER
622         /*
623          *      Workaround for servers which support LDAPS but not START TLS
624          */
625         if (inst->port == LDAPS_PORT || inst->tls_mode) {
626                 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
627         } else {
628                 inst->tls_mode = 0;
629         }
630 #endif
631
632         /*
633          *      Convert dereference strings to enumerated constants
634          */
635         if (inst->dereference_str) {
636                 inst->dereference = fr_str2int(ldap_dereference, inst->dereference_str, -1);
637                 if (inst->dereference < 0) {
638                         LDAP_ERR("Invalid 'dereference' value \"%s\", expected 'never', 'searching', "
639                                  "'finding' or 'always'", inst->dereference_str);
640                         goto error;
641                 }
642         }
643
644 #if LDAP_SET_REBIND_PROC_ARGS != 3
645         /*
646          *      The 2-argument rebind doesn't take an instance variable.  Our rebind function needs the instance
647          *      variable for the username, password, etc.
648          */
649         if (inst->rebind == true) {
650                 LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API "
651                          "that we need");
652
653                 goto error;
654         }
655 #endif
656
657         /*
658          *      Convert scope strings to enumerated constants
659          */
660         inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1);
661         if (inst->userobj_scope < 0) {
662                 LDAP_ERR("Invalid 'user.scope' value \"%s\", expected 'sub', 'one'"
663 #ifdef LDAP_SCOPE_CHILDREN
664                          ", 'base' or 'children'"
665 #else
666                          " or 'base'"
667 #endif
668                          , inst->userobj_scope_str);
669                 goto error;
670         }
671
672         inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1);
673         if (inst->groupobj_scope < 0) {
674                 LDAP_ERR("Invalid 'group.scope' value \"%s\", expected 'sub', 'one'"
675 #ifdef LDAP_SCOPE_CHILDREN
676                          ", 'base' or 'children'"
677 #else
678                          " or 'base'"
679 #endif
680                          , inst->groupobj_scope_str);
681                 goto error;
682         }
683
684         inst->clientobj_scope = fr_str2int(ldap_scope, inst->clientobj_scope_str, -1);
685         if (inst->clientobj_scope < 0) {
686                 LDAP_ERR("Invalid 'client.scope' value \"%s\", expected 'sub', 'one'"
687 #ifdef LDAP_SCOPE_CHILDREN
688                          ", 'base' or 'children'"
689 #else
690                          " or 'base'"
691 #endif
692                          , inst->clientobj_scope_str);
693                 goto error;
694         }
695
696         if (inst->tls_require_cert_str) {
697 #ifdef LDAP_OPT_X_TLS_NEVER
698                 /*
699                  *      Convert cert strictness to enumerated constants
700                  */
701                 inst->tls_require_cert = fr_str2int(ldap_tls_require_cert, inst->tls_require_cert_str, -1);
702                 if (inst->tls_require_cert < 0) {
703                         LDAP_ERR("Invalid 'tls.require_cert' value \"%s\", expected 'never', 'demand', 'allow', "
704                                  "'try' or 'hard'", inst->tls_require_cert_str);
705                         goto error;
706                 }
707 #else
708                 LDAP_ERR("Modifying 'tls.require_cert' is not supported by current version of libldap. "
709                          "Please upgrade or substitute current libldap and rebuild this module");
710
711                 goto error;
712 #endif
713         }
714         /*
715          *      Build the attribute map
716          */
717         if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
718                 goto error;
719         }
720
721         /*
722          *      Group comparison checks.
723          */
724         if (cf_section_name2(conf)) {
725                 static ATTR_FLAGS flags;
726                 char buffer[256];
727
728                 snprintf(buffer, sizeof(buffer), "%s-Ldap-Group", inst->xlat_name);
729                 if (dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags) < 0) {
730                         LDAP_ERR("Error creating group attribute: %s", fr_strerror());
731
732                         return -1;
733                 }
734                 inst->group_da = dict_attrbyname(buffer);
735                 if (!inst->group_da) {
736                         LDAP_ERR("Failed creating attribute %s", buffer);
737
738                         goto error;
739                 }
740
741                 paircompare_register(inst->group_da, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst);
742         /*
743          *      Were the default instance
744          */
745         } else {
746                 inst->group_da = dict_attrbyvalue(PW_LDAP_GROUP, 0);
747                 paircompare_register(dict_attrbyvalue(PW_LDAP_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0),
748                                 false, rlm_ldap_groupcmp, inst);
749         }
750
751         xlat_register(inst->xlat_name, ldap_xlat, rlm_ldap_escape_func, inst);
752
753         /*
754          *      Setup the cache attribute
755          */
756         if (inst->cache_attribute) {
757                 static ATTR_FLAGS flags;
758
759                 if (dict_addattr(inst->cache_attribute, -1, 0, PW_TYPE_STRING, flags) < 0) {
760                         LDAP_ERR("Error creating cache attribute: %s", fr_strerror());
761
762                         return -1;
763                 }
764                 inst->cache_da = dict_attrbyname(inst->cache_attribute);
765         } else {
766                 inst->cache_da = inst->group_da;        /* Default to the group_da */
767         }
768
769         /*
770          *      Initialize the socket pool.
771          */
772         inst->pool = fr_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, NULL, NULL);
773         if (!inst->pool) {
774                 return -1;
775         }
776
777         /*
778          *      Bulk load dynamic clients.
779          */
780         if (inst->do_clients) {
781                 if (rlm_ldap_load_clients(inst) < 0) {
782                         LDAP_ERR("Error loading clients");
783
784                         return -1;
785                 }
786         }
787
788         return 0;
789
790 error:
791         return -1;
792 }
793
794 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
795 {
796         rlm_rcode_t     rcode;
797         ldap_rcode_t    status;
798         char const      *dn;
799         ldap_instance_t *inst = instance;
800         ldap_handle_t   *conn;
801
802         /*
803          * Ensure that we're being passed a plain-text password, and not
804          * anything else.
805          */
806
807         if (!request->username) {
808                 REDEBUG("Attribute \"User-Name\" is required for authentication");
809
810                 return RLM_MODULE_INVALID;
811         }
812
813         if (!request->password ||
814             (request->password->da->attr != PW_USER_PASSWORD)) {
815                 RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
816                 RWDEBUG("*********************************************");
817                 RWDEBUG("* THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
818                 RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
819                 RWDEBUG("*********************************************");
820
821                 REDEBUG("Attribute \"User-Password\" is required for authentication");
822
823                 return RLM_MODULE_INVALID;
824         }
825
826         if (request->password->length == 0) {
827                 REDEBUG("Empty password supplied");
828
829                 return RLM_MODULE_INVALID;
830         }
831
832         RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
833
834         conn = rlm_ldap_get_socket(inst, request);
835         if (!conn) return RLM_MODULE_FAIL;
836
837         /*
838          *      Get the DN by doing a search.
839          */
840         dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
841         if (!dn) {
842                 rlm_ldap_release_socket(inst, conn);
843
844                 return rcode;
845         }
846
847         /*
848          *      Bind as the user
849          */
850         conn->rebound = true;
851         status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, true);
852         switch (status) {
853         case LDAP_PROC_SUCCESS:
854                 rcode = RLM_MODULE_OK;
855                 RDEBUG("Bind as user \"%s\" was successful", dn);
856                 break;
857
858         case LDAP_PROC_NOT_PERMITTED:
859                 rcode = RLM_MODULE_USERLOCK;
860                 break;
861
862         case LDAP_PROC_REJECT:
863                 rcode = RLM_MODULE_REJECT;
864                 break;
865
866         case LDAP_PROC_BAD_DN:
867                 rcode = RLM_MODULE_INVALID;
868                 break;
869
870         case LDAP_PROC_NO_RESULT:
871                 rcode = RLM_MODULE_NOTFOUND;
872                 break;
873
874         default:
875                 rcode = RLM_MODULE_FAIL;
876                 break;
877         };
878
879         rlm_ldap_release_socket(inst, conn);
880
881         return rcode;
882 }
883
884 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
885 {
886         rlm_rcode_t     rcode = RLM_MODULE_OK;
887         ldap_rcode_t    status;
888         int             ldap_errno;
889         int             i;
890         ldap_instance_t *inst = instance;
891         char            **vals;
892         VALUE_PAIR      *vp;
893         ldap_handle_t   *conn;
894         LDAPMessage     *result, *entry;
895         char const      *dn = NULL;
896         rlm_ldap_map_xlat_t     expanded; /* faster than mallocing every time */
897
898         if (!request->username) {
899                 RDEBUG2("Attribute \"User-Name\" is required for authorization");
900
901                 return RLM_MODULE_NOOP;
902         }
903
904         /*
905          *      Check for valid input, zero length names not permitted
906          */
907         if (request->username->length == 0) {
908                 RDEBUG2("Zero length username not permitted");
909
910                 return RLM_MODULE_INVALID;
911         }
912
913         if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
914                 return RLM_MODULE_FAIL;
915         }
916
917         conn = rlm_ldap_get_socket(inst, request);
918         if (!conn) return RLM_MODULE_FAIL;
919
920         /*
921          *      Add any additional attributes we need for checking access, memberships, and profiles
922          */
923         if (inst->userobj_access_attr) {
924                 expanded.attrs[expanded.count++] = inst->userobj_access_attr;
925         }
926
927         if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) {
928                 expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
929         }
930
931         if (inst->profile_attr) {
932                 expanded.attrs[expanded.count++] = inst->profile_attr;
933         }
934
935         if (inst->valuepair_attr) {
936                 expanded.attrs[expanded.count++] = inst->valuepair_attr;
937         }
938
939         expanded.attrs[expanded.count] = NULL;
940
941         dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode);
942         if (!dn) {
943                 goto finish;
944         }
945
946         entry = ldap_first_entry(conn->handle, result);
947         if (!entry) {
948                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
949                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
950
951                 goto finish;
952         }
953
954         /*
955          *      Check for access.
956          */
957         if (inst->userobj_access_attr) {
958                 rcode = rlm_ldap_check_access(inst, request, conn, entry);
959                 if (rcode != RLM_MODULE_OK) {
960                         goto finish;
961                 }
962         }
963
964         /*
965          *      Check if we need to cache group memberships
966          */
967         if (inst->cacheable_group_dn || inst->cacheable_group_name) {
968                 if (inst->userobj_membership_attr) {
969                         rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr);
970                         if (rcode != RLM_MODULE_OK) {
971                                 goto finish;
972                         }
973                 }
974
975                 rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
976                 if (rcode != RLM_MODULE_OK) {
977                         goto finish;
978                 }
979         }
980
981 #ifdef WITH_EDIR
982         /*
983          *      We already have a Cleartext-Password.  Skip edir.
984          */
985         if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
986                 goto skip_edir;
987         }
988
989         /*
990          *      Retrieve Universal Password if we use eDirectory
991          */
992         if (inst->edir) {
993                 int res = 0;
994                 char password[256];
995                 size_t pass_size = sizeof(password);
996
997                 /*
998                  *      Retrive universal password
999                  */
1000                 res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
1001                 if (res != 0) {
1002                         REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res));
1003                         rcode = RLM_MODULE_FAIL;
1004
1005                         goto finish;
1006                 }
1007
1008                 /*
1009                  *      Add Cleartext-Password attribute to the request
1010                  */
1011                 vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
1012                 pairstrcpy(vp, password);
1013                 vp->length = pass_size;
1014
1015                 if (RDEBUG_ENABLED3) {
1016                         RDEBUG3("Added eDirectory password.  control:%s += '%s'", vp->da->name, vp->vp_strvalue);
1017                 } else {
1018                         RDEBUG2("Added eDirectory password");
1019                 }
1020
1021                 if (inst->edir_autz) {
1022                         RDEBUG2("Binding as user for eDirectory authorization checks");
1023                         /*
1024                          *      Bind as the user
1025                          */
1026                         conn->rebound = true;
1027                         status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true);
1028                         switch (status) {
1029                         case LDAP_PROC_SUCCESS:
1030                                 rcode = RLM_MODULE_OK;
1031                                 RDEBUG("Bind as user '%s' was successful", dn);
1032                                 break;
1033
1034                         case LDAP_PROC_NOT_PERMITTED:
1035                                 rcode = RLM_MODULE_USERLOCK;
1036                                 goto finish;
1037
1038                         case LDAP_PROC_REJECT:
1039                                 rcode = RLM_MODULE_REJECT;
1040                                 goto finish;
1041
1042                         case LDAP_PROC_BAD_DN:
1043                                 rcode = RLM_MODULE_INVALID;
1044                                 goto finish;
1045
1046                         case LDAP_PROC_NO_RESULT:
1047                                 rcode = RLM_MODULE_NOTFOUND;
1048                                 goto finish;
1049
1050                         default:
1051                                 rcode = RLM_MODULE_FAIL;
1052                                 goto finish;
1053                         };
1054                 }
1055         }
1056
1057 skip_edir:
1058 #endif
1059
1060         /*
1061          *      Apply ONE user profile, or a default user profile.
1062          */
1063         if (inst->default_profile) {
1064                 char profile[1024];
1065
1066                 if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) {
1067                         REDEBUG("Failed creating default profile string");
1068
1069                         rcode = RLM_MODULE_INVALID;
1070                         goto finish;
1071                 }
1072
1073                 rlm_ldap_map_profile(inst, request, &conn, profile, &expanded);
1074         }
1075
1076         /*
1077          *      Apply a SET of user profiles.
1078          */
1079         if (inst->profile_attr) {
1080                 vals = ldap_get_values(conn->handle, entry, inst->profile_attr);
1081                 if (vals != NULL) {
1082                         for (i = 0; vals[i] != NULL; i++) {
1083                                 rlm_ldap_map_profile(inst, request, &conn, vals[i], &expanded);
1084                         }
1085
1086                         ldap_value_free(vals);
1087                 }
1088         }
1089
1090         if (inst->user_map || inst->valuepair_attr) {
1091                 RDEBUG("Processing user attributes");
1092                 rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
1093                 rlm_ldap_check_reply(inst, request);
1094         }
1095
1096 finish:
1097         rlm_ldap_map_xlat_free(&expanded);
1098         if (result) {
1099                 ldap_msgfree(result);
1100         }
1101         rlm_ldap_release_socket(inst, conn);
1102
1103         return rcode;
1104 }
1105
1106 /** Modify user's object in LDAP
1107  *
1108  * Process a modifcation map to update a user object in the LDAP directory.
1109  *
1110  * @param inst rlm_ldap instance.
1111  * @param request Current request.
1112  * @param section that holds the map to process.
1113  * @return one of the RLM_MODULE_* values.
1114  */
1115 static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
1116 {
1117         rlm_rcode_t     rcode = RLM_MODULE_OK;
1118         ldap_rcode_t    status;
1119
1120         ldap_handle_t   *conn = NULL;
1121
1122         LDAPMod         *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
1123         LDAPMod         **modify = mod_p;
1124
1125         char            *passed[LDAP_MAX_ATTRMAP * 2];
1126         int             i, total = 0, last_pass = 0;
1127
1128         char            *expanded[LDAP_MAX_ATTRMAP];
1129         int             last_exp = 0;
1130
1131         char const      *attr;
1132         char const      *value;
1133
1134         char const      *dn;
1135         /*
1136          *      Build our set of modifications using the update sections in
1137          *      the config.
1138          */
1139         CONF_ITEM       *ci;
1140         CONF_PAIR       *cp;
1141         CONF_SECTION    *cs;
1142         FR_TOKEN        op;
1143         char            path[MAX_STRING_LEN];
1144
1145         char            *p = path;
1146
1147         rad_assert(section);
1148
1149         /*
1150          *      Locate the update section were going to be using
1151          */
1152         if (section->reference[0] != '.') {
1153                 *p++ = '.';
1154         }
1155
1156         if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) {
1157                 goto error;
1158         }
1159
1160         ci = cf_reference_item(NULL, section->cs, path);
1161         if (!ci) {
1162                 goto error;
1163         }
1164
1165         if (!cf_item_is_section(ci)){
1166                 REDEBUG("Reference must resolve to a section");
1167
1168                 goto error;
1169         }
1170
1171         cs = cf_section_sub_find(cf_itemtosection(ci), "update");
1172         if (!cs) {
1173                 REDEBUG("Section must contain 'update' subsection");
1174
1175                 goto error;
1176         }
1177
1178         /*
1179          *      Iterate over all the pairs, building our mods array
1180          */
1181         for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
1182                 bool do_xlat = false;
1183
1184                 if (total == LDAP_MAX_ATTRMAP) {
1185                         REDEBUG("Modify map size exceeded");
1186
1187                         goto error;
1188                 }
1189
1190                 if (!cf_item_is_pair(ci)) {
1191                         REDEBUG("Entry is not in \"ldap-attribute = value\" format");
1192
1193                         goto error;
1194                 }
1195
1196                 /*
1197                  *      Retrieve all the information we need about the pair
1198                  */
1199                 cp = cf_itemtopair(ci);
1200                 value = cf_pair_value(cp);
1201                 attr = cf_pair_attr(cp);
1202                 op = cf_pair_operator(cp);
1203
1204                 if (!value || (*value == '\0')) {
1205                         RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
1206
1207                         continue;
1208                 }
1209
1210                 switch (cf_pair_value_type(cp)) {
1211                 case T_BARE_WORD:
1212                 case T_SINGLE_QUOTED_STRING:
1213                         break;
1214
1215                 case T_BACK_QUOTED_STRING:
1216                 case T_DOUBLE_QUOTED_STRING:
1217                         do_xlat = true;
1218                         break;
1219
1220                 default:
1221                         rad_assert(0);
1222                         goto error;
1223                 }
1224
1225                 if (op == T_OP_CMP_FALSE) {
1226                         passed[last_pass] = NULL;
1227                 } else if (do_xlat) {
1228                         char *exp = NULL;
1229
1230                         if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) {
1231                                 RDEBUG("Skipping attribute \"%s\"", attr);
1232
1233                                 talloc_free(exp);
1234
1235                                 continue;
1236                         }
1237
1238                         expanded[last_exp++] = exp;
1239                         passed[last_pass] = exp;
1240                 /*
1241                  *      Static strings
1242                  */
1243                 } else {
1244                         memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
1245                 }
1246
1247                 passed[last_pass + 1] = NULL;
1248
1249                 mod_s[total].mod_values = &(passed[last_pass]);
1250
1251                 last_pass += 2;
1252
1253                 switch (op) {
1254                 /*
1255                  *  T_OP_EQ is *NOT* supported, it is impossible to
1256                  *  support because of the lack of transactions in LDAP
1257                  */
1258                 case T_OP_ADD:
1259                         mod_s[total].mod_op = LDAP_MOD_ADD;
1260                         break;
1261
1262                 case T_OP_SET:
1263                         mod_s[total].mod_op = LDAP_MOD_REPLACE;
1264                         break;
1265
1266                 case T_OP_SUB:
1267                 case T_OP_CMP_FALSE:
1268                         mod_s[total].mod_op = LDAP_MOD_DELETE;
1269                         break;
1270
1271 #ifdef LDAP_MOD_INCREMENT
1272                 case T_OP_INCRM:
1273                         mod_s[total].mod_op = LDAP_MOD_INCREMENT;
1274                         break;
1275 #endif
1276                 default:
1277                         REDEBUG("Operator '%s' is not supported for LDAP modify operations",
1278                                 fr_int2str(fr_tokens, op, "<INVALID>"));
1279
1280                         goto error;
1281                 }
1282
1283                 /*
1284                  *      Now we know the value is ok, copy the pointers into
1285                  *      the ldapmod struct.
1286                  */
1287                 memcpy(&(mod_s[total].mod_type), &attr, sizeof(mod_s[total].mod_type));
1288
1289                 mod_p[total] = &(mod_s[total]);
1290                 total++;
1291         }
1292
1293         if (total == 0) {
1294                 rcode = RLM_MODULE_NOOP;
1295                 goto release;
1296         }
1297
1298         mod_p[total] = NULL;
1299
1300         conn = rlm_ldap_get_socket(inst, request);
1301         if (!conn) return RLM_MODULE_FAIL;
1302
1303
1304         dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1305         if (!dn || (rcode != RLM_MODULE_OK)) {
1306                 goto error;
1307         }
1308
1309         status = rlm_ldap_modify(inst, request, &conn, dn, modify);
1310         switch (status) {
1311         case LDAP_PROC_SUCCESS:
1312                 break;
1313
1314         case LDAP_PROC_REJECT:
1315         case LDAP_PROC_BAD_DN:
1316                 rcode = RLM_MODULE_INVALID;
1317                 break;
1318
1319         default:
1320                 rcode = RLM_MODULE_FAIL;
1321                 break;
1322         };
1323
1324         release:
1325         error:
1326         /*
1327          *      Free up any buffers we allocated for xlat expansion
1328          */
1329         for (i = 0; i < last_exp; i++) {
1330                 talloc_free(expanded[i]);
1331         }
1332
1333         rlm_ldap_release_socket(inst, conn);
1334
1335         return rcode;
1336 }
1337
1338 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
1339         ldap_instance_t *inst = instance;
1340
1341         if (inst->accounting) {
1342                 return user_modify(inst, request, inst->accounting);
1343         }
1344
1345         return RLM_MODULE_NOOP;
1346 }
1347
1348 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request)
1349 {
1350         ldap_instance_t *inst = instance;
1351
1352         if (inst->postauth) {
1353                 return user_modify(inst, request, inst->postauth);
1354         }
1355
1356         return RLM_MODULE_NOOP;
1357 }
1358
1359
1360 /* globally exported name */
1361 module_t rlm_ldap = {
1362         RLM_MODULE_INIT,
1363         "ldap",
1364         RLM_TYPE_THREAD_SAFE,   /* type: reserved        */
1365         sizeof(ldap_instance_t),
1366         module_config,
1367         mod_instantiate,        /* instantiation         */
1368         mod_detach,             /* detach                */
1369         {
1370                 mod_authenticate,       /* authentication        */
1371                 mod_authorize,          /* authorization         */
1372                 NULL,                   /* preaccounting         */
1373                 mod_accounting,         /* accounting            */
1374                 NULL,                   /* checksimul            */
1375                 NULL,                   /* pre-proxy             */
1376                 NULL,                   /* post-proxy            */
1377                 mod_post_auth           /* post-auth */
1378         },
1379 };