RSS

(root)/mod_ldap_userdir/head : /mod_ldap_userdir.c (revision 35)

Line Revision Contents
1 1
/*
2
 * mod_ldap_userdir - LDAP UserDir module for the Apache web server
3 30
 * Copyright 1999, 2000-9, John Morrissey <jwm@horde.net>
4 1
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307, USA.
18 3
 *
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 1
 */
24
25
/*
26 30
 * mod_ldap_userdir v1.1.16
27 1
 *
28
 * Description: A module for the Apache web server that performs UserDir
29
 * (home directory) lookups from an LDAP directory.
30
 *
31
 * Example (request for /~bar/one/two.html):
32
 *
33 5
 * LDAPUserDir public_html       -> ~bar/public_html/one/two.html
34
 * LDAPUserDir /usr/web          -> /usr/web/bar/one/two.html
35
 * LDAPUserDir /home/ * /www     -> /home/bar/www/one/two.html
36
 *       NOTE: these ^ ^ spaces are here to allow this to work in a C-style
37
 *             comment; they should not be included in your configuration.
38
 * LDAPUserDir http://x/users/ * -> (302) http://x/users/bar/one/two.html
39
 *       NOTE:           this ^ space is here to allow this to work in a
40
 *             C-style comment; they should not be included in your
41
 *             configuration.
42
 * LDAPUserDir http://x/ * /y    -> (302) http://x/bar/y/one/two.html
43 14
 *       NOTE:    these ^ ^ spaces are here to allow this to work in a
44 5
 *             C-style comment; they should not be included in your
45
 *             configuration.
46 1
 *
47
 * You can use multiple entries, to specify alternate user
48
 * directories (a la DirectoryIndex). For example:
49
 *
50
 * LDAPUserDir public_html public_www
51 5
 * LDAPUserDir public_html /usr/web http://www.xyz.com/users
52 1
 */
53
54
55 13
#include "config.h"
56
57 1
#include "httpd.h"
58
#include "http_config.h"
59
#include "http_log.h"
60 5
#include "http_request.h"
61 1
62 15
/* httpd.h, please don't complain about my strtoul() usage.
63
 * sunos4 doesn't matter much to me.
64
 */
