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