Pass msg_id into ldap_result
[freeradius.git] / src / modules / rlm_ldap / rlm_ldap.c
1 /*
2  * rlm_ldap.c   LDAP authorization and authentication module.
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  *   Copyright 1999-2012 The FreeRADIUS Server Project.
19  *
20  *   Copyright 2012 Alan DeKok <aland@freeradius.org>
21  *   Copyright 2012 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include        <freeradius-devel/radiusd.h>
28 #include        <freeradius-devel/modules.h>
29 #include        <freeradius-devel/rad_assert.h>
30
31 #include        <stdarg.h>
32 #include        <ctype.h>
33
34 #include        <lber.h>
35 #include        <ldap.h>
36
37 #define MAX_ATTRMAP             128
38 #define MAX_ATTR_STR_LEN        256
39 #define MAX_FILTER_STR_LEN      1024
40
41 #ifdef WITH_EDIR
42 extern int nmasldap_get_password(LDAP *ld,char *objectDN, char *pwd, size_t *pwdSize);
43
44 #endif
45
46 typedef struct ldap_acct_section {
47         CONF_SECTION    *cs;
48         
49         const char *reference;
50 } ldap_acct_section_t;
51
52
53 typedef struct {
54         CONF_SECTION    *cs;
55         fr_connection_pool_t *pool;
56
57         char            *server;
58         int             port;
59
60         char            *login;
61         char            *password;
62
63         char            *filter;
64         char            *basedn;
65
66         int             chase_referrals;
67         int             rebind;
68
69         int             ldap_debug; /* Debug flag for LDAP SDK */
70
71         const char      *xlat_name; /* name used to xlat */
72
73         int             expect_password;
74         
75         /*
76          *      RADIUS attribute to LDAP attribute maps
77          */
78         VALUE_PAIR_MAP  *user_map;      /* Applied to users and profiles */
79         
80         /*
81          *      Access related configuration
82          */
83         char            *access_attr;
84         int             positive_access_attr;
85
86         /*
87          *      Profiles
88          */
89         char            *base_filter;
90         char            *default_profile;
91         char            *profile_attr;
92
93         /*
94          *      Group checking.
95          */
96         char            *groupname_attr;
97         char            *groupmemb_filter;
98         char            *groupmemb_attr;
99         
100         /*
101          *      Accounting
102          */
103         ldap_acct_section_t *postauth;
104         ldap_acct_section_t *accounting;
105
106         /*
107          *      TLS items.  We should really normalize these with the
108          *      TLS code in 3.0.
109          */
110         int             tls_mode;
111         int             start_tls;
112         char            *tls_cacertfile;
113         char            *tls_cacertdir;
114         char            *tls_certfile;
115         char            *tls_keyfile;
116         char            *tls_randfile;
117         char            *tls_require_cert;
118
119         /*
120          *      Options
121          */
122         int             timelimit;
123         int             net_timeout;
124         int             timeout;
125         int             is_url;
126
127 #ifdef WITH_EDIR
128         /*
129          *      eDir support
130          */
131         int             edir;
132         int             edir_autz;
133 #endif
134         /*
135          *      For keep-alives.
136          */
137 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
138         int             keepalive_idle;
139 #endif
140 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
141         int             keepalive_probes;
142 #endif
143 #ifdef LDAP_OPT_ERROR_NUMBER
144         int             keepalive_interval;
145 #endif
146
147 }  ldap_instance;
148
149 /* The default setting for TLS Certificate Verification */
150 #define TLS_DEFAULT_VERIFY "allow"
151
152 /*
153  *      TLS Configuration
154  */
155 static CONF_PARSER tls_config[] = {
156         {"start_tls", PW_TYPE_BOOLEAN,
157          offsetof(ldap_instance,start_tls), NULL, "no"},
158         {"cacertfile", PW_TYPE_FILENAME,
159          offsetof(ldap_instance,tls_cacertfile), NULL, NULL},
160         {"cacertdir", PW_TYPE_FILENAME,
161          offsetof(ldap_instance,tls_cacertdir), NULL, NULL},
162         {"certfile", PW_TYPE_FILENAME,
163          offsetof(ldap_instance,tls_certfile), NULL, NULL},
164         {"keyfile", PW_TYPE_FILENAME,
165          offsetof(ldap_instance,tls_keyfile), NULL, NULL},
166         {"randfile", PW_TYPE_STRING_PTR, /* OK if it changes on HUP */
167          offsetof(ldap_instance,tls_randfile), NULL, NULL},
168         {"require_cert", PW_TYPE_STRING_PTR,
169          offsetof(ldap_instance,tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
170         { NULL, -1, 0, NULL, NULL }
171 };
172
173
174 static CONF_PARSER attr_config[] = {
175         /*
176          *      Access limitations
177          */
178         /* LDAP attribute name that controls remote access */
179         {"access_attr", PW_TYPE_STRING_PTR,
180          offsetof(ldap_instance,access_attr), NULL, NULL},
181         {"positive_access_attr", PW_TYPE_BOOLEAN,
182          offsetof(ldap_instance,positive_access_attr), NULL, "yes"},
183
184         {"base_filter", PW_TYPE_STRING_PTR,
185          offsetof(ldap_instance,base_filter), NULL,
186          "(objectclass=radiusprofile)"},
187         {"default_profile", PW_TYPE_STRING_PTR,
188          offsetof(ldap_instance,default_profile), NULL, NULL},
189         {"profile_attribute", PW_TYPE_STRING_PTR,
190          offsetof(ldap_instance,profile_attr), NULL, NULL},
191
192         { NULL, -1, 0, NULL, NULL }
193 };
194
195
196 /*
197  *      Group configuration
198  */
199 static CONF_PARSER group_config[] = {
200         /*
201          *      Group checks.  These could probably be done
202          *      via dynamic xlat's.
203          */
204         {"name_attribute", PW_TYPE_STRING_PTR,
205          offsetof(ldap_instance,groupname_attr), NULL, "cn"},
206         {"membership_filter", PW_TYPE_STRING_PTR,
207          offsetof(ldap_instance,groupmemb_filter), NULL,
208          "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))"
209          "(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"},
210         {"membership_attribute", PW_TYPE_STRING_PTR,
211          offsetof(ldap_instance,groupmemb_attr), NULL, NULL},
212
213
214         { NULL, -1, 0, NULL, NULL }
215 };
216
217 /*
218  *      Reference for accounting updates
219  */
220 static const CONF_PARSER acct_section_config[] = {
221         {"reference", PW_TYPE_STRING_PTR,
222           offsetof(ldap_acct_section_t, reference), NULL, "."},
223         {NULL, -1, 0, NULL, NULL}
224 };
225
226 /*
227  *      Various options that don't belong in the main configuration.
228  *
229  *      Note that these overlap a bit with the connection pool code!
230  */
231 static CONF_PARSER option_config[] = {
232         /*
233          *      Debugging flags to the server
234          */
235         {"ldap_debug", PW_TYPE_INTEGER,
236          offsetof(ldap_instance,ldap_debug), NULL, "0x0000"},
237
238         {"chase_referrals", PW_TYPE_BOOLEAN,
239          offsetof(ldap_instance,chase_referrals), NULL, NULL},
240
241         {"rebind", PW_TYPE_BOOLEAN,
242          offsetof(ldap_instance,rebind), NULL, NULL},
243
244         /* timeout on network activity */
245         {"net_timeout", PW_TYPE_INTEGER,
246          offsetof(ldap_instance,net_timeout), NULL, "10"},
247
248         /* timeout for search results */
249         {"timeout", PW_TYPE_INTEGER,
250          offsetof(ldap_instance,timeout), NULL, "20"},
251
252         /* allow server unlimited time for search (server-side limit) */
253         {"timelimit", PW_TYPE_INTEGER,
254          offsetof(ldap_instance,timelimit), NULL, "20"},
255
256 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
257         {"idle", PW_TYPE_INTEGER,
258          offsetof(ldap_instance,keepalive_idle), NULL, "60"},
259 #endif
260 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
261         {"probes", PW_TYPE_INTEGER,
262          offsetof(ldap_instance,keepalive_probes), NULL, "3"},
263 #endif
264 #ifdef LDAP_OPT_ERROR_NUMBER
265         {"interval", PW_TYPE_INTEGER, 
266          offsetof(ldap_instance,keepalive_interval), NULL, "30"},
267 #endif
268         { NULL, -1, 0, NULL, NULL }
269 };
270
271
272 static const CONF_PARSER module_config[] = {
273         {"server", PW_TYPE_STRING_PTR,
274          offsetof(ldap_instance,server), NULL, "localhost"},
275         {"port", PW_TYPE_INTEGER,
276          offsetof(ldap_instance,port), NULL, "389"},
277
278         {"password", PW_TYPE_STRING_PTR,
279          offsetof(ldap_instance,password), NULL, ""},
280         {"identity", PW_TYPE_STRING_PTR,
281          offsetof(ldap_instance,login), NULL, ""},
282
283         /*
284          *      DN's and filters.
285          */
286         {"basedn", PW_TYPE_STRING_PTR,
287          offsetof(ldap_instance,basedn), NULL, "o=notexist"},
288
289         {"filter", PW_TYPE_STRING_PTR,
290          offsetof(ldap_instance,filter), NULL, "(uid=%u)"},
291
292         /* turn off the annoying warning if we don't expect a password */
293         {"expect_password", PW_TYPE_BOOLEAN,
294          offsetof(ldap_instance,expect_password), NULL, "yes"},
295          
296 #ifdef WITH_EDIR
297         /* support for eDirectory Universal Password */
298         {"edir", PW_TYPE_BOOLEAN,
299          offsetof(ldap_instance,edir), NULL, NULL}, /* NULL defaults to "no" */
300
301         /*
302          * Attempt to bind with the Cleartext password we got from eDirectory
303          * Universal password for additional authorization checks.
304          */
305         {"edir_autz", PW_TYPE_BOOLEAN,
306          offsetof(ldap_instance,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
307 #endif
308
309         /*
310          *      Terrible things which should be deleted.
311          */
312         { "profiles", PW_TYPE_SUBSECTION, 0, NULL, (const void *) attr_config },
313
314         { "group", PW_TYPE_SUBSECTION, 0, NULL, (const void *) group_config },
315
316         { "options", PW_TYPE_SUBSECTION, 0, NULL,
317          (const void *) option_config },
318
319         { "tls", PW_TYPE_SUBSECTION, 0, NULL, (const void *) tls_config },
320
321         {NULL, -1, 0, NULL, NULL}
322 };
323
324 typedef struct ldap_conn {
325         LDAP    *handle;
326         int     rebound;
327         int     referred;
328         ldap_instance *inst;
329 } LDAP_CONN;
330
331 typedef struct xlat_attrs {
332         const VALUE_PAIR_MAP *maps;
333         const char *attrs[MAX_ATTRMAP];
334 } xlat_attrs_t;
335
336 typedef struct rlm_ldap_result {
337         char    **values;
338         int     count;
339 } rlm_ldap_result_t;
340
341
342 #if LDAP_SET_REBIND_PROC_ARGS == 3
343 /*
344  *      Rebind && chase referral stuff
345  */
346 static int ldap_rebind(LDAP *handle, LDAP_CONST char *url,
347                        UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
348                        void *ctx )
349 {
350         LDAP_CONN *conn = ctx;
351
352         conn->referred = TRUE;
353         conn->rebound = TRUE;   /* not really, but oh well... */
354         rad_assert(handle == conn->handle);
355
356         DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
357         
358         return ldap_bind_s(handle, conn->inst->login, conn->inst->password,
359                            LDAP_AUTH_SIMPLE);
360 }
361 #endif
362
363 static int ldap_bind_wrapper(LDAP_CONN **pconn, const char *user,
364                              const char *password,
365                              const char **perror_str, int do_rebind)
366 {
367         int             rcode, ldap_errno, msg_id;
368         int             module_rcode = RLM_MODULE_FAIL;
369         int             reconnect = FALSE;
370         const char      *error_string;
371         LDAP_CONN       *conn = *pconn;
372         ldap_instance   *inst = conn->inst;
373         LDAPMessage     *result = NULL;
374         struct timeval tv;
375
376 redo:
377         msg_id = ldap_bind(conn->handle, user, password,
378                            LDAP_AUTH_SIMPLE);
379         if (msg_id < 0) {
380         get_error:
381                 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
382                                 &ldap_errno);
383                                 
384                 error_string = ldap_err2string(ldap_errno);
385
386                 if (do_rebind && !reconnect) {
387                         conn = fr_connection_reconnect(inst->pool, conn);
388                         *pconn = conn;
389                         if (!conn) return RLM_MODULE_FAIL;
390                         goto redo;
391                 }
392
393         print_error:
394                 if (perror_str) *perror_str = error_string;
395
396 #ifdef HAVE_LDAP_INITIALIZE
397                 if (inst->is_url) {
398                         radlog(L_ERR, "rlm_ldap (%s): %s bind to %s failed: %s",
399                                inst->xlat_name, user,
400                                inst->server, error_string);
401                 } else
402 #endif
403                 {
404                         radlog(L_ERR, "rlm_ldap (%s): %s bind to %s:%d "
405                                       "failed: %s",
406                                inst->xlat_name, user,
407                                inst->server, inst->port,
408                                error_string);
409                 }
410
411                 return module_rcode; /* caller closes the connection */
412         }
413
414         DEBUG3("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
415
416         tv.tv_sec = inst->timeout;
417         tv.tv_usec = 0;
418         rcode = ldap_result(conn->handle, msg_id, 1, &tv, &result);
419         if (rcode < 0) goto get_error;
420
421         if (rcode == 0) {
422                 error_string = "timeout";
423                 goto print_error;
424         }
425
426         ldap_errno = ldap_result2error(conn->handle, result, 1);
427         switch (ldap_errno) {
428         case LDAP_SUCCESS:
429                 break;
430
431         case LDAP_INVALID_CREDENTIALS:
432         case LDAP_CONSTRAINT_VIOLATION:
433                 rcode = RLM_MODULE_REJECT;
434                 /* FALL-THROUGH */
435
436         default:
437                 goto get_error;
438         }
439
440         return RLM_MODULE_OK;
441 }
442
443 /** Create and return a new connection
444  * This function is probably too big.
445  */
446 static void *ldap_conn_create(void *ctx)
447 {
448         int module_rcode;
449         int ldap_errno, ldap_version;
450         struct timeval tv;
451         ldap_instance *inst = ctx;
452         LDAP *handle = NULL;
453         LDAP_CONN *conn = NULL;
454         const char *error;
455
456 #ifdef HAVE_LDAP_INITIALIZE
457         if (inst->is_url) {
458                 DEBUG("rlm_ldap (%s): Connect to %s", inst->xlat_name,
459                       inst->server);
460
461                 ldap_errno = ldap_initialize(&handle, inst->server);
462                 if (ldap_errno != LDAP_SUCCESS) {
463                         radlog(L_ERR, "rlm_ldap (%s): ldap_initialize() "
464                                "failed: %s",
465                                inst->xlat_name, ldap_err2string(ldap_errno));
466                         goto conn_fail;
467                 }
468         } else
469 #endif
470         {
471                 DEBUG("rlm_ldap (%s): Connect to %s:%d", inst->xlat_name,
472                       inst->server, inst->port);
473
474                 handle = ldap_init(inst->server, inst->port);
475                 if (!handle) {
476                         radlog(L_ERR, "rlm_ldap (%s): ldap_init() failed",
477                                inst->xlat_name);
478                 conn_fail:
479                         if (handle) ldap_unbind_s(handle);
480                         return NULL;
481                 }
482         }
483
484         /*
485          *      We now have a connection structure, but no actual TCP connection.
486          *
487          *      Set a bunch of LDAP options, using common code.
488          */
489 #define do_ldap_option(_option, _name, _value) \
490         if (ldap_set_option(handle, _option, _value) != LDAP_OPT_SUCCESS) { \
491                 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
492                 radlog(L_ERR, "rlm_ldap (%s): Could not set %s: %s", \
493                        inst->xlat_name, _name, ldap_err2string(ldap_errno)); \
494         }
495                 
496         if (inst->ldap_debug) {
497                 do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug",
498                                &(inst->ldap_debug));
499         }
500
501         /*
502          *      Leave "chase_referrals" unset to use the OpenLDAP
503          *      default.
504          */
505         if (inst->chase_referrals != 2) {
506                 if (inst->chase_referrals) {
507                         do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals",
508                                        LDAP_OPT_ON);
509                         
510 #if LDAP_SET_REBIND_PROC_ARGS == 3
511                         if (inst->rebind == 1) {
512                                 ldap_set_rebind_proc(handle, ldap_rebind, inst);
513                         }
514 #endif
515                 } else {
516                         do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals",
517                                        LDAP_OPT_OFF);
518                 }
519         }
520
521         tv.tv_sec = inst->net_timeout;
522         tv.tv_usec = 0;
523         do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
524
525         do_ldap_option(LDAP_OPT_TIMELIMIT, "timelimit",
526                        &(inst->timelimit));
527
528         ldap_version = LDAP_VERSION3;
529         do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version",
530                        &ldap_version);
531
532 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
533         do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle",
534                        &(inst->keepalive_idle));
535 #endif
536
537 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
538         do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes",
539                        &(inst->keepalive_probes));
540 #endif
541
542 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
543         do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval",
544                        &(inst->keepalive_interval));
545 #endif
546
547 #ifdef HAVE_LDAP_START_TLS
548         /*
549          *      Set all of the TLS options
550          */
551         if (inst->tls_mode) {
552                 do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
553         }
554
555 #define maybe_ldap_option(_option, _name, _value) \
556         if (_value) do_ldap_option(_option, _name, _value)
557
558         maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE,
559                           "cacertfile", inst->tls_cacertfile);
560         maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR,
561                           "cacertdir", inst->tls_cacertdir);
562
563 #ifdef HAVE_LDAP_INT_TLS_CONFIG
564         if (ldap_int_tls_config(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
565                                 (inst->tls_require_cert)) != LDAP_OPT_SUCCESS) {
566                 ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
567                 radlog(L_ERR, "rlm_ldap (%s): could not set "
568                        "LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s",
569                        inst->xlat_name, 
570                        inst->tls_require_cert,
571                        ldap_err2string(ldap_errno));
572         }
573 #endif
574
575         maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE,
576                           "certfile", inst->tls_certfile);
577         maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE,
578                           "keyfile", inst->tls_keyfile);
579         maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE,
580                           "randfile", inst->tls_randfile);
581
582         /*
583          *      And finally start the TLS code.
584          */
585         if (inst->start_tls && (inst->port != 636)) {
586                 ldap_errno = ldap_start_tls_s(handle, NULL, NULL);
587                 if (ldap_errno != LDAP_SUCCESS) {
588                         ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER,
589                                         &ldap_errno);
590                         radlog(L_ERR, "rlm_ldap (%s): could not start TLS: %s",
591                                inst->xlat_name,
592                                ldap_err2string(ldap_errno));
593                         goto conn_fail;
594                 }
595         }
596 #endif /* HAVE_LDAP_START_TLS */
597
598         conn = rad_malloc(sizeof(*conn));
599         conn->inst = inst;
600         conn->handle = handle;
601         conn->rebound = FALSE;
602         conn->referred = FALSE;
603
604         module_rcode = ldap_bind_wrapper(&conn, inst->login, inst->password,
605                                          &error, FALSE);
606         if (module_rcode != RLM_MODULE_OK) {
607                 radlog(L_ERR, "rlm_ldap (%s): failed binding to LDAP "
608                        "server: %s",
609                        inst->xlat_name, error);
610
611                 /*
612                  *      FIXME: print "check config, morians!
613                  */
614                 goto conn_fail;
615         }
616
617         return conn;
618 }
619
620
621 /** Close and delete a connection
622  *
623  */
624 static int ldap_conn_delete(UNUSED void *ctx, void *connection)
625 {
626         LDAP_CONN *conn = connection;
627
628         ldap_unbind_s(conn->handle);
629         free(conn);
630
631         return 0;
632 }
633
634
635 /** Gets an LDAP socket from the connection pool
636  *
637  */
638 static LDAP_CONN *ldap_get_socket(ldap_instance *inst)
639 {
640         LDAP_CONN *conn;
641
642         conn = fr_connection_get(inst->pool);
643         if (!conn) {
644                 radlog(L_ERR, "rlm_ldap (%s): all ldap connections are in use",
645                        inst->xlat_name);
646                 return NULL;
647         }
648
649         return conn;
650 }
651
652 /** Frees an LDAP socket back to the connection pool
653  *
654  */
655 static void ldap_release_socket(ldap_instance *inst, LDAP_CONN *conn)
656 {
657         /*
658          *      Could have already been free'd due to a previous error.
659          */
660         if (!conn) return;
661
662         /*
663          *      We chased a referral to another server.
664          *
665          *      This connection is no longer part of the pool which is
666          *      connected to and bound to the configured server.
667          *      Close it.
668          *
669          *      Note that we do NOT close it if it was bound to
670          *      another user.  Instead, we let the next caller do the
671          *      rebind.
672          */
673         if (conn->referred) {
674                 fr_connection_del(inst->pool, conn);
675                 return;
676         }
677
678         fr_connection_release(inst->pool, conn);
679         return;
680 }
681
682
683 /* Converts "bad" strings into ones which are safe for LDAP
684  *
685  */
686 static size_t ldap_escape_func(UNUSED REQUEST *request, char *out,
687                                size_t outlen, const char *in, UNUSED void *arg)
688 {
689         size_t len = 0;
690
691         while (in[0]) {
692                 /*
693                  *      Encode unsafe characters.
694                  */
695                 if (((len == 0) &&
696                     ((in[0] == ' ') || (in[0] == '#'))) ||
697                     (strchr(",+\"\\<>;*=()", *in))) {
698                         static const char hex[] = "0123456789abcdef";
699
700                         /*
701                          *      Only 3 or less bytes available.
702                          */
703                         if (outlen <= 3) {
704                                 break;
705                         }
706
707                         *(out++) = '\\';
708                         *(out++) = hex[((*in) >> 4) & 0x0f];
709                         *(out++) = hex[(*in) & 0x0f];
710                         outlen -= 3;
711                         len += 3;
712                         in++;
713                         continue;
714                 }
715
716                 /*
717                  *      Only one byte left.
718                  */
719                 if (outlen <= 1) {
720                         break;
721                 }
722
723                 /*
724                  *      Allowed character.
725                  */
726                 *(out++) = *(in++);
727                 outlen--;
728                 len++;
729         }
730         *out = '\0';
731         return len;
732 }
733
734 /** Do a search and get a response
735  *
736  */
737 static int perform_search(ldap_instance *inst, REQUEST *request,
738                           LDAP_CONN **pconn, const char *search_basedn,
739                           int scope, const char *filter, 
740                           const char * const *attrs, LDAPMessage **presult)
741 {
742         int             ldap_errno;
743         int             count = 0;
744         int             reconnect = FALSE;
745         LDAP_CONN       *conn = *pconn;
746         struct timeval  tv;
747
748         /*
749          *      OpenLDAP library doesn't declare attrs array as const, but
750          *      it really should be *sigh*.
751          */
752         char **search_attrs;
753         memcpy(&search_attrs, &attrs, sizeof(attrs));
754
755         *presult = NULL;
756
757         /*
758          *      Do all searches as the default admin user.
759          */
760         if (conn->rebound) {
761                 ldap_errno = ldap_bind_wrapper(pconn,
762                                                inst->login, inst->password,
763                                                NULL, TRUE);
764                 if (ldap_errno != RLM_MODULE_OK) {
765                         return -1;
766                 }
767
768                 rad_assert(*pconn != NULL);
769                 conn = *pconn;
770                 conn->rebound = FALSE;
771         }
772
773         tv.tv_sec = inst->timeout;
774         tv.tv_usec = 0;
775         RDEBUG2("Performing search in '%s' with filter '%s'",
776                 search_basedn ? search_basedn : "(null)" ,
777                 filter);
778
779 retry:
780         ldap_errno = ldap_search_ext_s(conn->handle, search_basedn, scope,
781                                        filter, search_attrs, 0, NULL, NULL,
782                                        &tv, 0, presult);
783         switch (ldap_errno) {
784         case LDAP_SUCCESS:
785         case LDAP_NO_SUCH_OBJECT:
786                 break;
787
788         case LDAP_SERVER_DOWN:
789         do_reconnect:
790                 ldap_msgfree(*presult);
791
792                 if (reconnect) return -1;
793                 reconnect = TRUE;
794
795                 conn = fr_connection_reconnect(inst->pool, conn);
796                 *pconn = conn;  /* tell the caller we have a new connection */
797                 if (!conn) return -1;
798                 goto retry;
799
800         case LDAP_INSUFFICIENT_ACCESS:
801                 radlog(L_ERR, "rlm_ldap (%s): Search failed: "
802                        "Insufficient access. Check the identity and password "
803                        "configuration directives", inst->xlat_name);
804                 ldap_msgfree(*presult);
805                 return -1;
806
807         case LDAP_TIMEOUT:
808                 exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
809                 radlog(L_ERR, "rlm_ldap (%s): Search failed: Timed out "
810                        "while waiting for server to respond"
811                        "Please increase the timeout", inst->xlat_name);
812                 ldap_msgfree(*presult);
813                 return -1;
814
815         case LDAP_FILTER_ERROR:
816                 radlog(L_ERR, "rlm_ldap (%s): Search failed: Bad search "
817                        "filter: %s", inst->xlat_name,filter);
818                 ldap_msgfree(*presult);
819                 return -1;
820
821         case LDAP_TIMELIMIT_EXCEEDED:
822                 exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
823
824         case LDAP_BUSY:
825         case LDAP_UNAVAILABLE:
826                 /*
827                  *      Reconnect.  There's an issue with the socket
828                  *      or LDAP server.
829                  */
830                 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
831                                 &ldap_errno);
832                 radlog(L_ERR, "rlm_ldap (%s): Search failed: %s",
833                        inst->xlat_name, ldap_err2string(ldap_errno));
834                 goto do_reconnect;
835
836         default:
837                 ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
838                                 &ldap_errno);
839                 radlog(L_ERR, "rlm_ldap (%s): Search failed: %s",
840                        inst->xlat_name, ldap_err2string(ldap_errno));
841                 ldap_msgfree(*presult);
842                 return -1;
843         }
844
845         count = ldap_count_entries(conn->handle, *presult);
846         if (count == 0) {
847                 ldap_msgfree(*presult);
848                 RDEBUG("Search returned no results");
849                 
850                 return -2;
851         }
852
853         if (count != 1) {
854                 ldap_msgfree(*presult);
855                 RDEBUG("Got ambiguous search result (%d results)", count);
856                       
857                 return -2;
858         }
859
860         return 0;
861 }
862
863 /** Expand an LDAP URL into a query, and return a string result from that query.
864  *
865  */
866 static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
867                         char *out, size_t freespace)
868 {
869         int rcode;
870         size_t length = 0;
871         ldap_instance *inst = instance;
872         LDAPURLDesc *ldap_url;
873         LDAPMessage *result = NULL;
874         LDAPMessage *entry = NULL;
875         char **vals;
876         LDAP_CONN *conn;
877         int ldap_errno;
878         const char *url;
879         const char **attrs;
880         char buffer[MAX_FILTER_STR_LEN];
881
882         if (strchr(fmt, '%') != NULL) {
883                 if (!radius_xlat(buffer, sizeof(buffer), fmt, request,
884                                  ldap_escape_func, NULL)) {
885                         radlog(L_ERR,
886                                "rlm_ldap (%s): Unable to create LDAP URL", 
887                                inst->xlat_name);
888                         return 0;
889                 }
890                 url = buffer;
891         } else {
892                 url = fmt;
893         }
894
895         if (!ldap_is_ldap_url(url)) {
896                 radlog(L_ERR, "rlm_ldap (%s): String passed does not look "
897                        "like an LDAP URL", inst->xlat_name);
898                 return 0;
899         }
900
901         if (ldap_url_parse(url, &ldap_url)){
902                 radlog(L_ERR, "rlm_ldap (%s): Parsing LDAP URL failed",
903                        inst->xlat_name);
904                 return 0;
905         }
906
907         /*
908          *      Nothing, empty string, "*" string, or got 2 things, die.
909          */
910         if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
911             !*ldap_url->lud_attrs[0] ||
912             (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
913             ldap_url->lud_attrs[1]) {
914                 radlog(L_ERR, "rlm_ldap (%s): Bad attributes list in LDAP "
915                        "URL. URL must specify exactly one attribute to "
916                        "retrieve",
917                        inst->xlat_name);
918                        
919                 goto free_urldesc;
920         }
921
922         if (ldap_url->lud_host &&
923             ((strncmp(inst->server, ldap_url->lud_host,
924                       strlen(inst->server)) != 0) ||
925              (ldap_url->lud_port != inst->port))) {
926                 RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host,
927                        inst->port);
928                 
929                 goto free_urldesc;
930         }
931
932         conn = ldap_get_socket(inst);
933         if (!conn) goto free_urldesc;
934
935         memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
936         
937         rcode = perform_search(inst, request, &conn, ldap_url->lud_dn, 
938                                ldap_url->lud_scope, ldap_url->lud_filter, attrs,
939                                &result);
940         if (rcode < 0) {
941                 if (rcode == -2) {
942                         RDEBUG("Search returned not found", inst->xlat_name);
943                         goto free_socket;
944                 }
945
946                 goto free_socket;
947         }
948
949         entry = ldap_first_entry(conn->handle, result);
950         if (!entry) {
951                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
952                                 &ldap_errno);
953                 radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
954                        inst->xlat_name,
955                        ldap_err2string(ldap_errno));
956                 goto free_result;
957         }
958
959         vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
960         if (!vals) {
961                 RDEBUG("No \"%s\" attributes found in specified object",
962                        inst->xlat_name, ldap_url->lud_attrs[0]);
963                 goto free_result;
964         }
965
966         length = strlen(vals[0]);
967         if (length >= freespace){
968
969                 goto free_vals;
970         }
971
972         strlcpy(out, vals[0], freespace);
973
974 free_vals:
975         ldap_value_free(vals);
976 free_result:
977         ldap_msgfree(result);
978 free_socket:
979         ldap_release_socket(inst, conn);
980 free_urldesc:
981         ldap_free_urldesc(ldap_url);
982
983         return length;
984 }
985
986
987 static char *get_userdn(LDAP_CONN **pconn, REQUEST *request, int *module_rcode)
988 {
989         int             rcode;
990         VALUE_PAIR      *vp;
991         ldap_instance   *inst = (*pconn)->inst;
992         LDAP            *handle = (*pconn)->handle;
993         LDAPMessage     *result, *entry;
994         int             ldap_errno;
995         static char     firstattr[] = "uid";
996         char            *user_dn;
997         const char      *attrs[] = {firstattr, NULL};
998         char            filter[MAX_FILTER_STR_LEN];     
999         char            basedn[MAX_FILTER_STR_LEN];     
1000
1001         *module_rcode = RLM_MODULE_FAIL;
1002
1003         vp = pairfind(request->config_items, PW_LDAP_USERDN, 0);
1004         if (vp) {
1005                 *module_rcode = RLM_MODULE_OK;
1006                 return vp->vp_strvalue;
1007         }
1008         
1009         if (!radius_xlat(filter, sizeof(filter), inst->filter,
1010                          request, ldap_escape_func, NULL)) {
1011                 radlog(L_ERR, "rlm_ldap (%s): Unable to create filter",
1012                        inst->xlat_name);
1013                 *module_rcode = RLM_MODULE_INVALID;
1014                 return NULL;
1015         }
1016
1017         if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
1018                          request, ldap_escape_func, NULL)) {
1019                 radlog(L_ERR, "rlm_ldap (%s): Unable to create basedn",
1020                        inst->xlat_name);
1021                 *module_rcode = RLM_MODULE_INVALID;
1022                 return NULL;
1023         }
1024
1025         rcode = perform_search(inst, request, pconn, basedn, LDAP_SCOPE_SUBTREE,
1026                                filter, attrs, &result);
1027         if (rcode < 0) {
1028                 if (rcode == -2) {
1029                         *module_rcode = RLM_MODULE_NOTFOUND;
1030                 }
1031
1032                 return NULL;
1033         }
1034
1035         if ((entry = ldap_first_entry(handle, result)) == NULL) {
1036                 ldap_get_option(handle, LDAP_OPT_RESULT_CODE,
1037                                 &ldap_errno);
1038                 radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
1039                        inst->xlat_name,
1040                        ldap_err2string(ldap_errno));
1041                 ldap_msgfree(result);
1042                 return NULL;
1043         }
1044
1045         if ((user_dn = ldap_get_dn(handle, entry)) == NULL) {
1046                 ldap_get_option(handle, LDAP_OPT_RESULT_CODE,
1047                                 &ldap_errno);
1048                 radlog(L_ERR, "rlm_ldap (%s): ldap_get_dn() failed: %s",
1049                        inst->xlat_name,
1050                        ldap_err2string(ldap_errno));
1051                        
1052                 ldap_msgfree(result);
1053                 return NULL;
1054         }
1055
1056         vp = pairmake("LDAP-UserDn", user_dn, T_OP_EQ);
1057         if (!vp) {
1058                 ldap_memfree(user_dn);
1059                 ldap_msgfree(result);
1060                 return NULL;
1061         }
1062         
1063         *module_rcode = RLM_MODULE_OK;
1064         
1065         pairadd(&request->config_items, vp);
1066         ldap_memfree(user_dn);
1067         ldap_msgfree(result);
1068
1069         return vp->vp_strvalue;
1070 }
1071
1072
1073 /** Perform LDAP-Group comparison checking
1074  *
1075  */
1076 static int ldap_groupcmp(void *instance, REQUEST *request,
1077                          UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
1078                          UNUSED VALUE_PAIR *check_pairs,
1079                          UNUSED VALUE_PAIR **reply_pairs)
1080 {
1081         ldap_instance   *inst = instance;
1082         int             i, rcode, found, module_rcode;
1083         LDAPMessage     *result = NULL;
1084         LDAPMessage     *entry = NULL;
1085         int             ldap_errno;
1086         int             check_is_dn = FALSE, value_is_dn = FALSE;
1087         static char     firstattr[] = "dn";
1088         const char      *attrs[] = {firstattr, NULL};
1089         char            **vals;
1090         const char      *group_attrs[] = {inst->groupmemb_attr, NULL};
1091         LDAP_CONN       *conn;
1092         char            *user_dn;
1093
1094         char            gr_filter[MAX_FILTER_STR_LEN];
1095         char            filter[MAX_FILTER_STR_LEN];
1096         char            basedn[MAX_FILTER_STR_LEN];
1097
1098         RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
1099
1100         if (check->length == 0) {
1101                 RDEBUG("Cannot do comparison (group name is empty)");
1102                 return 1;
1103         }
1104
1105         conn = ldap_get_socket(inst);
1106         if (!conn) return 1;
1107
1108         /*
1109          *      This is used in the default membership filter.
1110          */
1111         user_dn = get_userdn(&conn, request, &module_rcode);
1112         if (!user_dn) {
1113                 ldap_release_socket(inst, conn);
1114                 return 1;
1115         }
1116
1117         if (!inst->groupmemb_filter) goto check_attr;
1118
1119         if (!radius_xlat(gr_filter, sizeof(gr_filter),
1120                          inst->groupmemb_filter, request, ldap_escape_func,
1121                          NULL)) {
1122                 radlog(L_ERR, "rlm_ldap (%s): Failed creating group filter",
1123                        inst->xlat_name);
1124                 return 1;
1125         }
1126
1127         /*
1128          *      If it's a DN, use that.
1129          */
1130         check_is_dn = strchr(check->vp_strvalue,',') == NULL ? FALSE : TRUE;
1131         
1132         if (check_is_dn) {
1133                 strlcpy(filter, gr_filter, sizeof(filter));
1134                 strlcpy(basedn, check->vp_strvalue, sizeof(basedn));    
1135         } else {
1136                 snprintf(filter, sizeof(filter), "(&(%s=%s)%s)",
1137                          inst->groupname_attr,
1138                          check->vp_strvalue, gr_filter);
1139
1140                 /*
1141                  *      get_userdn does this, too.  Oh well.
1142                  */
1143                 if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
1144                                  request, ldap_escape_func, NULL)) {
1145                         radlog(L_ERR, "rlm_ldap (%s): Failed creating basedn",
1146                                inst->xlat_name);
1147                         return 1;
1148                 }
1149         }
1150
1151         rcode = perform_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE,
1152                                filter, attrs, &result);
1153         if (rcode == 0) {
1154                 ldap_release_socket(inst, conn);
1155                 ldap_msgfree(result);
1156                         
1157                 RDEBUG("User found in group object");
1158                 
1159                 return 0;
1160         }
1161
1162         if (rcode == -1) {
1163                 ldap_release_socket(inst, conn);
1164                 return 1;
1165         }
1166
1167         /* else the search returned -2, for "not found" */
1168
1169         /*
1170          *      Else the search returned NOTFOUND.  See if we're
1171          *      configured to search for group membership using user
1172          *      object attribute.
1173          */
1174         if (!inst->groupmemb_attr) {
1175                 ldap_release_socket(inst, conn);
1176                 RDEBUG("Group object \"%s\" not found, or user is not a member",
1177                        check->vp_strvalue);
1178                 return 1;
1179         }
1180
1181 check_attr:
1182         RDEBUG2("Checking user object membership (%s) attributes",
1183                 inst->groupmemb_attr);
1184
1185         snprintf(filter ,sizeof(filter), "(objectclass=*)");
1186
1187         rcode = perform_search(inst, request, &conn, user_dn, LDAP_SCOPE_BASE,
1188                                filter, group_attrs, &result);
1189         if (rcode < 0) {
1190                 if (rcode == -2) {
1191                         RDEBUG("Can't check membership attributes, user object "
1192                                "not found");
1193                 }
1194                 ldap_release_socket(inst, conn);
1195                 return 1;
1196         }
1197
1198         entry = ldap_first_entry(conn->handle, result);
1199         if (!entry) {
1200                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
1201                                 &ldap_errno);
1202                 radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
1203                        inst->xlat_name,
1204                        ldap_err2string(ldap_errno));
1205                                
1206                 ldap_release_socket(inst, conn);
1207                 ldap_msgfree(result);
1208                 return 1;
1209         }
1210
1211         vals = ldap_get_values(conn->handle, entry, inst->groupmemb_attr);
1212         if (!vals) {
1213                 RDEBUG("No group membership attribute(s) found in user object");
1214                 ldap_release_socket(inst, conn);
1215                 ldap_msgfree(result);
1216                 return 1;
1217         }
1218
1219         /*
1220          *      Loop over the list of groups the user is a member of,
1221          *      looking for a match.
1222          */
1223         found = FALSE;
1224         for (i = 0; i < ldap_count_values(vals); i++) {
1225                 LDAPMessage *gr_result = NULL;
1226                 
1227                 value_is_dn = strchr(vals[i], ',') == NULL ? FALSE : TRUE;
1228                 
1229                 RDEBUG2("Processing group membership value \"%s\"", vals[i]);
1230
1231                 /*
1232                  *      Both literal group names, do case sensitive comparison
1233                  */
1234                 if (!check_is_dn && !value_is_dn) {
1235                         if (strcmp(vals[i], check->vp_strvalue) == 0){
1236                                 RDEBUG("User found (membership value matches "
1237                                        "check value)");
1238                                
1239                                 found = TRUE;
1240                                 break;
1241                         }
1242                         
1243                         continue;
1244                 }
1245
1246                 /*
1247                  *      Both DNs, do case insensitive comparison
1248                  */
1249                 if (check_is_dn && value_is_dn) {
1250                         if (strcasecmp(vals[i], check->vp_strvalue) == 0){
1251                                 RDEBUG("User found (membership DN matches "
1252                                        "check DN)");
1253                                
1254                                 found = TRUE;
1255                                 break;
1256                         }
1257                         
1258                         continue;
1259                 }
1260                 
1261                 /*
1262                  *      If the value is not a DN, or the check item is a DN
1263                  *      there's nothing more we can do.
1264                  */
1265                 if (!value_is_dn && check_is_dn) continue;
1266
1267                 /*
1268                  *      We have a value which is a DN, and a check item which
1269                  *      specifies the name of a group, search using the value
1270                  *      DN for the group, and see if it has a groupname_attr
1271                  *      which matches our check val.
1272                  */
1273                 RDEBUG2("Searching with membership DN and group name");
1274
1275                 snprintf(filter,sizeof(filter), "(%s=%s)",
1276                          inst->groupname_attr, check->vp_strvalue);
1277
1278                 rcode = perform_search(inst, request, &conn, vals[i],
1279                                        LDAP_SCOPE_BASE, filter, attrs,
1280                                        &gr_result);
1281                                        
1282                 ldap_msgfree(gr_result);
1283
1284                 /* Error occurred */
1285                 if (rcode == -1) {
1286                         ldap_value_free(vals);
1287                         ldap_msgfree(result);
1288                         ldap_release_socket(inst, conn);
1289                         return 1;
1290                 }
1291                 
1292                 /*
1293                  *      Either the group DN wasn't found, or it didn't have the
1294                  *      correct name. Continue looping over the attributes.
1295                  */
1296                 if (rcode == -2) {
1297                         ldap_msgfree(gr_result);
1298                         continue;
1299                 }
1300
1301                 found = TRUE;
1302
1303                 RDEBUG("User found (group name in membership DN matches check "
1304                        "value)");
1305
1306                 break;
1307         }
1308
1309         ldap_value_free(vals);
1310         ldap_msgfree(result);
1311         ldap_release_socket(inst, conn);
1312
1313         if (!found){
1314                 RDEBUG("User is not a member of specified group");
1315                 return 1;
1316         }
1317
1318         return 0;
1319 }
1320
1321 /** Parse update section
1322  *
1323  * Verify that the ldap update section makes sense, and add attribute names
1324  * to array of attributes for efficient querying later.
1325  */
1326 static int build_attrmap(CONF_SECTION *cs, VALUE_PAIR_MAP **head)
1327 {
1328         const char *cs_list, *p;
1329
1330         request_refs_t request_def = REQUEST_CURRENT;
1331         pair_lists_t list_def = PAIR_LIST_REQUEST;
1332
1333         CONF_ITEM *ci = cf_sectiontoitem(cs);
1334         CONF_PAIR *cp;
1335
1336         unsigned int total = 0;
1337         VALUE_PAIR_MAP **tail, *map;
1338         *head = NULL;
1339         tail = head;
1340         
1341         if (!cs) return 0;
1342         
1343         cs_list = p = cf_section_name2(cs);
1344         if (cs_list) {
1345                 request_def = radius_request_name(&p, REQUEST_UNKNOWN);
1346                 if (request_def == REQUEST_UNKNOWN) {
1347                         cf_log_err(ci, "rlm_ldap: Default request specified "
1348                                    "in mapping section is invalid");
1349                         return -1;
1350                 }
1351                 
1352                 list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
1353                 if (list_def == PAIR_LIST_UNKNOWN) {
1354                         cf_log_err(ci, "rlm_ldap: Default list specified "
1355                                    "in mapping section is invalid");
1356                         return -1;
1357                 }
1358         }
1359
1360         for (ci = cf_item_find_next(cs, NULL);
1361              ci != NULL;
1362              ci = cf_item_find_next(cs, ci)) {
1363                 if (total++ == MAX_ATTRMAP) {
1364                         cf_log_err(ci, "rlm_ldap: Attribute map size exceeded");
1365                         goto error;
1366                 }
1367                 
1368                 if (!cf_item_is_pair(ci)) {
1369                         cf_log_err(ci, "rlm_ldap: Entry is not in \"attribute ="
1370                                        " ldap-attribute\" format");
1371                         goto error;
1372                 }
1373         
1374                 cp = cf_itemtopair(ci);
1375                 map = radius_cp2map(cp, REQUEST_CURRENT, list_def);
1376                 if (!map) {
1377                         goto error;
1378                 }
1379                 
1380                 *tail = map;
1381                 tail = &(map->next);
1382         }
1383
1384         return 0;
1385         
1386         error:
1387                 radius_mapfree(head);
1388                 return -1;
1389 }
1390
1391 /** Detach from the LDAP server and cleanup internal state.
1392  *
1393  */
1394 static int ldap_detach(void *instance)
1395 {
1396         ldap_instance *inst = instance;
1397
1398         if (inst->postauth) free(inst->postauth);
1399         if (inst->accounting) free(inst->accounting);
1400         
1401         fr_connection_pool_delete(inst->pool);
1402         
1403         if (inst->user_map) {
1404                 radius_mapfree(&inst->user_map);
1405         }
1406
1407         free(inst);
1408
1409         return 0;
1410 }
1411
1412 static int parse_sub_section(CONF_SECTION *parent, 
1413                              UNUSED ldap_instance *inst,
1414                              ldap_acct_section_t **config,
1415                              rlm_components_t comp)
1416 {
1417         CONF_SECTION *cs;
1418
1419         const char *name = section_type_value[comp].section;
1420         
1421         cs = cf_section_sub_find(parent, name);
1422         if (!cs) {
1423                 radlog(L_INFO, "Couldn't find configuration for %s. "
1424                        "Will return NOOP for calls from this section.", name);
1425                 
1426                 return 0;
1427         }
1428         
1429         *config = rad_calloc(sizeof(**config));
1430         if (cf_section_parse(cs, *config, acct_section_config) < 0) {
1431                 radlog(L_ERR, "Failed parsing configuration for section %s",
1432                        name);
1433                 
1434                 free(*config);
1435                 *config = NULL;
1436                 
1437                 return -1;
1438         }
1439                 
1440         (*config)->cs = cs;
1441
1442         return 0;
1443 }
1444
1445 /** Parses config
1446  * Uses section of radiusd config file passed as parameter to create an
1447  * instance of the module.
1448  */
1449 static int ldap_instantiate(CONF_SECTION * conf, void **instance)
1450 {
1451         ldap_instance *inst;
1452         CONF_SECTION *cs;
1453
1454         inst = rad_calloc(sizeof *inst);
1455         inst->cs = conf;
1456
1457         inst->chase_referrals = 2; /* use OpenLDAP defaults */
1458         inst->rebind = 2;
1459         
1460         inst->xlat_name = cf_section_name2(conf);
1461         if (!inst->xlat_name) {
1462                 inst->xlat_name = cf_section_name1(conf);
1463         }
1464                 
1465         rad_assert(inst->xlat_name);
1466
1467         /*
1468          *      If the configuration parameters can't be parsed, then fail.
1469          */
1470         if ((cf_section_parse(conf, inst, module_config) < 0) ||
1471             (parse_sub_section(conf, inst,
1472                                &inst->accounting,
1473                                RLM_COMPONENT_ACCT) < 0) ||
1474             (parse_sub_section(conf, inst,
1475                                &inst->postauth,
1476                                RLM_COMPONENT_POST_AUTH) < 0)) {
1477                 radlog(L_ERR, "rlm_ldap (%s): Failed parsing configuration",
1478                        inst->xlat_name);
1479                 goto error;
1480         }
1481
1482         if (inst->server == NULL) {
1483                 radlog(L_ERR, "rlm_ldap (%s): Missing 'server' directive",
1484                        inst->xlat_name);
1485                 goto error;
1486         }
1487
1488         /*
1489          *      Check for URLs.  If they're used and the library doesn't
1490          *      support them, then complain.
1491          */
1492         inst->is_url = 0;
1493         if (ldap_is_ldap_url(inst->server)) {
1494 #ifdef HAVE_LDAP_INITIALIZE
1495                 inst->is_url = 1;
1496                 inst->port = 0;
1497 #else
1498                 radlog(L_ERR, "rlm_ldap (%s): 'server' directive is in URL "
1499                        "form but ldap_initialize() is not available",
1500                        inst->xlat_name);
1501                 goto error;
1502 #endif
1503         }
1504
1505         /* workaround for servers which support LDAPS but not START TLS */
1506         if (inst->port == LDAPS_PORT || inst->tls_mode) {
1507                 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
1508         } else {
1509                 inst->tls_mode = 0;
1510         }
1511
1512 #if LDAP_SET_REBIND_PROC_ARGS != 3
1513         /*
1514          *      The 2-argument rebind doesn't take an instance
1515          *      variable.  Our rebind function needs the instance
1516          *      variable for the username, password, etc.
1517          */
1518         if (inst->rebind == 1) {
1519                 radlog(L_ERR, "rlm_ldap (%s): Cannot use 'rebind' directive "
1520                        "as this version of libldap does not support the API "
1521                        "that we need", inst->xlat-name);
1522                 goto error;
1523         }
1524 #endif
1525
1526         /*
1527          *      Build the attribute map
1528          */
1529         cs = cf_section_sub_find(conf, "update");
1530         if (cs) {       
1531                 if (build_attrmap(cs, &(inst->user_map)) < 0) {
1532                         goto error;
1533                 }
1534         }
1535
1536         /*
1537          *      Group comparison checks.
1538          */
1539         paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst); 
1540         if (cf_section_name2(conf)) {
1541                 DICT_ATTR *da;
1542                 ATTR_FLAGS flags;
1543                 char buffer[256];
1544
1545                 snprintf(buffer, sizeof(buffer), "%s-Ldap-Group",
1546                          inst->xlat_name);
1547                 memset(&flags, 0, sizeof(flags));
1548
1549                 dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
1550                 da = dict_attrbyname(buffer);
1551                 if (!da) {
1552                         radlog(L_ERR, "rlm_ldap (%s): Failed creating "
1553                                "attribute %s", inst->xlat_name, buffer);
1554                         goto error;
1555                 }
1556
1557                 paircompare_register(da->attr, PW_USER_NAME, ldap_groupcmp,
1558                                      inst);
1559         }
1560
1561         xlat_register(inst->xlat_name, ldap_xlat, inst);
1562
1563         /*
1564          *      Initialize the socket pool.
1565          */
1566         inst->pool = fr_connection_pool_init(inst->cs, inst,
1567                                              ldap_conn_create,
1568                                              NULL,
1569                                              ldap_conn_delete);
1570         if (!inst->pool) {
1571                 ldap_detach(inst);
1572                 return -1;
1573         }
1574         
1575         *instance = inst;
1576         return 0;
1577         
1578         error:
1579         ldap_detach(inst);
1580         return -1;
1581 }
1582
1583 static int check_access(ldap_instance *inst, REQUEST* request, LDAP_CONN *conn,
1584                         LDAPMessage *entry)
1585 {
1586         int rcode = -1;
1587         char **vals = NULL;
1588
1589         vals = ldap_get_values(conn->handle, entry, inst->access_attr);
1590         if (vals) {
1591                 if (inst->positive_access_attr) {
1592                         if (strncmp(vals[0], "FALSE", 5) == 0) {
1593                                 RDEBUG("Dialup access disabled");
1594
1595                         } else {
1596                                 rcode = 0;
1597                         }
1598
1599                 } else {
1600                         RDEBUG("\"%s\" attribute exists - access denied by"
1601                                " default", inst->access_attr);
1602                 }
1603
1604                 ldap_value_free(vals);
1605
1606         } else if (inst->positive_access_attr) {
1607                 RDEBUG("No %s attribute - access denied by default",
1608                        inst->access_attr);
1609
1610         } else {
1611                 rcode = 0;
1612         }
1613
1614         return rcode;
1615 }
1616
1617
1618 static VALUE_PAIR *ldap_getvalue(REQUEST *request, const VALUE_PAIR_TMPL *dst,
1619                                  void *ctx)
1620 {
1621         rlm_ldap_result_t *self = ctx;
1622         VALUE_PAIR *head, **tail, *vp;
1623         int i;
1624         
1625         request = request;
1626         
1627         head = NULL;
1628         tail = &head;
1629         
1630         /*
1631          *      Iterate over all the retrieved values,
1632          *      don't try and be clever about changing operators
1633          *      just use whatever was set in the attribute map. 
1634          */
1635         for (i = 0; i < self->count; i++) {
1636                 vp = pairalloc(dst->da);
1637                 rad_assert(vp);
1638
1639                 pairparsevalue(vp, self->values[i]);
1640                 
1641                 *tail = vp;
1642                 tail = &(vp->next);
1643         }
1644         
1645         return head;            
1646 }
1647
1648
1649 static void xlat_attrsfree(const xlat_attrs_t *expanded)
1650 {
1651         const VALUE_PAIR_MAP *map;
1652         unsigned int total = 0;
1653         
1654         const char *name;
1655         
1656         for (map = expanded->maps; map != NULL; map = map->next)
1657         {
1658                 name = expanded->attrs[total++];
1659                 if (!name) return;
1660                 
1661                 if (map->src->do_xlat) {
1662                         rad_cfree(name);
1663                 }
1664         }
1665 }
1666
1667
1668 static int xlat_attrs(REQUEST *request, const VALUE_PAIR_MAP *maps,
1669                       xlat_attrs_t *expanded)
1670 {
1671         const VALUE_PAIR_MAP *map;
1672         unsigned int total = 0;
1673         
1674         size_t len;
1675         char *buffer;
1676
1677         for (map = maps; map != NULL; map = map->next)
1678         {
1679                 if (map->src->do_xlat) {
1680                         buffer = rad_malloc(MAX_ATTR_STR_LEN);
1681                         len = radius_xlat(buffer, MAX_ATTR_STR_LEN,
1682                                           map->src->name, request, NULL, NULL);
1683                                           
1684                         if (!len) {
1685                                 RDEBUG("Expansion of LDAP attribute "
1686                                        "\"%s\" failed", map->src->name);
1687                                        
1688                                 expanded->attrs[total] = NULL;
1689                                 
1690                                 xlat_attrsfree(expanded);
1691                                 
1692                                 return -1;
1693                         }
1694                         
1695                         expanded->attrs[total++] = buffer;
1696                 } else {
1697                         expanded->attrs[total++] = map->src->name;
1698                 }
1699         }
1700         
1701         expanded->attrs[total] = NULL;
1702         expanded->maps = maps;
1703         
1704         return 0;
1705 }
1706
1707
1708 /** Convert attribute map into valuepairs
1709  *
1710  * Use the attribute map built earlier to convert LDAP values into valuepairs
1711  * and insert them into whichever list they need to go into.
1712  *
1713  * This is *NOT* atomic, but there's no condition in which we should error
1714  * out...
1715  */
1716 static void do_attrmap(UNUSED ldap_instance *inst, REQUEST *request,
1717                        LDAP *handle, const xlat_attrs_t *expanded,
1718                        LDAPMessage *entry)
1719 {
1720         const VALUE_PAIR_MAP    *map;
1721         unsigned int            total = 0;
1722         
1723         rlm_ldap_result_t       result;
1724         const char              *name;
1725
1726         for (map = expanded->maps; map != NULL; map = map->next)
1727         {
1728                 name = expanded->attrs[total++];
1729                 
1730                 result.values = ldap_get_values(handle, entry, name);
1731                 if (!result.values) {
1732                         RDEBUG2("Attribute \"%s\" not found in LDAP object",
1733                                 name);
1734                                 
1735                         goto next;
1736                 }
1737                 
1738                 /*
1739                  *      Find out how many values there are for the
1740                  *      attribute and extract all of them.
1741                  */
1742                 result.count = ldap_count_values(result.values);
1743                 
1744                 /*
1745                  *      If something bad happened, just skip, this is probably
1746                  *      a case of the dst being incorrect for the current
1747                  *      request context
1748                  */
1749                 if (radius_map2request(request, map, name, ldap_getvalue,
1750                                        &result) < 0) {
1751                         goto next;
1752                 }
1753                 
1754                 next:
1755                 
1756                 ldap_value_free(result.values);
1757         }
1758 }
1759
1760
1761 static void do_check_reply(ldap_instance *inst, REQUEST *request)
1762 {
1763        /*
1764         *       More warning messages for people who can't be bothered
1765         *       to read the documentation.
1766         */
1767         if (inst->expect_password && (debug_flag > 1)) {
1768                 if (!pairfind(request->config_items,PW_CLEARTEXT_PASSWORD, 0) &&
1769                         !pairfind(request->config_items,
1770                                   PW_NT_PASSWORD, 0) &&
1771                         !pairfind(request->config_items,
1772                                   PW_USER_PASSWORD, 0) &&
1773                         !pairfind(request->config_items,
1774                                   PW_PASSWORD_WITH_HEADER, 0) &&
1775                         !pairfind(request->config_items,
1776                                   PW_CRYPT_PASSWORD, 0)) {
1777                                 RDEBUG("WARNING: No \"known good\" password "
1778                                        "was found in LDAP.  Are you sure that "
1779                                        "the user is configured correctly?");
1780                }
1781        }
1782 }
1783
1784
1785 static void apply_profile(ldap_instance *inst, REQUEST *request,
1786                           LDAP_CONN **pconn, const char *profile,
1787                           const xlat_attrs_t *expanded)
1788 {
1789         int rcode;
1790         LDAPMessage     *result, *entry;
1791         int             ldap_errno;
1792         LDAP            *handle = (*pconn)->handle;
1793         char            filter[MAX_FILTER_STR_LEN];
1794
1795         if (!profile || !*profile) return;
1796
1797         strlcpy(filter, inst->base_filter, sizeof(filter));
1798
1799         rcode = perform_search(inst, request, pconn, profile, LDAP_SCOPE_BASE,
1800                                filter, expanded->attrs, &result);
1801                 
1802         if (rcode < 0) {
1803                 if (rcode == -2) {
1804                         RDEBUG("Profile \"%s\" not found", profile);
1805                 }
1806                 goto free_result;
1807         }
1808
1809         entry = ldap_first_entry(handle, result);
1810         if (!entry) {
1811                 ldap_get_option(handle, LDAP_OPT_RESULT_CODE,
1812                                 &ldap_errno);
1813                 radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
1814                        inst->xlat_name,
1815                        ldap_err2string(ldap_errno));
1816                        
1817                 goto free_result;
1818         }
1819         
1820         do_attrmap(inst, request, handle, expanded, entry);
1821
1822 free_result:
1823         ldap_msgfree(result);
1824 }
1825
1826
1827 /** Check if user is authorized for remote access
1828  *
1829  */
1830 static int ldap_authorize(void *instance, REQUEST * request)
1831 {
1832         int rcode;
1833         int module_rcode = RLM_MODULE_OK;
1834         ldap_instance   *inst = instance;
1835         char            *user_dn = NULL;
1836         char            **vals;
1837         VALUE_PAIR      *vp;
1838         LDAP_CONN       *conn;
1839         LDAPMessage     *result, *entry;
1840         int             ldap_errno;
1841         char            filter[MAX_FILTER_STR_LEN];
1842         char            basedn[MAX_FILTER_STR_LEN];
1843         xlat_attrs_t    expanded; /* faster that mallocing every time */
1844         
1845         if (!request->username) {
1846                 RDEBUG2("Attribute \"User-Name\" is required for "
1847                         "authorization.");
1848                 return RLM_MODULE_NOOP;
1849         }
1850
1851         /*
1852          *      Check for valid input, zero length names not permitted
1853          */
1854         if (request->username->length == 0) {
1855                 RDEBUG2("Zero length username not permitted");
1856                 return RLM_MODULE_INVALID;
1857         }
1858
1859         if (!radius_xlat(filter, sizeof(filter), inst->filter,
1860                          request, ldap_escape_func, NULL)) {
1861                 radlog(L_ERR, "rlm_ldap (%s): Failed creating filter",
1862                        inst->xlat_name);
1863                 return RLM_MODULE_INVALID;
1864         }
1865
1866         if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
1867                          request, ldap_escape_func, NULL)) {
1868                 radlog(L_ERR, "rlm_ldap (%s): Failed creating basedn",
1869                        inst->xlat_name);
1870                 return RLM_MODULE_INVALID;
1871         }
1872
1873         conn = ldap_get_socket(inst);
1874         if (!conn) return RLM_MODULE_FAIL;
1875         
1876         if (xlat_attrs(request, inst->user_map, &expanded) < 0) {
1877                 return RLM_MODULE_FAIL;
1878         }
1879         
1880         rcode = perform_search(inst, request, &conn, basedn,
1881                                LDAP_SCOPE_SUBTREE, filter, expanded.attrs,
1882                                &result);
1883         
1884         if (rcode < 0) {
1885                 if (rcode == -2) {
1886                         module_failure_msg(request,
1887                                            "rlm_ldap (%s): User object not "
1888                                            " found",
1889                                            inst->xlat_name);
1890                                            
1891                         RDEBUG("User object not found", inst->xlat_name);
1892                                
1893                         module_rcode = RLM_MODULE_NOTFOUND;
1894                         goto free_socket;
1895                 }
1896
1897                 goto free_socket;
1898         }
1899
1900         entry = ldap_first_entry(conn->handle, result);
1901         if (!entry) {
1902                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
1903                                 &ldap_errno);
1904                 radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
1905                        inst->xlat_name,
1906                        ldap_err2string(ldap_errno));
1907                        
1908                 goto free_result;
1909         }
1910
1911         user_dn = ldap_get_dn(conn->handle, entry);
1912         if (!user_dn) {
1913                 ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
1914                                 &ldap_errno);
1915                 radlog(L_ERR, "rlm_ldap (%s): ldap_get_dn() failed: %s",
1916                        inst->xlat_name,
1917                        ldap_err2string(ldap_errno));
1918                 goto free_result;
1919         }
1920         
1921         RDEBUG2("User found at DN \"%s\"", user_dn);
1922         /*
1923          *      Adding attribute containing the Users' DN.
1924          */
1925         pairadd(&request->config_items,
1926                 pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
1927
1928 #ifdef WITH_EDIR
1929         /*
1930          *      We already have a Cleartext-Password.  Skip edir.
1931          */
1932         if (inst->edir && pairfind(request->config_items,
1933                                    PW_CLEARTEXT_PASSWORD, 0)) {
1934                 goto skip_edir;
1935         }
1936
1937         /*
1938          *      Retrieve Universal Password if we use eDirectory
1939          */
1940         if (inst->edir) {
1941                 int res = 0;
1942                 size_t bufsize;
1943                 char buffer[256];
1944
1945                 bufsize = sizeof(buffer);
1946
1947                 /* retrive universal password */
1948                 res = nmasldap_get_password(conn->handle, user_dn,
1949                                             buffer, &bufsize);
1950                 if (res != 0) {
1951                         RDEBUG2("Failed to retrieve eDirectory password. Check "
1952                                 "your configuration !");
1953                         module_rcode = RLM_MODULE_NOOP;
1954                         goto free_result;
1955                 }
1956
1957                 /* Add Cleartext-Password attribute to the request */
1958                 vp = radius_paircreate(request, &request->config_items,
1959                                        PW_CLEARTEXT_PASSWORD, 0,
1960                                        PW_TYPE_STRING);
1961                 strlcpy(vp->vp_strvalue, buffer, sizeof(vp->vp_strvalue));
1962                 vp->length = strlen(vp->vp_strvalue);
1963                 
1964                 RDEBUG2("Added eDirectory password in check items as %s = %s",
1965                         vp->name, vp->vp_strvalue);
1966                         
1967                 if (inst->edir_autz) {
1968                         RDEBUG2("Binding as user for eDirectory authorization "
1969                                 "checks");
1970                         /*
1971                          *      Bind as the user
1972                          */
1973                         conn->rebound = TRUE;
1974                         module_rcode = ldap_bind_wrapper(&conn, user_dn,
1975                                                          vp->vp_strvalue,
1976                                                          NULL, TRUE);
1977                         if (module_rcode != RLM_MODULE_OK) {
1978                                 goto free_result;
1979                         }
1980                         
1981                         RDEBUG("Bind as user \"%s\" was successful", user_dn);
1982                 }
1983         }
1984
1985 skip_edir:
1986 #endif
1987
1988         /*
1989          *      Check for access.
1990          */
1991         if (inst->access_attr) {
1992                 if (check_access(inst, request, conn, entry) < 0) {
1993                         module_rcode = RLM_MODULE_USERLOCK;
1994                         goto free_result;
1995                 }
1996         }
1997
1998         /*
1999          *      Apply ONE user profile, or a default user profile.
2000          */
2001         vp = pairfind(request->config_items, PW_USER_PROFILE, 0);
2002         if (vp || inst->default_profile) {
2003                 char *profile = inst->default_profile;
2004
2005                 if (vp) profile = vp->vp_strvalue;
2006
2007                 apply_profile(inst, request, &conn, profile, &expanded);
2008         }
2009
2010         /*
2011          *      Apply a SET of user profiles.
2012          */
2013         if (inst->profile_attr) {
2014                 vals = ldap_get_values(conn->handle, entry, inst->profile_attr);
2015                 if (vals != NULL) {
2016                         int i;
2017         
2018                         for (i = 0; (vals[i] != NULL) && (*vals[i] != '\0');
2019                              i++) {
2020                                 apply_profile(inst, request, &conn, vals[i],
2021                                               &expanded);
2022                         }
2023         
2024                         ldap_value_free(vals);
2025                 }
2026         }
2027
2028         if (inst->user_map) {
2029                 do_attrmap(inst, request, conn->handle, &expanded, entry);
2030                 do_check_reply(inst, request);
2031         }
2032         
2033 free_result:
2034         if (user_dn) ldap_memfree(user_dn);
2035         xlat_attrsfree(&expanded);
2036         ldap_msgfree(result);
2037 free_socket:
2038         ldap_release_socket(inst, conn);
2039
2040         return module_rcode;
2041 }
2042
2043
2044 /** Check the user's password against ldap database
2045  *
2046  */
2047 static int ldap_authenticate(void *instance, REQUEST * request)
2048 {
2049         int             module_rcode;
2050         const char      *user_dn;
2051         ldap_instance   *inst = instance;
2052         LDAP_CONN       *conn;
2053
2054         /*
2055          * Ensure that we're being passed a plain-text password, and not
2056          * anything else.
2057          */
2058
2059         if (!request->username) {
2060                 radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Name\" is "
2061                        "required for authentication", inst->xlat_name);
2062                 return RLM_MODULE_INVALID;
2063         }
2064
2065         if (!request->password) {
2066                 radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Password\" "
2067                        "is required for authentication.", inst->xlat_name);
2068                 RDEBUG2("  You have set \"Auth-Type := LDAP\" somewhere.");
2069                 RDEBUG2("  *********************************************");
2070                 RDEBUG2("  * THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
2071                 RDEBUG2("  * YOU ARE PREVENTING THE SERVER FROM WORKING.");
2072                 RDEBUG2("  *********************************************");
2073                 return RLM_MODULE_INVALID;
2074         }
2075
2076         if (request->password->attribute != PW_USER_PASSWORD) {
2077                 radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Password\" "
2078                        "is required for authentication. Cannot use \"%s\".",
2079                        inst->xlat_name, request->password->name);
2080                 return RLM_MODULE_INVALID;
2081         }
2082
2083         if (request->password->length == 0) {
2084                 module_failure_msg(request,
2085                                    "rlm_ldap (%s): Empty password supplied",
2086                                    inst->xlat_name);
2087                 return RLM_MODULE_INVALID;
2088         }
2089
2090         conn = ldap_get_socket(inst);
2091         if (!conn) return RLM_MODULE_FAIL;
2092
2093         RDEBUG("Login attempt by \"%s\" with password \"%s\"",
2094                request->username->vp_strvalue, request->password->vp_strvalue);
2095
2096         /*
2097          *      Get the DN by doing a search.
2098          */
2099         user_dn = get_userdn(&conn, request, &module_rcode);
2100         if (!user_dn) {
2101                 ldap_release_socket(inst, conn);
2102                 return module_rcode;
2103         }
2104
2105         /*
2106          *      Bind as the user
2107          */
2108         conn->rebound = TRUE;
2109         module_rcode = ldap_bind_wrapper(&conn, user_dn,
2110                                          request->password->vp_strvalue,
2111                                          NULL, TRUE);
2112         if (module_rcode == RLM_MODULE_OK) {
2113                 RDEBUG("Bind as user \"%s\" was successful", user_dn);
2114         }
2115
2116         ldap_release_socket(inst, conn);
2117         return module_rcode;
2118 }
2119
2120 /** Modify user's object in LDAP
2121  *
2122  */
2123 static int user_modify(ldap_instance *inst, REQUEST *request,
2124                        ldap_acct_section_t *section)
2125 {
2126         int             module_rcode = RLM_MODULE_OK;
2127         int             ldap_errno;
2128         const char      *error_string;
2129         
2130         LDAP_CONN       *conn;
2131         
2132         LDAPMod         *mod_p[MAX_ATTRMAP + 1], mod_s[MAX_ATTRMAP];
2133         LDAPMod         **modify = mod_p;
2134         
2135         char            *passed[MAX_ATTRMAP * 2];
2136         int             i, total = 0, last_pass = 0;
2137         
2138         char            *expanded[MAX_ATTRMAP];
2139         int             last_exp = 0;
2140         
2141         const char      *attr;
2142         const char      *value;
2143         
2144         const char      *user_dn;
2145
2146         conn = ldap_get_socket(inst);
2147         if (!conn) return RLM_MODULE_FAIL;
2148
2149         /*
2150          *      Build our set of modifications using the update sections in
2151          *      the config.
2152          */
2153         CONF_ITEM       *ci;
2154         CONF_PAIR       *cp;
2155         CONF_SECTION    *cs;
2156         FR_TOKEN        op;
2157         char            path[MAX_STRING_LEN];
2158         
2159         char            *p = path;
2160
2161         rad_assert(section);
2162         
2163         /*
2164          *      Locate the update section were going to be using
2165          */
2166         if (section->reference[0] != '.') {
2167                 *p++ = '.';
2168         }
2169         
2170         if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1,
2171                          section->reference, request, NULL, NULL)) {
2172                 goto error;     
2173         }
2174
2175         ci = cf_reference_item(NULL, section->cs, path);
2176         if (!ci) {
2177                 goto error;     
2178         }
2179         
2180         if (!cf_item_is_section(ci)){
2181                 radlog(L_ERR, "rlm_ldap (%s): Reference must resolve to a "
2182                        "section", inst->xlat_name);
2183                 
2184                 goto error;     
2185         }
2186         
2187         cs = cf_section_sub_find(cf_itemtosection(ci), "update");
2188         if (!cs) {
2189                 radlog(L_ERR, "rlm_ldap (%s): Section must contain 'update' "
2190                        "subsection",
2191                        inst->xlat_name);
2192                 
2193                 goto error;
2194         }
2195         
2196         /*
2197          *      Iterate over all the pairs, building our mods array
2198          */
2199         for (ci = cf_item_find_next(cs, NULL);
2200              ci != NULL;
2201              ci = cf_item_find_next(cs, ci)) {
2202                 int do_xlat = FALSE;
2203                 int is_wildcard = FALSE;
2204                 
2205                 if (total == MAX_ATTRMAP) {
2206                         radlog(L_ERR, "rlm_ldap (%s): Modify map size exceeded",
2207                                inst->xlat_name);
2208         
2209                         goto error;
2210                 }
2211                 
2212                 if (!cf_item_is_pair(ci)) {
2213                         radlog(L_ERR, "rlm_ldap (%s): Entry is not in "
2214                                "\"ldap-attribute = value\" format",
2215                                inst->xlat_name);
2216                                
2217                         goto error;
2218                 }
2219         
2220                 /*
2221                  *      Retrieve all the information we need about the pair
2222                  */
2223                 cp = cf_itemtopair(ci);
2224                 value = cf_pair_value(cp);
2225                 attr = cf_pair_attr(cp);
2226                 op = cf_pair_operator(cp);
2227                 
2228                 if ((value == NULL) || (*value == '\0')) {
2229                         RDEBUG("empty value string, "
2230                                "skipping attribute \"%s\"", attr);
2231                         
2232                         continue;
2233                 }
2234                 
2235                 switch (cf_pair_value_type(cp))
2236                 {
2237                         case T_BARE_WORD:
2238                                 if (strcmp(value, "ANY") == 0) {
2239                                         if (op != T_OP_SUB) {
2240                                                 radlog(L_ERR, "rlm_ldap (%s): "
2241                                                        "ANY is only supported "
2242                                                        "for delete operations ",
2243                                                        inst->xlat_name);
2244                                                 
2245                                                 goto error;     
2246                                         }
2247                                         
2248                                         is_wildcard = TRUE;
2249                                 }
2250                         case T_SINGLE_QUOTED_STRING:
2251                         break;
2252                         case T_BACK_QUOTED_STRING:
2253                         case T_DOUBLE_QUOTED_STRING:
2254                                 do_xlat = TRUE;         
2255                         break;
2256                         default:
2257                                 rad_assert(0);
2258                                 goto error;
2259                 }
2260                 
2261                 /* 
2262                  *      Will be xlat expanded
2263                  */
2264                 if (do_xlat) {
2265                         p = rad_malloc(1024);
2266                         radius_xlat(p, 1024, value, request, NULL, NULL);
2267                         
2268                         if (*p == '\0') {
2269                         
2270                                 RDEBUG("xlat failed or empty value string, "
2271                                        "skipping attribute \"%s\"", attr);
2272                                        
2273                                 free(p);
2274                                 
2275                                 continue;
2276                         }
2277                         
2278                         expanded[last_exp++] = p;
2279                         passed[last_pass] = p;
2280                 /* 
2281                  *      The ANY keyword used as a wildcard
2282                  */
2283                 } else if (is_wildcard) {
2284                         passed[last_pass] = NULL;
2285                         
2286                 /* 
2287                  *      Static strings
2288                  */
2289                 } else {
2290                         memcpy(&(passed[last_pass]), &value,
2291                                sizeof(passed[last_pass]));
2292                 }
2293                 
2294                 passed[last_pass + 1] = NULL;
2295                 
2296                 mod_s[total].mod_values = &(passed[last_pass]);
2297                                         
2298                 last_pass += 2;
2299                 
2300                 switch (op)
2301                 {
2302                         /*
2303                          *  T_OP_EQ is *NOT* supported, it is impossible to
2304                          *  support because of the lack of transactions in LDAP
2305                          */
2306                         case T_OP_ADD:
2307                                 mod_s[total].mod_op = LDAP_MOD_ADD;
2308                         break;
2309                         case T_OP_SET:
2310                                 mod_s[total].mod_op = LDAP_MOD_REPLACE;
2311                         break;
2312                         case T_OP_SUB:
2313                                 mod_s[total].mod_op = LDAP_MOD_DELETE;
2314                         break;
2315 #ifdef LDAP_MOD_INCREMENT
2316                         case T_OP_INCRM:
2317                                 mod_s[total].mod_op = LDAP_MOD_INCREMENT;
2318                         break;
2319 #endif
2320                         default:
2321                                 radlog(L_ERR, "rlm_ldap (%s): Operator '%s' "
2322                                        "is not supported for LDAP modify "
2323                                        "operations", inst->xlat_name,
2324                                        fr_int2str(fr_tokens, op, "¿unknown?"));
2325                                        
2326                                 goto error;
2327                 }
2328                 
2329                 /*
2330                  *      Now we know the value is ok, copy the pointers into
2331                  *      the ldapmod struct.
2332                  */
2333                 memcpy(&(mod_s[total].mod_type), &(attr), 
2334                        sizeof(mod_s[total].mod_type));
2335                 
2336                 mod_p[total] = &(mod_s[total]);
2337                 total++;
2338         }
2339         
2340         mod_p[total] = NULL;
2341         
2342         /*
2343          *      Perform all modifications as the default admin user.
2344          */
2345         if (conn->rebound) {
2346                 ldap_errno = ldap_bind_wrapper(&conn,
2347                                                inst->login, inst->password,
2348                                                NULL, TRUE);
2349                 if (ldap_errno != RLM_MODULE_OK) {
2350                         goto error;
2351                 }
2352
2353                 rad_assert(conn != NULL);
2354                 conn->rebound = FALSE;
2355         }
2356
2357         user_dn = get_userdn(&conn, request, &module_rcode);
2358         if (!user_dn) {
2359                 module_rcode = RLM_MODULE_NOTFOUND;
2360                 goto release;
2361         }
2362         
2363         RDEBUG2("Modifying user object with DN \"%s\"", user_dn);
2364         
2365         ldap_errno = ldap_modify_ext_s(conn->handle, user_dn, modify, NULL,
2366                                        NULL);
2367                              
2368         if (ldap_errno != LDAP_SUCCESS) {
2369                 error_string = ldap_err2string(ldap_errno);
2370
2371                 radlog(L_ERR, "rlm_ldap (%s): Modifying DN \"%s\" failed: %s",
2372                        inst->xlat_name, user_dn, error_string);
2373                 
2374         error:
2375                 module_rcode = RLM_MODULE_FAIL;
2376                 
2377                 goto release;
2378         }
2379         
2380         RDEBUG2("Modification successful!");
2381         
2382         release:
2383         
2384         /*
2385          *      Free up any buffers we allocated for xlat expansion
2386          */     
2387         for (i = 0; i < last_exp; i++) {
2388                 free(expanded[i]);
2389         }
2390         
2391         ldap_release_socket(inst, conn);
2392         
2393         return module_rcode;
2394 }
2395
2396
2397 static int ldap_accounting(void *instance, REQUEST * request) {
2398         ldap_instance *inst = instance;         
2399
2400         if (inst->accounting) {
2401                 return user_modify(inst, request, inst->accounting); 
2402         }
2403         
2404         return RLM_MODULE_NOOP;
2405 }
2406
2407
2408 /** Check the user's password against ldap database
2409  *
2410  */
2411 static int ldap_postauth(void *instance, REQUEST * request)
2412 {
2413         ldap_instance   *inst = instance;
2414
2415         if (inst->postauth) {
2416                 return user_modify(inst, request, inst->postauth); 
2417         }
2418
2419         return RLM_MODULE_NOOP;
2420 }
2421
2422
2423 /* globally exported name */
2424 module_t rlm_ldap = {
2425         RLM_MODULE_INIT,
2426         "ldap",
2427         RLM_TYPE_THREAD_SAFE,   /* type: reserved        */
2428         ldap_instantiate,       /* instantiation         */
2429         ldap_detach,            /* detach                */
2430         {
2431                 ldap_authenticate,      /* authentication        */
2432                 ldap_authorize,         /* authorization         */
2433                 NULL,                   /* preaccounting         */
2434                 ldap_accounting,        /* accounting            */
2435                 NULL,                   /* checksimul            */
2436                 NULL,                   /* pre-proxy             */
2437                 NULL,                   /* post-proxy            */
2438                 ldap_postauth           /* post-auth */
2439         },
2440 };