]> andersk Git - mod-vhost-ldap.git/blame - mod_vhost_ldap.c
release 0.2.3
[mod-vhost-ldap.git] / mod_vhost_ldap.c
CommitLineData
7f9875bb
OS
1/* ============================================================
2 * Copyright (c) 2003-2004, Ondrej Sury
3 * All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19/*
20 * mod_vhost_ldap.c --- read virtual host config from LDAP directory
21 */
22
9ee2dda1 23#define CORE_PRIVATE
53a6ea83 24#define MOD_VHOST_LDAP_VERSION "mod_vhost_ldap/0.2.2"
9ee2dda1 25
7f9875bb
OS
26#include <unistd.h>
27
28#include "httpd.h"
29#include "http_config.h"
30#include "http_core.h"
31#include "http_log.h"
32#include "http_request.h"
33#include "apr_ldap.h"
34#include "apr_strings.h"
35#include "apr_reslist.h"
36#include "util_ldap.h"
37
38#ifndef APU_HAS_LDAP
39#error mod_vhost_ldap requires APR-util to have LDAP support built in
40#endif
41
42#if !defined(WIN32) && !defined(OS2) && !defined(BEOS) && !defined(NETWARE)
43#define HAVE_UNIX_SUEXEC
44#endif
45
46#ifdef HAVE_UNIX_SUEXEC
47#include "unixd.h" /* Contains the suexec_identity hook used on Unix */
48#endif
49
50#define MIN_UID 1000
51#define MIN_GID 1000
52
53module AP_MODULE_DECLARE_DATA vhost_ldap_module;
54
55typedef struct mod_vhost_ldap_config_t {
56 apr_pool_t *pool; /* Pool that this config is allocated from */
57#if APR_HAS_THREADS
58 apr_thread_mutex_t *lock; /* Lock for this config */
59#endif
60 int enabled; /* Is vhost_ldap enabled? */
61
62 /* These parameters are all derived from the VhostLDAPURL directive */
63 char *url; /* String representation of LDAP URL */
64
65 char *host; /* Name of the LDAP server (or space separated list) */
66 int port; /* Port of the LDAP server */
67 char *basedn; /* Base DN to do all searches from */
68 int scope; /* Scope of the search */
69 char *filter; /* Filter to further limit the search */
70 deref_options deref; /* how to handle alias dereferening */
71
72 char *binddn; /* DN to bind to server (can be NULL) */
73 char *bindpw; /* Password to bind to server (can be NULL) */
74
75 int have_ldap_url; /* Set if we have found an LDAP url */
76
77 int secure; /* True if SSL connections are requested */
78} mod_vhost_ldap_config_t;
79
80typedef struct mod_vhost_ldap_request_t {
81 char *dn; /* The saved dn from a successful search */
82 char *name; /* ServerName */
83 char *admin; /* ServerAdmin */
84 char *docroot; /* DocumentRoot */
9ee2dda1 85 char *cgiroot; /* ScriptAlias */
7f9875bb
OS
86 char *uid; /* Suexec Uid */
87 char *gid; /* Suexec Gid */
88} mod_vhost_ldap_request_t;
89
90char *attributes[] =
91 { "apacheServerName", "apacheServerAdmin", "apacheDocumentRoot", "apacheScriptAlias", "apacheSuexecUid", "apacheSuexecGid", 0 };
92
93static int mod_vhost_ldap_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
94{
95 /* make sure that mod_ldap (util_ldap) is loaded */
96 if (ap_find_linked_module("util_ldap.c") == NULL) {
97 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, s,
98 "Module mod_ldap missing. Mod_ldap (aka. util_ldap) "
99 "must be loaded in order for mod_vhost_ldap to function properly");
100 return HTTP_INTERNAL_SERVER_ERROR;
101
102 }
103
9ee2dda1 104 ap_add_version_component(p, MOD_VHOST_LDAP_VERSION);
7f9875bb
OS
105
106 return OK;
107}
108
109static void *
110mod_vhost_ldap_create_server_config (apr_pool_t *p, server_rec *s)
111{
112 mod_vhost_ldap_config_t *cfg =
113 (mod_vhost_ldap_config_t *)apr_pcalloc(p, sizeof (mod_vhost_ldap_config_t));
114
115 cfg->pool = p;
116
117#if APR_HAS_THREADS
118 apr_thread_mutex_create(&cfg->lock, APR_THREAD_MUTEX_DEFAULT, p);
119#endif
120
121 cfg->enabled = 0;
122 cfg->have_ldap_url = 0;
123 cfg->url = "";
124 cfg->host = NULL;
125 cfg->binddn = NULL;
126 cfg->bindpw = NULL;
127 cfg->deref = always;
128 cfg->secure = 0;
129
130 return cfg;
131}
132
133/*
134 * Use the ldap url parsing routines to break up the ldap url into
135 * host and port.
136 */
137static const char *mod_vhost_ldap_parse_url(cmd_parms *cmd,
138 void *dummy,
139 const char *url)
140{
141 int result;
142 apr_ldap_url_desc_t *urld;
143
144 mod_vhost_ldap_config_t *cfg =
145 (mod_vhost_ldap_config_t *)ap_get_module_config(cmd->server->module_config,
146 &vhost_ldap_module);
147
148 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
149 cmd->server, "[mod_vhost_ldap.c] url parse: `%s'",
150 url);
151
152 result = apr_ldap_url_parse(url, &(urld));
153 if (result != LDAP_SUCCESS) {
154 switch (result) {
155 case LDAP_URL_ERR_NOTLDAP:
156 return "LDAP URL does not begin with ldap://";
157 case LDAP_URL_ERR_NODN:
158 return "LDAP URL does not have a DN";
159 case LDAP_URL_ERR_BADSCOPE:
160 return "LDAP URL has an invalid scope";
161 case LDAP_URL_ERR_MEM:
162 return "Out of memory parsing LDAP URL";
163 default:
164 return "Could not parse LDAP URL";
165 }
166 }
167 cfg->url = apr_pstrdup(cmd->pool, url);
168
169 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
170 cmd->server, "[mod_vhost_ldap.c] url parse: Host: %s", urld->lud_host);
171 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
172 cmd->server, "[mod_vhost_ldap.c] url parse: Port: %d", urld->lud_port);
173 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
174 cmd->server, "[mod_vhost_ldap.c] url parse: DN: %s", urld->lud_dn);
175 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
176 cmd->server, "[mod_vhost_ldap.c] url parse: attrib: %s", urld->lud_attrs? urld->lud_attrs[0] : "(null)");
177 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
178 cmd->server, "[mod_vhost_ldap.c] url parse: scope: %s",
179 (urld->lud_scope == LDAP_SCOPE_SUBTREE? "subtree" :
180 urld->lud_scope == LDAP_SCOPE_BASE? "base" :
181 urld->lud_scope == LDAP_SCOPE_ONELEVEL? "onelevel" : "unknown"));
182 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
183 cmd->server, "[mod_vhost_ldap.c] url parse: filter: %s", urld->lud_filter);
184
185 /* Set all the values, or at least some sane defaults */
186 if (cfg->host) {
187 char *p = apr_palloc(cmd->pool, strlen(cfg->host) + strlen(urld->lud_host) + 2);
188 strcpy(p, urld->lud_host);
189 strcat(p, " ");
190 strcat(p, cfg->host);
191 cfg->host = p;
192 }
193 else {
194 cfg->host = urld->lud_host? apr_pstrdup(cmd->pool, urld->lud_host) : "localhost";
195 }
196 cfg->basedn = urld->lud_dn? apr_pstrdup(cmd->pool, urld->lud_dn) : "";
197
198 cfg->scope = urld->lud_scope == LDAP_SCOPE_ONELEVEL ?
199 LDAP_SCOPE_ONELEVEL : LDAP_SCOPE_SUBTREE;
200
201 if (urld->lud_filter) {
202 if (urld->lud_filter[0] == '(') {
203 /*
204 * Get rid of the surrounding parens; later on when generating the
205 * filter, they'll be put back.
206 */
207 cfg->filter = apr_pstrdup(cmd->pool, urld->lud_filter+1);
208 cfg->filter[strlen(cfg->filter)-1] = '\0';
209 }
210 else {
211 cfg->filter = apr_pstrdup(cmd->pool, urld->lud_filter);
212 }
213 }
214 else {
215 cfg->filter = "objectClass=apacheConfig";
216 }
217
218 /* "ldaps" indicates secure ldap connections desired
219 */
220 if (strncasecmp(url, "ldaps", 5) == 0)
221 {
222 cfg->secure = 1;
223 cfg->port = urld->lud_port? urld->lud_port : LDAPS_PORT;
224 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
225 "LDAP: vhost_ldap using SSL connections");
226 }
227 else
228 {
229 cfg->secure = 0;
230 cfg->port = urld->lud_port? urld->lud_port : LDAP_PORT;
231 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
232 "LDAP: vhost_ldap not using SSL connections");
233 }
234
235 cfg->have_ldap_url = 1;
236 apr_ldap_free_urldesc(urld);
237 return NULL;
238}
239
240static const char *mod_vhost_ldap_set_enabled(cmd_parms *cmd, void *dummy, int enabled)
241{
242 mod_vhost_ldap_config_t *cfg =
243 (mod_vhost_ldap_config_t *)ap_get_module_config(cmd->server->module_config,
244 &vhost_ldap_module);
245
246 cfg->enabled = enabled;
247 return NULL;
248}
249
250static const char *mod_vhost_ldap_set_binddn(cmd_parms *cmd, void *dummy, const char *binddn)
251{
252 mod_vhost_ldap_config_t *cfg =
253 (mod_vhost_ldap_config_t *)ap_get_module_config(cmd->server->module_config,
254 &vhost_ldap_module);
255
256 cfg->binddn = apr_pstrdup(cmd->pool, binddn);
257 return NULL;
258}
259
260static const char *mod_vhost_ldap_set_bindpw(cmd_parms *cmd, void *dummy, const char *bindpw)
261{
262 mod_vhost_ldap_config_t *cfg =
263 (mod_vhost_ldap_config_t *)ap_get_module_config(cmd->server->module_config,
264 &vhost_ldap_module);
265
266 cfg->bindpw = apr_pstrdup(cmd->pool, bindpw);
267 return NULL;
268}
269
270static const char *mod_vhost_ldap_set_deref(cmd_parms *cmd, void *dummy, const char *deref)
271{
272 mod_vhost_ldap_config_t *cfg =
273 (mod_vhost_ldap_config_t *)ap_get_module_config (cmd->server->module_config,
274 &vhost_ldap_module);
275
276 if (strcmp(deref, "never") == 0 || strcasecmp(deref, "off") == 0) {
277 cfg->deref = never;
278 }
279 else if (strcmp(deref, "searching") == 0) {
280 cfg->deref = searching;
281 }
282 else if (strcmp(deref, "finding") == 0) {
283 cfg->deref = finding;
284 }
285 else if (strcmp(deref, "always") == 0 || strcasecmp(deref, "on") == 0) {
286 cfg->deref = always;
287 }
288 else {
289 return "Unrecognized value for VhostLDAPAliasDereference directive";
290 }
291 return NULL;
292}
293
294command_rec mod_vhost_ldap_cmds[] = {
295 AP_INIT_TAKE1("VhostLDAPURL", mod_vhost_ldap_parse_url, NULL, RSRC_CONF,
a940f969 296 "URL to define LDAP connection. This should be an RFC 2255 compliant\n"
7f9875bb
OS
297 "URL of the form ldap://host[:port]/basedn[?attrib[?scope[?filter]]].\n"
298 "<ul>\n"
299 "<li>Host is the name of the LDAP server. Use a space separated list of hosts \n"
300 "to specify redundant servers.\n"
301 "<li>Port is optional, and specifies the port to connect to.\n"
302 "<li>basedn specifies the base DN to start searches from\n"
303 "</ul>\n"),
304
305 AP_INIT_TAKE1 ("VhostLDAPBindDN", mod_vhost_ldap_set_binddn, NULL, RSRC_CONF,
306 "DN to use to bind to LDAP server. If not provided, will do an anonymous bind."),
307
308 AP_INIT_TAKE1("VhostLDAPBindPassword", mod_vhost_ldap_set_bindpw, NULL, RSRC_CONF,
309 "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
310
311 AP_INIT_FLAG("VhostLDAPEnabled", mod_vhost_ldap_set_enabled, NULL, RSRC_CONF,
312 "Set to off to disable vhost_ldap, even if it's been enabled in a higher tree"),
313
314 AP_INIT_TAKE1("VhostLDAPDereferenceAliases", mod_vhost_ldap_set_deref, NULL, RSRC_CONF,
a940f969 315 "Determines how aliases are handled during a search. Can be one of the"
7f9875bb
OS
316 "values \"never\", \"searching\", \"finding\", or \"always\". "
317 "Defaults to always."),
318
319 {NULL}
320};
321
322#define FILTER_LENGTH MAX_STRING_LEN
323static int
324mod_vhost_ldap_translate_name (request_rec * r)
325{
326 apr_table_t *e;
327 int failures = 0;
328 const char **vals = NULL;
329 char filtbuf[FILTER_LENGTH];
330 mod_vhost_ldap_config_t *cfg =
331 (mod_vhost_ldap_config_t *)ap_get_module_config(r->server->module_config, &vhost_ldap_module);
9ee2dda1
OS
332 core_server_config * core =
333 (core_server_config *) ap_get_module_config(r->server->module_config, &core_module);
7f9875bb
OS
334 util_ldap_connection_t *ldc = NULL;
335 int result = 0;
336 const char *dn = NULL;
337 char *cgi;
338
339 mod_vhost_ldap_request_t *req =
340 (mod_vhost_ldap_request_t *)apr_pcalloc(r->pool, sizeof(mod_vhost_ldap_request_t));
341 ap_set_module_config(r->request_config, &vhost_ldap_module, req);
342
343 if (!cfg->enabled) {
344 return DECLINED;
345 }
346
347 if (!cfg->have_ldap_url) {
348 return DECLINED;
349 }
350
351start_over:
352
353 if (cfg->host) {
354 ldc = util_ldap_connection_find(r, cfg->host, cfg->port,
355 cfg->binddn, cfg->bindpw, cfg->deref,
356 cfg->secure);
357 }
358 else {
359 ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r,
360 "[mod_vhost_ldap.c] translate: no sec->host - weird...?");
361 return DECLINED;
362 }
363
364 ap_log_rerror (APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
365 "[mod_vhost_ldap.c]: translating %s", r->parsed_uri.path);
366
367 apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(|(apacheServerName=%s)(apacheServerAlias=%s)))", cfg->filter, r->hostname, r->hostname);
368
369 result = util_ldap_cache_getuserdn(r, ldc, cfg->url, cfg->basedn, cfg->scope,
370 attributes, filtbuf, &dn, &vals);
371
372 util_ldap_connection_close(ldc);
373
374 /* sanity check - if server is down, retry it up to 5 times */
375 if (result == LDAP_SERVER_DOWN) {
376 if (failures++ <= 5) {
377 goto start_over;
378 }
379 }
380
381 /* handle bind failure */
382 if (result != LDAP_SUCCESS) {
383 ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r,
384 "[mod_vhost_ldap.c] translate: "
385 "translate failed; URI %s [%s][%s]",
386 r->parsed_uri.path, ldc->reason, ldap_err2string(result));
387 return DECLINED;
388 }
389
390 /* mark the user and DN */
391 req->dn = apr_pstrdup(r->pool, dn);
392
393 /* Optimize */
394 if (vals) {
395 int i = 0;
396 while (attributes[i]) {
397
398 if (strcasecmp (attributes[i], "apacheServerName") == 0) {
399 req->name = apr_pstrdup (r->pool, vals[i]);
400 }
401 else if (strcasecmp (attributes[i], "apacheServerAdmin") == 0) {
402 req->admin = apr_pstrdup (r->pool, vals[i]);
403 }
404 else if (strcasecmp (attributes[i], "apacheDocumentRoot") == 0) {
405 req->docroot = apr_pstrdup (r->pool, vals[i]);
406 }
407 else if (strcasecmp (attributes[i], "apacheScriptAlias") == 0) {
408 req->cgiroot = apr_pstrdup (r->pool, vals[i]);
409 }
410 else if (strcasecmp (attributes[i], "apacheSuexecUid") == 0) {
411 req->uid = apr_pstrdup(r->pool, vals[i]);
412 }
413 else if (strcasecmp (attributes[i], "apacheSuexecGid") == 0) {
414 req->gid = apr_pstrdup(r->pool, vals[i]);
415 }
416 i++;
417 }
418 }
419
420 if ((req->name == NULL)||(req->docroot == NULL)) {
421 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
422 "[mod_vhost_ldap.c] translate: "
423 "translate failed; ServerName or DocumentRoot not defined");
424 return DECLINED;
425 }
426
427 cgi = NULL;
428
429 if (req->cgiroot) {
430 cgi = strstr(r->parsed_uri.path, "cgi-bin/");
431 if (cgi && (cgi != r->uri + strspn(r->parsed_uri.path, "/"))) {
432 cgi = NULL;
433 }
9ee2dda1
OS
434 }
435
436 if (cgi) {
437 r->filename =
438 apr_pstrcat (r->pool, req->cgiroot, cgi + strlen("cgi-bin"), NULL);
439 r->handler = "cgi-script";
440 apr_table_setn(r->notes, "alias-forced-type", r->handler);
441 } else {
442 r->filename =
443 apr_pstrcat (r->pool, req->docroot, r->parsed_uri.path, NULL);
7f9875bb
OS
444 }
445
446 r->server->server_hostname = apr_pstrdup (r->pool, req->name);
447
448 if (req->admin) {
449 r->server->server_admin = apr_pstrdup (r->pool, req->admin);
450 }
451
452 // set environment variables
453 e = r->subprocess_env;
454 apr_table_addn (e, "SERVER_ROOT", req->docroot);
455
9ee2dda1
OS
456 core->ap_document_root = apr_pstrdup(r->pool, req->docroot);
457
7f9875bb
OS
458 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
459 "[mod_vhost_ldap.c]: translated to %s", r->filename);
460
461 return OK;
462}
463
464#ifdef HAVE_UNIX_SUEXEC
465static ap_unix_identity_t *mod_vhost_ldap_get_suexec_id_doer(const request_rec * r)
466{
467 ap_unix_identity_t *ugid = NULL;
468 mod_vhost_ldap_config_t *cfg =
469 (mod_vhost_ldap_config_t *)ap_get_module_config(r->server->module_config,
470 &vhost_ldap_module);
471 mod_vhost_ldap_request_t *req =
472 (mod_vhost_ldap_request_t *)ap_get_module_config(r->request_config,
473 &vhost_ldap_module);
474
475 uid_t uid = -1;
476 gid_t gid = -1;
477
478 // mod_vhost_ldap is disabled
479 if (!cfg->enabled) {
480 return NULL;
481 }
482
483 if ((req == NULL)||(req->uid == NULL)||(req->gid == NULL)) {
484 return NULL;
485 }
486
487 if ((ugid = apr_palloc(r->pool, sizeof(ap_unix_identity_t))) == NULL) {
488 return NULL;
489 }
490
491 uid = (uid_t)atoll(req->uid);
492 gid = (gid_t)atoll(req->gid);
493
eea38f6d 494 if ((uid < MIN_UID)||(gid < MIN_GID)) {
7f9875bb
OS
495 return NULL;
496 }
497
498 ugid->uid = uid;
499 ugid->gid = gid;
500 ugid->userdir = 0;
501
502 return ugid;
503}
504#endif
505
506static void
507mod_vhost_ldap_register_hooks (apr_pool_t * p)
508{
509 ap_hook_post_config(mod_vhost_ldap_post_config, NULL, NULL, APR_HOOK_MIDDLE);
510 ap_hook_translate_name(mod_vhost_ldap_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
511#ifdef HAVE_UNIX_SUEXEC
512 ap_hook_get_suexec_identity(mod_vhost_ldap_get_suexec_id_doer, NULL, NULL, APR_HOOK_MIDDLE);
513#endif
514}
515
516module AP_MODULE_DECLARE_DATA vhost_ldap_module = {
517 STANDARD20_MODULE_STUFF,
518 NULL,
519 NULL,
520 mod_vhost_ldap_create_server_config,
521 NULL,
522 mod_vhost_ldap_cmds,
523 mod_vhost_ldap_register_hooks,
524};
This page took 1.107065 seconds and 5 git commands to generate.