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