65
#undef strtoul
66 5
#ifdef STANDARD20_MODULE_STUFF
67 3
# define APR_WANT_STRFUNC
68 34
# include "apr_hash.h"
69
# include "apr_strings.h"
70 3
# include "apr_want.h"
71 11
# include <time.h>
72 3
# define AP_POOL apr_pool_t
73
# define AP_PSTRDUP apr_pstrdup
74
# define AP_PSTRCAT apr_pstrcat
75
# define AP_PCALLOC apr_pcalloc
76 6
# define AP_STRSTR ap_strstr
77
# define AP_STRSTR_C ap_strstr_c
78
# define AP_STRCHR_C ap_strchr_c
79
# define AP_TABLE_SETN apr_table_setn
80 34
# define AP_MAKE_ARRAY apr_array_make
81
# define AP_PUSH_ARRAY apr_array_push
82 8
# if !defined(WIN32) && !defined(OS2) && !defined(BEOS) && !defined(NETWARE)
83
#  define HAVE_UNIX_SUEXEC
84
#  include "unixd.h"  /* Contains the suexec_identity hook used on Unix */
85
# endif
86 5
#else /* STANDARD20_MODULE_STUFF */
87 3
# define AP_POOL pool
88
# define AP_PSTRDUP ap_pstrdup
89
# define AP_PSTRCAT ap_pstrcat
90
# define AP_PCALLOC ap_pcalloc
91 6
# define AP_STRSTR strstr
92
# define AP_STRSTR_C strstr
93
# define AP_STRCHR_C strchr
94
# define AP_TABLE_SETN ap_table_setn
95 34
# define AP_MAKE_ARRAY ap_make_array
96
# define AP_PUSH_ARRAY ap_push_array
97 5
# if !defined(NETWARE)
98
#  include <sys/types.h>
99
# endif
100 3
# include <sys/stat.h>
101 5
# if !defined(WIN32)
102
#  include <unistd.h>
103
# endif
104
#endif /* STANDARD20_MODULE_STUFF */
105 3
106 1
#include <lber.h>
107
#include <ldap.h>
108
109 34
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_VENDOR_VERSION >= 192)
110
# define HAS_LDAP_UNBIND_EXT_S
111
#endif
112
113
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_VENDOR_VERSION >= 19905)
114
# define HAS_LDAP_INITIALIZE
115
#endif
116
117 16
#if LDAP_API_VERSION >= 2000
118
# define LDAP_VALUE(values, i) (values[i]->bv_val)
119
# define LDAP_VALUE_FREE(values) (ldap_value_free_len(values))
120
#else
121
# define LDAP_VALUE(values, i) (values[i])
122
# define LDAP_VALUE_FREE(values) (ldap_value_free(values))
123 34
#endif
124
125
#ifdef HAS_LDAP_UNBIND_EXT_S
126
# define LDAP_UNBIND(ld) (ldap_unbind_ext_s(ld, NULL, NULL))
127
#else
128 16
# define LDAP_UNBIND(ld) (ldap_unbind_s(ld, NULL, NULL))
129
#endif
130
131 5
/* Thanks, Sun. */
132 1
#ifndef LDAP_OPT_SUCCESS
133
# define LDAP_OPT_SUCCESS LDAP_SUCCESS
134
#endif
135
136 5
#ifdef STANDARD20_MODULE_STUFF
137 3
module AP_MODULE_DECLARE_DATA ldap_userdir_module;
138
#else
139 5
module MODULE_VAR_EXPORT ldap_userdir_module;
140 3
#endif
141 1
142
typedef struct ldap_userdir_config {
143
	char *userdir;
144 34
145
#ifdef STANDARD20_MODULE_STUFF
146
	apr_array_header_t *servers;
147
#else
148
	array_header *servers;
149
#endif
150
	unsigned int cur_server_index;
151
152 22
#if LDAP_API_VERSION >= 2000
153
	char *url;
154
#else /* LDAP_API_VERSION >= 2000 */
155
	char *server;
156
	int port;
157
#endif /* LDAP_API_VERSION >= 2000 */
158 34
159 22
	char *ldap_dn, *dn_pass,
160 14
	     *basedn, *filter_template,
161 15
	     *home_attr, *username_attr, *uidNumber_attr, *gidNumber_attr;
162 22
	int search_scope, protocol_version;
163 34
164 15
#ifdef STANDARD20_MODULE_STUFF
165
	int cache_timeout;
166
#endif
167 34
168 12
#ifdef TLS
169
	int use_tls;
170
#endif
171
172
	LDAP *ld;
173
174
	unsigned int got_url;
175 11
176
#ifdef STANDARD20_MODULE_STUFF
177
	apr_hash_t *homedirHt;
178
#endif /* STANDARD20_MODULE_STUFF */
179 1
} ldap_userdir_config;
180
181 11
#ifdef STANDARD20_MODULE_STUFF
182
struct hash_entry {
183
	time_t inserted_at;
184
	char *homedir;
185 14
	char *posix_username;
186 15
	char *uid;
187
	char *gid;
188 11
};
189
#endif /* STANDARD20_MODULE_STUFF */
190
191 1
static void *
192 3
create_ldap_userdir_config(AP_POOL *p, server_rec *s)
193 1
{
194 3
	ldap_userdir_config *newcfg = (ldap_userdir_config *) AP_PCALLOC(p, sizeof(ldap_userdir_config));
195 1
196 34
	newcfg->servers = AP_MAKE_ARRAY(p, 2, sizeof(char *));
197
198 22
#if LDAP_API_VERSION < 2000
199 12
	newcfg->port = -1;
200 22
#endif /* LDAP_API_VERSION < 2000 */
201 34
202 12
	newcfg->search_scope = -1;
203 15
	newcfg->protocol_version = -1;
204 34
205 15
#ifdef STANDARD20_MODULE_STUFF
206 12
	newcfg->cache_timeout = -1;
207 15
#endif
208 34
209 13
#ifdef TLS
210 12
	newcfg->use_tls = -1;
211 13
#endif /* TLS */
212 12
213 11
#ifdef STANDARD20_MODULE_STUFF
214
	newcfg->homedirHt = apr_hash_make(p);
215
#endif /* STANDARD20_MODULE_STUFF */
216 34
217 1
	return (void *)newcfg;
218
}
219
220 12
static void *
221
merge_ldap_userdir_config(AP_POOL *p, void *server1_conf, void *server2_conf)
222
{
223
	ldap_userdir_config *s_cfg1 = (ldap_userdir_config *) server1_conf,
224
	                    *s_cfg2 = (ldap_userdir_config *) server2_conf;
225
226 15
	ldap_userdir_config *merged_cfg =
227
		(ldap_userdir_config *) AP_PCALLOC(p, sizeof(ldap_userdir_config));
228 12
	memcpy(merged_cfg, s_cfg2, sizeof(ldap_userdir_config));
229
230
	if (!merged_cfg->userdir) {
231
		merged_cfg->userdir = AP_PSTRDUP(p, s_cfg1->userdir);
232
	}
233 34
234
	if (!merged_cfg->servers) {
235
		merged_cfg->servers = apr_array_copy_hdr(p, s_cfg1->servers);
236
	}
237
238 22
#if LDAP_API_VERSION >= 2000
239
	if (!merged_cfg->url) {
240
		merged_cfg->url = AP_PSTRDUP(p, s_cfg1->url);
241
	}
242
#else /* LDAP_API_VERSION >= 2000 */
243 12
	if (!merged_cfg->server) {
244
		merged_cfg->server = AP_PSTRDUP(p, s_cfg1->server);
245
	}
246 34
	if (merged_cfg->port == -1) {
247
		merged_cfg->port = s_cfg1->port;
248
	}
249 22
#endif /* LDAP_API_VERSION >= 2000 */
250 34
251 12
	if (!merged_cfg->ldap_dn) {
252
		merged_cfg->ldap_dn = AP_PSTRDUP(p, s_cfg1->ldap_dn);
253
	}
254
	if (!merged_cfg->dn_pass) {
255
		merged_cfg->dn_pass = AP_PSTRDUP(p, s_cfg1->dn_pass);
256
	}
257
	if (!merged_cfg->basedn) {
258
		merged_cfg->basedn = AP_PSTRDUP(p, s_cfg1->basedn);
259
	}
260
	if (!merged_cfg->filter_template) {
261
		merged_cfg->filter_template = AP_PSTRDUP(p, s_cfg1->filter_template);
262
	}
263
	if (!merged_cfg->home_attr) {
264
		merged_cfg->home_attr = AP_PSTRDUP(p, s_cfg1->home_attr);
265
	}
266 14
	if (!merged_cfg->username_attr) {
267
		merged_cfg->username_attr = AP_PSTRDUP(p, s_cfg1->username_attr);
268
	}
269 15
	if (!merged_cfg->uidNumber_attr) {
270
		merged_cfg->uidNumber_attr = AP_PSTRDUP(p, s_cfg1->uidNumber_attr);
271
	}
272
	if (!merged_cfg->gidNumber_attr) {
273
		merged_cfg->gidNumber_attr = AP_PSTRDUP(p, s_cfg1->gidNumber_attr);
274
	}
275 34
276 12
	if (merged_cfg->search_scope == -1) {
277
		merged_cfg->search_scope = s_cfg1->search_scope;
278
	}
279 15
	if (merged_cfg->protocol_version == -1) {
280
		merged_cfg->protocol_version = s_cfg1->protocol_version;
281
	}
282 34
283 15
#ifdef STANDARD20_MODULE_STUFF
284 12
	if (merged_cfg->cache_timeout == -1) {
285
		merged_cfg->cache_timeout = s_cfg1->cache_timeout;
286
	}
287 15
#endif /* STANDARD20_MODULE_STUFF */
288 34
289 13
#ifdef TLS
290 12
	if (merged_cfg->use_tls == -1) {
291
		merged_cfg->use_tls = s_cfg1->use_tls;
292
	}
293 13
#endif /* TLS */
294 12
295 15
	return (void *) merged_cfg;
296 12
}
297
298 1
static const char *
299 3
set_ldap_user_dir(cmd_parms *cmd, void *dummy, const char *arg)
300 1
{
301 12
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
302 1
303 5
	if (strlen(arg) == 0) {
304 1
		return "LDAPUserDir must be supplied with the public subdirectory in users' home directories (e.g., 'public_html').";
305 5
	}
306 1
307 3
	s_cfg->userdir = AP_PSTRDUP(cmd->pool, arg);
308 12
	return NULL;
309
}
310
311
static const char *
312
set_url(cmd_parms *cmd, void *dummy, const char *arg)
313
{
314
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
315
	LDAPURLDesc *url;
316
317 22
#if LDAP_API_VERSION < 2000
318
	if (s_cfg->server) {
319
		return "LDAPUserDirServerURL can't be combined with LDAPUserDirServer.";
320
	}
321
#endif /* LDAP_API_VERSION < 2000 */
322
	if (s_cfg->basedn || s_cfg->filter_template || s_cfg->search_scope != -1) {
323
		return "LDAPUserDirServerURL can't be combined with LDAPUserDirBaseDN, LDAPUserDirFilter, or LDAPUserDirSearchScope.";
324
	}
325 12
	s_cfg->got_url = 1;
326
327
	if (ldap_url_parse(arg, &url) != LDAP_SUCCESS) {
328
		return "LDAPUserDirServerURL must be supplied with a valid LDAP URL.";
329
	}
330
331 13
#ifdef HAVE_LDAPURLDESC_LUD_SCHEME
332
# define SCHEME_IS(scheme) \
333
	((strlen(url->lud_scheme) == strlen(scheme) - 1) && \
334
	 (strncasecmp(url->lud_scheme, scheme, strlen(scheme) - 1) == 0))
335
#else /* HAVE_LDAPURLDESC_LUD_SCHEME */
336
# define SCHEME_IS(scheme) (strncasecmp(arg, scheme, strlen(scheme)) == 0)
337
#endif /* HAVE_LDAPURLDESC_LUD_SCHEME */
338
339 22
	if (!SCHEME_IS("ldaps:") && !SCHEME_IS("ldap:")) {
340 12
		return "Invalid scheme specified by LDAPUserDirServerURL. Valid schemes are 'ldap' or 'ldaps'.";
341
	}
342 22
343
#ifdef TLS
344
	if (SCHEME_IS("ldaps:") && s_cfg->use_tls != -1) {
345
		return "ldaps:// scheme in LDAPUserDirServerURL can't be combined with LDAPUserDirUseTLS.";
346 13
	}
347
#endif /* TLS */
348 12
349
	ldap_free_urldesc(url);
350 34
351
	*(char **)AP_PUSH_ARRAY(s_cfg->servers) = AP_PSTRDUP(cmd->pool, arg);
352 1
	return NULL;
353
}
354
355
static const char *
356 11
set_server(cmd_parms *cmd, void *dummy, const char *arg)
357 1
{
358
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
359
360 12
	if (s_cfg->got_url) {
361
		return "LDAPUserDirServer can't be combined with LDAPUserDirServerURL.";
362
	}
363
364 5
	if (strlen(arg) == 0) {
365 1
		return "LDAPUserDirServer must be supplied with the name of an LDAP server.";
366 5
	}
367 1
368 34
	*(char **)AP_PUSH_ARRAY(s_cfg->servers) = AP_PSTRDUP(cmd->pool, arg);
369 1
	return NULL;
370
}
371
372
static const char *
373 3
set_ldap_dninfo(cmd_parms *cmd, void *dummy,
374
                const char *dn, const char *pass)
