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