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