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