1 /*
2  * mod_ldap - LDAP password lookup module for ProFTPD
3  * Copyright (c) 1999-2013, John Morrissey <jwm@horde.net>
4  * Copyright (c) 2013-2020 The ProFTPD Project
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19  *
20  * Furthermore, John Morrissey gives permission to link this program with
21  * OpenSSL, and distribute the resulting executable, without including the
22  * source code for OpenSSL in the source distribution.
23  *
24  * -----DO NOT EDIT BELOW THIS LINE-----
25  * $Libraries: -lldap -llber$
26  */
27 
28 #include "conf.h"
29 #include "privs.h"
30 
31 #define MOD_LDAP_VERSION	"mod_ldap/2.9.5"
32 
33 #if PROFTPD_VERSION_NUMBER < 0x0001030103
34 # error MOD_LDAP_VERSION " requires ProFTPD 1.3.4rc1 or later"
35 #endif
36 
37 #if defined(HAVE_CRYPT_H) && !defined(AIX4) && !defined(AIX5)
38 # include <crypt.h>
39 #endif
40 
41 #include <lber.h>
42 #include <ldap.h>
43 #if defined(HAVE_SASL_SASL_H)
44 # include <sasl/sasl.h>
45 #endif /* HAVE_SASL_SASL_H */
46 
47 extern xaset_t *server_list;
48 
49 module ldap_module;
50 
51 static int ldap_logfd = -1;
52 static pool *ldap_pool = NULL;
53 
54 static const char *trace_channel = "ldap";
55 #if defined(LBER_OPT_LOG_PRINT_FN)
56 static const char *libtrace_channel = "ldap.library";
57 #endif
58 
59 #if LDAP_API_VERSION >= 2000
60 # define HAS_LDAP_SASL_BIND_S
61 #endif
62 
63 #if defined(LDAP_SASL_QUIET)
64 # define HAS_LDAP_SASL_INTERACTIVE_BIND_S
65 #endif /* LDAP_SASL_QUIET */
66 
67 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_VENDOR_VERSION >= 192)
68 # define HAS_LDAP_UNBIND_EXT_S
69 #endif
70 
71 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_VENDOR_VERSION >= 19905)
72 # define HAS_LDAP_INITIALIZE
73 #endif
74 
75 #ifdef HAS_LDAP_UNBIND_EXT_S
76 # define LDAP_UNBIND(ld) (ldap_unbind_ext_s(ld, NULL, NULL))
77 #else
78 # define LDAP_UNBIND(ld) (ldap_unbind_s(ld))
79 static char *ldap_server;
80 static int ldap_port = LDAP_PORT;
81 #endif
82 
83 /* On some systems LDAP_OPT_DIAGNOSTIC_MESSAGE isn't there (e.g. OpenLDAP-2.3.x)
84  * but LDAP_OPT_ERROR_STRING is.
85  */
86 #ifndef LDAP_OPT_DIAGNOSTIC_MESSAGE
87 # ifdef LDAP_OPT_ERROR_STRING
88 #  define LDAP_OPT_DIAGNOSTIC_MESSAGE LDAP_OPT_ERROR_STRING
89 # endif
90 #endif
91 
92 #if LDAP_API_VERSION >= 2000
93 # define LDAP_VALUE_T struct berval
94 # define LDAP_GET_VALUES(ld, entry, attr) ldap_get_values_len(ld, entry, attr)
95 # define LDAP_VALUE(values, i) (values[i]->bv_val)
96 # define LDAP_COUNT_VALUES(values) (ldap_count_values_len(values))
97 # define LDAP_VALUE_FREE(values) (ldap_value_free_len(values))
98 # define LDAP_SEARCH(ld, base, scope, filter, attrs, timeout, sizelimit, res) \
99    ldap_search_ext_s(ld, base, scope, filter, attrs, 0, NULL, NULL, \
100                      timeout, sizelimit, res)
101 #else /* LDAP_API_VERSION >= 2000 */
102 # define LDAP_VALUE_T char
103 # define LDAP_GET_VALUES(ld, entry, attr) ldap_get_values(ld, entry, attr)
104 # define LDAP_VALUE(values, i) (values[i])
105 # define LDAP_COUNT_VALUES(values) (ldap_count_values(values))
106 # define LDAP_VALUE_FREE(values) (ldap_value_free(values))
107 
pr_ldap_set_sizelimit(LDAP * limit_ld,int limit)108 static void pr_ldap_set_sizelimit(LDAP *limit_ld, int limit) {
109 #ifdef LDAP_OPT_SIZELIMIT
110   int res;
111 
112   res = ldap_set_option(limit_ld, LDAP_OPT_SIZELIMIT, (void *) &limit);
113   if (res != LDAP_OPT_SUCCESS) {
114     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
115       "failed to set LDAP option for search query size limit to %d entries: %s",
116       limit, ldap_err2string(res));
117 
118   } else {
119     pr_trace_msg(trace_channel, 5,
120       "set search query size limit to %d entries", limit);
121   }
122 
123 #else
124   limit_ld->ld_sizelimit = limit;
125 
126   pr_trace_msg(trace_channel, 5,
127     "set search query size limit to %d entries", limit);
128 #endif
129 }
130 
131 static int
LDAP_SEARCH(LDAP * ld,char * base,int scope,char * filter,char * attrs[],struct timeval * timeout,int sizelimit,LDAPMessage ** res)132 LDAP_SEARCH(LDAP *ld, char *base, int scope, char *filter, char *attrs[],
133             struct timeval *timeout, int sizelimit, LDAPMessage **res) {
134   pr_ldap_set_sizelimit(ld, sizelimit);
135   return ldap_search_st(ld, base, scope, filter, attrs, 0, timeout, res);
136 }
137 #endif /* LDAP_API_VERSION >= 2000 */
138 
139 /* Thanks, Sun. */
140 #ifndef LDAP_OPT_SUCCESS
141 # define LDAP_OPT_SUCCESS LDAP_SUCCESS
142 #endif
143 #ifndef LDAP_URL_SUCCESS
144 # define LDAP_URL_SUCCESS LDAP_SUCCESS
145 #endif
146 #ifndef LDAP_SCOPE_DEFAULT
147 # define LDAP_SCOPE_DEFAULT LDAP_SCOPE_SUBTREE
148 #endif
149 
150 /* Config entries */
151 
152 struct server_info {
153   const char *info_text;
154   char *host;
155   int port;
156   char *port_text;
157   LDAPURLDesc *url_desc;
158   char *url_text;
159   int use_starttls;
160 
161   /* For configuring the SSL/TLS session to the LDAP server. */
162   const char *ssl_cert_file;
163   const char *ssl_key_file;
164   const char *ssl_ca_file;
165   const char *ssl_ciphers;
166   int ssl_verify;
167   const char *ssl_verify_text;
168 };
169 
170 struct sasl_info {
171   pool *pool;
172 
173   /* SASL_AUTHCID from ldap.conf */
174   const char *authentication_id;
175 
176   /* SASL_AUTHZID from ldap.conf */
177   const char *authorization_id;
178 
179   const char *password;
180 
181   /* SASL_REALM from ldap.conf */
182   const char *realm;
183 };
184 
185 static array_header *ldap_servers = NULL;
186 static struct server_info *curr_server_info = NULL;
187 static unsigned int curr_server_index = 0;
188 
189 static char *ldap_dn, *ldap_dnpass, *ldap_sasl_mechs = NULL,
190             *ldap_user_basedn = NULL, *ldap_user_name_filter = NULL,
191             *ldap_user_uid_filter = NULL,
192             *ldap_gid_basedn = NULL, *ldap_group_gid_filter = NULL,
193             *ldap_group_name_filter = NULL, *ldap_group_member_filter = NULL,
194             *ldap_defaultauthscheme = "crypt", *ldap_authbind_dn = NULL,
195             *ldap_genhdir_prefix = NULL, *ldap_default_quota = NULL,
196             *ldap_attr_uid = "uid",
197             *ldap_attr_uidnumber = "uidNumber",
198             *ldap_attr_gidnumber = "gidNumber",
199             *ldap_attr_homedirectory = "homeDirectory",
200             *ldap_attr_userpassword = "userPassword",
201             *ldap_attr_loginshell = "loginShell",
202             *ldap_attr_cn = "cn",
203             *ldap_attr_memberuid = "memberUid",
204             *ldap_attr_ftpquota = "ftpQuota",
205             *ldap_attr_ftpquota_profiledn = "ftpQuotaProfileDN",
206             *ldap_attr_ssh_pubkey = "sshPublicKey";
207 
208 static int ldap_do_users = FALSE, ldap_do_groups = FALSE,
209            ldap_authbinds = TRUE, ldap_querytimeout = 0,
210            ldap_genhdir = FALSE, ldap_genhdir_prefix_nouname = FALSE,
211            ldap_forcedefaultuid = FALSE, ldap_forcedefaultgid = FALSE,
212            ldap_forcegenhdir = FALSE, ldap_protocol_version = 3,
213            ldap_dereference = LDAP_DEREF_NEVER,
214            ldap_search_scope = LDAP_SCOPE_SUBTREE;
215 
216 static size_t ldap_dnpasslen = 0;
217 
218 static struct timeval ldap_querytimeout_tv;
219 #define PR_LDAP_QUERY_TIMEOUT_DEFAULT		5
220 
221 static uid_t ldap_defaultuid = -1;
222 static gid_t ldap_defaultgid = -1;
223 static LDAP *ld = NULL;
224 static array_header *cached_quota = NULL;
225 static array_header *cached_ssh_pubkeys = NULL;
226 
227 /* Necessary prototypes */
228 static int ldap_sess_init(void);
229 static struct sasl_info *sasl_info_create(pool *, LDAP *);
230 static void sasl_info_get_authcid_from_dn(struct sasl_info *, const char *);
231 
pr_ldap_unbind(void)232 static void pr_ldap_unbind(void) {
233   int res;
234 
235   if (ld == NULL) {
236     return;
237   }
238 
239   res = LDAP_UNBIND(ld);
240   if (res != LDAP_SUCCESS) {
241     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
242       "error unbinding connection: %s", ldap_err2string(res));
243 
244   } else {
245     pr_trace_msg(trace_channel, 8, "connection successfully unbound");
246   }
247 
248   ld = NULL;
249 }
250 
log_sasl_mechs(LDAP * conn_ld,const char * url_text)251 static void log_sasl_mechs(LDAP *conn_ld, const char *url_text) {
252 #if defined(LDAP_OPT_X_SASL_MECHLIST)
253   char **sasl_mechs = NULL;
254 
255   if (ldap_get_option(conn_ld, LDAP_OPT_X_SASL_MECHLIST,
256       &sasl_mechs) == LDAP_OPT_SUCCESS) {
257     if (sasl_mechs != NULL) {
258       register unsigned int i;
259 
260       for (i = 0; sasl_mechs[i]; i++) {
261         pr_trace_msg(trace_channel, 22,
262           "%s: LDAP supported SASL mechanism = %s", url_text, sasl_mechs[i]);
263       }
264     }
265   }
266 #endif /* LDAP_OPT_X_SASL_MECHLIST */
267 }
268 
do_ldap_prepare(LDAP ** conn_ld)269 static int do_ldap_prepare(LDAP **conn_ld) {
270 #if defined(HAS_LDAP_INITIALIZE)
271   int res;
272   char *url_text = NULL;
273 
274   if (curr_server_info != NULL) {
275     url_text = curr_server_info->url_text;
276     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
277       "attempting connection to URL %s", url_text);
278 
279   } else {
280     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
281       "attempting connection (see ldap.conf for details)");
282   }
283 
284   res = ldap_initialize(conn_ld, url_text);
285   if (res != LDAP_SUCCESS) {
286     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
287       "ldap_initialize() to URL %s failed: %s",
288       url_text ? url_text : "(null)", ldap_err2string(res));
289 
290     *conn_ld = NULL;
291     return -1;
292   }
293 
294   ldap_search_scope = curr_server_info->url_desc->lud_scope;
295 
296 #else
297   char *host = NULL;
298   int port = LDAP_PORT;
299 
300   if (curr_server_info != NULL) {
301     host = curr_server_info->host;
302     port = curr_server_info->port;
303 
304     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
305       "attempting connection to %s:%d", host, port);
306 
307   } else {
308     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
309       "attempting connection (see ldap.conf for details)");
310   }
311 
312   *conn_ld = ldap_init(host, port);
313   if (*conn_ld == NULL) {
314     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
315       "ldap_init() to %s:%d failed: %s", host ? host : "(null)", port,
316       strerror(errno));
317     return -1;
318   }
319 #endif /* HAS_LDAP_INITIALIZE */
320 
321   log_sasl_mechs(*conn_ld, curr_server_info->info_text);
322   return 0;
323 }
324 
sasl_interact_cb(LDAP * conn_ld,unsigned int flags,void * user_data,void * interact_data)325 static int sasl_interact_cb(LDAP *conn_ld, unsigned int flags, void *user_data,
326     void *interact_data) {
327   int res = LDAP_OPERATIONS_ERROR;
328 
329 #if defined(HAS_LDAP_SASL_INTERACTIVE_BIND_S) && \
330     defined(HAVE_SASL_SASL_H)
331   sasl_interact_t *interacts;
332   struct sasl_info *sasl;
333 
334   interacts = interact_data;
335   sasl = user_data;
336 
337   while (interacts->id != SASL_CB_LIST_END) {
338     pr_signals_handle();
339 
340     switch (interacts->id) {
341       case SASL_CB_AUTHNAME:
342         interacts->result = sasl->authentication_id;
343         interacts->len = strlen(interacts->result);
344         pr_trace_msg(trace_channel, 19, "SASL interaction: CB_AUTHNAME = '%s'",
345           (char *) interacts->result);
346         break;
347 
348       case SASL_CB_GETREALM:
349         /* The cyrus-sasl library works just fine with an empty string for our
350          * currently supported mechanisms.
351          */
352         interacts->result = sasl->realm;
353         interacts->len = strlen(interacts->result);
354         pr_trace_msg(trace_channel, 19, "SASL interaction: CB_GETREALM = '%s'",
355           (char *) interacts->result);
356         break;
357 
358       case SASL_CB_PASS:
359         interacts->result = sasl->password;
360         interacts->len = strlen(interacts->result);
361         pr_trace_msg(trace_channel, 19, "SASL interaction: CB_PASS = '...'");
362         break;
363 
364       case SASL_CB_USER:
365         /* The cyrus-sasl library handles this as the "authorization ID",
366          * and works just fine with an empty string for our currently
367          * supported mechanisms.
368          */
369         interacts->result = sasl->authorization_id;
370         interacts->len = strlen(interacts->result);
371         pr_trace_msg(trace_channel, 19, "SASL interaction: CB_USER = '%s'",
372           (char *) interacts->result);
373         break;
374 
375       case SASL_CB_ECHOPROMPT:
376       case SASL_CB_NOECHOPROMPT:
377         /* Ignore */
378         break;
379 
380       default:
381         break;
382     }
383 
384     interacts++;
385   }
386 
387   res = LDAP_SUCCESS;
388 #else
389   pr_log_debug(DEBUG0, MOD_LDAP_VERSION
390     ": interactive SASL authentication unsupported");
391   res = LDAP_OPERATIONS_ERROR;
392 #endif /* HAS_LDAP_SASL_INTERACTIVE_BIND_S and HAVE_SASL_SASL_H */
393 
394   return res;
395 }
396 
do_ldap_bind(LDAP * conn_ld)397 static int do_ldap_bind(LDAP *conn_ld) {
398   int res;
399 
400 #if defined(HAS_LDAP_SASL_INTERACTIVE_BIND_S)
401   if (ldap_sasl_mechs != NULL) {
402     int sasl_flags;
403     struct sasl_info *sasl;
404 
405     pr_trace_msg(trace_channel, 17, "performing bind using SASL mechs: '%s'",
406       ldap_sasl_mechs);
407 
408     sasl = sasl_info_create(session.pool, conn_ld);
409     sasl_info_get_authcid_from_dn(sasl, ldap_dn);
410     sasl->password = ldap_dnpass;
411 
412     sasl_flags = LDAP_SASL_QUIET;
413 
414     res = ldap_sasl_interactive_bind_s(conn_ld, ldap_dn, ldap_sasl_mechs,
415       NULL, NULL, sasl_flags, sasl_interact_cb, sasl);
416 
417     destroy_pool(sasl->pool);
418 
419   } else {
420     struct berval bindcred;
421 
422     bindcred.bv_val = ldap_dnpass;
423     bindcred.bv_len = ldap_dnpasslen;
424 
425     res = ldap_sasl_bind_s(conn_ld, ldap_dn, NULL, &bindcred, NULL, NULL, NULL);
426 
427     if (res == LDAP_SUCCESS) {
428       if (ldap_dnpasslen > 0) {
429         pr_trace_msg(trace_channel, 9,
430           "bind to '%s' used simple authentication",
431           curr_server_info->info_text);
432 
433       } else {
434         pr_trace_msg(trace_channel, 9,
435           "bind to '%s' used anonymous authentication",
436           curr_server_info->info_text);
437       }
438     }
439   }
440 #else /* HAS_LDAP_SASL_BIND_S */
441   res = ldap_simple_bind_s(conn_ld, ldap_dn, ldap_dnpass);
442 #endif /* HAS_LDAP_SASL_BIND_S */
443 
444   if (res != LDAP_SUCCESS) {
445     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
446       "bind as DN '%s' failed for '%s': %s",
447       ldap_dn ? ldap_dn : "(anonymous)", curr_server_info->info_text,
448       ldap_err2string(res));
449     return -1;
450   }
451 
452   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
453     "successfully bound as DN '%s' with password %s for '%s'",
454     ldap_dn ? ldap_dn : "(anonymous)",
455     ldap_dnpass ? "(see config)" : "(none)", curr_server_info->info_text);
456 
457   return 0;
458 }
459 
do_ldap_connect(LDAP ** conn_ld,int do_bind)460 static int do_ldap_connect(LDAP **conn_ld, int do_bind) {
461   int res, version;
462 
463   /* Note that because we use ldap_init(3)/ldap_initialize(3), the first
464    * actual TCP connection does not occur until the first operation is
465    * requested.
466    */
467   res = do_ldap_prepare(conn_ld);
468   if (res < 0) {
469     return -1;
470   }
471 
472   version = LDAP_VERSION3;
473   if (ldap_protocol_version == 2) {
474     version = LDAP_VERSION2;
475   }
476 
477   res = ldap_set_option(*conn_ld, LDAP_OPT_PROTOCOL_VERSION, &version);
478   if (res != LDAP_OPT_SUCCESS) {
479     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
480       "error setting LDAP protocol version option to %d: %s", version,
481       ldap_err2string(res));
482     pr_ldap_unbind();
483     return -1;
484   }
485 
486   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
487     "set LDAP protocol version to %d", version);
488 
489   if (curr_server_info->use_starttls == TRUE) {
490 #if defined(LDAP_OPT_X_TLS)
491 # if defined(LDAP_OPT_X_TLS_CACERTFILE)
492     if (curr_server_info->ssl_ca_file != NULL) {
493       res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
494         curr_server_info->ssl_ca_file);
495       if (res != LDAP_OPT_SUCCESS) {
496         pr_log_debug(DEBUG5, MOD_LDAP_VERSION
497           ": error setting LDAP_OPT_X_TLS_CACERTFILE = %s: %s",
498           curr_server_info->ssl_ca_file, ldap_err2string(res));
499 
500       } else {
501         pr_trace_msg(trace_channel, 17,
502           "set LDAP_OPT_X_TLS_CACERTFILE = %s for '%s'",
503           curr_server_info->ssl_ca_file, curr_server_info->info_text);
504       }
505     }
506 # endif /* LDAP_OPT_X_TLS_CACERTFILE */
507 
508 # if defined(LDAP_OPT_X_TLS_CERTFILE)
509     if (curr_server_info->ssl_cert_file != NULL) {
510       res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE,
511         curr_server_info->ssl_cert_file);
512       if (res != LDAP_OPT_SUCCESS) {
513         pr_log_debug(DEBUG5, MOD_LDAP_VERSION
514           ": error setting LDAP_OPT_X_TLS_CERTFILE = %s: %s",
515           curr_server_info->ssl_cert_file, ldap_err2string(res));
516 
517       } else {
518         pr_trace_msg(trace_channel, 17,
519           "set LDAP_OPT_X_TLS_CERTFILE = %s for '%s'",
520           curr_server_info->ssl_cert_file, curr_server_info->info_text);
521       }
522     }
523 # endif /* LDAP_OPT_X_TLS_CERTFILE */
524 
525 # if defined(LDAP_OPT_X_TLS_KEYFILE)
526     if (curr_server_info->ssl_key_file != NULL) {
527       res = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE,
528         curr_server_info->ssl_key_file);
529       if (res != LDAP_OPT_SUCCESS) {
530         pr_log_debug(DEBUG5, MOD_LDAP_VERSION
531           ": error setting LDAP_OPT_X_TLS_KEYFILE = %s: %s",
532           curr_server_info->ssl_key_file, ldap_err2string(res));
533 
534       } else {
535         pr_trace_msg(trace_channel, 17,
536           "set LDAP_OPT_X_TLS_KEYFILE = %s for '%s'",
537           curr_server_info->ssl_key_file, curr_server_info->info_text);
538       }
539     }
540 # endif /* LDAP_OPT_X_TLS_KEYFILE */
541 
542 # if defined(LDAP_OPT_X_TLS_CIPHER_SUITE)
543     if (curr_server_info->ssl_ciphers != NULL) {
544       res = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
545         curr_server_info->ssl_ciphers);
546       if (res != LDAP_OPT_SUCCESS) {
547         pr_log_debug(DEBUG5, MOD_LDAP_VERSION
548           ": error setting LDAP_OPT_X_TLS_CIPHER_SUITE = %s: %s",
549           curr_server_info->ssl_ciphers, ldap_err2string(res));
550 
551       } else {
552         pr_trace_msg(trace_channel, 17,
553           "set LDAP_OPT_X_TLS_CIPHER_SUITE = %s for '%s'",
554           curr_server_info->ssl_ciphers, curr_server_info->info_text);
555       }
556     }
557 # endif /* LDAP_OPT_X_TLS_CIPHER_SUITE */
558 
559 # if defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
560     if (curr_server_info->ssl_verify != -1) {
561       res = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
562         &(curr_server_info->ssl_verify));
563       if (res != LDAP_OPT_SUCCESS) {
564         pr_log_debug(DEBUG5, MOD_LDAP_VERSION
565           ": error setting LDAP_OPT_X_TLS_REQUIRE_CERT = %s: %s",
566           curr_server_info->ssl_verify_text, ldap_err2string(res));
567 
568       } else {
569         pr_trace_msg(trace_channel, 17,
570           "set LDAP_OPT_X_TLS_REQUIRE_CERT = %s for '%s'",
571           curr_server_info->ssl_verify_text, curr_server_info->info_text);
572       }
573     }
574 # endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
575 
576     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
577       "LDAPUseTLS in effect, performing STARTTLS handshake on '%s'",
578       curr_server_info->info_text);
579     res = ldap_start_tls_s(*conn_ld, NULL, NULL);
580     if (res != LDAP_SUCCESS) {
581       char *diag_msg = NULL;
582 
583       (void) ldap_get_option(*conn_ld, LDAP_OPT_DIAGNOSTIC_MESSAGE,
584         (void *) &diag_msg);
585 
586       if (diag_msg != NULL) {
587         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
588          "failed to start TLS: %s: %s", ldap_err2string(res), diag_msg);
589         ldap_memfree(diag_msg);
590 
591       } else {
592         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
593          "failed to start TLS: %s", ldap_err2string(res));
594       }
595 
596       pr_ldap_unbind();
597       return -1;
598     }
599 
600     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
601       "enabled TLS for connection to '%s'", curr_server_info->info_text);
602   }
603 #endif /* LDAP_OPT_X_TLS */
604 
605   if (do_bind == TRUE) {
606     res = do_ldap_bind(*conn_ld);
607     if (res < 0) {
608       pr_ldap_unbind();
609       return -1;
610     }
611   }
612 
613 #ifdef LDAP_OPT_DEREF
614   res = ldap_set_option(*conn_ld, LDAP_OPT_DEREF, (void *) &ldap_dereference);
615   if (res != LDAP_OPT_SUCCESS) {
616     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
617       "failed to set LDAP option for dereference to %d: %s", ldap_dereference,
618       ldap_err2string(res));
619     pr_ldap_unbind();
620     return -1;
621   }
622 
623 #else
624   deref_ld->ld_deref = ldap_dereference;
625 #endif
626 
627   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
628     "set dereferencing to %d", ldap_dereference);
629 
630   ldap_querytimeout_tv.tv_sec = (ldap_querytimeout > 0 ? ldap_querytimeout :
631     PR_LDAP_QUERY_TIMEOUT_DEFAULT);
632   ldap_querytimeout_tv.tv_usec = 0;
633 
634   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
635     "set query timeout to %u secs", (unsigned int) ldap_querytimeout_tv.tv_sec);
636 
637   return 0;
638 }
639 
640 #if defined(LBER_OPT_LOG_PRINT_FN)
ldap_tracelog_cb(const char * msg)641 static void ldap_tracelog_cb(const char *msg) {
642   (void) pr_trace_msg(libtrace_channel, 1, "%s", msg);
643 }
644 #endif /* no LBER_OPT_LOG_PRINT_FN */
645 
pr_ldap_connect(LDAP ** conn_ld,int do_bind)646 static int pr_ldap_connect(LDAP **conn_ld, int do_bind) {
647   unsigned int start_server_index;
648 
649   start_server_index = curr_server_index;
650   do {
651     int res, debug_level = -1;
652 
653     pr_signals_handle();
654 
655     /* Note that ldap_servers might be NULL if no LDAPServer directive was
656      * specified.  We fall back to using the SDK default.
657      */
658     if (ldap_servers != NULL) {
659       curr_server_info = ((struct server_info **) ldap_servers->elts)[curr_server_index];
660     }
661 
662     res = do_ldap_connect(conn_ld, do_bind);
663     if (res < 0) {
664       ++curr_server_index;
665       if (curr_server_index >= ldap_servers->nelts) {
666         curr_server_index = 0;
667       }
668 
669       continue;
670     }
671 
672     /* This debug level value should be LDAP_DEBUG_ANY, but that macro is, I
673      * think, OpenLDAP-specific.
674      */
675     res = ldap_set_option(*conn_ld, LDAP_OPT_DEBUG_LEVEL, &debug_level);
676     if (res != LDAP_OPT_SUCCESS) {
677       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
678         "error setting DEBUG_ANY debug level: %s", ldap_err2string(res));
679     }
680 
681     return 0;
682 
683   } while (curr_server_index != start_server_index);
684 
685   return -1;
686 }
687 
pr_ldap_interpolate_filter(pool * p,char * template,const char * value)688 static const char *pr_ldap_interpolate_filter(pool *p, char *template,
689     const char *value) {
690   const char *escaped_value, *filter;
691 
692   escaped_value = sreplace(p, (char *) value,
693     "\\", "\\\\",
694     "*", "\\*",
695     "(", "\\(",
696     ")", "\\)",
697     NULL
698   );
699 
700   if (escaped_value == NULL) {
701     return NULL;
702   }
703 
704   filter = sreplace(p, template,
705     "%u", escaped_value,
706     "%v", escaped_value,
707     NULL
708   );
709 
710   if (filter == NULL) {
711     return NULL;
712   }
713 
714   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
715     "generated filter %s from template %s and value %s", filter, template,
716     value);
717   return filter;
718 }
719 
pr_ldap_search(const char * basedn,const char * filter,char * attrs[],int sizelimit,int retry)720 static LDAPMessage *pr_ldap_search(const char *basedn, const char *filter,
721     char *attrs[], int sizelimit, int retry) {
722   int res;
723   LDAPMessage *result;
724 
725   if (basedn == NULL) {
726     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
727       "no LDAP base DN specified for search filter %s, declining request",
728       filter ? filter : "(null)");
729     return NULL;
730   }
731 
732   /* If the LDAP connection has gone away or hasn't been established
733    * yet, attempt to establish it now.
734    */
735   if (ld == NULL) {
736     /* If we _still_ can't connect, give up and return NULL. */
737     if (pr_ldap_connect(&ld, TRUE) < 0) {
738       return NULL;
739     }
740   }
741 
742   res = LDAP_SEARCH(ld, basedn, ldap_search_scope, filter, attrs,
743     &ldap_querytimeout_tv, sizelimit, &result);
744   if (res != LDAP_SUCCESS) {
745     if (res != LDAP_SERVER_DOWN) {
746       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
747         "LDAP search use DN '%s', filter '%s' failed: %s", basedn, filter,
748         ldap_err2string(res));
749       return NULL;
750     }
751 
752     if (!retry) {
753       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
754         "LDAP connection went away, search failed");
755       pr_ldap_unbind();
756       return NULL;
757     }
758 
759     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
760       "LDAP connection went away, retrying search operation");
761     pr_ldap_unbind();
762     return pr_ldap_search(basedn, filter, attrs, sizelimit, FALSE);
763   }
764 
765   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
766     "searched under base DN %s using filter %s", basedn,
767     filter ? filter : "(null)");
768   return result;
769 }
770 
pr_ldap_user_lookup(pool * p,char * filter_template,const char * replace,const char * basedn,char * attrs[],char ** user_dn)771 static struct passwd *pr_ldap_user_lookup(pool *p, char *filter_template,
772     const char *replace, const char *basedn, char *attrs[], char **user_dn) {
773   const char *filter;
774   char *dn;
775   int i = 0;
776   struct passwd *pw;
777   LDAPMessage *result, *e;
778   LDAP_VALUE_T **values;
779 
780   filter = pr_ldap_interpolate_filter(p, filter_template, replace);
781   if (filter == NULL) {
782     return NULL;
783   }
784 
785   result = pr_ldap_search(basedn, filter, attrs, 2, TRUE);
786   if (result == NULL) {
787     return NULL;
788   }
789 
790   if (ldap_count_entries(ld, result) > 1) {
791     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
792       "LDAP search returned multiple entries during user lookup, "
793       "aborting query");
794     ldap_msgfree(result);
795     return NULL;
796   }
797 
798   e = ldap_first_entry(ld, result);
799   if (e == NULL) {
800     ldap_msgfree(result);
801 
802     /* No LDAP entries for this user. */
803     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
804       "no entries for filter %s under base DN %s", filter, basedn);
805     return NULL;
806   }
807 
808   pw = pcalloc(ldap_pool, sizeof(struct passwd));
809   while (attrs[i] != NULL) {
810     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
811       "fetching values for attribute %s", attrs[i]);
812 
813     values = LDAP_GET_VALUES(ld, e, attrs[i]);
814     if (values == NULL) {
815       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
816         "no values for attribute %s, trying defaults", attrs[i]);
817 
818       /* Apply default values for attrs with no explicit values. */
819 
820       /* If we can't find the [ug]idNumber attrs, just fill the passwd
821        * struct in with default values from the config file.
822        */
823       if (strcasecmp(attrs[i], ldap_attr_uidnumber) == 0) {
824         if (ldap_defaultuid == (uid_t) -1) {
825           dn = ldap_get_dn(ld, e);
826 
827           (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
828             "no %s attribute for DN %s found, and LDAPDefaultUID not "
829             "configured", ldap_attr_uidnumber, dn);
830           free(dn);
831           return NULL;
832         }
833 
834         pw->pw_uid = ldap_defaultuid;
835         ++i;
836 
837         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
838           "using LDAPDefaultUID %s", pr_uid2str(NULL, pw->pw_uid));
839         continue;
840       }
841 
842       if (strcasecmp(attrs[i], ldap_attr_gidnumber) == 0) {
843         if (ldap_defaultgid == (gid_t) -1) {
844           dn = ldap_get_dn(ld, e);
845 
846           (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
847             "no %s attribute found for DN %s,  and LDAPDefaultGID not "
848             "configured", ldap_attr_gidnumber, dn);
849           free(dn);
850           return NULL;
851         }
852 
853         pw->pw_gid = ldap_defaultgid;
854         ++i;
855 
856         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
857           "using LDAPDefaultGID %s", pr_gid2str(NULL, pw->pw_gid));
858         continue;
859       }
860 
861       if (strcasecmp(attrs[i], ldap_attr_homedirectory) == 0) {
862         if (ldap_genhdir == FALSE ||
863             ldap_genhdir_prefix == NULL) {
864           dn = ldap_get_dn(ld, e);
865 
866           if (ldap_genhdir == FALSE) {
867             (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
868               "no %s attribute for DN %s, LDAPGenerateHomedir not enabled",
869               ldap_attr_homedirectory, dn);
870 
871           } else {
872             (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
873               "no %s attribute for DN %s, LDAPGenerateHomedir enabled but "
874               "LDAPGenerateHomedirPrefix not configured",
875               ldap_attr_homedirectory, dn);
876           }
877 
878           free(dn);
879           return NULL;
880         }
881 
882         if (ldap_genhdir_prefix_nouname == TRUE) {
883           pw->pw_dir = pstrcat(session.pool, ldap_genhdir_prefix, NULL);
884 
885         } else {
886           LDAP_VALUE_T **canon_username;
887           canon_username = LDAP_GET_VALUES(ld, e, ldap_attr_uid);
888           if (canon_username == NULL) {
889             dn = ldap_get_dn(ld, e);
890 
891             (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
892               "could not get %s attribute for canonical username for DN %s",
893               ldap_attr_uid, dn);
894             free(dn);
895             return NULL;
896           }
897 
898           pw->pw_dir = pstrcat(session.pool, ldap_genhdir_prefix, "/",
899             LDAP_VALUE(canon_username, 0), NULL);
900           LDAP_VALUE_FREE(canon_username);
901         }
902 
903         ++i;
904 
905         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
906           "using default homedir %s", pw->pw_dir);
907         continue;
908       }
909 
910       /* Don't worry if we don't have a loginShell attr. */
911       if (strcasecmp(attrs[i], ldap_attr_loginshell) == 0) {
912         /* Prevent a segfault if no loginShell attribute, and
913          * "RequireValidShell on" is in effect.
914          */
915         pw->pw_shell = pstrdup(session.pool, "");
916         ++i;
917         continue;
918       }
919 
920       /* We only restart the while loop above if we can fill in alternate
921        * values for certain attributes. If something odd has happened, we
922        * fall through so we can complain.
923        */
924 
925       dn = ldap_get_dn(ld, e);
926 
927       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
928         "could not get values for attribute %s for DN %s, ignoring request "
929         "(perhaps this DN's entry does not have the attribute?)", attrs[i], dn);
930       free(dn);
931       ldap_msgfree(result);
932       return NULL;
933     }
934 
935     /* Now that we've handled default values, fill in the struct as normal;
936      * the if branches below for nonexistent attrs will just never be
937      * called.
938      */
939 
940     if (strcasecmp(attrs[i], ldap_attr_uid) == 0) {
941       pw->pw_name = pstrdup(session.pool, LDAP_VALUE(values, 0));
942 
943     } else if (strcasecmp(attrs[i], ldap_attr_userpassword) == 0) {
944       pw->pw_passwd = pstrdup(session.pool, LDAP_VALUE(values, 0));
945 
946     } else if (strcasecmp(attrs[i], ldap_attr_uidnumber) == 0) {
947       if (ldap_forcedefaultuid == TRUE &&
948           ldap_defaultuid != (uid_t) -1) {
949         pw->pw_uid = ldap_defaultuid;
950 
951       } else {
952         pw->pw_uid = (uid_t) strtoul(LDAP_VALUE(values, 0), NULL, 10);
953       }
954 
955     } else if (strcasecmp(attrs[i], ldap_attr_gidnumber) == 0) {
956       if (ldap_forcedefaultgid == TRUE &&
957           ldap_defaultgid != (gid_t) -1) {
958         pw->pw_gid = ldap_defaultgid;
959 
960       } else {
961         pw->pw_gid = (gid_t) strtoul(LDAP_VALUE(values, 0), NULL, 10);
962       }
963 
964     } else if (strcasecmp(attrs[i], ldap_attr_homedirectory) == 0) {
965       if (ldap_forcegenhdir == TRUE) {
966         if (ldap_genhdir == FALSE ||
967             ldap_genhdir_prefix == NULL) {
968 
969           if (ldap_genhdir == FALSE) {
970             (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
971               "LDAPForceGeneratedHomedir enabled but LDAPGenerateHomedir is "
972               "not enabled");
973 
974           } else {
975             (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
976               "LDAPForceGeneratedHomedir and LDAPGenerateHomedir enabled, but "
977               "missing required LDAPGenerateHomedirPrefix");
978           }
979 
980           return NULL;
981         }
982 
983         if (pw->pw_dir != NULL) {
984           pr_trace_msg(trace_channel, 8, "LDAPForceGeneratedHomedir in effect, "
985             "overriding current LDAP home directory '%s'", pw->pw_dir);
986         }
987 
988         if (ldap_genhdir_prefix_nouname == TRUE) {
989           pw->pw_dir = pstrdup(session.pool, ldap_genhdir_prefix);
990 
991         } else {
992           LDAP_VALUE_T **canon_username;
993           canon_username = LDAP_GET_VALUES(ld, e, ldap_attr_uid);
994           if (canon_username == NULL) {
995             dn = ldap_get_dn(ld, e);
996 
997             (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
998               "could not get %s attribute for canonical username for DN %s",
999               ldap_attr_uid, dn);
1000             free(dn);
1001             return NULL;
1002           }
1003 
1004           pw->pw_dir = pstrcat(session.pool, ldap_genhdir_prefix, "/",
1005             LDAP_VALUE(canon_username, 0), NULL);
1006           LDAP_VALUE_FREE(canon_username);
1007         }
1008 
1009       } else {
1010         pw->pw_dir = pstrdup(session.pool, LDAP_VALUE(values, 0));
1011       }
1012 
1013       pr_trace_msg(trace_channel, 8, "using LDAP home directory '%s'",
1014         pw->pw_dir);
1015 
1016     } else if (strcasecmp(attrs[i], ldap_attr_loginshell) == 0) {
1017       pw->pw_shell = pstrdup(session.pool, LDAP_VALUE(values, 0));
1018 
1019     } else {
1020       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1021         "user lookup attribute/value loop found unknown attribute %s",
1022         attrs[i]);
1023     }
1024 
1025     LDAP_VALUE_FREE(values);
1026     ++i;
1027   }
1028 
1029   /* If we're doing auth binds, save the DN of this entry so we can
1030    * bind to the LDAP server as it later.
1031    */
1032   if (user_dn) {
1033     *user_dn = ldap_get_dn(ld, e);
1034   }
1035 
1036   ldap_msgfree(result);
1037 
1038   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1039     "found user %s, UID %s, GID %s, homedir %s, shell %s",
1040     pw->pw_name, pr_uid2str(p, pw->pw_uid), pr_gid2str(p, pw->pw_gid),
1041     pw->pw_dir, pw->pw_shell);
1042   return pw;
1043 }
1044 
pr_ldap_group_lookup(pool * p,char * filter_template,const char * replace,char * attrs[])1045 static struct group *pr_ldap_group_lookup(pool *p, char *filter_template,
1046     const char *replace, char *attrs[]) {
1047   const char *filter;
1048   char *dn;
1049   int i = 0, value_count = 0, value_offset;
1050   struct group *gr;
1051   LDAPMessage *result, *e;
1052   LDAP_VALUE_T **values;
1053 
1054   if (ldap_gid_basedn == NULL) {
1055     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1056       "no LDAP base DN specified for group lookups");
1057     return NULL;
1058   }
1059 
1060   filter = pr_ldap_interpolate_filter(p, filter_template, replace);
1061   if (filter == NULL) {
1062     return NULL;
1063   }
1064 
1065   result = pr_ldap_search(ldap_gid_basedn, filter, attrs, 2, TRUE);
1066   if (result == NULL) {
1067     return NULL;
1068   }
1069 
1070   e = ldap_first_entry(ld, result);
1071   if (e == NULL) {
1072     ldap_msgfree(result);
1073 
1074     /* No LDAP entries found for this user. */
1075     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1076       "no group entries for filter %s", filter);
1077     return NULL;
1078   }
1079 
1080   gr = pcalloc(session.pool, sizeof(struct group));
1081   while (attrs[i] != NULL) {
1082     pr_signals_handle();
1083 
1084     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1085       "fetching values for attribute %s", attrs[i]);
1086 
1087     values = LDAP_GET_VALUES(ld, e, attrs[i]);
1088     if (values == NULL) {
1089       if (strcasecmp(attrs[i], ldap_attr_memberuid) == 0) {
1090         gr->gr_mem = palloc(session.pool, 2 * sizeof(char *));
1091         gr->gr_mem[0] = pstrdup(session.pool, "");
1092         gr->gr_mem[1] = NULL;
1093 
1094         ++i;
1095         continue;
1096       }
1097 
1098       ldap_msgfree(result);
1099       dn = ldap_get_dn(ld, e);
1100 
1101       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1102         "could not get values for attribute %s for DN %s, ignoring request "
1103         "(perhaps that DN does not have that attribute?)", attrs[i], dn);
1104       free(dn);
1105       return NULL;
1106     }
1107 
1108     if (strcasecmp(attrs[i], ldap_attr_cn) == 0) {
1109       gr->gr_name = pstrdup(session.pool, LDAP_VALUE(values, 0));
1110 
1111     } else if (strcasecmp(attrs[i], ldap_attr_gidnumber) == 0) {
1112       gr->gr_gid = strtoul(LDAP_VALUE(values, 0), NULL, 10);
1113 
1114     } else if (strcasecmp(attrs[i], ldap_attr_memberuid) == 0) {
1115       value_count = LDAP_COUNT_VALUES(values);
1116       gr->gr_mem = (char **) palloc(session.pool, value_count * sizeof(char *));
1117 
1118       for (value_offset = 0; value_offset < value_count; ++value_offset) {
1119         gr->gr_mem[value_offset] =
1120           pstrdup(session.pool, LDAP_VALUE(values, value_offset));
1121       }
1122 
1123     } else {
1124       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1125         "group lookup attribute/value loop found unknown attribute %s",
1126         attrs[i]);
1127     }
1128 
1129     LDAP_VALUE_FREE(values);
1130     ++i;
1131   }
1132 
1133   ldap_msgfree(result);
1134 
1135   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1136     "found group %s, GID %s", gr->gr_name, pr_gid2str(NULL, gr->gr_gid));
1137   for (i = 0; i < value_count; ++i) {
1138     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1139       "+ member: %s", gr->gr_mem[i]);
1140   }
1141 
1142   return gr;
1143 }
1144 
parse_quota(pool * p,const char * replace,char * str)1145 static void parse_quota(pool *p, const char *replace, char *str) {
1146   char **elts, *token;
1147 
1148   if (cached_quota == NULL) {
1149     cached_quota = make_array(p, 9, sizeof(char *));
1150   }
1151 
1152   elts = (char **) cached_quota->elts;
1153   elts[0] = pstrdup(session.pool, replace);
1154   cached_quota->nelts = 1;
1155 
1156   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1157     "parsing ftpQuota attribute value '%s'", str);
1158 
1159   while ((token = strsep(&str, ","))) {
1160     pr_signals_handle();
1161     *((char **) push_array(cached_quota)) = pstrdup(session.pool, token);
1162   }
1163 }
1164 
pr_ldap_quota_lookup(pool * p,char * filter_template,const char * replace,const char * basedn)1165 static unsigned char pr_ldap_quota_lookup(pool *p, char *filter_template,
1166     const char *replace, const char *basedn) {
1167   const char *filter = NULL;
1168   char *attrs[] = {
1169          ldap_attr_ftpquota, ldap_attr_ftpquota_profiledn, NULL,
1170        };
1171   int orig_scope, res;
1172   LDAPMessage *result, *e;
1173   LDAP_VALUE_T **values;
1174 
1175   if (basedn == NULL) {
1176     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1177       "no LDAP base DN specified for quota lookups, declining request");
1178     return FALSE;
1179   }
1180 
1181   if (filter_template != NULL) {
1182     filter = pr_ldap_interpolate_filter(p, filter_template, replace);
1183     if (filter == NULL) {
1184       return FALSE;
1185     }
1186   }
1187 
1188   result = pr_ldap_search(basedn, filter, attrs, 2, TRUE);
1189   if (result == NULL) {
1190     return FALSE;
1191   }
1192 
1193   if (ldap_count_entries(ld, result) > 1) {
1194     ldap_msgfree(result);
1195 
1196     if (ldap_default_quota != NULL) {
1197       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1198         "multiple entries found for DN %s, using default quota %s", basedn,
1199           ldap_default_quota);
1200       parse_quota(p, replace, pstrdup(p, ldap_default_quota));
1201       return TRUE;
1202 
1203     } else {
1204       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1205         "multiple entries found for DN %s, aborting query", basedn);
1206     }
1207 
1208     return FALSE;
1209   }
1210 
1211   e = ldap_first_entry(ld, result);
1212   if (e == NULL) {
1213     ldap_msgfree(result);
1214     if (ldap_default_quota == NULL) {
1215       if (filter == NULL) {
1216         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1217           "no entries for DN %s, and no default quota defined", basedn);
1218 
1219       } else {
1220         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1221           "no entries for filter %s, and no default quota defined", filter);
1222       }
1223 
1224       return FALSE;
1225     }
1226 
1227     if (filter == NULL) {
1228       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1229         "no entries for DN %s, using default quota %s", basedn,
1230         ldap_default_quota);
1231 
1232     } else {
1233       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1234         "no entries for filter %s, using default quota %s", filter,
1235         ldap_default_quota);
1236     }
1237 
1238     parse_quota(p, replace, pstrdup(p, ldap_default_quota));
1239     return TRUE;
1240   }
1241 
1242   values = LDAP_GET_VALUES(ld, e, attrs[0]);
1243   if (values != NULL) {
1244     parse_quota(p, replace, pstrdup(p, LDAP_VALUE(values, 0)));
1245     LDAP_VALUE_FREE(values);
1246     ldap_msgfree(result);
1247     return TRUE;
1248   }
1249 
1250   if (filter == NULL) {
1251     if (ldap_default_quota == NULL) {
1252       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1253         "referenced DN %s does not have an ftpQuota attribute, and no "
1254         "default quota defined", basedn);
1255       return FALSE;
1256     }
1257 
1258     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1259       "no ftpQuota attribute found for DN %s, using default quota %s", basedn,
1260       ldap_default_quota);
1261     parse_quota(p, replace, pstrdup(p, ldap_default_quota));
1262     return TRUE;
1263   }
1264 
1265   values = LDAP_GET_VALUES(ld, e, attrs[1]);
1266   if (values != NULL) {
1267     orig_scope = ldap_search_scope;
1268     ldap_search_scope = LDAP_SCOPE_BASE;
1269     res = pr_ldap_quota_lookup(p, NULL, replace, LDAP_VALUE(values, 0));
1270     ldap_search_scope = orig_scope;
1271     LDAP_VALUE_FREE(values);
1272     ldap_msgfree(result);
1273     return res;
1274   }
1275 
1276   ldap_msgfree(result);
1277   if (ldap_default_quota != NULL) {
1278     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1279       "no %s or %s attribute, using default quota %s", attrs[0], attrs[1],
1280       ldap_default_quota);
1281     parse_quota(p, replace, pstrdup(p, ldap_default_quota));
1282     return TRUE;
1283   }
1284 
1285   /* No quota attributes for this user. */
1286   (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1287     "no %s or %s attribute, and no default quota defined", attrs[0], attrs[1]);
1288   return FALSE;
1289 }
1290 
pr_ldap_ssh_pubkey_lookup(pool * p,char * filter_template,const char * replace,char * basedn)1291 static unsigned char pr_ldap_ssh_pubkey_lookup(pool *p, char *filter_template,
1292     const char *replace, char *basedn) {
1293   const char *filter;
1294   char *attrs[] = {
1295     ldap_attr_ssh_pubkey, NULL,
1296   };
1297   int num_keys, i;
1298   LDAPMessage *result, *e;
1299   LDAP_VALUE_T **values;
1300 
1301   if (basedn == NULL) {
1302     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1303       "no LDAP base DN specified for user lookups, declining SSH publickey "
1304       "lookup request");
1305     return FALSE;
1306   }
1307 
1308   filter = pr_ldap_interpolate_filter(p, filter_template, replace);
1309   if (filter == NULL) {
1310     return FALSE;
1311   }
1312 
1313   result = pr_ldap_search(basedn, filter, attrs, 2, TRUE);
1314   if (result == NULL) {
1315     return FALSE;
1316   }
1317 
1318   if (ldap_count_entries(ld, result) > 1) {
1319     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1320       "LDAP search for SSH publickey using DN %s, filter %s returned multiple "
1321       "entries, aborting query", basedn, filter);
1322     ldap_msgfree(result);
1323     return FALSE;
1324   }
1325 
1326   e = ldap_first_entry(ld, result);
1327   if (e == NULL) {
1328     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1329       "LDAP search for SSH publickey using DN %s, filter %s returned "
1330       "no entries", basedn, filter);
1331     ldap_msgfree(result);
1332     return FALSE;
1333   }
1334 
1335   values = LDAP_GET_VALUES(ld, e, attrs[0]);
1336   if (values == NULL) {
1337     return FALSE;
1338   }
1339 
1340   num_keys = LDAP_COUNT_VALUES(values);
1341   cached_ssh_pubkeys = make_array(p, num_keys, sizeof(char *));
1342   for (i = 0; i < num_keys; ++i) {
1343     *((char **) push_array(cached_ssh_pubkeys)) = pstrdup(p,
1344       LDAP_VALUE(values, i));
1345   }
1346   LDAP_VALUE_FREE(values);
1347 
1348   ldap_msgfree(result);
1349   return TRUE;
1350 }
1351 
pr_ldap_getgrnam(pool * p,const char * group_name)1352 static struct group *pr_ldap_getgrnam(pool *p, const char *group_name) {
1353   char *group_attrs[] = {
1354     ldap_attr_cn, ldap_attr_gidnumber, ldap_attr_memberuid, NULL,
1355   };
1356 
1357   return pr_ldap_group_lookup(p, ldap_group_name_filter, group_name,
1358     group_attrs);
1359 }
1360 
pr_ldap_getgrgid(pool * p,gid_t gid)1361 static struct group *pr_ldap_getgrgid(pool *p, gid_t gid) {
1362   const char *gidstr;
1363   char *group_attrs[] = {
1364     ldap_attr_cn, ldap_attr_gidnumber, ldap_attr_memberuid, NULL,
1365   };
1366 
1367   gidstr = pr_gid2str(p, gid);
1368   return pr_ldap_group_lookup(p, ldap_group_gid_filter, gidstr, group_attrs);
1369 }
1370 
pr_ldap_getpwnam(pool * p,const char * username)1371 static struct passwd *pr_ldap_getpwnam(pool *p, const char *username) {
1372   const char *filter;
1373   char *name_attrs[] = {
1374          ldap_attr_userpassword, ldap_attr_uid, ldap_attr_uidnumber,
1375          ldap_attr_gidnumber, ldap_attr_homedirectory,
1376          ldap_attr_loginshell, NULL,
1377        };
1378 
1379   filter = pr_ldap_interpolate_filter(p, ldap_user_basedn, username);
1380   if (filter == NULL) {
1381     return NULL;
1382   }
1383 
1384   /* pr_ldap_user_lookup() returns NULL if it doesn't find an entry or
1385    * encounters an error. If everything goes all right, it returns a
1386    * struct passwd, so we can just return its result directly.
1387    *
1388    * We also do some cute stuff here to work around lameness in LDAP servers
1389    * like Sun Directory Services (SDS) 1.x and 3.x. If you request an attr
1390    * that you don't have access to, SDS totally ignores any entries with
1391    * that attribute. Thank you, Sun; how very smart of you. So if we're
1392    * doing auth binds, we don't request the userPassword attr.
1393    *
1394    * NOTE: if the UserPassword directive is configured, mod_auth will pass
1395    * a crypted password to ldap_auth_check(), which will NOT do auth binds
1396    * in order to support UserPassword. (Otherwise, it would try binding to
1397    * the directory and would ignore UserPassword.)
1398    *
1399    * We're reasonably safe in making that assumption as long as we never
1400    * fetch userPassword from the directory if auth binds are enabled. If we
1401    * fetched userPassword, auth binds would never be done because
1402    * ldap_auth_check() would always get a crypted password.
1403    */
1404   return pr_ldap_user_lookup(p, ldap_user_name_filter, username, filter,
1405     ldap_authbinds ? name_attrs + 1 : name_attrs,
1406     ldap_authbinds ? &ldap_authbind_dn : NULL);
1407 }
1408 
pr_ldap_getpwuid(pool * p,uid_t uid)1409 static struct passwd *pr_ldap_getpwuid(pool *p, uid_t uid) {
1410   const char *uidstr;
1411   char *uid_attrs[] = {
1412     ldap_attr_uid, ldap_attr_uidnumber, ldap_attr_gidnumber,
1413     ldap_attr_homedirectory, ldap_attr_loginshell, NULL,
1414   };
1415 
1416   uidstr = pr_uid2str(p, uid);
1417   return pr_ldap_user_lookup(p, ldap_user_uid_filter, uidstr,
1418     ldap_user_basedn, uid_attrs, ldap_authbinds ? &ldap_authbind_dn : NULL);
1419 }
1420 
handle_ldap_quota_lookup(cmd_rec * cmd)1421 MODRET handle_ldap_quota_lookup(cmd_rec *cmd) {
1422   const char *basedn;
1423 
1424   basedn = pr_ldap_interpolate_filter(cmd->tmp_pool,
1425     ldap_user_basedn, cmd->argv[0]);
1426   if (basedn == NULL) {
1427     return PR_DECLINED(cmd);
1428   }
1429 
1430   if (cached_quota == NULL ||
1431       strcasecmp(((char **) cached_quota->elts)[0], cmd->argv[0]) != 0) {
1432 
1433     if (pr_ldap_quota_lookup(cmd->tmp_pool, ldap_user_name_filter,
1434         cmd->argv[0], basedn) == FALSE) {
1435       return PR_DECLINED(cmd);
1436     }
1437 
1438   } else {
1439     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1440       "returning cached quota for user %s", (char *) cmd->argv[0]);
1441   }
1442 
1443   return mod_create_data(cmd, cached_quota);
1444 }
1445 
handle_ldap_ssh_pubkey_lookup(cmd_rec * cmd)1446 MODRET handle_ldap_ssh_pubkey_lookup(cmd_rec *cmd) {
1447   char *user;
1448 
1449   if (ldap_do_users == FALSE) {
1450     return PR_DECLINED(cmd);
1451   }
1452 
1453   user = cmd->argv[0];
1454 
1455   if (cached_ssh_pubkeys != NULL &&
1456       cached_ssh_pubkeys->nelts > 0 &&
1457       strcasecmp(((char **) cached_ssh_pubkeys->elts)[0], user) == 0) {
1458 
1459     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1460       "returning cached SSH public keys for user %s", user);
1461     return mod_create_data(cmd, cached_ssh_pubkeys);
1462   }
1463 
1464   if (pr_ldap_ssh_pubkey_lookup(cmd->tmp_pool, ldap_user_name_filter,
1465       user, ldap_user_basedn) == FALSE) {
1466     return PR_DECLINED(cmd);
1467   }
1468 
1469   return mod_create_data(cmd, cached_ssh_pubkeys);
1470 }
1471 
1472 /* Auth handlers */
ldap_auth_setpwent(cmd_rec * cmd)1473 MODRET ldap_auth_setpwent(cmd_rec *cmd) {
1474   if (ldap_do_users == FALSE &&
1475       ldap_do_groups == FALSE) {
1476     return PR_DECLINED(cmd);
1477   }
1478 
1479   if (ld == NULL) {
1480     (void) pr_ldap_connect(&ld, TRUE);
1481   }
1482 
1483   return PR_HANDLED(cmd);
1484 }
1485 
ldap_auth_endpwent(cmd_rec * cmd)1486 MODRET ldap_auth_endpwent(cmd_rec *cmd) {
1487   if (ldap_do_users == FALSE &&
1488       ldap_do_groups == FALSE) {
1489     return PR_DECLINED(cmd);
1490   }
1491 
1492   pr_ldap_unbind();
1493   return PR_HANDLED(cmd);
1494 }
1495 
ldap_auth_getpwuid(cmd_rec * cmd)1496 MODRET ldap_auth_getpwuid(cmd_rec *cmd) {
1497   struct passwd *pw = NULL;
1498 
1499   if (ldap_do_users == FALSE) {
1500     return PR_DECLINED(cmd);
1501   }
1502 
1503   pw = pr_ldap_getpwuid(cmd->tmp_pool, *((uid_t *) cmd->argv[0]));
1504   if (pw != NULL) {
1505     return mod_create_data(cmd, pw);
1506   }
1507 
1508   return PR_DECLINED(cmd);
1509 }
1510 
ldap_auth_getpwnam(cmd_rec * cmd)1511 MODRET ldap_auth_getpwnam(cmd_rec *cmd) {
1512   struct passwd *pw = NULL;
1513 
1514   if (ldap_do_users == FALSE) {
1515     return PR_DECLINED(cmd);
1516   }
1517 
1518   pw = pr_ldap_getpwnam(cmd->tmp_pool, cmd->argv[0]);
1519   if (pw != NULL) {
1520     return mod_create_data(cmd, pw);
1521   }
1522 
1523   return PR_DECLINED(cmd);
1524 }
1525 
ldap_auth_getgrnam(cmd_rec * cmd)1526 MODRET ldap_auth_getgrnam(cmd_rec *cmd) {
1527   struct group *gr = NULL;
1528 
1529   if (ldap_do_groups == FALSE) {
1530     return PR_DECLINED(cmd);
1531   }
1532 
1533   gr = pr_ldap_getgrnam(cmd->tmp_pool, cmd->argv[0]);
1534   if (gr != NULL) {
1535     return mod_create_data(cmd, gr);
1536   }
1537 
1538   return PR_DECLINED(cmd);
1539 }
1540 
ldap_auth_getgrgid(cmd_rec * cmd)1541 MODRET ldap_auth_getgrgid(cmd_rec *cmd) {
1542   struct group *gr = NULL;
1543 
1544   if (ldap_do_groups == FALSE) {
1545     return PR_DECLINED(cmd);
1546   }
1547 
1548   gr = pr_ldap_getgrgid(cmd->tmp_pool, *((gid_t *) cmd->argv[0]));
1549   if (gr != NULL) {
1550     return mod_create_data(cmd, gr);
1551   }
1552 
1553   return PR_DECLINED(cmd);
1554 }
1555 
ldap_auth_getgroups(cmd_rec * cmd)1556 MODRET ldap_auth_getgroups(cmd_rec *cmd) {
1557   const char *filter;
1558   char *w[] = {
1559     ldap_attr_gidnumber, ldap_attr_cn, NULL,
1560   };
1561   struct passwd *pw;
1562   struct group *gr;
1563   LDAPMessage *result = NULL, *e;
1564   LDAP_VALUE_T **gidNumber, **cn;
1565   array_header *gids   = (array_header *)cmd->argv[1],
1566                *groups = (array_header *)cmd->argv[2];
1567 
1568   if (ldap_do_groups == FALSE) {
1569     return PR_DECLINED(cmd);
1570   }
1571 
1572   if (gids == NULL ||
1573       groups == NULL) {
1574     return PR_DECLINED(cmd);
1575   }
1576 
1577   pw = pr_ldap_getpwnam(cmd->tmp_pool, cmd->argv[0]);
1578   if (pw != NULL) {
1579     gr = pr_ldap_getgrgid(cmd->tmp_pool, pw->pw_gid);
1580     if (gr != NULL) {
1581       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1582         "adding user %s primary group %s/%s", pw->pw_name, gr->gr_name,
1583         pr_gid2str(NULL, pw->pw_gid));
1584       *((gid_t *) push_array(gids)) = pw->pw_gid;
1585       *((char **) push_array(groups)) = pstrdup(session.pool, gr->gr_name);
1586 
1587     } else {
1588       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1589         "unable to determine group name for user %s primary GID %s, skipping",
1590         pw->pw_name, pr_gid2str(NULL, pw->pw_gid));
1591     }
1592   }
1593 
1594   if (ldap_gid_basedn == NULL) {
1595     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1596       "no LDAP base DN specified for group lookups");
1597     goto return_groups;
1598   }
1599 
1600   filter = pr_ldap_interpolate_filter(cmd->tmp_pool,
1601     ldap_group_member_filter, cmd->argv[0]);
1602   if (filter == NULL) {
1603     return NULL;
1604   }
1605 
1606   result = pr_ldap_search(ldap_gid_basedn, filter, w, 0, TRUE);
1607   if (result == NULL) {
1608     return FALSE;
1609   }
1610 
1611   if (ldap_count_entries(ld, result) == 0) {
1612     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1613       "no entries found for filter %s", filter);
1614     goto return_groups;
1615   }
1616 
1617   for (e = ldap_first_entry(ld, result); e; e = ldap_next_entry(ld, e)) {
1618     gidNumber = LDAP_GET_VALUES(ld, e, w[0]);
1619     if (gidNumber == NULL) {
1620       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1621         "could not get values for %s attribute for getgroups(2), skipping "
1622         "current group", ldap_attr_gidnumber);
1623       continue;
1624     }
1625 
1626     cn = LDAP_GET_VALUES(ld, e, w[1]);
1627     if (cn == NULL) {
1628       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1629         "could not get values for %s attribute for getgroups(2), skipping "
1630         "current group", ldap_attr_cn);
1631       continue;
1632     }
1633 
1634     if (pw == NULL ||
1635         strtoul(LDAP_VALUE(gidNumber, 0), NULL, 10) != pw->pw_gid) {
1636       *((gid_t *) push_array(gids)) = strtoul(LDAP_VALUE(gidNumber, 0), NULL, 10);
1637       *((char **) push_array(groups)) = pstrdup(session.pool, LDAP_VALUE(cn, 0));
1638 
1639       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1640         "added user %s secondary group %s/%s",
1641         (pw && pw->pw_name) ? pw->pw_name : (char *) cmd->argv[0],
1642         LDAP_VALUE(cn, 0), LDAP_VALUE(gidNumber, 0));
1643     }
1644 
1645     LDAP_VALUE_FREE(gidNumber);
1646     LDAP_VALUE_FREE(cn);
1647   }
1648 
1649 return_groups:
1650   if (result) {
1651     ldap_msgfree(result);
1652   }
1653 
1654   if (gids->nelts > 0) {
1655     return mod_create_data(cmd, (void *) &gids->nelts);
1656   }
1657 
1658   return PR_DECLINED(cmd);
1659 }
1660 
1661 /* cmd->argv[0] : user name
1662  * cmd->argv[1] : cleartext password
1663  */
ldap_auth_auth(cmd_rec * cmd)1664 MODRET ldap_auth_auth(cmd_rec *cmd) {
1665   const char *filter = NULL, *username;
1666   char *pass_attrs[] = {
1667          ldap_attr_userpassword, ldap_attr_uid, ldap_attr_uidnumber,
1668          ldap_attr_gidnumber, ldap_attr_homedirectory,
1669          ldap_attr_loginshell, NULL,
1670        };
1671   struct passwd *pw = NULL;
1672   int res;
1673 
1674   if (ldap_do_users == FALSE) {
1675     return PR_DECLINED(cmd);
1676   }
1677 
1678   username = cmd->argv[0];
1679 
1680   filter = pr_ldap_interpolate_filter(cmd->tmp_pool, ldap_user_basedn,
1681     username);
1682   if (filter == NULL) {
1683     return NULL;
1684   }
1685 
1686   /* If anything here fails hard (IOW, we've found an LDAP entry for the
1687    * user, but they appear to have entered the wrong password), fail auth.
1688    * Normally, I'd DECLINE here so other modules could have a shot, but if
1689    * we've found their LDAP entry, chances are that nothing else will be
1690    * able to auth them.
1691    */
1692 
1693   pw = pr_ldap_user_lookup(cmd->tmp_pool,
1694     ldap_user_name_filter, username, filter,
1695     ldap_authbinds ? pass_attrs + 1 : pass_attrs,
1696     ldap_authbinds ? &ldap_authbind_dn : NULL);
1697   if (pw == NULL) {
1698     /* Can't find the user in the LDAP directory. */
1699     return PR_DECLINED(cmd);
1700   }
1701 
1702   if (ldap_authbinds == FALSE &&
1703       pw->pw_passwd == NULL) {
1704     (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1705       "LDAPAuthBinds not enabled, and unable to retrieve password for user %s",
1706       pw->pw_name);
1707     return PR_ERROR_INT(cmd, PR_AUTH_NOPWD);
1708   }
1709 
1710   res = pr_auth_check(cmd->tmp_pool, ldap_authbinds ? NULL : pw->pw_passwd,
1711     username, cmd->argv[1]);
1712   if (res != 0) {
1713     if (res == -1) {
1714       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1715         "bad password for user %s: %s", pw->pw_name, strerror(errno));
1716 
1717     } else {
1718       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1719         "bad password for user %s", pw->pw_name);
1720     }
1721 
1722     return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1723   }
1724 
1725   session.auth_mech = "mod_ldap.c";
1726   return PR_HANDLED(cmd);
1727 }
1728 
1729 /* cmd->argv[0] = hashed password
1730  * cmd->argv[1] = user
1731  * cmd->argv[2] = cleartext
1732  */
ldap_auth_check(cmd_rec * cmd)1733 MODRET ldap_auth_check(cmd_rec *cmd) {
1734   char *pass, *cryptpass, *hash_method, *crypted;
1735   int encname_len, res;
1736   LDAP *ld_auth;
1737 #ifdef HAS_LDAP_SASL_BIND_S
1738   struct berval bindcred;
1739 #endif
1740 
1741   if (ldap_do_users == FALSE) {
1742     return PR_DECLINED(cmd);
1743   }
1744 
1745   cryptpass = cmd->argv[0];
1746   pass = cmd->argv[2];
1747 
1748   /* At this point, any encrypted password must have come from the UserPassword
1749    * directive. Don't perform auth binds in this case, since the crypted
1750    * password specified should override auth binds.
1751    */
1752   if (ldap_authbinds == TRUE &&
1753       cryptpass == NULL) {
1754     /* Don't try to do auth binds with a NULL/empty DN or password. */
1755     if (pass == NULL ||
1756         strlen(pass) == 0) {
1757       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1758         "LDAPAuthBinds is enabled, but no user-supplied cleartext password "
1759         "was found");
1760       return PR_DECLINED(cmd);
1761     }
1762 
1763     if (ldap_authbind_dn == NULL ||
1764         strlen(ldap_authbind_dn) == 0) {
1765       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1766         "LDAPAuthBinds is enabled, but no LDAP DN was found");
1767       return PR_DECLINED(cmd);
1768     }
1769 
1770     if (pr_ldap_connect(&ld_auth, FALSE) < 0) {
1771       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1772         "unable to check login: LDAP connection failed");
1773       return PR_DECLINED(cmd);
1774     }
1775 
1776 #ifdef HAS_LDAP_SASL_BIND_S
1777     bindcred.bv_val = cmd->argv[2];
1778     bindcred.bv_len = strlen(cmd->argv[2]);
1779     res = ldap_sasl_bind_s(ld_auth, ldap_authbind_dn, NULL, &bindcred,
1780       NULL, NULL, NULL);
1781 #else /* HAS_LDAP_SASL_BIND_S */
1782     res = ldap_simple_bind_s(ld_auth, ldap_authbind_dn, cmd->argv[2]);
1783 #endif /* HAS_LDAP_SASL_BIND_S */
1784 
1785     if (res != LDAP_SUCCESS) {
1786       if (res != LDAP_INVALID_CREDENTIALS) {
1787         (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1788           "unable to check login: bind as %s failed: %s", ldap_authbind_dn,
1789           ldap_err2string(res));
1790       }
1791 
1792       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
1793         "invalid credentials used for %s", ldap_authbind_dn);
1794       LDAP_UNBIND(ld_auth);
1795       return PR_ERROR(cmd);
1796     }
1797 
1798     LDAP_UNBIND(ld_auth);
1799     session.auth_mech = "mod_ldap.c";
1800     return PR_HANDLED(cmd);
1801   }
1802 
1803   /* Get the length of "scheme" in the leading {scheme} so we can skip it
1804    * in the password comparison.
1805    */
1806   encname_len = strcspn(cryptpass + 1, "}");
1807   hash_method = pstrndup(cmd->tmp_pool, cryptpass + 1, encname_len);
1808 
1809   /* Check to see how the password is encrypted, and check accordingly. */
1810 
1811   if ((size_t) encname_len == strlen(cryptpass + 1)) {
1812     /* No leading {scheme}. */
1813     hash_method = ldap_defaultauthscheme;
1814     encname_len = 0;
1815 
1816   } else {
1817     encname_len += 2;
1818   }
1819 
1820   /* The {crypt} scheme */
1821   if (strncasecmp(hash_method, "crypt", strlen(hash_method)) == 0) {
1822     crypted = crypt(pass, cryptpass + encname_len);
1823     if (crypted == NULL) {
1824       pr_trace_msg(trace_channel, 19,
1825         "using %s auth scheme, crypt(3) failed: %s", hash_method,
1826         strerror(errno));
1827       return PR_ERROR(cmd);
1828     }
1829 
1830     if (strcmp(crypted, cryptpass + encname_len) != 0) {
1831       pr_trace_msg(trace_channel, 19,
1832         "using '%s' auth scheme, comparison failed", hash_method);
1833       return PR_ERROR(cmd);
1834     }
1835 
1836   /* The {clear} scheme */
1837   } else if (strncasecmp(hash_method, "clear", strlen(hash_method)) == 0) {
1838     if (strcmp(pass, cryptpass + encname_len) != 0) {
1839       pr_trace_msg(trace_channel, 19,
1840         "using '%s' auth scheme, comparison failed", hash_method);
1841       return PR_ERROR(cmd);
1842     }
1843 
1844   } else {
1845     /* Can't find a supported {scheme} */
1846     pr_trace_msg(trace_channel, 3,
1847       "unsupported userPassword auth scheme: %s", hash_method);
1848     return PR_DECLINED(cmd);
1849   }
1850 
1851   session.auth_mech = "mod_ldap.c";
1852   return PR_HANDLED(cmd);
1853 }
1854 
ldap_auth_uid2name(cmd_rec * cmd)1855 MODRET ldap_auth_uid2name(cmd_rec *cmd) {
1856   struct passwd *pw = NULL;
1857 
1858   if (ldap_do_users == FALSE) {
1859     return PR_DECLINED(cmd);
1860   }
1861 
1862   pw = pr_ldap_getpwuid(cmd->tmp_pool, *((uid_t *) cmd->argv[0]));
1863   if (pw == NULL) {
1864     /* Can't find the user in the LDAP directory. */
1865     return PR_DECLINED(cmd);
1866   }
1867 
1868   return mod_create_data(cmd, pstrdup(permanent_pool, pw->pw_name));
1869 }
1870 
ldap_auth_gid2name(cmd_rec * cmd)1871 MODRET ldap_auth_gid2name(cmd_rec *cmd) {
1872   struct group *gr = NULL;
1873 
1874   if (ldap_do_groups == FALSE) {
1875     return PR_DECLINED(cmd);
1876   }
1877 
1878   gr = pr_ldap_getgrgid(cmd->tmp_pool, *((gid_t *) cmd->argv[0]));
1879   if (gr == NULL) {
1880     /* Can't find the user in the LDAP directory. */
1881     return PR_DECLINED(cmd);
1882   }
1883 
1884   return mod_create_data(cmd, pstrdup(permanent_pool, gr->gr_name));
1885 }
1886 
ldap_auth_name2uid(cmd_rec * cmd)1887 MODRET ldap_auth_name2uid(cmd_rec *cmd) {
1888   struct passwd *pw = NULL;
1889 
1890   if (ldap_do_users == FALSE) {
1891     return PR_DECLINED(cmd);
1892   }
1893 
1894   pw = pr_ldap_getpwnam(cmd->tmp_pool, cmd->argv[0]);
1895   if (pw == NULL) {
1896     return PR_DECLINED(cmd);
1897   }
1898 
1899   return mod_create_data(cmd, (void *) &pw->pw_uid);
1900 }
1901 
ldap_auth_name2gid(cmd_rec * cmd)1902 MODRET ldap_auth_name2gid(cmd_rec *cmd) {
1903   struct group *gr = NULL;
1904 
1905   if (ldap_do_groups == FALSE) {
1906     return PR_DECLINED(cmd);
1907   }
1908 
1909   gr = pr_ldap_getgrnam(cmd->tmp_pool, cmd->argv[0]);
1910   if (gr == NULL) {
1911     return PR_DECLINED(cmd);
1912   }
1913 
1914   return mod_create_data(cmd, (void *) &gr->gr_gid);
1915 }
1916 
1917 /* sasl_info functions. */
1918 
1919 /* Note: by proving empty strings, rather than NULLs, for the default values,
1920  * we make the logic in sasl_interact_cb() simpler.
1921  */
sasl_info_create(pool * p,LDAP * conn_ld)1922 static struct sasl_info *sasl_info_create(pool *p, LDAP *conn_ld) {
1923   pool *sasl_pool;
1924   struct sasl_info *sasl;
1925 
1926   sasl_pool = make_sub_pool(p);
1927   pr_pool_tag(sasl_pool, "SASL Info Pool");
1928 
1929   sasl = pcalloc(sasl_pool, sizeof(struct sasl_info));
1930   sasl->pool = sasl_pool;
1931 
1932 #if defined(LDAP_OPT_X_SASL_AUTHCID)
1933   {
1934     int res;
1935     char *sasl_authcid = NULL;
1936 
1937     res = ldap_get_option(conn_ld, LDAP_OPT_X_SASL_AUTHCID, &sasl_authcid);
1938     if (res == LDAP_OPT_SUCCESS) {
1939       if (sasl_authcid != NULL) {
1940         pr_trace_msg(trace_channel, 12,
1941           "LDAP SASL default authentication ID = %s (see SASL_AUTHCID in "
1942           "ldap.conf)", sasl_authcid);
1943         sasl->authentication_id = pstrdup(sasl_pool, sasl_authcid);
1944         ldap_memfree(sasl_authcid);
1945 
1946       } else {
1947         sasl->authentication_id = pstrdup(sasl_pool, "");
1948       }
1949 
1950     } else {
1951       pr_trace_msg(trace_channel, 3,
1952         "error retrieving LDAP_OPT_X_SASL_AUTHCID: %s", ldap_err2string(res));
1953     }
1954   }
1955 #endif /* LDAP_OPT_X_SASL_AUTHCID */
1956 
1957 #if defined(LDAP_OPT_X_SASL_AUTHZID)
1958   {
1959     int res;
1960     char *sasl_authzid = NULL;
1961 
1962     res = ldap_get_option(conn_ld, LDAP_OPT_X_SASL_AUTHZID, &sasl_authzid);
1963     if (res == LDAP_OPT_SUCCESS) {
1964       if (sasl_authzid != NULL) {
1965         pr_trace_msg(trace_channel, 12,
1966           "LDAP SASL default authorization ID = %s (see SASL_AUTHZID in "
1967           "ldap.conf)", sasl_authzid);
1968         sasl->authorization_id = pstrdup(sasl_pool, sasl_authzid);
1969         ldap_memfree(sasl_authzid);
1970 
1971       } else {
1972         sasl->authorization_id = pstrdup(sasl_pool, "");
1973       }
1974 
1975     } else {
1976       pr_trace_msg(trace_channel, 3,
1977         "error retrieving LDAP_OPT_X_SASL_AUTHZID: %s", ldap_err2string(res));
1978     }
1979   }
1980 #endif /* LDAP_OPT_X_SASL_AUTHZID */
1981 
1982 #if defined(LDAP_OPT_X_SASL_REALM)
1983   {
1984     int res;
1985     char *sasl_realm = NULL;
1986 
1987     res = ldap_get_option(conn_ld, LDAP_OPT_X_SASL_REALM, &sasl_realm);
1988     if (res == LDAP_OPT_SUCCESS) {
1989       if (sasl_realm != NULL) {
1990         pr_trace_msg(trace_channel, 12,
1991           "LDAP SASL default realm = %s (see SASL_REALM in ldap.conf)",
1992           sasl_realm);
1993         sasl->realm = pstrdup(sasl_pool, sasl_realm);
1994         ldap_memfree(sasl_realm);
1995 
1996       } else {
1997         sasl->realm = pstrdup(sasl_pool, "");
1998       }
1999 
2000     } else {
2001       pr_trace_msg(trace_channel, 3,
2002         "error retrieving LDAP_OPT_X_SASL_REALM: %s", ldap_err2string(res));
2003     }
2004   }
2005 #endif /* LDAP_OPT_X_SASL_REALM */
2006 
2007   return sasl;
2008 }
2009 
sasl_info_get_authcid_from_dn(struct sasl_info * sasl,const char * dn_text)2010 static void sasl_info_get_authcid_from_dn(struct sasl_info *sasl,
2011     const char *dn_text) {
2012   register unsigned int i;
2013   LDAPDN dn;
2014   int flags = LDAP_DN_FORMAT_LDAPV3, res;
2015 
2016   /* LDAPDN is actually LDAPRDN *, a pointer to a list of LDAPRDN.  Which
2017    * makes sense, since a DN is a list of RDNs.  And each LDAPRDN is actually
2018    * LDAPAVA *, a pointer to a list of LDAPAVA (Attribute Value Assertions).
2019    */
2020   res = ldap_str2dn(dn_text, &dn, flags);
2021   if (res != LDAP_SUCCESS) {
2022     pr_trace_msg(trace_channel, 3,
2023       "error parsing DN '%s': %s", dn_text, ldap_err2string(res));
2024     return;
2025   }
2026 
2027   for (i = 0; dn[i]; i++) {
2028     LDAPRDN rdn;
2029     char *rdn_text = NULL;
2030 
2031     rdn = dn[i];
2032     res = ldap_rdn2str(rdn, &rdn_text, flags);
2033     if (res == LDAP_SUCCESS) {
2034       if (strncasecmp(rdn_text, "CN=", 3) == 0) {
2035         sasl->authentication_id = pstrdup(sasl->pool, rdn_text + 3);
2036       }
2037       ldap_memfree(rdn_text);
2038 
2039       if (sasl->authentication_id != NULL) {
2040         break;
2041       }
2042 
2043     } else {
2044       pr_trace_msg(trace_channel, 3,
2045         "error converting RDN to text: %s", ldap_err2string(res));
2046     }
2047   }
2048 
2049   ldap_dnfree(dn);
2050 }
2051 
2052 /* server_info functions. */
server_info_create(pool * p)2053 static struct server_info *server_info_create(pool *p) {
2054   struct server_info *info;
2055 
2056   info = pcalloc(p, sizeof(struct server_info));
2057   info->ssl_verify = -1;
2058 
2059   return info;
2060 }
2061 
server_info_get_ssl_defaults(struct server_info * info)2062 static void server_info_get_ssl_defaults(struct server_info *info) {
2063 #if defined(LDAP_OPT_X_TLS)
2064   int res, ssl_verify;
2065   char *ssl_val = NULL;
2066 
2067   /* Fill in the SSL defaults, if not explicitly configured. */
2068 
2069 # if defined(LDAP_OPT_X_TLS_CACERTFILE)
2070   if (info->ssl_ca_file == NULL) {
2071     ssl_val = NULL;
2072     res = ldap_get_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, &ssl_val);
2073     if (res == LDAP_OPT_SUCCESS) {
2074       if (ssl_val != NULL) {
2075         pr_trace_msg(trace_channel, 17,
2076           "using default 'ssl-ca' value: %s", ssl_val);
2077         info->ssl_ca_file = ldap_strdup(ssl_val);
2078       }
2079     }
2080   }
2081 # endif /* LDAP_OPT_X_TLS_CACERTFILE */
2082 
2083 # if defined(LDAP_OPT_X_TLS_CERTFILE)
2084   if (info->ssl_cert_file == NULL) {
2085     ssl_val = NULL;
2086     res = ldap_get_option(NULL, LDAP_OPT_X_TLS_CERTFILE, &ssl_val);
2087     if (res == LDAP_OPT_SUCCESS) {
2088       if (ssl_val != NULL) {
2089         pr_trace_msg(trace_channel, 17,
2090           "using default 'ssl-cert' value: %s", ssl_val);
2091         info->ssl_cert_file = ldap_strdup(ssl_val);
2092       }
2093     }
2094   }
2095 # endif /* LDAP_OPT_X_TLS_CERTFILE */
2096 
2097 # if defined(LDAP_OPT_X_TLS_KEYFILE)
2098   if (info->ssl_key_file == NULL) {
2099     ssl_val = NULL;
2100     res = ldap_get_option(NULL, LDAP_OPT_X_TLS_KEYFILE, &ssl_val);
2101     if (res == LDAP_OPT_SUCCESS) {
2102       if (ssl_val != NULL) {
2103         pr_trace_msg(trace_channel, 17,
2104           "using default 'ssl-key' value: %s", ssl_val);
2105         info->ssl_key_file = ldap_strdup(ssl_val);
2106       }
2107     }
2108   }
2109 # endif /* LDAP_OPT_X_TLS_KEYFILE */
2110 
2111 # if defined(LDAP_OPT_X_TLS_CIPHER_SUITE)
2112   if (info->ssl_ciphers == NULL) {
2113     ssl_val = NULL;
2114     res = ldap_get_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, &ssl_val);
2115     if (res == LDAP_OPT_SUCCESS) {
2116       if (ssl_val != NULL) {
2117         pr_trace_msg(trace_channel, 17,
2118           "using default 'ssl-ciphers' value: %s", ssl_val);
2119         info->ssl_ciphers = ldap_strdup(ssl_val);
2120       }
2121     }
2122   }
2123 # endif /* LDAP_OPT_X_TLS_CIPHER_SUITE */
2124 
2125 # if defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
2126   if (info->ssl_verify == -1) {
2127     res = ldap_get_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ssl_verify);
2128     if (res == LDAP_OPT_SUCCESS) {
2129       ssl_val = NULL;
2130 
2131       switch (ssl_verify) {
2132 #  if defined(LDAP_OPT_X_TLS_NEVER)
2133         case LDAP_OPT_X_TLS_NEVER:
2134           ssl_val = "never";
2135           break;
2136 #  endif /* LDAP_OPT_X_TLS_NEVER */
2137 
2138 #  if defined(LDAP_OPT_X_TLS_HARD)
2139         case LDAP_OPT_X_TLS_HARD:
2140           ssl_val = "hard";
2141           break;
2142 #  endif /* LDAP_OPT_X_TLS_HARD */
2143 
2144 #  if defined(LDAP_OPT_X_TLS_DEMAND)
2145         case LDAP_OPT_X_TLS_DEMAND:
2146           ssl_val = "demand";
2147           break;
2148 #  endif /* LDAP_OPT_X_TLS_DEMAND */
2149 
2150 #  if defined(LDAP_OPT_X_TLS_ALLOW)
2151         case LDAP_OPT_X_TLS_ALLOW:
2152           ssl_val = "allow";
2153           break;
2154 #  endif /* LDAP_OPT_X_TLS_ALLOW */
2155 
2156 #  if defined(LDAP_OPT_X_TLS_TRY)
2157         case LDAP_OPT_X_TLS_TRY:
2158           ssl_val = "try";
2159           break;
2160 #  endif /* LDAP_OPT_X_TLS_TRY */
2161 
2162         default:
2163           ssl_val = NULL;
2164       }
2165 
2166       pr_trace_msg(trace_channel, 17,
2167         "using default 'ssl-verify' value: %s", ssl_val ? ssl_val : "UNKNOWN");
2168 
2169       info->ssl_verify = ssl_verify;
2170 
2171       if (ssl_val != NULL) {
2172         info->ssl_verify_text = ldap_strdup(ssl_val);
2173       }
2174     }
2175   }
2176 # endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
2177 #endif /* LDAP_OPT_X_TLS */
2178 }
2179 
2180 /* Free up any library-allocated memory. */
server_info_free(struct server_info * info)2181 static void server_info_free(struct server_info *info) {
2182   if (info->url_desc != NULL) {
2183     ldap_free_urldesc(info->url_desc);
2184     info->url_desc = NULL;
2185   }
2186 
2187   if (info->url_text != NULL) {
2188     ldap_memfree(info->url_text);
2189     info->url_text = NULL;
2190   }
2191 
2192   if (info->ssl_ca_file != NULL) {
2193     ldap_memfree((char *) info->ssl_ca_file);
2194     info->ssl_ca_file = NULL;
2195   }
2196 
2197   if (info->ssl_cert_file != NULL) {
2198     ldap_memfree((char *) info->ssl_cert_file);
2199     info->ssl_cert_file = NULL;
2200   }
2201 
2202   if (info->ssl_key_file != NULL) {
2203     ldap_memfree((char *) info->ssl_key_file);
2204     info->ssl_key_file = NULL;
2205   }
2206 
2207   if (info->ssl_ciphers != NULL) {
2208     ldap_memfree((char *) info->ssl_ciphers);
2209     info->ssl_ciphers = NULL;
2210   }
2211 
2212   info->ssl_verify = -1;
2213 
2214   if (info->ssl_verify_text != NULL) {
2215     ldap_memfree((char *) info->ssl_verify_text);
2216     info->ssl_verify_text = NULL;
2217   }
2218 }
2219 
server_infos_free(void)2220 static void server_infos_free(void) {
2221   server_rec *s;
2222 
2223   for (s = (server_rec *) server_list->xas_list; s; s = s->next) {
2224     register unsigned int i;
2225     config_rec *c;
2226     array_header *infos;
2227 
2228     c = find_config(s->conf, CONF_PARAM, "LDAPServer", FALSE);
2229     if (c == NULL) {
2230       continue;
2231     }
2232 
2233     pr_signals_handle();
2234 
2235     infos = c->argv[0];
2236     for (i = 0; i < infos->nelts; i++) {
2237       struct server_info *info;
2238 
2239       info = ((struct server_info **) infos->elts)[i];
2240       server_info_free(info);
2241     }
2242   }
2243 }
2244 
2245 /* Configuration handlers
2246  */
2247 
2248 /* usage: LDAPLog path|"none" */
set_ldaplog(cmd_rec * cmd)2249 MODRET set_ldaplog(cmd_rec *cmd) {
2250   CHECK_ARGS(cmd, 1);
2251   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2252 
2253   (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2254   return PR_HANDLED(cmd);
2255 }
2256 
set_ldapprotoversion(cmd_rec * cmd)2257 MODRET set_ldapprotoversion(cmd_rec *cmd) {
2258   int i = 0;
2259   config_rec *c;
2260   char *version;
2261 
2262   CHECK_ARGS(cmd, 1);
2263   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2264 
2265   version = cmd->argv[1];
2266   while (version[i]) {
2267     if (!PR_ISDIGIT((int) version[i])) {
2268       CONF_ERROR(cmd, "LDAPProtocolVersion: argument must be numeric!");
2269     }
2270 
2271     ++i;
2272   }
2273 
2274   c = add_config_param(cmd->argv[0], 1, NULL);
2275   c->argv[0] = pcalloc(c->pool, sizeof(int));
2276   *((int *) c->argv[0]) = atoi(version);
2277 
2278   return PR_HANDLED(cmd);
2279 }
2280 
2281 /* usage: LDAPServer info [ssl-cert:<path>] [ssl-key:<path>] [ssl-ca:<path>]
2282  *          [ssl-ciphers:ciphers] [ssl-verify:verify]
2283  */
set_ldapserver(cmd_rec * cmd)2284 MODRET set_ldapserver(cmd_rec *cmd) {
2285   register unsigned int i;
2286   struct server_info *info = NULL;
2287   array_header *infos, *items;
2288   config_rec *c;
2289 
2290   if (cmd->argc < 2) {
2291     CONF_ERROR(cmd, "wrong number of parameters");
2292   }
2293   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2294 
2295   c = add_config_param(cmd->argv[0], 1, NULL);
2296   infos = make_array(c->pool, cmd->argc - 1, sizeof(struct server_info *));
2297   c->argv[0] = infos;
2298 
2299   /* For historical reasons (and leaky abstractions from underlying LDAP
2300    * libraries), the LDAPServer directive supports having multiple hosts/URLs
2301    * being passed as a single string, quoted and separated by whitespace.
2302    * That is not really necessary, but we need to handle such items as well.
2303    *
2304    * Order matters, since the ordering of these hosts/URLs dictates the
2305    * order in which they are tried when attempting to connect.
2306    */
2307 
2308   items = make_array(cmd->tmp_pool, cmd->argc - 1, sizeof(char *));
2309   for (i = 1; i < cmd->argc; i++) {
2310     char *item;
2311 
2312     item = cmd->argv[i];
2313 
2314     /* Split non-URL arguments on whitespace and insert them as separate
2315      * servers.
2316      */
2317     while (*item) {
2318       size_t len;
2319 
2320       pr_signals_handle();
2321 
2322       len = strcspn(item, " \f\n\r\t\v");
2323       *((char **) push_array(items)) = pstrndup(cmd->tmp_pool, item, len);
2324 
2325       item += len;
2326       while (PR_ISSPACE(*item)) {
2327         ++item;
2328       }
2329     }
2330   }
2331 
2332   for (i = 0; i < items->nelts; i++) {
2333     char *item;
2334 
2335     item = ((char **) items->elts)[i];
2336 
2337     /* Is this an SSL option?  If so, it needs to be applied to the
2338      * previously defined LDAP URL/host.  Otherwise, it's a misconfiguration.
2339      */
2340     if (strncmp(item, "ssl-ca:", 7) == 0) {
2341       char *path;
2342 
2343       if (info == NULL) {
2344         CONF_ERROR(cmd, "wrong order of parameters");
2345       }
2346 
2347       path = item;
2348 
2349       /* Advance past the "ssl-ca:" prefix. */
2350       path += 7;
2351 
2352       if (file_exists2(cmd->tmp_pool, path) != TRUE) {
2353         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "SSL CA file '", path, "': ",
2354           strerror(ENOENT), NULL));
2355       }
2356 
2357       info->ssl_ca_file = ldap_strdup(path);
2358       continue;
2359 
2360     } else if (strncmp(item, "ssl-cert:", 9) == 0) {
2361       char *path;
2362 
2363       if (info == NULL) {
2364         CONF_ERROR(cmd, "wrong order of parameters");
2365       }
2366 
2367       path = item;
2368 
2369       /* Advance past the "ssl-cert:" prefix. */
2370       path += 9;
2371 
2372       if (file_exists2(cmd->tmp_pool, path) != TRUE) {
2373         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "SSL certificate file '",
2374           path, "': ", strerror(ENOENT), NULL));
2375       }
2376 
2377       info->ssl_cert_file = ldap_strdup(path);
2378       continue;
2379 
2380     } else if (strncmp(item, "ssl-key:", 8) == 0) {
2381       char *path;
2382 
2383       if (info == NULL) {
2384         CONF_ERROR(cmd, "wrong order of parameters");
2385       }
2386 
2387       path = item;
2388 
2389       /* Advance past the "ssl-key:" prefix. */
2390       path += 8;
2391 
2392       if (file_exists2(cmd->tmp_pool, path) != TRUE) {
2393         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "SSL certificate key file '",
2394           path, "': ", strerror(ENOENT), NULL));
2395       }
2396 
2397       info->ssl_key_file = ldap_strdup(path);
2398       continue;
2399 
2400     } else if (strncmp(item, "ssl-ciphers:", 12) == 0) {
2401       char *ciphers;
2402 
2403       if (info == NULL) {
2404         CONF_ERROR(cmd, "wrong order of parameters");
2405       }
2406 
2407       ciphers = item;
2408 
2409       /* Advance past the "ssl-ciphers:" prefix. */
2410       ciphers += 12;
2411 
2412       info->ssl_ciphers = ldap_strdup(ciphers);
2413       continue;
2414 
2415     } else if (strncmp(item, "ssl-verify:", 11) == 0) {
2416       int b, ssl_verify = -1;
2417       char *verify_text;
2418 
2419       if (info == NULL) {
2420         CONF_ERROR(cmd, "wrong order of parameters");
2421       }
2422 
2423       verify_text = item;
2424 
2425       /* Advance past the "ssl-verify:" prefix. */
2426       verify_text += 11;
2427 
2428       /* For convenience, we accept the usual on/off values, AND the
2429        * LDAP specific names, for those who think they want the finer
2430        * control.
2431        */
2432 
2433       b = pr_str_is_boolean(verify_text);
2434       if (b < 0) {
2435 #if defined(LDAP_OPT_X_TLS_ALLOW)
2436         if (strcasecmp(verify_text, "allow") == 0) {
2437           ssl_verify = LDAP_OPT_X_TLS_ALLOW;
2438 #else
2439         if (FALSE) {
2440 #endif /* LDAP_OPT_X_TLS_ALLOW */
2441 
2442 #if defined(LDAP_OPT_X_TLS_DEMAND)
2443         } else if (strcasecmp(verify_text, "demand") == 0) {
2444           ssl_verify = LDAP_OPT_X_TLS_DEMAND;
2445 #endif /* LDAP_OPT_X_TLS_DEMAND */
2446 
2447 #if defined(LDAP_OPT_X_TLS_NEVER)
2448         } else if (strcasecmp(verify_text, "never") == 0) {
2449           ssl_verify = LDAP_OPT_X_TLS_NEVER;
2450 #endif /* LDAP_OPT_X_TLS_NEVER */
2451 
2452 #if defined(LDAP_OPT_X_TLS_TRY)
2453         } else if (strcasecmp(verify_text, "try") == 0) {
2454           ssl_verify = LDAP_OPT_X_TLS_TRY;
2455 #endif /* LDAP_OPT_X_TLS_TRY */
2456 
2457         } else {
2458           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2459             "unknown/unsupported 'ssl-verify' value: ", verify_text, NULL));
2460         }
2461 
2462       } else {
2463         if (b == TRUE) {
2464 #if defined(LDAP_OPT_X_TLS_DEMAND)
2465           ssl_verify = LDAP_OPT_X_TLS_DEMAND;
2466 #endif /* LDAP_OPT_X_TLS_DEMAND */
2467           verify_text = "demand";
2468 
2469         } else {
2470 #if defined(LDAP_OPT_X_TLS_NEVER)
2471           ssl_verify = LDAP_OPT_X_TLS_NEVER;
2472 #endif /* LDAP_OPT_X_TLS_NEVER */
2473           verify_text = "never";
2474         }
2475       }
2476 
2477       info->ssl_verify = ssl_verify;
2478       info->ssl_verify_text = ldap_strdup(verify_text);
2479       continue;
2480 
2481     } else {
2482       info = server_info_create(c->pool);
2483     }
2484 
2485     info->info_text = pstrdup(c->pool, item);
2486 
2487     if (ldap_is_ldap_url(item)) {
2488       int res;
2489       LDAPURLDesc *url_desc;
2490       char *url_text;
2491 
2492       res = ldap_url_parse(item, &url_desc);
2493       if (res != LDAP_URL_SUCCESS) {
2494         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2495           "invalid LDAP URL '", item, "': ", ldap_err2string(res), NULL));
2496       }
2497 
2498       info->url_desc = url_desc;
2499 
2500       url_text = ldap_url_desc2str(url_desc);
2501       if (url_text != NULL) {
2502         pr_log_debug(DEBUG0, "%s: parsed URL '%s' as '%s'",
2503           (char *) cmd->argv[0], item, url_text);
2504         info->url_text = url_text;
2505       }
2506 
2507 #ifdef HAS_LDAP_INITIALIZE
2508       if (strcasecmp(url_desc->lud_scheme, "ldap") != 0 &&
2509           strcasecmp(url_desc->lud_scheme, "ldaps") != 0) {
2510         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid scheme specified by URL '", item, "': valid schemes are 'ldap' or 'ldaps'", NULL));
2511       }
2512 #else /* HAS_LDAP_INITIALIZE */
2513       if (strcasecmp(url_desc->lud_scheme, "ldap") != 0) {
2514         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported scheme specified by URL '", item, "': valid scheme is only 'ldap'", NULL));
2515       }
2516 #endif /* HAS_LDAP_INITIALIZE */
2517 
2518       if (url_desc->lud_dn != NULL &&
2519           strcmp(url_desc->lud_dn, "") != 0) {
2520         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, item, ": a base DN may not be specified by an LDAPServer URL, only by LDAPUsers or LDAPGroups", NULL));
2521       }
2522 
2523       if (url_desc->lud_filter != NULL &&
2524          strcmp(url_desc->lud_filter, "") != 0) {
2525         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, item, ": a search filter may not be specified by an LDAPServer URL, only by LDAPUsers or LDAPGroups", NULL));
2526       }
2527 
2528       if (url_desc->lud_scope == LDAP_SCOPE_BASE) {
2529         pr_log_debug(DEBUG0, MOD_LDAP_VERSION
2530           ": WARNING: '%s': LDAP URL search scopes default to 'base', "
2531           "not 'subtree', and may not be what you want (see LDAPSearchScope)",
2532           item);
2533       }
2534 
2535       /* Need to keep parsed host and port for pre-2000 ldap_init(3). */
2536       if (url_desc->lud_host != NULL) {
2537         info->host = pstrdup(c->pool, url_desc->lud_host);
2538       }
2539 
2540       if (url_desc->lud_port != 0) {
2541         info->port = url_desc->lud_port;
2542       }
2543 
2544     } else {
2545       char *ptr;
2546 
2547       /* The given item is just a hostname/IP address, maybe a port. */
2548       ptr = strchr(item, ':');
2549       if (ptr == NULL) {
2550         info->host = pstrdup(c->pool, item);
2551         info->port = LDAP_PORT;
2552 
2553       } else {
2554         int port;
2555 
2556         info->host = pstrndup(c->pool, item, ptr - item);
2557 
2558         port = atoi(ptr + 1);
2559         if (port == 0) {
2560           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse '", ptr + 1,
2561             "' as a port number for '", item, "'", NULL));
2562         }
2563 
2564         info->port = port;
2565         info->port_text = pstrdup(c->pool, ptr + 1);
2566       }
2567     }
2568 
2569     *((struct server_info **) push_array(infos)) = info;
2570   }
2571 
2572   return PR_HANDLED(cmd);
2573 }
2574 
2575 /* usage: LDAPUseSASL mech1 ... */
2576 MODRET set_ldapusesasl(cmd_rec *cmd) {
2577 #if defined(HAVE_SASL_SASL_H)
2578   register unsigned int i;
2579   config_rec *c;
2580   char *sasl_mechs = "";
2581 
2582   if (cmd->argc < 1) {
2583     CONF_ERROR(cmd, "wrong number of parameters");
2584   }
2585 
2586   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2587 
2588   c = add_config_param(cmd->argv[0], 1, NULL);
2589 
2590   for (i = 1; i < cmd->argc; i++) {
2591     register unsigned int j;
2592     char *sasl_mech;
2593 
2594     /* Only allow known supported (and tested) mechanisms for now. */
2595     if (strcasecmp(cmd->argv[i], "ANONYMOUS") == 0 ||
2596         strcasecmp(cmd->argv[i], "CRAM-MD5") == 0 ||
2597         strcasecmp(cmd->argv[i], "DIGEST-MD5") == 0 ||
2598         strcasecmp(cmd->argv[i], "PLAIN") == 0 ||
2599         strcasecmp(cmd->argv[i], "LOGIN") == 0 ||
2600         strncasecmp(cmd->argv[i], "SCRAM-SHA-", 10) == 0) {
2601       sasl_mech = cmd->argv[i];
2602 
2603     } else {
2604       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported SASL mechanism: ",
2605         cmd->argv[i], NULL));
2606     }
2607 
2608     /* Convert the mechanism name to all uppercase, per convention. */
2609     for (j = 0; j < strlen(sasl_mech); j++) {
2610       sasl_mech[j] = toupper(sasl_mech[j]);
2611     }
2612 
2613     sasl_mechs = pstrcat(c->pool, sasl_mechs, *sasl_mechs ? " " : "",
2614       sasl_mech, NULL);
2615   }
2616 
2617   c->argv[0] = sasl_mechs;
2618   return PR_HANDLED(cmd);
2619 #else
2620   CONF_ERROR(cmd, "Your system libraries do not appear to support SASL (missing <sasl/sasl.h>)");
2621 #endif /* HAVE_SASL_SASL_H */
2622 }
2623 
2624 /* usage: LDAPUseTLS on|off */
2625 MODRET set_ldapusetls(cmd_rec *cmd) {
2626 #if defined(LDAP_OPT_X_TLS)
2627   int use_tls;
2628   config_rec *c;
2629 
2630   CHECK_ARGS(cmd, 1);
2631   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2632 
2633   use_tls = get_boolean(cmd, 1);
2634   if (use_tls == -1) {
2635     CONF_ERROR(cmd, "expected Boolean parameter");
2636   }
2637 
2638   c = add_config_param(cmd->argv[0], 1, NULL);
2639   c->argv[0] = pcalloc(c->pool, sizeof(int));
2640   *((int *) c->argv[0]) = use_tls;
2641 
2642   return PR_HANDLED(cmd);
2643 
2644 #else /* LDAP_OPT_X_TLS */
2645   CONF_ERROR(cmd, "Your LDAP libraries do not appear to support TLS");
2646 #endif /* LDAP_OPT_X_TLS */
2647 }
2648 
2649 MODRET set_ldapbinddn(cmd_rec *cmd) {
2650   CHECK_ARGS(cmd, 1);
2651   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2652 
2653   add_config_param_str(cmd->argv[0], 2, cmd->argv[1], cmd->argv[2]);
2654   return PR_HANDLED(cmd);
2655 }
2656 
2657 MODRET set_ldapsearchscope(cmd_rec *cmd) {
2658   config_rec *c;
2659   const char *scope_name;
2660   int search_scope;
2661 
2662   CHECK_ARGS(cmd, 1);
2663   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2664 
2665   scope_name = cmd->argv[1];
2666 
2667   if (strcasecmp(scope_name, "base") == 0) {
2668     search_scope = LDAP_SCOPE_BASE;
2669 
2670   } else if (strcasecmp(scope_name, "one") == 0 ||
2671              strcasecmp(scope_name, "onelevel") == 0) {
2672     search_scope = LDAP_SCOPE_ONELEVEL;
2673 
2674   } else if (strcasecmp(scope_name, "sub") == 0 ||
2675              strcasecmp(scope_name, "subtree") == 0) {
2676     search_scope = LDAP_SCOPE_SUBTREE;
2677 
2678   } else {
2679     CONF_ERROR(cmd, "search scope must be one of: base, onelevel, subtree");
2680   }
2681 
2682   c = add_config_param(cmd->argv[0], 1, NULL);
2683   c->argv[0] = palloc(c->pool, sizeof(int));
2684   *((int *) c->argv[0]) = search_scope;
2685 
2686   return PR_HANDLED(cmd);
2687 }
2688 
2689 MODRET set_ldapquerytimeout(cmd_rec *cmd) {
2690   config_rec *c;
2691   int timeout;
2692 
2693   CHECK_ARGS(cmd, 1);
2694   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2695 
2696   if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
2697     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
2698       cmd->argv[1], "': ", strerror(errno), NULL));
2699   }
2700 
2701   c = add_config_param(cmd->argv[0], 1, NULL);
2702   c->argv[0] = pcalloc(c->pool, sizeof(int));
2703   *((int *) c->argv[0]) = timeout;
2704 
2705   return PR_HANDLED(cmd);
2706 }
2707 
2708 MODRET set_ldapaliasdereference(cmd_rec *cmd) {
2709   int value;
2710   config_rec *c;
2711 
2712   CHECK_ARGS(cmd, 1);
2713   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2714 
2715   if (strcasecmp(cmd->argv[1], "never") == 0) {
2716     value = LDAP_DEREF_NEVER;
2717 
2718   } else if (strcasecmp(cmd->argv[1], "search") == 0) {
2719     value = LDAP_DEREF_SEARCHING;
2720 
2721   } else if (strcasecmp(cmd->argv[1], "find") == 0) {
2722     value = LDAP_DEREF_FINDING;
2723 
2724   } else if (strcasecmp(cmd->argv[1], "always") == 0) {
2725     value = LDAP_DEREF_ALWAYS;
2726 
2727   } else {
2728     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2729       "expected a valid dereference (never, search, find, always): ",
2730       cmd->argv[1], NULL));
2731   }
2732 
2733   c = add_config_param(cmd->argv[0], 1, NULL);
2734   c->argv[0] = pcalloc(c->pool, sizeof(int));
2735   *((int *) c->argv[0]) = value;
2736 
2737   return PR_HANDLED(cmd);
2738 }
2739 
2740 MODRET set_ldapauthbinds(cmd_rec *cmd) {
2741   int b;
2742   config_rec *c;
2743 
2744   CHECK_ARGS(cmd, 1);
2745   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2746 
2747   b = get_boolean(cmd, 1);
2748   if (b == -1) {
2749     CONF_ERROR(cmd, "expected Boolean parameter");
2750   }
2751 
2752   c = add_config_param(cmd->argv[0], 1, NULL);
2753   c->argv[0] = pcalloc(c->pool, sizeof(int));
2754   *((int *) c->argv[0]) = b;
2755 
2756   return PR_HANDLED(cmd);
2757 }
2758 
2759 MODRET set_ldapdefaultauthscheme(cmd_rec *cmd) {
2760   CHECK_ARGS(cmd, 1);
2761   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2762 
2763   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2764   return PR_HANDLED(cmd);
2765 }
2766 
2767 MODRET set_ldapattr(cmd_rec *cmd) {
2768   CHECK_ARGS(cmd, 2);
2769   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2770 
2771   if (strcasecmp(cmd->argv[1], "uid") != 0 &&
2772       strcasecmp(cmd->argv[1], "uidNumber") != 0 &&
2773       strcasecmp(cmd->argv[1], "gidNumber") != 0 &&
2774       strcasecmp(cmd->argv[1], "homeDirectory") != 0 &&
2775       strcasecmp(cmd->argv[1], "userPassword") != 0 &&
2776       strcasecmp(cmd->argv[1], "loginShell") != 0 &&
2777       strcasecmp(cmd->argv[1], "cn") != 0 &&
2778       strcasecmp(cmd->argv[1], "memberUid") != 0 &&
2779       strcasecmp(cmd->argv[1], "ftpQuota") != 0 &&
2780       strcasecmp(cmd->argv[1], "ftpQuotaProfileDN") != 0) {
2781     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2782       ": unknown attribute name: ", cmd->argv[1], NULL));
2783   }
2784 
2785   add_config_param_str(cmd->argv[0], 2, cmd->argv[1], cmd->argv[2]);
2786   return PR_HANDLED(cmd);
2787 }
2788 
2789 /* usage: LDAPUsers base-dn [name-filter-template [uid-filter-template]] */
2790 MODRET set_ldapusers(cmd_rec *cmd) {
2791   config_rec *c;
2792 
2793   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2794 
2795   if (get_boolean(cmd, 1) != -1) {
2796     CONF_ERROR(cmd, "first parameter must be the base DN, not on/off");
2797   }
2798 
2799   c = add_config_param(cmd->argv[0], cmd->argc - 1, NULL, NULL, NULL);
2800   c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
2801   if (cmd->argc > 2) {
2802     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
2803   }
2804   if (cmd->argc > 3) {
2805     c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
2806   }
2807 
2808   return PR_HANDLED(cmd);
2809 }
2810 
2811 MODRET set_ldapdefaultuid(cmd_rec *cmd) {
2812   config_rec *c;
2813   uid_t uid;
2814 
2815   CHECK_ARGS(cmd, 1);
2816   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2817 
2818   c = add_config_param(cmd->argv[0], 1, NULL);
2819   c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
2820 
2821   if (pr_str2uid(cmd->argv[1], &uid) < 0) {
2822     CONF_ERROR(cmd, "LDAPDefaultUID: UID argument must be numeric");
2823   }
2824 
2825   *((uid_t *) c->argv[0]) = uid;
2826   return PR_HANDLED(cmd);
2827 }
2828 
2829 MODRET set_ldapdefaultgid(cmd_rec *cmd) {
2830   config_rec *c;
2831   gid_t gid;
2832 
2833   CHECK_ARGS(cmd, 1);
2834   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2835 
2836   c = add_config_param(cmd->argv[0], 1, NULL);
2837   c->argv[0] = pcalloc(c->pool, sizeof(gid_t));
2838 
2839   if (pr_str2gid(cmd->argv[1], &gid) < 0) {
2840     CONF_ERROR(cmd, "LDAPDefaultGID: GID argument must be numeric");
2841   }
2842 
2843   *((gid_t *) c->argv[0]) = gid;
2844   return PR_HANDLED(cmd);
2845 }
2846 
2847 MODRET set_ldapforcedefaultuid(cmd_rec *cmd) {
2848   int b;
2849   config_rec *c;
2850 
2851   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2852 
2853   b = get_boolean(cmd, 1);
2854   if (b == -1) {
2855     CONF_ERROR(cmd, "expected Boolean parameter");
2856   }
2857 
2858   c = add_config_param(cmd->argv[0], 1, NULL);
2859   c->argv[0] = pcalloc(c->pool, sizeof(int));
2860   *((int *) c->argv[0]) = b;
2861 
2862   return PR_HANDLED(cmd);
2863 }
2864 
2865 MODRET set_ldapforcedefaultgid(cmd_rec *cmd) {
2866   int b;
2867   config_rec *c;
2868 
2869   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2870 
2871   b = get_boolean(cmd, 1);
2872   if (b == -1) {
2873     CONF_ERROR(cmd, "expected Boolean parameter");
2874   }
2875 
2876   c = add_config_param(cmd->argv[0], 1, NULL);
2877   c->argv[0] = pcalloc(c->pool, sizeof(int));
2878   *((int *) c->argv[0]) = b;
2879 
2880   return PR_HANDLED(cmd);
2881 }
2882 
2883 MODRET set_ldapgenhdir(cmd_rec *cmd) {
2884   int b;
2885   config_rec *c;
2886 
2887   CHECK_ARGS(cmd, 1);
2888   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2889 
2890   b = get_boolean(cmd, 1);
2891   if (b == -1) {
2892     CONF_ERROR(cmd, "expected Boolean parameter");
2893   }
2894 
2895   c = add_config_param(cmd->argv[0], 1, NULL);
2896   c->argv[0] = pcalloc(c->pool, sizeof(int));
2897   *((int *) c->argv[0]) = b;
2898 
2899   return PR_HANDLED(cmd);
2900 }
2901 
2902 MODRET set_ldapgenhdirprefix(cmd_rec *cmd) {
2903   char *prefix;
2904 
2905   CHECK_ARGS(cmd, 1);
2906   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2907 
2908   prefix = cmd->argv[1];
2909   if (strlen(prefix) == 0) {
2910     CONF_ERROR(cmd, "must not be an empty string");
2911   }
2912 
2913   add_config_param_str(cmd->argv[0], 1, prefix);
2914   return PR_HANDLED(cmd);
2915 }
2916 
2917 MODRET set_ldapgenhdirprefixnouname(cmd_rec *cmd) {
2918   int b;
2919   config_rec *c;
2920 
2921   CHECK_ARGS(cmd, 1);
2922   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2923 
2924   b = get_boolean(cmd, 1);
2925   if (b == -1) {
2926     CONF_ERROR(cmd, "expected Boolean parameter");
2927   }
2928 
2929   c = add_config_param(cmd->argv[0], 1, NULL);
2930   c->argv[0] = pcalloc(c->pool, sizeof(int));
2931   *((int *) c->argv[0]) = b;
2932 
2933   return PR_HANDLED(cmd);
2934 }
2935 
2936 MODRET set_ldapforcegenhdir(cmd_rec *cmd) {
2937   int b;
2938   config_rec *c;
2939 
2940   CHECK_ARGS(cmd, 1);
2941   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2942 
2943   b = get_boolean(cmd, 1);
2944   if (b == -1) {
2945     CONF_ERROR(cmd, "expected Boolean parameter");
2946   }
2947 
2948   c = add_config_param(cmd->argv[0], 1, NULL);
2949   c->argv[0] = pcalloc(c->pool, sizeof(int));
2950   *((int *) c->argv[0]) = b;
2951 
2952   return PR_HANDLED(cmd);
2953 }
2954 
2955 MODRET set_ldapgrouplookups(cmd_rec *cmd) {
2956   config_rec *c;
2957 
2958   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2959 
2960   if (get_boolean(cmd, 1) != -1) {
2961     CONF_ERROR(cmd, "first parameter must be the base DN, not on/off.");
2962   }
2963 
2964   c = add_config_param(cmd->argv[0], cmd->argc - 1, NULL);
2965   c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
2966   if (cmd->argc > 2) {
2967     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
2968   }
2969 
2970   if (cmd->argc > 3) {
2971     c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
2972   }
2973 
2974   if (cmd->argc > 4) {
2975     c->argv[3] = pstrdup(c->pool, cmd->argv[4]);
2976   }
2977 
2978   return PR_HANDLED(cmd);
2979 }
2980 
2981 MODRET set_ldapdefaultquota(cmd_rec *cmd) {
2982   CHECK_ARGS(cmd, 1);
2983   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2984 
2985   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2986   return PR_HANDLED(cmd);
2987 }
2988 
2989 /* Event listeners
2990  */
2991 
2992 #if defined(PR_SHARED_MODULE)
2993 static void ldap_mod_unload_ev(const void *event_data, void *user_data) {
2994   if (strcmp("mod_ldap.c", (const char *) event_data) != 0) {
2995     return;
2996   }
2997 
2998   pr_event_unregister(&ldap_module, NULL, NULL);
2999   server_infos_free();
3000 }
3001 #endif /* PR_SHARED_MODULE */
3002 
3003 static void ldap_postparse_ev(const void *event_data, void *user_data) {
3004   server_rec *s = NULL;
3005 
3006   /* Check the configured LDAPServer directives.  Specifically, if the URL
3007    * syntax has been used, we look for any LDAPSearchScope directive and
3008    * warn that they are ignored.  Similarly, if there are "ldaps" URLs,
3009    * we warn that any LDAPUseTLS directive will be ignored.  Otherwise,
3010    * cases where LDAP URLs have not been used, we construct them.
3011    */
3012   for (s = (server_rec *) server_list->xas_list; s; s = s->next) {
3013     register unsigned int i;
3014     config_rec *c;
3015     array_header *infos;
3016     int search_scope = -1, use_tls = -1;
3017 
3018     pr_signals_handle();
3019 
3020     c = find_config(s->conf, CONF_PARAM, "LDAPSearchScope", FALSE);
3021     if (c != NULL) {
3022       search_scope = *((int *) c->argv[0]);
3023 
3024     } else {
3025       /* Per documentation, the default search scope is "subtree". */
3026       search_scope = LDAP_SCOPE_SUBTREE;
3027     }
3028 
3029 #if defined(LDAP_OPT_X_TLS)
3030     c = find_config(s->conf, CONF_PARAM, "LDAPUseTLS", FALSE);
3031     if (c != NULL) {
3032       use_tls = *((int *) c->argv[0]);
3033     }
3034 #endif /* LDAP_OPT_X_TLS */
3035 
3036     c = find_config(s->conf, CONF_PARAM, "LDAPServer", FALSE);
3037     if (c == NULL) {
3038       continue;
3039     }
3040 
3041     infos = c->argv[0];
3042 
3043     for (i = 0; i < infos->nelts; i++) {
3044       struct server_info *info;
3045 
3046       info = ((struct server_info **) infos->elts)[i];
3047 
3048       if (info->url_desc != NULL) {
3049         if (info->url_desc->lud_scope != search_scope) {
3050           /* When LDAP URLs are used, the search scope is part of the URL
3051            * syntax, which takes precedence over LDAPSearchScope.
3052            */
3053           pr_log_debug(DEBUG2, MOD_LDAP_VERSION
3054             ": ignoring configured LDAPSearchScope for LDAP URL '%s'",
3055             info->info_text);
3056         }
3057 
3058         /* If the scheme is "ldaps", AND LDAPUseTLS has been explicitly
3059          * configured, warn that it will be ignored.
3060          */
3061         if (use_tls != -1) {
3062           if (strcasecmp(info->url_desc->lud_scheme, "ldaps") == 0) {
3063             pr_log_debug(DEBUG2, MOD_LDAP_VERSION
3064               ": ignoring configured LDAPUseTLS for explicit LDAPS URL '%s'",
3065               info->info_text);
3066           } else {
3067             info->use_starttls = use_tls;
3068           }
3069         }
3070 
3071       } else {
3072         int res;
3073         char *url, *url_text;
3074         LDAPURLDesc *url_desc;
3075 
3076         /* Construct an LDAP URL, and parse it. */
3077 
3078         url = pstrcat(c->pool, "ldap://", info->host, NULL);
3079         if (info->port_text != NULL) {
3080           url = pstrcat(c->pool, url, ":", info->port_text, NULL);
3081         }
3082 
3083         switch (search_scope) {
3084           case LDAP_SCOPE_SUBTREE:
3085             url = pstrcat(c->pool, url, "/??sub", NULL);
3086             break;
3087 
3088           case LDAP_SCOPE_BASE:
3089             url = pstrcat(c->pool, url, "/??base", NULL);
3090             break;
3091 
3092           case LDAP_SCOPE_ONELEVEL:
3093             url = pstrcat(c->pool, url, "/??one", NULL);
3094             break;
3095 
3096           default:
3097             break;
3098         }
3099 
3100         res = ldap_url_parse(url, &url_desc);
3101         if (res != LDAP_URL_SUCCESS) {
3102           /* This should only happen as a result of a coding bug. */
3103           pr_log_pri(PR_LOG_NOTICE, MOD_LDAP_VERSION
3104             ": invalid LDAP URL '%s': %s", url, ldap_err2string(res));
3105           pr_session_disconnect(&ldap_module, PR_SESS_DISCONNECT_BAD_CONFIG,
3106             NULL);
3107         }
3108 
3109         info->url_desc = url_desc;
3110         info->port = url_desc->lud_port;
3111 
3112         url_text = ldap_url_desc2str(url_desc);
3113         if (url_text != NULL) {
3114           pr_log_debug(DEBUG0, "%s: parsed URL '%s' as '%s'", c->name, url,
3115             url_text);
3116           info->url_text = url_text;
3117         }
3118 
3119         if (use_tls != -1) {
3120           info->use_starttls = use_tls;
3121         }
3122       }
3123 
3124       server_info_get_ssl_defaults(info);
3125     }
3126   }
3127 }
3128 
3129 static void ldap_sess_reinit_ev(const void *event_data, void *user_data) {
3130   int res;
3131 
3132   /* A HOST command changed the main_server pointer; reinitialize ourselves. */
3133 
3134   pr_event_unregister(&ldap_module, "core.session-reinit", ldap_sess_reinit_ev);
3135 
3136   /* Restore defaults. */
3137   (void) close(ldap_logfd);
3138   ldap_logfd = -1;
3139   ldap_protocol_version = 3;
3140   ldap_servers = NULL;
3141   ldap_dn = NULL;
3142   ldap_dnpass = NULL;
3143   ldap_dnpasslen = 0;
3144   ldap_search_scope = LDAP_SCOPE_SUBTREE;
3145   ldap_sasl_mechs = NULL;
3146   ldap_querytimeout = 0;
3147   ldap_dereference = LDAP_DEREF_NEVER;
3148   ldap_authbinds = TRUE;
3149   ldap_defaultauthscheme = "crypt";
3150   ldap_attr_uid = "uid";
3151   ldap_attr_uidnumber = "uidNumber";
3152   ldap_attr_gidnumber = "gidNumber";
3153   ldap_attr_homedirectory = "homeDirectory";
3154   ldap_attr_userpassword = "userPassword";
3155   ldap_attr_loginshell = "loginShell";
3156   ldap_attr_cn = "cn";
3157   ldap_attr_memberuid = "memberUid";
3158   ldap_attr_ftpquota = "ftpQuota";
3159   ldap_attr_ftpquota_profiledn = "ftpQuotaProfileDN";
3160   ldap_do_users = FALSE;
3161   ldap_user_basedn = NULL;
3162   ldap_user_name_filter = NULL;
3163   ldap_user_uid_filter = NULL;
3164   ldap_do_groups = FALSE;
3165   ldap_group_name_filter = NULL;
3166   ldap_group_gid_filter = NULL;
3167   ldap_group_member_filter = NULL;
3168   ldap_default_quota = NULL;
3169   ldap_defaultuid = (uid_t) -1;
3170   ldap_defaultgid = (gid_t) -1;
3171   ldap_forcedefaultuid = FALSE;
3172   ldap_forcedefaultgid = FALSE;
3173   ldap_forcegenhdir = FALSE;
3174   ldap_genhdir = FALSE;
3175   ldap_genhdir_prefix = NULL;
3176   ldap_genhdir_prefix_nouname = FALSE;
3177 
3178   curr_server_info = NULL;
3179   curr_server_index = 0;
3180 
3181   destroy_pool(ldap_pool);
3182   ldap_pool = NULL;
3183 
3184   res = ldap_sess_init();
3185   if (res < 0) {
3186     pr_session_disconnect(&ldap_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED,
3187       NULL);
3188   }
3189 }
3190 
3191 static void ldap_shutdown_ev(const void *event_data, void *user_data) {
3192   server_infos_free();
3193 }
3194 
3195 /* Initialization routines
3196  */
3197 
3198 static int ldap_mod_init(void) {
3199   pr_log_debug(DEBUG2, MOD_LDAP_VERSION
3200     ": compiled using LDAP vendor '%s', LDAP API version %lu",
3201     LDAP_VENDOR_NAME, (unsigned long) LDAP_API_VERSION);
3202 
3203 #if defined(LDAP_OPT_API_INFO)
3204   {
3205     int res;
3206     LDAPAPIInfo api_info;
3207 
3208     api_info.ldapai_info_version = LDAP_API_INFO_VERSION;
3209     res = ldap_get_option(NULL, LDAP_OPT_API_INFO, &api_info);
3210     if (res == LDAP_OPT_SUCCESS) {
3211       pool *tmp_pool;
3212       char *feats = "";
3213 
3214       tmp_pool = make_sub_pool(permanent_pool);
3215 
3216       if (api_info.ldapai_extensions != NULL) {
3217         register unsigned int i;
3218 
3219         for (i = 0; api_info.ldapai_extensions[i]; i++) {
3220           feats = pstrcat(tmp_pool, feats, i != 0 ? ", " : "",
3221             api_info.ldapai_extensions[i], NULL);
3222           ldap_memfree(api_info.ldapai_extensions[i]);
3223         }
3224 
3225         ldap_memfree(api_info.ldapai_extensions);
3226       }
3227 
3228       pr_log_debug(DEBUG10, MOD_LDAP_VERSION
3229         " linked with LDAP vendor '%s' (LDAP API version %d, "
3230         "vendor version %d), features: %s", api_info.ldapai_vendor_name,
3231         api_info.ldapai_api_version, api_info.ldapai_vendor_version,
3232         feats);
3233       ldap_memfree(api_info.ldapai_vendor_name);
3234       destroy_pool(tmp_pool);
3235 
3236     } else {
3237       pr_trace_msg(trace_channel, 3,
3238         "error retrieving LDAP_OPT_API_INFO: %s", ldap_err2string(res));
3239     }
3240   }
3241 #endif /* LDAP_OPT_API_INFO */
3242 
3243 #if defined(LDAP_OPT_X_TLS_PACKAGE)
3244   {
3245     int res;
3246     char *tls_package = NULL;
3247 
3248     res = ldap_get_option(NULL, LDAP_OPT_X_TLS_PACKAGE, &tls_package);
3249     if (res == LDAP_OPT_SUCCESS) {
3250       pr_log_debug(DEBUG10, MOD_LDAP_VERSION
3251         ": LDAP TLS package = %s", tls_package);
3252 
3253     } else {
3254       pr_trace_msg(trace_channel, 3,
3255         "error retrieving LDAP_OPT_X_TLS_PACKAGE: %s", ldap_err2string(res));
3256     }
3257   }
3258 #endif /* LDAP_OPT_X_TLS_PACKAGE */
3259 
3260 #if defined(PR_SHARED_MODULE)
3261   pr_event_register(&ldap_module, "core.module-unload", ldap_mod_unload_ev,
3262     NULL);
3263 #endif /* PR_SHARED_MODULE */
3264   pr_event_register(&ldap_module, "core.postparse", ldap_postparse_ev, NULL);
3265   pr_event_register(&ldap_module, "core.shutdown", ldap_shutdown_ev, NULL);
3266 
3267   return 0;
3268 }
3269 
3270 static int ldap_sess_init(void) {
3271   config_rec *c;
3272   void *ptr;
3273 
3274   pr_event_register(&ldap_module, "core.session-reinit", ldap_sess_reinit_ev,
3275     NULL);
3276 
3277   ldap_pool = make_sub_pool(session.pool);
3278   pr_pool_tag(ldap_pool, MOD_LDAP_VERSION);
3279 
3280   c = find_config(main_server->conf, CONF_PARAM, "LDAPLog", FALSE);
3281   if (c != NULL) {
3282     char *path;
3283 
3284     path = c->argv[0];
3285 
3286     if (strncasecmp(path, "none", 5) != 0) {
3287       int res, xerrno = 0;
3288 
3289       pr_signals_block();
3290       PRIVS_ROOT
3291       res = pr_log_openfile(path, &ldap_logfd, PR_LOG_SYSTEM_MODE);
3292       xerrno = errno;
3293       PRIVS_RELINQUISH
3294       pr_signals_unblock();
3295 
3296       if (res < 0) {
3297         if (res == -1) {
3298           pr_log_pri(PR_LOG_NOTICE, MOD_LDAP_VERSION
3299             ": notice: unable to open LDAPLog '%s': %s", path,
3300             strerror(xerrno));
3301 
3302         } else if (res == PR_LOG_WRITABLE_DIR) {
3303           pr_log_pri(PR_LOG_WARNING, MOD_LDAP_VERSION
3304             ": notice: unable to open LDAPPLog '%s': parent directory is "
3305             "world-writable", path);
3306 
3307         } else if (res == PR_LOG_SYMLINK) {
3308           pr_log_pri(PR_LOG_WARNING, MOD_LDAP_VERSION
3309             ": notice: unable to open LDAPLog '%s': cannot log to a symlink",
3310             path);
3311         }
3312       }
3313     }
3314   }
3315 
3316   ptr = get_param_ptr(main_server->conf, "LDAPProtocolVersion", FALSE);
3317   if (ptr != NULL) {
3318     ldap_protocol_version = *((int *) ptr);
3319   }
3320 
3321   /* Allow for multiple LDAPServer directives. */
3322   c = find_config(main_server->conf, CONF_PARAM, "LDAPServer", FALSE);
3323   while (c != NULL) {
3324     pr_signals_handle();
3325 
3326     if (ldap_servers != NULL) {
3327       array_cat(ldap_servers, c->argv[0]);
3328 
3329     } else {
3330       ldap_servers = c->argv[0];
3331     }
3332 
3333     c = find_config_next(c, c->next, CONF_PARAM, "LDAPServer", FALSE);
3334   }
3335 
3336   if (ldap_servers == NULL) {
3337     pr_log_pri(PR_LOG_NOTICE, MOD_LDAP_VERSION
3338       ": no LDAPServer configured, using LDAP library defaults");
3339   }
3340 
3341   c = find_config(main_server->conf, CONF_PARAM, "LDAPBindDN", FALSE);
3342   if (c != NULL) {
3343     ldap_dn = pstrdup(ldap_pool, c->argv[0]);
3344     ldap_dnpass = pstrdup(ldap_pool, c->argv[1]);
3345     ldap_dnpasslen = strlen(ldap_dnpass);
3346   }
3347 
3348   c = find_config(main_server->conf, CONF_PARAM, "LDAPSearchScope", FALSE);
3349   if (c != NULL) {
3350     ldap_search_scope = *((int *) c->argv[0]);
3351   }
3352 
3353   ptr = get_param_ptr(main_server->conf, "LDAPQueryTimeout", FALSE);
3354   if (ptr != NULL) {
3355     ldap_querytimeout = *((int *) ptr);
3356   }
3357 
3358   ptr = get_param_ptr(main_server->conf, "LDAPAliasDereference", FALSE);
3359   if (ptr != NULL) {
3360     ldap_dereference = *((int *) ptr);
3361   }
3362 
3363   ptr = get_param_ptr(main_server->conf, "LDAPAuthBinds", FALSE);
3364   if (ptr != NULL) {
3365     ldap_authbinds = *((int *) ptr);
3366   }
3367 
3368   ptr = get_param_ptr(main_server->conf, "LDAPDefaultAuthScheme", FALSE);
3369   if (ptr != NULL) {
3370     ldap_defaultauthscheme = (char *) ptr;
3371   }
3372 
3373   /* Look up any attr redefinitions (LDAPAttr) before using those
3374    * variables, such as when generating the default search filters.
3375    */
3376   c = find_config(main_server->conf, CONF_PARAM, "LDAPAttr", FALSE);
3377   if (c != NULL) {
3378     do {
3379       if (strcasecmp(c->argv[0], "uid") == 0) {
3380         ldap_attr_uid = pstrdup(ldap_pool, c->argv[1]);
3381 
3382       } else if (strcasecmp(c->argv[0], "uidNumber") == 0) {
3383         ldap_attr_uidnumber = pstrdup(ldap_pool, c->argv[1]);
3384 
3385       } else if (strcasecmp(c->argv[0], "gidNumber") == 0) {
3386         ldap_attr_gidnumber = pstrdup(ldap_pool, c->argv[1]);
3387 
3388       } else if (strcasecmp(c->argv[0], "homeDirectory") == 0) {
3389         ldap_attr_homedirectory = pstrdup(ldap_pool, c->argv[1]);
3390 
3391       } else if (strcasecmp(c->argv[0], "userPassword") == 0) {
3392         ldap_attr_userpassword = pstrdup(ldap_pool, c->argv[1]);
3393 
3394       } else if (strcasecmp(c->argv[0], "loginShell") == 0) {
3395         ldap_attr_loginshell = pstrdup(ldap_pool, c->argv[1]);
3396 
3397       } else if (strcasecmp(c->argv[0], "cn") == 0) {
3398         ldap_attr_cn = pstrdup(ldap_pool, c->argv[1]);
3399 
3400       } else if (strcasecmp(c->argv[0], "memberUid") == 0) {
3401         ldap_attr_memberuid = pstrdup(ldap_pool, c->argv[1]);
3402 
3403       } else if (strcasecmp(c->argv[0], "ftpQuota") == 0) {
3404         ldap_attr_ftpquota = pstrdup(ldap_pool, c->argv[1]);
3405 
3406       } else if (strcasecmp(c->argv[0], "ftpQuotaProfileDN") == 0) {
3407         ldap_attr_ftpquota_profiledn = pstrdup(ldap_pool, c->argv[1]);
3408       }
3409 
3410     } while ((c = find_config_next(c, c->next, CONF_PARAM, "LDAPAttr", FALSE)));
3411   }
3412 
3413   c = find_config(main_server->conf, CONF_PARAM, "LDAPUsers", FALSE);
3414   if (c != NULL) {
3415     ldap_do_users = TRUE;
3416     ldap_user_basedn = pstrdup(ldap_pool, c->argv[0]);
3417 
3418     if (c->argc > 1) {
3419       ldap_user_name_filter = pstrdup(ldap_pool, c->argv[1]);
3420 
3421     } else {
3422       ldap_user_name_filter = pstrcat(ldap_pool,
3423         "(&(", ldap_attr_uid, "=%v)(objectclass=posixAccount))", NULL);
3424     }
3425 
3426     if (c->argc > 2) {
3427       ldap_user_uid_filter = pstrdup(ldap_pool, c->argv[2]);
3428 
3429     } else {
3430       ldap_user_uid_filter = pstrcat(ldap_pool,
3431         "(&(", ldap_attr_uidnumber, "=%v)(objectclass=posixAccount))", NULL);
3432     }
3433   }
3434 
3435   ptr = get_param_ptr(main_server->conf, "LDAPDefaultUID", FALSE);
3436   if (ptr != NULL) {
3437     ldap_defaultuid = *((uid_t *) ptr);
3438   }
3439 
3440   ptr = get_param_ptr(main_server->conf, "LDAPDefaultGID", FALSE);
3441   if (ptr != NULL) {
3442     ldap_defaultgid = *((gid_t *) ptr);
3443   }
3444 
3445   ldap_default_quota = get_param_ptr(main_server->conf, "LDAPDefaultQuota",
3446     FALSE);
3447 
3448   ptr = get_param_ptr(main_server->conf, "LDAPForceDefaultUID", FALSE);
3449   if (ptr != NULL) {
3450     ldap_forcedefaultuid = *((int *) ptr);
3451   }
3452 
3453   ptr = get_param_ptr(main_server->conf, "LDAPForceDefaultGID", FALSE);
3454   if (ptr != NULL) {
3455     ldap_forcedefaultgid = *((int *) ptr);
3456   }
3457 
3458   ptr = get_param_ptr(main_server->conf, "LDAPForceGeneratedHomedir", FALSE);
3459   if (ptr != NULL) {
3460     ldap_forcegenhdir = *((int *) ptr);
3461   }
3462 
3463   ptr = get_param_ptr(main_server->conf, "LDAPGenerateHomedir", FALSE);
3464   if (ptr != NULL) {
3465     ldap_genhdir = *((int *) ptr);
3466   }
3467 
3468   ldap_genhdir_prefix = get_param_ptr(main_server->conf,
3469     "LDAPGenerateHomedirPrefix", FALSE);
3470 
3471   ptr = get_param_ptr(main_server->conf, "LDAPGenerateHomedirPrefixNoUsername",
3472     FALSE);
3473   if (ptr != NULL) {
3474     ldap_genhdir_prefix_nouname = *((int *) ptr);
3475   }
3476 
3477   c = find_config(main_server->conf, CONF_PARAM, "LDAPGroups", FALSE);
3478   if (c != NULL) {
3479     ldap_do_groups = TRUE;
3480     ldap_gid_basedn = pstrdup(ldap_pool, c->argv[0]);
3481 
3482     if (c->argc > 1) {
3483       ldap_group_name_filter = pstrdup(ldap_pool, c->argv[1]);
3484 
3485     } else {
3486       ldap_group_name_filter = pstrcat(ldap_pool,
3487         "(&(", ldap_attr_cn, "=%v)(objectclass=posixGroup))", NULL);
3488     }
3489 
3490     if (c->argc > 2) {
3491       ldap_group_gid_filter = pstrdup(ldap_pool, c->argv[2]);
3492 
3493     } else {
3494       ldap_group_gid_filter = pstrcat(ldap_pool,
3495         "(&(", ldap_attr_gidnumber, "=%v)(objectclass=posixGroup))", NULL);
3496     }
3497 
3498     if (c->argc > 3) {
3499       ldap_group_member_filter = pstrdup(ldap_pool, c->argv[3]);
3500 
3501     } else {
3502       ldap_group_member_filter = pstrcat(ldap_pool,
3503         "(&(", ldap_attr_memberuid, "=%v)(objectclass=posixGroup))", NULL);
3504     }
3505   }
3506 
3507   c = find_config(main_server->conf, CONF_PARAM, "LDAPUseSASL", FALSE);
3508   if (c != NULL) {
3509     ldap_sasl_mechs = c->argv[0];
3510   }
3511 
3512 #if defined(LBER_OPT_LOG_PRINT_FN)
3513   /* If trace logging is enabled for the 'ldap.library' channel, direct
3514    * libldap (via liblber) to log to our trace logging.
3515    */
3516   if (pr_trace_get_level(libtrace_channel) >= 1) {
3517     int res, trace_level;
3518 
3519     res = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, ldap_tracelog_cb);
3520     if (res != LBER_OPT_SUCCESS) {
3521       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
3522         "error setting trace logging function: %s", strerror(EINVAL));
3523     }
3524 
3525     /* Debug levels:
3526      *  Trace (1)
3527      *  Packets (2)
3528      *  Arguments (4)
3529      *  Filters (32)
3530      *  Access control (128)
3531      *
3532      * See include/ldap_log.h in the OpenLDAP source.
3533      */
3534     trace_level = pr_trace_get_level(libtrace_channel);
3535     res = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &trace_level);
3536     if (res != LDAP_OPT_SUCCESS) {
3537       (void) pr_log_writefile(ldap_logfd, MOD_LDAP_VERSION,
3538         "error setting LDAP debug level %d: %s", trace_level,
3539         strerror(EINVAL));
3540     }
3541   }
3542 #endif /* LBER_OPT_LOG_PRINT_FN */
3543 
3544   if (ldap_do_users == FALSE) {
3545     pr_log_pri(PR_LOG_WARNING, MOD_LDAP_VERSION
3546       ": LDAPUsers not configured, skipping LDAP-based user authentication");
3547   }
3548 
3549   if (ldap_do_groups == FALSE) {
3550     pr_log_pri(PR_LOG_NOTICE, MOD_LDAP_VERSION
3551       ": LDAPGroups not configured, skipping LDAP-based group memberships");
3552   }
3553 
3554   return 0;
3555 }
3556 
3557 /* Module API tables
3558  */
3559 
3560 static conftable ldap_conftab[] = {
3561   { "LDAPAliasDereference",	set_ldapaliasdereference,	NULL },
3562   { "LDAPAttr",			set_ldapattr,			NULL },
3563   { "LDAPAuthBinds",		set_ldapauthbinds,		NULL },
3564   { "LDAPBindDN",		set_ldapbinddn,			NULL },
3565   { "LDAPDefaultAuthScheme",	set_ldapdefaultauthscheme,	NULL },
3566   { "LDAPDefaultGID",		set_ldapdefaultgid,		NULL },
3567   { "LDAPDefaultQuota",		set_ldapdefaultquota,		NULL },
3568   { "LDAPDefaultUID",		set_ldapdefaultuid,		NULL },
3569   { "LDAPForceDefaultGID",	set_ldapforcedefaultgid,	NULL },
3570   { "LDAPForceDefaultUID",	set_ldapforcedefaultuid,	NULL },
3571   { "LDAPForceGeneratedHomedir",set_ldapforcegenhdir,		NULL },
3572   { "LDAPGenerateHomedir",	set_ldapgenhdir,		NULL },
3573   { "LDAPGenerateHomedirPrefix",set_ldapgenhdirprefix,		NULL },
3574   { "LDAPGenerateHomedirPrefixNoUsername",
3575 				set_ldapgenhdirprefixnouname,	NULL },
3576   { "LDAPGroups",		set_ldapgrouplookups,		NULL },
3577   { "LDAPLog",			set_ldaplog,			NULL },
3578   { "LDAPProtocolVersion",	set_ldapprotoversion,		NULL },
3579   { "LDAPQueryTimeout",		set_ldapquerytimeout,		NULL },
3580   { "LDAPSearchScope",		set_ldapsearchscope,		NULL },
3581   { "LDAPServer",		set_ldapserver,			NULL },
3582   { "LDAPUsers",		set_ldapusers,			NULL },
3583   { "LDAPUseSASL",		set_ldapusesasl,		NULL },
3584   { "LDAPUseTLS",		set_ldapusetls,			NULL },
3585 
3586   { NULL, NULL, NULL },
3587 };
3588 
3589 static cmdtable ldap_cmdtab[] = {
3590   { HOOK, "ldap_quota_lookup",		G_NONE, handle_ldap_quota_lookup, FALSE, FALSE},
3591   { HOOK, "ldap_ssh_publickey_lookup",	G_NONE, handle_ldap_ssh_pubkey_lookup, FALSE, FALSE},
3592 
3593   { 0, NULL}
3594 };
3595 
3596 static authtable ldap_authtab[] = {
3597   { 0, "setpwent",	ldap_auth_setpwent },
3598   { 0, "endpwent",	ldap_auth_endpwent },
3599   { 0, "setgrent",	ldap_auth_setpwent },
3600   { 0, "endgrent",	ldap_auth_endpwent },
3601   { 0, "getpwnam",	ldap_auth_getpwnam },
3602   { 0, "getpwuid",	ldap_auth_getpwuid },
3603   { 0, "getgrnam",	ldap_auth_getgrnam },
3604   { 0, "getgrgid",	ldap_auth_getgrgid },
3605   { 0, "getgroups",	ldap_auth_getgroups },
3606   { 0, "auth",		ldap_auth_auth },
3607   { 0, "check",		ldap_auth_check },
3608   { 0, "uid2name",	ldap_auth_uid2name },
3609   { 0, "gid2name",	ldap_auth_gid2name },
3610   { 0, "name2uid",	ldap_auth_name2uid },
3611   { 0, "name2gid",	ldap_auth_name2gid },
3612 
3613   { 0, NULL }
3614 };
3615 
3616 module ldap_module = {
3617   /* Always NULL */
3618   NULL, NULL,
3619 
3620   /* Module API version */
3621   0x20,
3622 
3623   /* Module name */
3624   "ldap",
3625 
3626   /* Module configuration handler table */
3627   ldap_conftab,
3628 
3629   /* Module command handler table */
3630   ldap_cmdtab,
3631 
3632   /* Module authentication handler table */
3633   ldap_authtab,
3634 
3635   /* Module initialization */
3636   ldap_mod_init,
3637 
3638   /* Session initialization */
3639   ldap_sess_init,
3640 
3641   /* Module version */
3642   MOD_LDAP_VERSION
3643 };
3644