375 1
{
376
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
377
378 5
	if (strlen(dn) == 0) {
379 1
		return "LDAPUserDirDNInfo must be supplied with a LDAP DN to bind as.";
380 5
	}
381
	if (strlen(pass) == 0) {
382 1
		return "LDAPUserDirDNInfo must be supplied with a password to bind with.";
383 5
	}
384 1
385 15
	s_cfg->ldap_dn = (char *)dn;
386 11
	s_cfg->dn_pass = (char *)pass;
387 1
388
	return NULL;
389
}
390
391
static const char *
392 11
set_basedn(cmd_parms *cmd, void *dummy, const char *arg)
393 1
{
394
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
395
396 12
	if (s_cfg->got_url) {
397
		return "LDAPUserDirBaseDN can't be combined with LDAPUserDirServerURL.";
398
	}
399
400 5
	if (strlen(arg) == 0) {
401 1
		return "LDAPUserDirBaseDN must be supplied with the LDAP base DN to use for UserDir lookups.";
402 5
	}
403 1
404 11
	s_cfg->basedn = AP_PSTRDUP(cmd->pool, arg);
405 1
	return NULL;
406
}
407
408
static const char *
409 11
set_filter_template(cmd_parms *cmd, void *dummy, const char *arg)
410 1
{
411
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
412
413 12
	if (s_cfg->got_url) {
414
		return "LDAPUserDirFilter can't be combined with LDAPUserDirServerURL.";
415
	}
416
417 5
	if (strlen(arg) == 0) {
418 1
		return "LDAPUserDirFilter must be supplied with a filter template to use for LDAP UserDir lookups.";
419 5
	}
420 1
421 11
	s_cfg->filter_template = AP_PSTRDUP(cmd->pool, arg);
422 1
	return NULL;
423
}
424
425
static const char *
426 11
set_search_scope(cmd_parms *cmd, void *dummy, const char *arg)
427 1
{
428
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
429
430 12
	if (s_cfg->got_url) {
431
		return "LDAPUserDirSearchScope can't be combined with LDAPUserDirServerURL.";
432
	}
433
434 5
	if (strlen(arg) == 0) {
435 1
		return "LDAPUserDirSearchScope must be supplied with a search scope (\"onelevel\" or \"subtree\")";
436 5
	}
437 1
438 5
	if (strcasecmp(arg, "onelevel") == 0) {
439 11
		s_cfg->search_scope = LDAP_SCOPE_ONELEVEL;
440 7
		return NULL;
441 5
	} else if (strcasecmp(arg, "subtree") == 0) {
442 11
		s_cfg->search_scope = LDAP_SCOPE_SUBTREE;
443 7
		return NULL;
444 5
	}
445 1
446 5
	return "LDAPUserDirSearchScope must be either \"onelevel\" or \"subtree\".";
447 1
}
448
449
static const char *
450 11
set_use_tls(cmd_parms *cmd, void *dummy, int arg)
451 1
{
452 3
#ifdef TLS
453 1
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
454 12
455 22
	if (arg == 1 && s_cfg->got_url &&
456
	    strncasecmp(s_cfg->url, "ldaps:", 6) == 0) {
457
458
		return "LDAPUserDirUseTLS can't be combined with ldaps:// in LDAPUserDirServerURL.";
459 12
	}
460 14
	if (s_cfg->protocol_version < 3 && s_cfg->protocol_version != -1) {
461
		return "LDAPProtocolVersion must be set to version 3 to use the LDAPUserDirUseTLS directive.";
462
	}
463 12
464 11
	s_cfg->use_tls = arg;
465 1
	return NULL;
466
#else
467 22
	return "mod_ldap_userdir was not built with LDAP TLS support.";
468 3
#endif /* TLS */
469 1
}
470
471 10
static const char *
472 14
set_attr_name(cmd_parms *cmd, void *dummy,
473
              const char *our_attr, const char *their_attr)
474 10
{
475
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
476
477 14
	if (strlen(their_attr) == 0) {
478
		return "LDAPAttributeName must be supplied with a non-empty attribute name for its second argument, such as \"homeDirectory\"";
479
	}
480
481
	if (strcasecmp(our_attr, "homeDirectory") == 0) {
482
		s_cfg->home_attr = AP_PSTRDUP(cmd->pool, their_attr);
483
	} else if (strcasecmp(our_attr, "uid") == 0) {
484
		s_cfg->username_attr = AP_PSTRDUP(cmd->pool, their_attr);
485 15
	} else if (strcasecmp(our_attr, "uidNumber") == 0) {
486
		s_cfg->uidNumber_attr = AP_PSTRDUP(cmd->pool, their_attr);
487
	} else if (strcasecmp(our_attr, "gidNumber") == 0) {
488
		s_cfg->gidNumber_attr = AP_PSTRDUP(cmd->pool, their_attr);
489 14
	} else {
490 15
		return "LDAPAttributeName accepts only \"homeDirectory\", \"uid\", \"uidNumber\", or \"gidNumber\" for its first argument.";
491 14
	}
492
493 11
	return NULL;
494
}
495
496 15
#ifdef STANDARD20_MODULE_STUFF
497 11
static const char *
498
set_cache_timeout(cmd_parms *cmd, void *dummy, const char *arg)
499
{
500 14
	char *invalid_char = NULL;
501 11
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
502
503
	s_cfg->cache_timeout = strtol(arg, &invalid_char, 10);
504 13
	if (arg[0] == '\0' || *invalid_char != '\0') {
505 11
		return "LDAPUserDirCacheTimeout must be supplied with a numeric cache timeout.";
506
	}
507 10
	return NULL;
508
}
509 15
#endif
510 10
511 14
static const char *
512
set_ldap_protocol_version(cmd_parms *cmd, void *dummy, const char *arg)
513
{
514
	char *invalid_char = NULL;
515
	ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(cmd->server->module_config, &ldap_userdir_module);
516
517
	s_cfg->protocol_version = strtol(arg, &invalid_char, 10);
518 22
	if (arg[0] == '\0' || *invalid_char != '\0' ||
519
	    (s_cfg->protocol_version != 2 && s_cfg->protocol_version != 3)) {
520 14
		return "LDAPProtocolVersion must be set as version 2 or version 3.";
521
	}
522
#ifdef TLS
523
	if (s_cfg->protocol_version < 3 && s_cfg->use_tls != -1) {
524
		return "LDAPProtocolVersion must be set to version 3 to use the LDAPUserDirUseTLS directive.";
525
	}
526
#endif /* TLS */
527
	return NULL;
528
}
529
530 13
static void
531
apply_config_defaults(ldap_userdir_config *cfg)
532
{
533
	if (!cfg->home_attr) {
534
		cfg->home_attr = "homeDirectory";
535
	}
536 14
	if (!cfg->username_attr) {
537
		cfg->username_attr = "uid";
538
	}
539 15
	if (!cfg->uidNumber_attr) {
540
		cfg->uidNumber_attr = "uidNumber";
541
	}
542
	if (!cfg->gidNumber_attr) {
543
		cfg->gidNumber_attr = "gidNumber";
544
	}
545 22
#if LDAP_API_VERSION < 2000
546 13
	if (cfg->port == -1) {
547
		cfg->port = LDAP_PORT;
548
	}
549 22
#endif /* LDAP_API_VERSION < 2000 */
550 13
	if (cfg->search_scope == -1) {
551
		cfg->search_scope = LDAP_SCOPE_SUBTREE;
552
	}
553 15
#ifdef STANDARD20_MODULE_STUFF
554 13
	if (cfg->cache_timeout == -1) {
555
		cfg->cache_timeout = 300;
556
	}
557 15
#endif /* STANDARD20_MODULE_STUFF */
558 14
	if (cfg->protocol_version == -1) {
559
		cfg->protocol_version = 3;
560
	}
561 13
#ifdef TLS
562
	if (cfg->use_tls == -1) {
563
		cfg->use_tls = 0;
564
	}
565
#endif /* TLS */
566
}
567
568 10
#ifdef STANDARD20_MODULE_STUFF
569
static int
570 12
init_ldap_userdir(AP_POOL *pconf, AP_POOL *plog,
571
                  AP_POOL *ptemp, server_rec *s)
