Added pairstrcpy()
[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 const FR_NAME_NUMBER ldap_scope[] = {
42         { "sub",        LDAP_SCOPE_SUB  },
43         { "one",        LDAP_SCOPE_ONE  },
44         { "base",       LDAP_SCOPE_BASE },
45         
46         {  NULL , -1 }
47 };
48
49 /*
50  *      TLS Configuration
51  */
52 static CONF_PARSER tls_config[] = {
53         {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, start_tls), NULL, "no"},
54         {"cacertfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertfile), NULL, NULL},
55         {"cacertdir", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertdir), NULL, NULL},
56         {"certfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_certfile), NULL, NULL},
57         {"keyfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_keyfile), NULL, NULL}, // OK if it changes on HUP
58         {"randfile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_randfile), NULL, NULL},
59         {"require_cert",PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
60
61         { NULL, -1, 0, NULL, NULL }
62 };
63
64
65 static CONF_PARSER profile_config[] = {
66         {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, profile_attr), NULL, NULL},
67         {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, default_profile), NULL, NULL},
68         {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, profile_filter), NULL, NULL},
69
70         { NULL, -1, 0, NULL, NULL }
71 };
72
73 /*
74  *      User configuration
75  */
76 static CONF_PARSER user_config[] = {
77         {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_filter), NULL, "(uid=%u)"},
78         {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_scope_str), NULL, "sub"},
79         {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_base_dn), NULL, NULL},
80         
81         {"access_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_access_attr), NULL, NULL},
82         {"access_positive", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, access_positive), NULL, "yes"},
83
84         { NULL, -1, 0, NULL, NULL }
85 };
86
87 /*
88  *      Group configuration
89  */
90 static CONF_PARSER group_config[] = {
91         {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_filter), NULL, NULL},
92         {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_scope_str), NULL, "sub"},
93         {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_base_dn), NULL, NULL},
94         
95         {"name_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_name_attr), NULL, "cn"},
96         {"membership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_membership_attr), NULL, NULL},
97         {"membership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_membership_filter), NULL, NULL},
98         {"cacheable_name", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_name), NULL, "no"},
99         {"cacheable_dn", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_dn), NULL, "no"},
100
101         { NULL, -1, 0, NULL, NULL }
102 };
103
104 /*
105  *      Reference for accounting updates
106  */
107 static const CONF_PARSER acct_section_config[] = {
108         {"reference", PW_TYPE_STRING_PTR, offsetof(ldap_acct_section_t, reference), NULL, "."},
109
110         {NULL, -1, 0, NULL, NULL}
111 };
112
113 /*
114  *      Various options that don't belong in the main configuration.
115  *
116  *      Note that these overlap a bit with the connection pool code!
117  */
118 static CONF_PARSER option_config[] = {
119         /*
120          *      Debugging flags to the server
121          */
122         {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance_t,ldap_debug), NULL, "0x0000"},
123
124         {"chase_referrals", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,chase_referrals), NULL, NULL},
125
126         {"rebind", PW_TYPE_BOOLEAN,offsetof(ldap_instance_t,rebind), NULL, NULL},
127
128         /* timeout on network activity */
129         {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,net_timeout), NULL, "10"},
130
131         /* timeout for search results */
132         {"res_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,res_timeout), NULL, "20"},
133
134         /* allow server unlimited time for search (server-side limit) */
135         {"srv_timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance_t,srv_timelimit), NULL, "20"},
136
137 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
138         {"idle", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_idle), NULL, "60"},
139 #endif
140 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
141         {"probes", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_probes), NULL, "3"},
142 #endif
143 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
144         {"interval", PW_TYPE_INTEGER,  offsetof(ldap_instance_t,keepalive_interval), NULL, "30"},
145 #endif
146
147         { NULL, -1, 0, NULL, NULL }
148 };
149
150
151 static const CONF_PARSER module_config[] = {
152         {"server", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(ldap_instance_t,server), NULL, "localhost"},
153         {"port", PW_TYPE_INTEGER, offsetof(ldap_instance_t,port), NULL, "389"},
154
155         {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,password), NULL, ""},
156         {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,admin_dn), NULL, ""},
157         
158         {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,base_dn), NULL, ""},
159         
160         {"valuepair_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, base_dn), NULL, NULL},
161
162 #ifdef WITH_EDIR
163         /* support for eDirectory Universal Password */
164         {"edir", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir), NULL, NULL}, /* NULL defaults to "no" */
165
166         /*
167          *      Attempt to bind with the Cleartext password we got from eDirectory
168          *      Universal password for additional authorization checks.
169          */
170         {"edir_autz", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
171 #endif
172
173         { "user", PW_TYPE_SUBSECTION, 0, NULL, (void const *) user_config },
174
175         { "group", PW_TYPE_SUBSECTION, 0, NULL, (void const *) group_config },
176         
177         { "profiles", PW_TYPE_SUBSECTION, 0, NULL, (void const *) profile_config },
178
179         { "options", PW_TYPE_SUBSECTION, 0, NULL, (void const *) option_config },
180
181         { "tls", PW_TYPE_SUBSECTION, 0, NULL, (void const *) tls_config },
182
183         {NULL, -1, 0, NULL, NULL}
184 };
185
186 /** Expand an LDAP URL into a query, and return a string result from that query.
187  *
188  */
189 static size_t ldap_xlat(void *instance, REQUEST *request, char const *fmt,
190                         char *out, size_t freespace)
191 {
192         ldap_rcode_t status;
193         size_t length = 0;
194         ldap_instance_t *inst = instance;
195         LDAPURLDesc *ldap_url;
196         LDAPMessage *result = NULL;
197         LDAPMessage *entry = NULL;
198         char **vals;
199         ldap_handle_t *conn;
200         int ldap_errno;
201         char const *url;
202         char const **attrs;
203
204         url = fmt;
205
206         if (!ldap_is_ldap_url(url)) {
207                 REDEBUG("String passed does not look like an LDAP URL");
208                 return 0;
209         }
210
211         if (ldap_url_parse(url, &ldap_url)){
212                 REDEBUG("Parsing LDAP URL failed");
213                 return 0;
214         }
215
216         /*
217          *      Nothing, empty string, "*" string, or got 2 things, die.
218          */
219         if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
220             !*ldap_url->lud_attrs[0] ||
221             (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
222             ldap_url->lud_attrs[1]) {
223                 REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
224                        
225                 goto free_urldesc;
226         }
227
228         if (ldap_url->lud_host && 
229             ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
230              (ldap_url->lud_port != inst->port))) {
231                 RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
232                 
233                 goto free_urldesc;
234         }
235
236         conn = rlm_ldap_get_socket(inst, request);
237         if (!conn) goto free_urldesc;
238
239         memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
240         
241         status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
242                                  attrs, &result);
243         switch (status) {
244                 case LDAP_PROC_SUCCESS:
245                         break;
246                 case LDAP_PROC_NO_RESULT:
247                         RDEBUG("Search returned not found");
248                 default:
249                         goto free_socket;
250         }
251
252         rad_assert(conn);
253         rad_assert(result);
254
255         entry = ldap_first_entry(conn->handle, result);
256         if (!entry) {
257                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
258                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
259                 goto free_result;
260         }
261
262         vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
263         if (!vals) {
264                 RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
265                 goto free_result;
266         }
267
268         length = strlen(vals[0]);
269         if (length >= freespace){
270
271                 goto free_vals;
272         }
273
274         strlcpy(out, vals[0], freespace);
275
276 free_vals:
277         ldap_value_free(vals);
278 free_result:
279         ldap_msgfree(result);
280 free_socket:
281         rlm_ldap_release_socket(inst, conn);
282 free_urldesc:
283         ldap_free_urldesc(ldap_url);
284
285         return length;
286 }
287
288 /** Perform LDAP-Group comparison checking
289  *
290  * Attempts to match users to groups using a variety of methods.
291  *
292  * @param instance of the rlm_ldap module.
293  * @param request Current request.
294  * @param thing Unknown.
295  * @param check Which group to check for user membership.
296  * @param check_pairs Unknown.
297  * @param reply_pairs Unknown.
298  * @return 1 on failure (or if the user is not a member), else 0.
299  */
300 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
301                              UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
302 {
303         ldap_instance_t *inst = instance;
304         rlm_rcode_t     rcode;
305         
306         int             found = false;
307         int             check_is_dn;
308
309         ldap_handle_t   *conn = NULL;
310         char const      *user_dn;
311         
312         RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
313
314         if (check->length == 0) {
315                 RDEBUG("Cannot do comparison (group name is empty)");
316                 return 1;
317         }
318
319         /*
320          *      Check if we can do cached membership verification
321          */
322         check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
323         if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
324                 switch(rlm_ldap_check_cached(inst, request, check)) {
325                         case RLM_MODULE_NOTFOUND:
326                                 break;
327                         case RLM_MODULE_OK:
328                                 found = true;
329                         default:
330                                 goto finish;
331                 }
332         }
333
334         conn = rlm_ldap_get_socket(inst, request);
335         if (!conn) return 1;
336
337         /*
338          *      This is used in the default membership filter.
339          */
340         user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
341         if (!user_dn) {
342                 rlm_ldap_release_socket(inst, conn);
343                 return 1;
344         }
345
346         rad_assert(conn);
347
348         /*
349          *      Check groupobj user membership
350          */
351         if (inst->groupobj_membership_filter) {
352                 switch(rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) {
353                         case RLM_MODULE_NOTFOUND:
354                                 break;
355                         case RLM_MODULE_OK:
356                                 found = true;
357                         default:
358                                 goto finish;
359                 }
360         }
361         
362         rad_assert(conn);
363
364         /*
365          *      Check userobj group membership
366          */
367         if (inst->userobj_membership_attr) {
368                 switch(rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) {
369                         case RLM_MODULE_NOTFOUND:
370                                 break;
371                         case RLM_MODULE_OK:
372                                 found = true;
373                         default:
374                                 goto finish;
375                 }
376         }
377         
378         rad_assert(conn);
379         
380         finish:
381         if (conn) {
382                 rlm_ldap_release_socket(inst, conn);
383         }
384         
385         if (!found) {
386                 RDEBUG("User is not a member of specified group");
387                 
388                 return 1;
389         }
390
391         return 0;
392 }
393
394 /** Detach from the LDAP server and cleanup internal state.
395  *
396  */
397 static int mod_detach(void *instance)
398 {
399         ldap_instance_t *inst = instance;
400         
401         fr_connection_pool_delete(inst->pool);
402
403         if (inst->user_map) {
404                 talloc_free(inst->user_map);
405         }
406
407         return 0;
408 }
409
410 /** Parse an accounting sub section.
411  *
412  * Allocate a new ldap_acct_section_t and write the config data into it.
413  *
414  * @param[in] inst rlm_ldap configuration.
415  * @param[in] parent of the config section.
416  * @param[out] config to write the sub section parameters to.
417  * @param[in] comp The section name were parsing the config for.
418  * @return 0 on success, else < 0 on failure.
419  */
420 static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
421                              rlm_components_t comp)
422 {
423         CONF_SECTION *cs;
424
425         char const *name = section_type_value[comp].section;
426         
427         cs = cf_section_sub_find(parent, name);
428         if (!cs) {
429                 INFO("rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
430                        "from this section", inst->xlat_name, name);
431                 
432                 return 0;
433         }
434         
435         *config = talloc_zero(inst, ldap_acct_section_t);
436         if (cf_section_parse(cs, *config, acct_section_config) < 0) {
437                 LDAP_ERR("Failed parsing configuration for section %s", name);
438                 
439                 return -1;
440         }
441                 
442         (*config)->cs = cs;
443
444         return 0;
445 }
446
447 /** Instantiate the module
448  * 
449  * Creates a new instance of the module reading parameters from a configuration section.
450  *
451  * @param conf to parse.
452  * @param instance Where to write pointer to configuration data.
453  * @return 0 on success < 0 on failure.
454  */
455 static int mod_instantiate(CONF_SECTION *conf, void *instance)
456 {
457         ldap_instance_t *inst = instance;
458
459         inst->cs = conf;
460
461         inst->chase_referrals = 2; /* use OpenLDAP defaults */
462         inst->rebind = 2;
463         
464         inst->xlat_name = cf_section_name2(conf);
465         if (!inst->xlat_name) {
466                 inst->xlat_name = cf_section_name1(conf);
467         }
468
469         /*
470          *      If the configuration parameters can't be parsed, then fail.
471          */
472         if ((parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
473             (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
474                 LDAP_ERR("Failed parsing configuration");
475                 
476                 goto error;
477         }
478
479         /*
480          *      Sanity checks for cacheable groups code.
481          */
482         if (inst->cacheable_group_name && inst->groupobj_membership_filter && !inst->groupobj_name_attr) {
483                 LDAP_ERR("Directive 'group.name_attribute' must be set if cacheable group names are enabled");
484                 
485                 goto error;
486         }
487
488         /*
489          *      Copy across values from base_dn to the object specific base_dn.
490          */
491         if (!inst->groupobj_base_dn) {
492                 if (!inst->base_dn) {
493                         LDAP_ERR("Must set 'base_dn' if there is no 'group_base_dn'");
494                         
495                         goto error;
496                 }
497                 
498                 inst->groupobj_base_dn = inst->base_dn;
499         }
500
501         if (!inst->userobj_base_dn) {
502                 if (!inst->base_dn) {
503                         LDAP_ERR("Must set 'base_dn' if there is no 'userobj_base_dn'");
504                         
505                         goto error;
506                 }
507                 
508                 inst->userobj_base_dn = inst->base_dn;
509         }
510         
511         /*
512          *      Check for URLs.  If they're used and the library doesn't support them, then complain.
513          */
514         inst->is_url = 0;
515         if (ldap_is_ldap_url(inst->server)) {
516 #ifdef HAVE_LDAP_INITIALIZE
517                 inst->is_url = 1;
518                 inst->port = 0;
519 #else
520                 LDAP_ERR("'server' directive is in URL form but ldap_initialize() is not available");
521                 goto error;
522 #endif
523         }
524
525         /*
526          *      Workaround for servers which support LDAPS but not START TLS
527          */
528         if (inst->port == LDAPS_PORT || inst->tls_mode) {
529                 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
530         } else {
531                 inst->tls_mode = 0;
532         }
533
534 #if LDAP_SET_REBIND_PROC_ARGS != 3
535         /*
536          *      The 2-argument rebind doesn't take an instance variable.  Our rebind function needs the instance
537          *      variable for the username, password, etc.
538          */
539         if (inst->rebind == 1) {
540                 LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API "
541                          "that we need");
542                          
543                 goto error;
544         }
545 #endif
546
547         /*
548          *      Convert scope strings to integers
549          */
550         inst->userobj_scope = fr_str2int(ldap_scope, inst->userobj_scope_str, -1);
551         if (inst->userobj_scope < 0) {
552                 LDAP_ERR("Invalid 'user.scope' value '%s', expected 'sub', 'one' or 'base'",
553                          inst->userobj_scope_str);
554                 goto error;
555         }
556         
557         inst->groupobj_scope = fr_str2int(ldap_scope, inst->groupobj_scope_str, -1);
558         if (inst->groupobj_scope < 0) {
559                 LDAP_ERR("Invalid 'group.scope' value '%s', expected 'sub', 'one' or 'base'",
560                          inst->groupobj_scope_str);
561                 goto error;
562         }
563
564         /*
565          *      Build the attribute map
566          */
567         if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
568                 goto error;
569         }
570
571         /*
572          *      Group comparison checks.
573          */
574         inst->group_da = dict_attrbyvalue(PW_LDAP_GROUP, 0);
575         paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, rlm_ldap_groupcmp, inst);     
576         if (cf_section_name2(conf)) {
577                 ATTR_FLAGS flags;
578                 char buffer[256];
579
580                 snprintf(buffer, sizeof(buffer), "%s-Ldap-Group",
581                          inst->xlat_name);
582                 memset(&flags, 0, sizeof(flags));
583
584                 dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
585                 inst->group_da = dict_attrbyname(buffer);
586                 if (!inst->group_da) {
587                         LDAP_ERR("Failed creating attribute %s", buffer);
588                         
589                         goto error;
590                 }
591                 
592
593                 paircompare_register(inst->group_da->attr, PW_USER_NAME, rlm_ldap_groupcmp, inst);
594         }
595
596         xlat_register(inst->xlat_name, ldap_xlat, rlm_ldap_escape_func, inst);
597
598         /*
599          *      Initialize the socket pool.
600          */
601         inst->pool = fr_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, mod_conn_delete, NULL);
602         if (!inst->pool) {
603                 return -1;
604         }
605         
606         return 0;
607
608 error:
609         return -1;
610 }
611
612 /** Check the user's password against ldap directory
613  * 
614  * @param instance rlm_ldap configuration.
615  * @param request Current request.
616  * @return one of the RLM_MODULE_* values.
617  */
618 static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
619 {
620         rlm_rcode_t     rcode;
621         ldap_rcode_t    status;
622         char const      *dn;
623         ldap_instance_t *inst = instance;
624         ldap_handle_t   *conn;
625
626         /*
627          * Ensure that we're being passed a plain-text password, and not
628          * anything else.
629          */
630
631         if (!request->username) {
632                 REDEBUG("Attribute \"User-Name\" is required for authentication");
633
634                 return RLM_MODULE_INVALID;
635         }
636
637         if (!request->password ||
638             (request->password->da->attr != PW_USER_PASSWORD)) {
639                 RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere.");
640                 RWDEBUG("*********************************************");
641                 RWDEBUG("* THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
642                 RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING.");
643                 RWDEBUG("*********************************************");
644                 
645                 REDEBUG("Attribute \"User-Password\" is required for authentication.");
646                 
647                 return RLM_MODULE_INVALID;
648         }
649
650         if (request->password->length == 0) {
651                 REDEBUG("Empty password supplied");
652                 
653                 return RLM_MODULE_INVALID;
654         }
655
656         RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
657
658         conn = rlm_ldap_get_socket(inst, request);
659         if (!conn) return RLM_MODULE_FAIL;
660
661         /*
662          *      Get the DN by doing a search.
663          */
664         dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
665         if (!dn) {
666                 rlm_ldap_release_socket(inst, conn);
667                 
668                 return rcode;
669         }
670
671         /*
672          *      Bind as the user
673          */
674         conn->rebound = true;
675         status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, true);
676         switch (status) {
677         case LDAP_PROC_SUCCESS:
678                 rcode = RLM_MODULE_OK;
679                 RDEBUG("Bind as user \"%s\" was successful", dn);
680                 
681                 break;
682         case LDAP_PROC_NOT_PERMITTED:
683                 rcode = RLM_MODULE_USERLOCK;
684                 
685                 break;
686         case LDAP_PROC_REJECT:
687                 rcode = RLM_MODULE_REJECT;
688                 
689                 break;
690         case LDAP_PROC_BAD_DN:
691                 rcode = RLM_MODULE_INVALID;
692                 
693                 break;
694         case LDAP_PROC_NO_RESULT:
695                 rcode = RLM_MODULE_NOTFOUND;
696                 
697                 break;
698         default:
699                 rcode = RLM_MODULE_FAIL;
700                 break;
701         };
702
703         rlm_ldap_release_socket(inst, conn);
704         
705         return rcode;
706 }
707
708 /** Check if user is authorized for remote access
709  *
710  */
711 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
712 {
713         rlm_rcode_t     rcode = RLM_MODULE_OK;
714         ldap_rcode_t    status;
715         int             ldap_errno;
716         int             i;
717         ldap_instance_t *inst = instance;
718         char            **vals;
719         VALUE_PAIR      *vp;
720         ldap_handle_t   *conn;
721         LDAPMessage     *result, *entry;
722         char const      *dn = NULL;
723         rlm_ldap_map_xlat_t     expanded; /* faster that mallocing every time */
724         
725         if (!request->username) {
726                 RDEBUG2("Attribute \"User-Name\" is required for authorization.");
727                 
728                 return RLM_MODULE_NOOP;
729         }
730
731         /*
732          *      Check for valid input, zero length names not permitted
733          */
734         if (request->username->length == 0) {
735                 RDEBUG2("Zero length username not permitted");
736                 
737                 return RLM_MODULE_INVALID;
738         }
739
740         if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
741                 return RLM_MODULE_FAIL;
742         }
743         
744         conn = rlm_ldap_get_socket(inst, request);
745         if (!conn) return RLM_MODULE_FAIL;
746         
747         /*
748          *      Add any additional attributes we need for checking access, memberships, and profiles
749          */
750         if (inst->userobj_access_attr) {
751                 expanded.attrs[expanded.count++] = inst->userobj_access_attr;
752         }
753
754         if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) {
755                 expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
756         }
757         
758         if (inst->profile_attr) {
759                 expanded.attrs[expanded.count++] = inst->profile_attr;
760         }
761         
762         if (inst->valuepair_attr) {
763                 expanded.attrs[expanded.count++] = inst->valuepair_attr;
764         }
765         
766         expanded.attrs[expanded.count] = NULL;
767         
768         dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode);
769         if (!dn) {
770                 goto finish;                    
771         }
772
773         entry = ldap_first_entry(conn->handle, result);
774         if (!entry) {
775                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
776                 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
777                          
778                 goto finish;
779         }
780
781         /*
782          *      Check for access.
783          */
784         if (inst->userobj_access_attr) {
785                 rcode = rlm_ldap_check_access(inst, request, conn, entry);
786                 if (rcode != RLM_MODULE_OK) {
787                         goto finish;
788                 }
789         }
790         
791         /*
792          *      Check if we need to cache group memberships
793          */
794         if (inst->cacheable_group_dn || inst->cacheable_group_name) {
795                 rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry);
796                 if (rcode != RLM_MODULE_OK) {
797                         goto finish;
798                 }
799                 
800                 rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
801                 if (rcode != RLM_MODULE_OK) {
802                         goto finish;
803                 }
804         }
805
806 #ifdef WITH_EDIR
807         /*
808          *      We already have a Cleartext-Password.  Skip edir.
809          */
810         if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
811                 goto skip_edir;
812         }
813
814         /*
815          *      Retrieve Universal Password if we use eDirectory
816          */
817         if (inst->edir) {
818                 int res = 0;
819                 char password[256];
820                 size_t pass_size = sizeof(password);
821
822                 /*
823                  *      Retrive universal password
824                  */
825                 res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
826                 if (res != 0) {
827                         RWDEBUG("Failed to retrieve eDirectory password");
828                         rcode = RLM_MODULE_NOOP;
829
830                         goto finish;
831                 }
832
833                 /*
834                  *      Add Cleartext-Password attribute to the request
835                  */
836                 vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
837                 pairstrcpy(vp, password);
838                 vp->length = pass_size;
839                 
840                 RDEBUG2("Added eDirectory password in check items as %s = %s", vp->da->name, vp->vp_strvalue);
841                         
842                 if (inst->edir_autz) {
843                         RDEBUG2("Binding as user for eDirectory authorization checks");
844                         /*
845                          *      Bind as the user
846                          */
847                         conn->rebound = true;
848                         status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true);
849                         switch (status) {
850                         case LDAP_PROC_SUCCESS:
851                                 rcode = RLM_MODULE_OK;
852                                 RDEBUG("Bind as user \"%s\" was successful", dn);
853                                 
854                                 break;
855                         case LDAP_PROC_NOT_PERMITTED:
856                                 rcode = RLM_MODULE_USERLOCK;
857                                 
858                                 goto finish;
859                         case LDAP_PROC_REJECT:
860                                 rcode = RLM_MODULE_REJECT;
861                                 
862                                 goto finish;
863                         case LDAP_PROC_BAD_DN:
864                                 rcode = RLM_MODULE_INVALID;
865                                 
866                                 goto finish;
867                         case LDAP_PROC_NO_RESULT:
868                                 rcode = RLM_MODULE_NOTFOUND;
869                                 
870                                 goto finish;
871                         default:
872                                 rcode = RLM_MODULE_FAIL;
873                                 
874                                 goto finish;
875                         };
876                 }
877         }
878
879 skip_edir:
880 #endif
881
882         /*
883          *      Apply ONE user profile, or a default user profile.
884          */
885         vp = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
886         if (vp || inst->default_profile) {
887                 char const *profile = inst->default_profile;
888
889                 if (vp) profile = vp->vp_strvalue;
890
891                 rlm_ldap_map_profile(inst, request, &conn, profile, &expanded);
892         }
893
894         /*
895          *      Apply a SET of user profiles.
896          */
897         if (inst->profile_attr) {
898                 vals = ldap_get_values(conn->handle, entry, inst->profile_attr);
899                 if (vals != NULL) {
900                         for (i = 0; vals[i] != NULL; i++) {
901                                 rlm_ldap_map_profile(inst, request, &conn, vals[i], &expanded);
902                         }
903         
904                         ldap_value_free(vals);
905                 }
906         }
907
908         if (inst->user_map) {
909                 rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
910                 rlm_ldap_check_reply(inst, request);
911         }
912         
913 finish:
914         rlm_ldap_map_xlat_free(&expanded);
915         if (result) {
916                 ldap_msgfree(result);
917         }
918         rlm_ldap_release_socket(inst, conn);
919
920         return rcode;
921 }
922
923 /** Modify user's object in LDAP
924  *
925  * Process a modifcation map to update a user object in the LDAP directory.
926  *
927  * @param inst rlm_ldap instance.
928  * @param request Current request.
929  * @param section that holds the map to process.
930  * @return one of the RLM_MODULE_* values.
931  */
932 static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
933 {
934         rlm_rcode_t     rcode = RLM_MODULE_OK;
935         
936         ldap_handle_t   *conn = NULL;
937         
938         LDAPMod         *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
939         LDAPMod         **modify = mod_p;
940         
941         char            *passed[LDAP_MAX_ATTRMAP * 2];
942         int             i, total = 0, last_pass = 0;
943         
944         char            *expanded[LDAP_MAX_ATTRMAP];
945         int             last_exp = 0;
946         
947         char const      *attr;
948         char const      *value;
949         
950         char const      *dn;
951         /*
952          *      Build our set of modifications using the update sections in
953          *      the config.
954          */
955         CONF_ITEM       *ci;
956         CONF_PAIR       *cp;
957         CONF_SECTION    *cs;
958         FR_TOKEN        op;
959         char            path[MAX_STRING_LEN];
960         
961         char            *p = path;
962
963         rad_assert(section);
964         
965         /*
966          *      Locate the update section were going to be using
967          */
968         if (section->reference[0] != '.') {
969                 *p++ = '.';
970         }
971         
972         if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) {
973                 goto error;     
974         }
975
976         ci = cf_reference_item(NULL, section->cs, path);
977         if (!ci) {
978                 goto error;     
979         }
980         
981         if (!cf_item_is_section(ci)){
982                 REDEBUG("Reference must resolve to a section");
983                 
984                 goto error;     
985         }
986         
987         cs = cf_section_sub_find(cf_itemtosection(ci), "update");
988         if (!cs) {
989                 REDEBUG("Section must contain 'update' subsection");
990                 
991                 goto error;
992         }
993         
994         /*
995          *      Iterate over all the pairs, building our mods array
996          */
997         for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
998                 int do_xlat = false;
999                 
1000                 if (total == LDAP_MAX_ATTRMAP) {
1001                         REDEBUG("Modify map size exceeded");
1002         
1003                         goto error;
1004                 }
1005                 
1006                 if (!cf_item_is_pair(ci)) {
1007                         REDEBUG("Entry is not in \"ldap-attribute = value\" format");
1008                                
1009                         goto error;
1010                 }
1011         
1012                 /*
1013                  *      Retrieve all the information we need about the pair
1014                  */
1015                 cp = cf_itemtopair(ci);
1016                 value = cf_pair_value(cp);
1017                 attr = cf_pair_attr(cp);
1018                 op = cf_pair_operator(cp);
1019                 
1020                 if (!value || (*value == '\0')) {
1021                         RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
1022                         
1023                         continue;
1024                 }
1025
1026                 switch (cf_pair_value_type(cp))
1027                 {
1028                         case T_BARE_WORD:
1029                         case T_SINGLE_QUOTED_STRING:
1030                         break;
1031                         case T_BACK_QUOTED_STRING:
1032                         case T_DOUBLE_QUOTED_STRING:
1033                                 do_xlat = true;         
1034                         break;
1035                         default:
1036                                 rad_assert(0);
1037                                 goto error;
1038                 }
1039                 
1040                 if (op == T_OP_CMP_FALSE) {
1041                         passed[last_pass] = NULL;
1042                 } else if (do_xlat) {
1043                         char *exp = NULL;
1044                         
1045                         if (radius_xlat(exp, 0, request, value, NULL, NULL) <= 0) {
1046                                 RDEBUG("Skipping attribute \"%s\"", attr);
1047                                        
1048                                 talloc_free(exp);
1049                                 
1050                                 continue;
1051                         }
1052                         
1053                         expanded[last_exp++] = exp;
1054                         passed[last_pass] = exp;
1055                 /* 
1056                  *      Static strings
1057                  */
1058                 } else {
1059                         memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
1060                 }
1061                 
1062                 passed[last_pass + 1] = NULL;
1063                 
1064                 mod_s[total].mod_values = &(passed[last_pass]);
1065                                         
1066                 last_pass += 2;
1067                 
1068                 switch (op)
1069                 {
1070                 /*
1071                  *  T_OP_EQ is *NOT* supported, it is impossible to
1072                  *  support because of the lack of transactions in LDAP
1073                  */
1074                 case T_OP_ADD:
1075                         mod_s[total].mod_op = LDAP_MOD_ADD;
1076                         break;
1077
1078                 case T_OP_SET:
1079                         mod_s[total].mod_op = LDAP_MOD_REPLACE;
1080                         break;
1081
1082                 case T_OP_SUB:
1083                 case T_OP_CMP_FALSE:
1084                         mod_s[total].mod_op = LDAP_MOD_DELETE;
1085                         break;
1086
1087 #ifdef LDAP_MOD_INCREMENT
1088                 case T_OP_INCRM:
1089                         mod_s[total].mod_op = LDAP_MOD_INCREMENT;
1090                         break;
1091 #endif
1092                 default:
1093                         REDEBUG("Operator '%s' is not supported for LDAP modify operations",
1094                                 fr_int2str(fr_tokens, op, "¿unknown?"));
1095                                
1096                         goto error;
1097                 }
1098                 
1099                 /*
1100                  *      Now we know the value is ok, copy the pointers into
1101                  *      the ldapmod struct.
1102                  */
1103                 memcpy(&(mod_s[total].mod_type), &(attr), sizeof(mod_s[total].mod_type));
1104                 
1105                 mod_p[total] = &(mod_s[total]);
1106                 total++;
1107         }
1108         
1109         if (total == 0) {
1110                 rcode = RLM_MODULE_NOOP;
1111                 goto release;
1112         }
1113         
1114         mod_p[total] = NULL;
1115         
1116         conn = rlm_ldap_get_socket(inst, request);
1117         if (!conn) return RLM_MODULE_FAIL;
1118
1119
1120         dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
1121         if (!dn || (rcode != RLM_MODULE_OK)) {
1122                 goto error;
1123         }
1124         
1125         rcode = rlm_ldap_modify(inst, request, &conn, dn, modify);
1126         
1127         release:
1128         error:
1129         /*
1130          *      Free up any buffers we allocated for xlat expansion
1131          */     
1132         for (i = 0; i < last_exp; i++) {
1133                 talloc_free(expanded[i]);
1134         }
1135
1136         rlm_ldap_release_socket(inst, conn);
1137         
1138         return rcode;
1139 }
1140
1141 static rlm_rcode_t mod_accounting(void *instance, REQUEST * request) {
1142         ldap_instance_t *inst = instance;               
1143
1144         if (inst->accounting) {
1145                 return user_modify(inst, request, inst->accounting); 
1146         }
1147         
1148         return RLM_MODULE_NOOP;
1149 }
1150
1151 static rlm_rcode_t mod_post_auth(void *instance, REQUEST * request)
1152 {
1153         ldap_instance_t *inst = instance;
1154
1155         if (inst->postauth) {
1156                 return user_modify(inst, request, inst->postauth); 
1157         }
1158
1159         return RLM_MODULE_NOOP;
1160 }
1161
1162
1163 /* globally exported name */
1164 module_t rlm_ldap = {
1165         RLM_MODULE_INIT,
1166         "ldap",
1167         RLM_TYPE_THREAD_SAFE,   /* type: reserved        */
1168         sizeof(ldap_instance_t),
1169         module_config,
1170         mod_instantiate,        /* instantiation         */
1171         mod_detach,             /* detach                */
1172         {
1173                 mod_authenticate,       /* authentication        */
1174                 mod_authorize,          /* authorization         */
1175                 NULL,                   /* preaccounting         */
1176                 mod_accounting,         /* accounting            */
1177                 NULL,                   /* checksimul            */
1178                 NULL,                   /* pre-proxy             */
1179                 NULL,                   /* post-proxy            */
1180                 mod_post_auth           /* post-auth */
1181         },
1182 };