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