572 10
{
573 12
	for (; s; s = s->next) {
574
		ldap_userdir_config *s_cfg =
575
			(ldap_userdir_config *) ap_get_module_config(s->module_config, &ldap_userdir_module);
576 13
		apply_config_defaults(s_cfg);
577 12
	}
578
579 30
	ap_add_version_component(pconf, "mod_ldap_userdir/1.1.16");
580 10
	return OK;
581
}
582
#else /* STANDARD20_MODULE_STUFF */
583 1
static void
584 3
init_ldap_userdir(server_rec *s, AP_POOL *p)
585 1
{
586 13
	for (; s; s = s->next) {
587
		ldap_userdir_config *s_cfg =
588
 			(ldap_userdir_config *) ap_get_module_config(s->module_config, &ldap_userdir_module);
589
		apply_config_defaults(s_cfg);
590
	}
591
592 30
	ap_add_version_component("mod_ldap_userdir/1.1.16");
593 1
}
594 10
#endif /* STANDARD20_MODULE_STUFF */
595 16
596
static char *
597
result2errmsg(const request_rec *r, LDAP *ld, LDAPMessage *result)
598
{
599
	int ret;
600
	char *result_errmsg, *errmsg;
601
#if LDAP_API_VERSION >= 2000
602
	ret = ldap_parse_result(ld, result, NULL, NULL, &result_errmsg,
603
		NULL, NULL, 0);
604
	if (ret == LDAP_SUCCESS) {
605
		errmsg = AP_PSTRDUP(r->pool, result_errmsg);
606
		ldap_memfree(result_errmsg);
607
	} else {
608
		errmsg = "Unknown";
609
	}
610
#else
611
	errmsg = ldap_err2string(ldap_result2error(ld, result, 0));
612
#endif
613
614
	return errmsg;
615
}
616 1
617
static int
618 34
_ldap_connect(ldap_userdir_config *s_cfg)
619 1
{
620 14
	int ret, sizelimit = 2, version;
621 16
#if LDAP_API_VERSION >= 2000
622
	struct berval bindcred;
623
#endif
624 1
625 34
#ifdef HAS_LDAP_INITIALIZE
626 22
	ret = ldap_initialize(&(s_cfg->ld), s_cfg->url);
627
	if (ret != LDAP_SUCCESS) {
628
# ifdef STANDARD20_MODULE_STUFF
629
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: ldap_initialize() to %s failed: %s", s_cfg->url, strerror(ret));
630
# else
631
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: ldap_initialize() to %s failed: %s", s_cfg->url, strerror(ret));
632
# endif
633
		return -1;
634
	}
635 34
#else /* HAS_LDAP_INITIALIZE */
636 14
	if ((s_cfg->ld = (LDAP *) ldap_init(s_cfg->server, s_cfg->port)) == NULL) {
637 22
# ifdef STANDARD20_MODULE_STUFF
638 11
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: ldap_init() to %s failed: %s", s_cfg->server, strerror(errno));
639 22
# else
640 11
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: ldap_init() to %s failed: %s", s_cfg->server, strerror(errno));
641 22
# endif
642 1
		return -1;
643
	}
644 34
#endif /* HAS_LDAP_INITIALIZE */
645 1
646 14
	switch (s_cfg->protocol_version) {
647
	case 2:
648
		version = LDAP_VERSION2;
649
		break;
650
	case 3:
651
	default:
652
		version = LDAP_VERSION3;
653
		break;
654
	}
655 1
656 14
	if ((ret = ldap_set_option(s_cfg->ld, LDAP_OPT_PROTOCOL_VERSION, &version)) != LDAP_OPT_SUCCESS) {
657 5
#ifdef STANDARD20_MODULE_STUFF
658 3
			ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: Setting LDAP version option failed: %s", ldap_err2string(ret));
659
#else
660 1
			ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: Setting LDAP version option failed: %s", ldap_err2string(ret));
661 3
#endif
662 16
			LDAP_UNBIND(s_cfg->ld);
663 12
			s_cfg->ld = NULL;
664 1
			return -1;
665
		}
666
667 14
#ifdef TLS
668
	if (s_cfg->use_tls) {
669 12
		if ((ret = ldap_start_tls_s(s_cfg->ld, NULL, NULL)) != LDAP_SUCCESS) {
670 5
#ifdef STANDARD20_MODULE_STUFF
671 3
			ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: Starting TLS failed: %s", ldap_err2string(ret));
672
#else
673 1
			ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: Starting TLS failed: %s", ldap_err2string(ret));
674 3
#endif
675 16
			LDAP_UNBIND(s_cfg->ld);
676 12
			s_cfg->ld = NULL;
677 1
			return -1;
678
		}
679
	}
680 3
#endif /* TLS */
681 1
682 16
#if LDAP_API_VERSION >= 2000
683
	bindcred.bv_val = s_cfg->dn_pass;
684 29
	if (s_cfg->dn_pass != NULL) {
685
		bindcred.bv_len = strlen(s_cfg->dn_pass);
686
	} else {
687
		bindcred.bv_len = 0;
688
	}
689 16
	ret = ldap_sasl_bind_s(s_cfg->ld, s_cfg->ldap_dn, NULL, &bindcred, NULL, NULL, NULL);
690
#else /* LDAP_API_VERSION >= 2000 */
691
	ret = ldap_simple_bind_s(s_cfg->ld, s_cfg->ldap_dn, s_cfg->dn_pass);
692 15
#endif /* LDAP_API_VERSION >= 2000 */
693 16
	if (ret != LDAP_SUCCESS) {
694
#ifdef STANDARD20_MODULE_STUFF
695
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: bind as %s failed: %s", s_cfg->ldap_dn, ldap_err2string(ret));
696
#else
697
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: bind as %s failed: %s", s_cfg->ldap_dn, ldap_err2string(ret));
698 3
#endif
699 1
		return -1;
700
	}
701
702
	/* I couldn't think of a better way to do this without having autoconf
703
	 * jump through hoops to detect whether ldap_set_option is present.
704
	 * I think this works fairly well, though, as we're sure to need
705
	 * LDAP_OPT_SIZELIMIT to use ldap_set_option in this case. :-)
706
	 */
707
#ifdef LDAP_OPT_SIZELIMIT
708 12
	if ((ret = ldap_set_option(s_cfg->ld, LDAP_OPT_SIZELIMIT, (void *)&sizelimit)) != LDAP_OPT_SUCCESS)
709 5
#ifdef STANDARD20_MODULE_STUFF
710 3
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: ldap_set_option() unable to set query size limit to 2 entries: %s", ldap_err2string(ret));
711 5
#else /* STANDARD20_MODULE_STUFF */
712 1
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: ldap_set_option() unable to set query size limit to 2 entries: %s", ldap_err2string(ret));
713 5
#endif /* STANDARD20_MODULE_STUFF */
714 3
#else /* LDAP_OPT_SIZELIMIT */
715 12
	s_cfg->ld->ld_sizelimit = sizelimit;
716 3
#endif /* LDAP_OPT_SIZELIMIT */
717 1
718
	return 1;
719
}
720
721 34
static int
722
connect_ldap_userdir(request_rec *r, ldap_userdir_config *s_cfg)
723
{
724
	int start_server_index;
725
	char *item;
726
	LDAPURLDesc *url;
727
728
	start_server_index = s_cfg->cur_server_index;
729
	do {
730
		/* We won't have any configured servers if no
731
		 * LDAPUserDirServer* directives were specified.
732
		 * Fall back and use the SDK default if so.
733
		 */
734
		if (s_cfg->servers->nelts) {
735
			item = ((char **)(s_cfg->servers->elts))[s_cfg->cur_server_index];
736
737
			if (ldap_is_ldap_url(item)) {
738
				if (ldap_url_parse(item, &url) != LDAP_URL_SUCCESS) {
739
#ifdef STANDARD20_MODULE_STUFF
740
					ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: URL %s was valid at startup, but is no longer valid?!", item);
741
#else /* STANDARD20_MODULE_STUFF */
742
					ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: URL %s was valid at startup, but is no longer valid?!", item);
743
#endif /* STANDARD20_MODULE_STUFF */
744
745
					++(s_cfg->cur_server_index);
746
					if (s_cfg->cur_server_index >= s_cfg->servers->nelts) {
747
						s_cfg->cur_server_index = 0;
748
					}
749
					continue;
750
				}
751
752
#ifdef HAS_LDAP_INITIALIZE
753
				s_cfg->url = AP_PSTRDUP(r->server->process->pool, item);
754
#else /* HAS_LDAP_INITIALIZE */
755
				if (url->lud_host != NULL) {
756
					s_cfg->server = AP_PSTRDUP(r->server->process->pool, url->lud_host);
757
				}
758
				if (url->lud_port != 0) {
759
					s_cfg->port = url->lud_port;
760
				}
761
#endif /* HAS_LDAP_INITIALIZE */
762
				if (url->lud_dn != NULL) {
763
					s_cfg->basedn = AP_PSTRDUP(r->server->process->pool, url->lud_dn);
764
				}
765
				if (url->lud_filter != NULL) {
766
					s_cfg->filter_template = AP_PSTRDUP(r->server->process->pool, url->lud_filter);
767
				}
768
769
				s_cfg->search_scope = url->lud_scope;
770
				if (s_cfg->search_scope == LDAP_SCOPE_BASE) {
771
# ifdef STANDARD20_MODULE_STUFF
772
					ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, NULL, "mod_ldap_userdir: WARNING: LDAP URL search scopes default to 'base' (not 'sub') and may not be what you want.");
773
# else
774
					ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, "mod_ldap_userdir: WARNING: LDAP URL search scopes default to 'base' (not 'sub') and may not be what you want.");
775
# endif
776
				}
777
778
				ldap_free_urldesc(url);
779
			} else {
780
#ifdef HAS_LDAP_INITIALIZE
781
				s_cfg->url = AP_PSTRCAT(r->server->process->pool,
782
					"ldap://", item, "/", NULL);
783
#else /* HAS_LDAP_INITIALIZE */
784
				s_cfg->server = AP_PSTRDUP(r->server->process->pool, item);
785
				s_cfg->port = LDAP_PORT;
786
#endif /* HAS_LDAP_INITIALIZE */
787
			}
788
		}
789
790
		if (_ldap_connect(s_cfg) == 1) {
791
			return 1;
792
		}
793
794
		++s_cfg->cur_server_index;
795
		if (s_cfg->cur_server_index >= s_cfg->servers->nelts) {
796
			s_cfg->cur_server_index = 0;
797
		}
798
	} while (s_cfg->cur_server_index != start_server_index);
799
800
	return -1;
801
}
802
803 3
static char *
804
generate_filter(AP_POOL *p, char *template, const char *entity)
805
{
806
	char *filter, *pos;
807
	int num_escapes = 0, i = 0, j = 0;
808
809
	pos = template;
810 6
	while ((pos = AP_STRSTR(pos + 2, "%v")) != NULL) {
811 3
		++num_escapes;
812 5
	}
813 12
	pos = template;
814
	while ((pos = AP_STRSTR(pos + 2, "%u")) != NULL) {
815
		++num_escapes;
816
	}
817 3
818 12
	/* -2 for the %u/%v, +1 for the NULL */
819 3
	filter = AP_PCALLOC(p, strlen(template) - (num_escapes * 2) + (num_escapes * strlen(entity)) + 1);
820
821
	while (template[i] != '\0') {
822 12
		if (template[i] == '%' &&
823
		    (template[i + 1] == 'u' || template[i + 1] == 'v'))
824
		{
825 3
			strcat(filter, entity);
826
			j += strlen(entity);
827
			i += 2;
828 5
		} else {
829 3
			filter[j++] = template[i++];
830 5
		}
831 3
	}
832
833
	return filter;
834
}
835
836 15
void
837
free_entry(struct hash_entry **entry)
838
{
839
	if (!*entry) {
840
		return;
841
	}
842
843
	if ((*entry)->homedir) {
844
		free((*entry)->homedir);
845
	}
846
	if ((*entry)->posix_username) {
847
		free((*entry)->posix_username);
848
	}
849
	if ((*entry)->uid) {
850
		free((*entry)->uid);
851
	}
852
	if ((*entry)->gid) {
853
		free((*entry)->gid);
854
	}
855
856
	free((*entry));
857
	*entry = NULL;
858
}
859 14
860
static struct hash_entry *
861 11
cache_fetch(const ldap_userdir_config *s_cfg, const char *username)
862
{
863 14
#ifndef STANDARD20_MODULE_STUFF
864
	return NULL;
865
#else /* STANDARD20_MODULE_STUFF */
866 15
	struct hash_entry *entry;
867 11
868
	/* A cache timeout of 0 disables caching. */
869
	if (s_cfg->cache_timeout == 0) {
870
		return NULL;
871
	}
872
873 15
	entry = apr_hash_get(s_cfg->homedirHt, username, APR_HASH_KEY_STRING);
874
	if (entry == NULL) {
875 11
		return NULL;
876
	}
877
878
	/* If this entry is still valid, return it. Otherwise, expire the
879
	 * stale entry.
880
	 */
881 15
	if (entry->inserted_at + s_cfg->cache_timeout > time(NULL)) {
882
		return entry;
883
	}
884
885
	free_entry(&entry);
886 11
	apr_hash_set(s_cfg->homedirHt, username, APR_HASH_KEY_STRING, NULL);
887
	return NULL;
888 14
#endif /* !STANDARD20_MODULE_STUFF */
889 11
}
890
891 14
static struct hash_entry *
892 12
get_ldap_homedir(ldap_userdir_config *s_cfg, request_rec *r,
893 1
                 const char *username)
894
{
895 16
	char *filter,
896 15
	     *attrs[] = {s_cfg->home_attr, s_cfg->username_attr,
897
	                 s_cfg->uidNumber_attr, s_cfg->gidNumber_attr, NULL};
898
	int i = 0, ret;
899 16
#if LDAP_API_VERSION >= 2000
900
	struct timeval timeout;
901
	struct berval **values;
902
#else
903
	char **values;
904
#endif
905 1
	LDAPMessage *result, *e;
906 15
	struct hash_entry *entry;
907
908
#ifdef STANDARD20_MODULE_STUFF
909
	entry = cache_fetch(s_cfg, username);
910
	if (entry != NULL) {
911
		return entry;
912 11
	}
913
#endif /* STANDARD20_MODULE_STUFF */
914
915 1
	/* If we haven't even connected yet, try to connect. If we still can't
916
	   connect, give up. */
917 12
	if (s_cfg->ld == NULL) {
918 34
		if (connect_ldap_userdir(r, s_cfg) != 1) {
919 1
			return NULL;
920 5
		}
921
	}
922 1
923 11
	if (s_cfg->filter_template && *(s_cfg->filter_template)) {
924
		filter = generate_filter(r->pool, s_cfg->filter_template, username);
925 5
	} else {
926 12
		filter = generate_filter(r->pool, "(&(uid=%u)(objectClass=posixAccount))", username);
927 5
	}
928 1
929 16
#if LDAP_API_VERSION >= 2000
930
	timeout.tv_sec = 2;
931
	timeout.tv_usec = 0;
932
	ret = ldap_search_ext_s(s_cfg->ld, s_cfg->basedn, s_cfg->search_scope,
933
		filter, attrs, 0, NULL, NULL, &timeout, 2, &result);
934
#else /* LDAP_API_VERSION >= 2000 */
935
	ret = ldap_search_s(s_cfg->ld, s_cfg->basedn, s_cfg->search_scope,
936
		filter, attrs, 0, &result);
937
#endif /* LDAP_API_VERSION >= 2000 */
938 15
	if (ret != LDAP_SUCCESS) {
939 1
		/* If the LDAP server went away, try to reconnect. If the reconnect
940 12
		 * fails, give up and log accordingly.
941
		 */
942 1
		if (ret == LDAP_SERVER_DOWN) {
943 16
			LDAP_UNBIND(s_cfg->ld);
944 1
945 34
			if (connect_ldap_userdir(r, s_cfg) != 1) {
946 5
#ifdef STANDARD20_MODULE_STUFF
947 3
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: LDAP server went away, couldn't reconnect. Declining request.");
948
#else
949 1
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: LDAP server went away, couldn't reconnect. Declining request.");
950 3
#endif
951 12
				s_cfg->ld = NULL;
952 1
				return NULL;
953
			}
954
955 16
#if LDAP_API_VERSION >= 2000
956
			ret = ldap_search_ext_s(s_cfg->ld, s_cfg->basedn, s_cfg->search_scope,
957
				filter, attrs, 0, NULL, NULL, &timeout, 2, &result);
958
#else /* LDAP_API_VERSION >= 2000 */
959
			ret = ldap_search_s(s_cfg->ld, s_cfg->basedn, s_cfg->search_scope,
960
				filter, attrs, 0, &result);
961
#endif /* LDAP_API_VERSION >= 2000 */
962
			if (ret != LDAP_SUCCESS) {
963
#ifdef STANDARD20_MODULE_STUFF
964
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: LDAP search failed: %s", ldap_err2string(ret));
965
#else
966
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: LDAP search failed: %s", ldap_err2string(ret));
967 3
#endif
968 1
				return NULL;
969
			}
970 5
		} else {
971
#ifdef STANDARD20_MODULE_STUFF
972 16
			ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: LDAP search failed: %s", ldap_err2string(ret));
973
#else
974
			ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: LDAP search failed: %s", ldap_err2string(ret));
975 3
#endif
976 1
			return NULL;
977
		}
978
	}
979
980 12
	if ((ret = ldap_count_entries(s_cfg->ld, result)) > 1) {
981 5
#ifdef STANDARD20_MODULE_STUFF
982 3
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: found too many entries (%d entries) for query, expecting 1 entry. Ignoring LDAP results.", ret);
983
#else
984 1
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: found too many entries (%d entries) for query, expecting 1 entry. Ignoring LDAP results.", ret);
985 3
#endif
986 1
		ldap_msgfree(result);
987
		return NULL;
988 5
	} else if (ret < 1) {
989 1
		/* We didn't find any users, don't bother calling ldap_first_entry(). */
990
		return NULL;
991
	}
992
993 12
	if ((e = ldap_first_entry(s_cfg->ld, result)) == NULL) {
994 5
#ifdef STANDARD20_MODULE_STUFF
995 16
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: ldap_first_entry() failed: %s", result2errmsg(r, s_cfg->ld, result));
996
#else
997
		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: ldap_first_entry() failed: %s", result2errmsg(r, s_cfg->ld, result));
998 3
#endif
999 1
		ldap_msgfree(result);
1000
		return NULL;
1001
	}
1002
1003 28
	entry = (struct hash_entry *) calloc(1, sizeof(struct hash_entry));
1004 15
	if (entry == NULL) {
1005 11
		ldap_msgfree(result);
1006
		return NULL;
1007
	}
1008 15
1009
	while (attrs[i] != NULL) {
1010 16
#if LDAP_API_VERSION >= 2000
1011
		values = ldap_get_values_len(s_cfg->ld, e, attrs[i]);
1012
#else
1013 15
		values = ldap_get_values(s_cfg->ld, e, attrs[i]);
1014 16
#endif
1015 15
		if (!values) {
1016
			if (strcmp(attrs[i], s_cfg->username_attr) == 0 ||
1017
			    strcmp(attrs[i], s_cfg->home_attr) == 0)
1018
			{
1019 25
#ifdef STANDARD20_MODULE_STUFF
1020
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: user %s has no %s attr, skipping request.", username, attrs[i]);
1021
#else
1022
				ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: user %s has no %s attr, skipping request.", username, attrs[i]);
1023
#endif
1024
1025 15
				free_entry(&entry);
1026
				ldap_msgfree(result);
1027
				return NULL;
1028
			}
1029 25
			++i;
1030 15
			continue;
1031
		}
1032
1033
		/* There doesn't seem to be any per-server/thread pool we can use
1034
		 * here, so we need to jump through some hoops to make sure we
1035
		 * don't leak if anything fails.
1036
		 */
1037
		if (strcmp(attrs[i], s_cfg->home_attr) == 0) {
1038 16
			entry->homedir = strdup(LDAP_VALUE(values, 0));
1039
			LDAP_VALUE_FREE(values);
1040 15
			if (entry->homedir == NULL) {
1041
				free_entry(&entry);
1042
				ldap_msgfree(result);
1043
				return NULL;
1044
			}
1045
		} else if (strcmp(attrs[i], s_cfg->username_attr) == 0) {
1046 16
			entry->posix_username = strdup(LDAP_VALUE(values, 0));
1047
			LDAP_VALUE_FREE(values);
1048 15
			if (entry->posix_username == NULL) {
1049
				free_entry(&entry);
1050
				ldap_msgfree(result);
1051
				return NULL;
1052
			}
1053
		} else if (strcmp(attrs[i], s_cfg->uidNumber_attr) == 0) {
1054 16
			entry->uid = strdup(LDAP_VALUE(values, 0));
1055
			LDAP_VALUE_FREE(values);
1056 15
			if (entry->uid == NULL) {
1057
				free_entry(&entry);
1058
				ldap_msgfree(result);
1059
				return NULL;
1060
			}
1061
		} else if (strcmp(attrs[i], s_cfg->gidNumber_attr) == 0) {
1062 16
			entry->gid = strdup(LDAP_VALUE(values, 0));
1063
			LDAP_VALUE_FREE(values);
1064 15
			if (entry->gid == NULL) {
1065
				free_entry(&entry);
1066
				ldap_msgfree(result);
1067
				return NULL;
1068
			}
1069
		} else {
1070
#ifdef STANDARD20_MODULE_STUFF
1071
			ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "mod_ldap_userdir: ldap_get_values() loop found unknown attr %s", attrs[i]);
1072
#else
1073
			ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "mod_ldap_userdir: ldap_get_values() loop found unknown attr %s", attrs[i]);
1074
#endif
1075 16
			LDAP_VALUE_FREE(values);
1076 15
		}
1077
1078
		++i;
1079
	}
1080
1081
#ifdef STANDARD20_MODULE_STUFF
1082
	/* A cache timeout of 0 disables caching. */
1083
	if (s_cfg->cache_timeout != 0) {
1084
		entry->inserted_at = time(NULL);
1085
		apr_hash_set(s_cfg->homedirHt, entry->posix_username,
1086
			APR_HASH_KEY_STRING, entry);
1087
	}
1088
#endif /* STANDARD20_MODULE_STUFF */
1089 11
1090 1
	ldap_msgfree(result);
1091 15
	return entry;
1092 1
}
1093
1094
static int
1095
translate_ldap_userdir(request_rec *r)
1096
{
1097 14
	const char *w, *dname;
1098
	char *name = r->uri;
1099 1
	const ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(r->server->module_config, &ldap_userdir_module);
1100 14
	const char *userdirs = s_cfg->userdir;
1101 23
	request_rec *notes_req;
1102 14
	struct hash_entry *user_info;
1103 1
1104
	/*
1105
	 * If the URI doesn't match our basic pattern, we've nothing to do with
1106
	 * it.
1107
	 */
1108 12
	if ((s_cfg->userdir == NULL) ||
1109 1
	    (name[0] != '/') ||
1110 5
	    (name[1] != '~'))
1111
	{
1112 1
		return DECLINED;
1113
	}
1114
1115
	dname = name + 2;
1116
	w = ap_getword(r->pool, &dname, '/');
1117
1118
	/*
1119
	 * This 'dname' funny business involves backing it up to capture the '/'
1120
	 * delimiting the "/~user" part from the rest of the URL, in case there
1121
	 * was one (the case where there wasn't being just "GET /~user HTTP/1.0",
1122
	 * for which we don't want to tack on a '/' onto the filename).
1123
	 */
1124 5
	if (dname[-1] == '/') {
1125 1
		--dname;
1126 5
	}
1127 1
1128
	/*
1129
	 * If there's no username, it's not for us. Ignore . and .. as well.
1130
	 */
1131 5
	if (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[3] == '\0')))) {
1132 1
		return DECLINED;
1133 5
	}
1134 1
1135 14
	user_info = get_ldap_homedir((ldap_userdir_config *)s_cfg, r, w);
1136
	if (user_info == NULL) {
1137 1
		return DECLINED;
1138 5
	}
1139 1
1140
	while (*userdirs) {
1141
		const char *userdir = ap_getword_conf(r->pool, &userdirs);
1142
		char *filename;
1143 5
#ifdef STANDARD20_MODULE_STUFF
1144 3
		apr_finfo_t statbuf;
1145
		apr_status_t rv;
1146 5
		int is_absolute = ap_os_is_path_absolute(r->pool, userdir);
1147 3
#else
1148 1
		struct stat statbuf;
1149 5
		int is_absolute = ap_os_is_path_absolute(userdir);
1150 3
#endif
1151 5
		char *x = NULL, *redirect;
1152
1153 6
		if (AP_STRCHR_C(userdir, '*')) {
1154 5
			x = ap_getword(r->pool, &userdir, '*');
1155
		}
1156
1157
		if (userdir[0] == '\0' || is_absolute) {
1158
			if (x) {
1159 6
				if (AP_STRSTR_C(x, "://") && !is_absolute)	{
1160 5
					redirect = AP_PSTRCAT(r->pool, x, w, userdir, dname, NULL);
1161 6
					AP_TABLE_SETN(r->headers_out, "Location", redirect);
1162 5
					return HTTP_MOVED_TEMPORARILY;
1163
				} else {
1164
					filename = AP_PSTRCAT(r->pool, x, w, userdir, NULL);
1165
				}
1166
			} else {
1167
				filename = AP_PSTRCAT(r->pool, userdir, "/", w, NULL);
1168
			}
1169 6
		} else if (x && AP_STRSTR_C(x, "://")) {
1170 5
			redirect = AP_PSTRCAT(r->pool, x, w, userdir, dname, NULL);
1171 6
			AP_TABLE_SETN(r->headers_out, "Location", redirect);
1172 5
			return HTTP_MOVED_TEMPORARILY;
1173
		} else {
1174 14
			filename = AP_PSTRCAT(r->pool, user_info->homedir, "/", userdir, NULL);
1175 5
		}
1176
1177
		/* Now see if it exists, or we're at the last entry. If we're
1178 1
		 * at the last entry, then use the filename generated (if there
1179
		 * is one) anyway, in the hope that some handler might handle
1180
		 * it. This can be used, for example, to run a CGI script for
1181
		 * the user.
1182
		 */
1183 5
#ifdef STANDARD20_MODULE_STUFF
1184
		if (filename &&
1185
		    (!*userdirs ||
1186
		     ((rv = apr_stat(&statbuf, filename, APR_FINFO_MIN, r->pool)) == APR_SUCCESS ||
1187
		      rv == APR_INCOMPLETE)))
1188 3
#else
1189
		if (filename && (!*userdirs || stat(filename, &statbuf) != -1))
1190
#endif
1191
		{
1192
			r->filename = AP_PSTRCAT(r->pool, filename, dname, NULL);
1193 5
			/* When statbuf contains info on r->filename, we can save
1194
			 * a syscall by copying it to r->finfo.
1195 1
			 */
1196 5
			if (*userdirs && dname[0] == 0) {
1197 1
				r->finfo = statbuf;
1198 5
			}
1199 8
1200 23
			/* We could be servicing a sub-request; make sure we put notes
1201
			 * on the main request.
1202
			 */
1203
			if (r->main) {
1204
				notes_req = r->main;
1205
			} else {
1206
				notes_req = r;
1207
			}
1208 8
			/* For use in the get_suexec_identity phase. */
1209 23
			AP_TABLE_SETN(notes_req->notes, "mod_ldap_userdir_user", user_info->posix_username);
1210
			AP_TABLE_SETN(notes_req->notes, "mod_ldap_userdir_uid", user_info->uid);
1211
			AP_TABLE_SETN(notes_req->notes, "mod_ldap_userdir_gid", user_info->gid);
1212 8
1213 1
			return OK;
1214
		}
1215
	}
1216
1217
	return DECLINED;
1218
}
1219
1220 5
#ifdef STANDARD20_MODULE_STUFF
1221 8
1222
#ifdef HAVE_UNIX_SUEXEC
1223
static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r)
1224
{
1225 15
	const char *username = apr_table_get(r->notes, "mod_ldap_userdir_user"),
1226
	           *uidNumber = apr_table_get(r->notes, "mod_ldap_userdir_uid"),
1227 18
	           *gidNumber = apr_table_get(r->notes, "mod_ldap_userdir_gid");
1228 15
	char *endptr = NULL;
1229 25
	const ldap_userdir_config *s_cfg = (ldap_userdir_config *) ap_get_module_config(r->server->module_config, &ldap_userdir_module);
1230
	ap_unix_identity_t *ugid;
1231
1232
	if (!username) {
1233
		return NULL;
1234
	}
1235
	if (!uidNumber) {
1236
# ifdef STANDARD20_MODULE_STUFF
1237
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: user %s has no %s attr, ignoring suexec request.", username, s_cfg->uidNumber_attr);
1238
# else
1239
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: user %s has no %s attr, ignoring suexec request.", username, s_cfg->uidNumber_attr);
1240
# endif
1241
		return NULL;
1242
	}
1243
1244
	if (!gidNumber) {
1245
# ifdef STANDARD20_MODULE_STUFF
1246
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: user %s has no %s attr, ignoring suexec request.", username, s_cfg->gidNumber_attr);
1247
# else
1248
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: user %s has no %s attr, ignoring suexec request.", username, s_cfg->gidNumber_attr);
1249
# endif
1250 8
		return NULL;
1251
	}
1252
1253 15
	ugid = apr_palloc(r->pool, sizeof(ap_unix_identity_t *));
1254
	if (ugid == NULL) {
1255 8
		return NULL;
1256
	}
1257
1258 15
	ugid->uid = (uid_t) strtoul(uidNumber, &endptr, 10);
1259 23
	if (*endptr != '\0') {
1260
# ifdef STANDARD20_MODULE_STUFF
1261
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: user %s has invalid UID %s, ignoring suexec request.", username, uidNumber);
1262
# else
1263
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: user %s has invalid UID %s, ignoring suexec request.", username, uidNumber);
1264
# endif
1265 8
		return NULL;
1266
	}
1267 15
	ugid->gid = (gid_t) strtoul(gidNumber, &endptr, 10);
1268 23
	if (*endptr != '\0') {
1269
# ifdef STANDARD20_MODULE_STUFF
1270
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, "mod_ldap_userdir: user %s has invalid GID %s, ignoring suexec request.", username, gidNumber);
1271
# else
1272
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, "mod_ldap_userdir: user %s has invalid GID %s, ignoring suexec request.", username, gidNumber);
1273
# endif
1274 15
		return NULL;
1275
	}
1276 8
1277
	ugid->userdir = 1;
1278
	return ugid;
1279
}
1280
#endif /* HAVE_UNIX_SUEXEC */
1281
1282 3
static void
1283
register_hooks(AP_POOL *p)
1284
{
1285
	static const char *const aszPre[]  = {"mod_alias.c",       NULL};
1286
	static const char *const aszSucc[] = {"mod_vhost_alias.c", NULL};
1287
	ap_hook_translate_name(translate_ldap_userdir, aszPre, aszSucc, APR_HOOK_MIDDLE);
1288 10
1289
	ap_hook_post_config(init_ldap_userdir, NULL, NULL, APR_HOOK_MIDDLE);
1290
1291 8
#ifdef HAVE_UNIX_SUEXEC
1292
	ap_hook_get_suexec_identity(get_suexec_id_doer, NULL, NULL, APR_HOOK_FIRST);
1293
#endif
1294 3
}
1295 10
1296 5
#endif /* STANDARD20_MODULE_STUFF */
1297 3
1298 1
static const command_rec ldap_userdir_cmds[] = {
1299 5
#ifdef STANDARD20_MODULE_STUFF
1300 3
	AP_INIT_RAW_ARGS("LDAPUserDir", set_ldap_user_dir, NULL, RSRC_CONF,
1301
	                 "the public subdirectory in users' home directories"),
1302 34
	AP_INIT_ITERATE("LDAPUserDirServer", set_server, NULL, RSRC_CONF,
1303 3
	              "the LDAP directory server that will be used for LDAP UserDir queries"),
1304 34
	AP_INIT_ITERATE("LDAPUserDirServerURL", set_url, NULL, RSRC_CONF,
1305 12
	              "the LDAP URL that will be used for LDAP UserDir queries"),
1306 3
	AP_INIT_TAKE2("LDAPUserDirDNInfo", set_ldap_dninfo, NULL, RSRC_CONF,
1307
	              "the DN and password that will be used to bind to the LDAP server when doing LDAP UserDir lookups"),
1308 11
	AP_INIT_TAKE1("LDAPUserDirBaseDN", set_basedn, NULL, RSRC_CONF,
1309 3
	              "the base DN that will be used when doing LDAP UserDir lookups"),
1310 11
	AP_INIT_TAKE1("LDAPUserDirFilter", set_filter_template, NULL, RSRC_CONF,
1311 12
	              "a template that will be used for the LDAP filter when doing LDAP UserDir lookups (%u and %v are replaced with the username being resolved)"),
1312 11
	AP_INIT_TAKE1("LDAPUserDirSearchScope", set_search_scope, NULL, RSRC_CONF,
1313 3
	              "the LDAP search scope (\"onelevel\" or \"subtree\") that will be used when doing LDAP UserDir lookups"),
1314 11
	AP_INIT_FLAG("LDAPUserDirUseTLS", set_use_tls, NULL, RSRC_CONF,
1315 3
	             "whether to use an encrypted connection to the LDAP server"),
1316 14
	AP_INIT_TAKE2("LDAPAttributeName", set_attr_name, NULL, RSRC_CONF,
1317
	             "alternate LDAP attribute names to use"),
1318 11
	AP_INIT_TAKE1("LDAPUserDirCacheTimeout", set_cache_timeout, NULL, RSRC_CONF,
1319
	              "how long, in seconds, to store cached LDAP entries"),
1320 14
	AP_INIT_TAKE1("LDAPProtocolVersion", set_ldap_protocol_version, NULL, RSRC_CONF,
1321
	              "the LDAP protocol version to use"),
1322 5
#else /* STANDARD20_MODULE_STUFF */
1323 1
	{"LDAPUserDir", set_ldap_user_dir, NULL, RSRC_CONF, RAW_ARGS,
1324
	 "the public subdirectory in users' home directories"},
1325 34
	{"LDAPUserDirServer", set_server, NULL, RSRC_CONF, ITERATE,
1326 1
	 "the LDAP directory server that will be used for LDAP UserDir queries"},
1327 34
	{"LDAPUserDirServerURL", set_url, NULL, RSRC_CONF, ITERATE,
1328 12
	 "the LDAP URL that will be used for LDAP UserDir queries"},
1329 1
	{"LDAPUserDirDNInfo", set_ldap_dninfo, NULL, RSRC_CONF, TAKE2,
1330
	 "the DN and password that will be used to bind to the LDAP server when doing LDAP UserDir lookups"},
1331 11
	{"LDAPUserDirBaseDN", set_basedn, NULL, RSRC_CONF, TAKE1,
1332 1
	 "the base DN that will be used when doing LDAP UserDir lookups"},
1333 11
	{"LDAPUserDirFilter", set_filter_template, NULL, RSRC_CONF, TAKE1,
1334 12
	 "a template that will be used for the LDAP filter when doing LDAP UserDir lookups (%u and %v are replaced with the username being resolved)"},
1335 11
	{"LDAPUserDirSearchScope", set_search_scope, NULL, RSRC_CONF, TAKE1,
1336 1
	 "the LDAP search scope (\"onelevel\" or \"subtree\") that will be used when doing LDAP UserDir lookups"},
1337 11
	{"LDAPUserDirUseTLS", set_use_tls, NULL, RSRC_CONF, FLAG,
1338 1
	 "whether to use an encrypted connection to the LDAP server"},
1339 14
	{"LDAPAttributeName", set_attr_name, NULL, RSRC_CONF, TAKE2,
1340
	 "alternate LDAP attribute names to use"},
1341
	{"LDAPProtocolVersion", set_ldap_protocol_version, NULL, RSRC_CONF, TAKE1,
1342
	 "the LDAP protocol version to use"},
1343 3
#endif
1344 1
	{NULL}
1345
};
1346
1347 5
#ifdef STANDARD20_MODULE_STUFF
1348
module AP_MODULE_DECLARE_DATA ldap_userdir_module = {
1349 3
	STANDARD20_MODULE_STUFF,
1350 10
	NULL,                        /* dir config creater */
1351
	NULL,                        /* dir merger --- default is to override */
1352
	create_ldap_userdir_config,  /* server config */
1353 12
	merge_ldap_userdir_config,   /* merge server config */
1354 10
	ldap_userdir_cmds,           /* command table */
1355
	register_hooks               /* register hooks */
1356 3
#else
1357 5
module MODULE_VAR_EXPORT ldap_userdir_module = {
1358 1
	STANDARD_MODULE_STUFF,
1359 10
	init_ldap_userdir,           /* initializer */
1360
	NULL,                        /* dir config creater */
1361
	NULL,                        /* dir merger --- default is to override */
1362
	create_ldap_userdir_config,  /* server config */
1363 12
	merge_ldap_userdir_config,   /* merge server config */
1364 10
	ldap_userdir_cmds,           /* command table */
1365
	NULL,                        /* handlers */
1366
	translate_ldap_userdir,      /* filename translation */
1367
	NULL,                        /* check_user_id */
1368
	NULL,                        /* check auth */
1369
	NULL,                        /* check access */
1370
	NULL,                        /* type_checker */
1371
	NULL,                        /* fixups */
1372
	NULL,                        /* logger */
1373
	NULL,                        /* header parser */
1374 12
	NULL,                        /* child_init */
1375 10
	NULL,                        /* child_exit */
1376
	NULL                         /* post read-request */
1377 3
#endif
1378 1
};

Loggerhead 1.17 is a web-based interface for Bazaar branches