1 /* ============================================================
2 * Copyright (c) 2003-2006, Ondrej Sury, Piotr Wadas
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 /* NOTE: only static members must be "used" to build, so for time-to-time used routines we don't declare static */
22 * mod_vhost_ldap.c --- read virtual host config from LDAP directory
23 * version 2.0 - included ldap-based basic auth & authz
26 //remember to add "-lcrypt" in Makefile if there's a need to generate new password
27 // for now not needed (validation only), this below is almost copy-paste from apache source, htpasswd.c
33 void to64(char *s, unsigned long v, int n)
35 static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
37 *s++ = itoa64[v&0x3f];
42 char *htenc(const char *clearpasswd) {
45 (void) srand((int) time((time_t *) NULL));
46 to64(&salt[0], rand(), 8);
48 res = crypt(clearpasswd, salt);
57 #include "http_config.h"
58 #include "http_core.h"
60 #include "http_request.h"
62 #include "apr_strings.h"
63 #include "apr_reslist.h"
64 #include "util_ldap.h"
70 #fatal "mod_vhost_ldap requires APR util to have LDAP support built in"
73 #if !defined(WIN32) && !defined(OS2) && !defined(BEOS) && !defined(NETWARE)
74 #define HAVE_UNIX_SUEXEC
77 #ifdef HAVE_UNIX_SUEXEC
78 #include "unixd.h" /* Contains the suexec_identity hook used on Unix */
81 /* do not accept empty "" strings */
82 #define strtrue(s) (s && *s)
86 #define FILTER_LENGTH MAX_STRING_LEN
88 module AP_MODULE_DECLARE_DATA vhost_ldap_module;
90 typedef enum mod_vhost_ldap_status_e {
94 } mod_vhost_ldap_status_e;
95 typedef struct mod_vhost_ldap_config_t {
96 mod_vhost_ldap_status_e enabled; /* Is vhost_ldap enabled? */
98 char *url; /* String representation of LDAP URL */
99 char *host; /* Name of the LDAP server (or space separated list) */
100 int port; /* Port of the LDAP server */
101 char *basedn; /* Base DN to do all searches from */
102 int scope; /* Scope of the search */
103 char *filter; /* Filter to further limit the search */
104 deref_options deref; /* how to handle alias dereferening */
105 char *binddn; /* DN to bind to server (can be NULL) */
106 char *bindpw; /* Password to bind to server (can be NULL) xx */
107 int have_deref; /* Set if we have found an Deref option */
108 int have_ldap_url; /* Set if we have found an LDAP url */
109 char *wlcbasedn; /* Base DN to do all location config searches */
110 char *wucbasedn; /* Base DN to do all webuser config searches */
111 int secure; /* True if SSL connections are requested */
112 } mod_vhost_ldap_config_t;
113 typedef struct mod_vhost_ldap_request_t {
114 char *dn; /* The saved dn from a successful search */
115 char *name; /* apacheServerName */
116 char *admin; /* apacheServerAdmin */
117 char *docroot; /* apacheDocumentRoot */
118 char *uid; /* Suexec Uid */
119 char *gid; /* Suexec Gid */
120 int has_reqlines; /* placeholder */
121 apr_array_header_t *serveralias; /* apacheServerAlias */
122 apr_array_header_t *rqlocationlines; /* apacheServerAlias */
124 } mod_vhost_ldap_request_t;
125 typedef struct mod_vhost_ldap_extconfig_object_t { /* what the hell this "t" means ??. eh, whatever ;) */
126 //we use apr_array_header_t for multi-value attributed, parsed later (yuck!) from grr ";" separated string
127 char *extconfname; /* apacheExtConfigObjectName, single-value, syntax SUP cn */
128 char *exturi; /* apacheExtConfigUri, single-value, uri for which this settings are here
129 * should be used in combine with extconfig server name */
130 int extconftype; /* apacheExtConfigRequireValidUser, single-value bool,
131 * if TRUE then require valid-user, if FALSE userlist-type config
134 apr_array_header_t *extservername; /* apacheExtConfigServerName",MULTI-value,
135 * e.g. for http://anyserver/statistics (?), syntax SUP cn
137 apr_array_header_t *extusers; /* "apacheExtConfigUserDn", MULTI-value, syntax SUP DN */
139 } mod_vhost_ldap_extconfig_object_t;
140 typedef struct mod_vhost_ldap_webuser_t {
142 char *webusername; /* apacheExtConfigUserName, single-value */
143 apr_array_header_t *webuserpassword; /* userPassword, multi-value */
144 char *webuserserver; /* apacheExtConfigUserServerName, server of this user, multi-value */
146 } mod_vhost_ldap_webuser_t;
147 static int strschrcount(apr_pool_t * p, const char *src, const char *delim)
152 if(strcasecmp(apr_pstrndup(p, src, i), (char *) delim) == 0) {
158 void log_dump_apr_array(request_rec * r, apr_array_header_t * arr, const char *prefix)
160 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Entering log_dump_apr_array");
162 char **aliases = (char **) arr->elts;
163 for (x = 0; x < arr->nelts; x++) {
164 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " log_dump_apr_array val %d %s %s", x, prefix, aliases[x]);
166 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Leaving log_dump_apr_array");
168 static apr_array_header_t *get_parsed_string_atrr_arr(request_rec * r, const char *server_alias_attrvar_line,
171 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Entering get_parsed_string_atrr_arr |%s|", server_alias_attrvar_line);
172 if(server_alias_attrvar_line) {
174 apr_collapse_spaces((char *) server_alias_attrvar_line, server_alias_attrvar_line);
175 int ccount = strschrcount(r->pool, server_alias_attrvar_line, delim) + 1;
177 apr_array_header_t *aliases_arr = apr_array_make(r->pool, ccount, sizeof(char *));
179 curralias = (char **) apr_array_push(aliases_arr);
181 char *curr_server_alias = ap_getword(r->pool, &server_alias_attrvar_line, ';');
182 char *tmp = apr_pstrdup(r->pool, (char *) curr_server_alias);;
186 while(server_alias_attrvar_line[0]) {
187 curr_server_alias = ap_getword(r->pool, &server_alias_attrvar_line, ';');
188 curralias = (char **) apr_array_push(aliases_arr);
189 tmp = apr_pstrdup(r->pool, (char *) curr_server_alias);
192 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Leaving get_parsed_string_atrr_arr OK");
196 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Leaving get_parsed_string_atrr_arr NULL");
199 static apr_array_header_t *get_ap_reqs(apr_pool_t * p, mod_vhost_ldap_extconfig_object_t * extreqc,
200 char *mainservername, char *userlist)
203 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL," get_ap_reqs Entering");
205 apr_array_header_t *res = apr_array_make(p, 2, sizeof(require_line));
208 apr_int64_t limited = -1;
210 rline = (require_line *) apr_array_push(res);
212 //currently we don't support playing with request types
213 rline->method_mask = limited;
215 if(extreqc->extconftype == 1) {
216 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL, " get_ap_reqs require valid-user = TRUE server %s",mainservername);
217 rline->requirement = apr_pstrdup(p, (char *) "valid-user");
220 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL, " get_ap_reqs require valid-user = FALSE server %s",mainservername);
222 rline->requirement = apr_pstrdup(p, userlist);
224 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL," Leaving get_ap_reqs, returning require line |require %s|", rline->requirement);
227 static void mod_vhost_ldap_dovhostconfig(request_rec * r, char *attributes[], const char **vals,
228 mod_vhost_ldap_request_t * reqc)
231 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " dovhostconfig Entering ");
233 while(attributes[i]) {
235 if(strcasecmp(attributes[i], "apacheServerName") == 0) {
236 reqc->name = apr_pstrdup(r->pool, vals[i]);
237 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " %d apacheServerName %s", i, reqc->name);
240 if(strcasecmp(attributes[i], "apacheServerAdmin") == 0) {
241 reqc->admin = apr_pstrdup(r->pool, vals[i]);
242 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d apacheServerAdmin %s", i, reqc->admin);
245 if(strcasecmp(attributes[i], "apacheDocumentRoot") == 0) {
246 reqc->docroot = apr_pstrdup(r->pool, vals[i]);
247 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d apacheDocumentRoot %s", i, reqc->docroot);
250 if(strcasecmp(attributes[i], "apacheSuexecUid") == 0) {
251 reqc->uid = apr_pstrdup(r->pool, vals[i]);
252 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d apacheSuexecUid %s", i, reqc->uid);
255 if(strcasecmp(attributes[i], "apacheSuexecGid") == 0) {
256 reqc->gid = apr_pstrdup(r->pool, vals[i]);
257 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d apacheSuexecGid %s", i, reqc->gid);
260 if(strcasecmp(attributes[i], "apacheExtConfigHasRequireLine") == 0) {
262 reqc->has_reqlines = strcasecmp("TRUE", apr_pstrdup(r->pool, vals[i])) == 0 ? 1 : 0;
264 if(reqc->has_reqlines) {
265 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " %d Vhost %s has extended access configuration", i, reqc->name);
269 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " %d Vhost %s doesn't have extended access configuration", i, reqc->name);
273 if(strcasecmp(attributes[i], "apacheServerAlias") == 0) {
276 (apr_array_header_t *) get_parsed_string_atrr_arr(r, vals[i],
278 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d apacheServerAlias is set", i);
281 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d No apacheServerAlias for this vhost found", i);
282 reqc->serveralias = NULL;
286 if(strcasecmp(attributes[i], "apacheLocationOptionsDn") == 0) {
288 reqc->rqlocationlines =
289 (apr_array_header_t *) get_parsed_string_atrr_arr(r, vals[i],
291 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, " %d apacheLocationOptionsDn is set", i);
294 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " host %s marked ext-configured but no attributes pointing extConfig !! ldap scheme should avoid it !!", reqc->name);
295 reqc->rqlocationlines = NULL;
300 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " dovhostconfig Leaving ");
302 static void mod_vhost_ldap_doextconfig(request_rec * r, char *extconfigattributes[], const char **extconfvals,
303 mod_vhost_ldap_extconfig_object_t * extreqc)
305 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " doextconfig Entering ");
308 while(extconfigattributes[i]) {
309 if(strcasecmp(extconfigattributes[i], "apacheExtConfigObjectName") == 0) {
310 extreqc->extconfname = apr_pstrdup(r->pool, extconfvals[i]);
313 if(strcasecmp(extconfigattributes[i], "apacheExtConfigUri") == 0) {
314 extreqc->exturi = apr_pstrdup(r->pool, extconfvals[i]);
317 if(strcasecmp(extconfigattributes[i], "apacheExtConfigRequireValidUser") == 0) {
320 //this value determines whether we have "require valid-user" object (TRUE) ,
321 //or (FALSE) object "require user johny mary dorothy witch"
322 //here set retrieved value, regardless what it is, to play with it later.
323 extreqc->extconftype =
324 strcasecmp("TRUE", apr_pstrdup(r->pool, extconfvals[i])) == 0 ? 1 : 0;
325 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Set require valid-user to %d (%s)", extreqc->extconftype, (char *) apr_pstrdup(r->pool, extconfvals[i]));
328 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " For this ext config require valid-user is not set");
332 if(strcasecmp(extconfigattributes[i], "apacheExtConfigServerName") == 0) {
335 extreqc->extservername =
336 (apr_array_header_t *) get_parsed_string_atrr_arr(r, extconfvals[i],
340 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
341 " No ExtServerName values found");
342 extreqc->extservername = NULL;
346 if(strcasecmp(extconfigattributes[i], "apacheExtConfigUserDn") == 0) {
350 (apr_array_header_t *) get_parsed_string_atrr_arr(r, extconfvals[i],
354 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
355 " apacheExtConfigUserDn values NOT found (any valid-user or no users specified.");
356 extreqc->extusers = NULL;
362 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " doextconfig Leaving ");
364 static void mod_vhost_ldap_doextuserconfig(request_rec * r, char *ldap_webuser_attributes[], const char **extuservals,
365 mod_vhost_ldap_webuser_t * extuserreqc)
367 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " doextuserconfig Entering");
369 while(ldap_webuser_attributes[i]) {
370 if(strcasecmp(ldap_webuser_attributes[i], "apacheExtConfigUserName") == 0) {
371 extuserreqc->webusername = apr_pstrdup(r->pool, extuservals[i]);
372 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
373 "%d apacheExtConfigUserName set to %s", i, extuserreqc->webusername);
375 if(strcasecmp(ldap_webuser_attributes[i], "apacheExtConfigUserServerName") == 0) {
376 extuserreqc->webuserserver = apr_pstrdup(r->pool, extuservals[i]);
377 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
378 "%d apacheExtConfigUserServerName set to %s", i, extuserreqc->webuserserver);
380 if(strcasecmp(ldap_webuser_attributes[i], "userPassword") == 0) {
381 extuserreqc->webuserpassword =
382 (apr_array_header_t *) get_parsed_string_atrr_arr(r, extuservals[i],
384 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
385 "%d userPassword retrievied", i);
389 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " doextuserconfig Leaving");
391 static int mod_vhost_ldap_authenticate_basic_user(request_rec * r)
394 mod_vhost_ldap_webuser_t *extuserreqc;
395 extuserreqc = (mod_vhost_ldap_webuser_t *) apr_pcalloc(r->pool, sizeof(mod_vhost_ldap_webuser_t));
396 int rc = ap_get_basic_auth_pw(r, &sent_pw);
399 if(strtrue(r->user) && strtrue(sent_pw)) {
400 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " Entering mod_vhost_ldap_authenticate_basic_user");
402 char userfilter[FILTER_LENGTH];
403 mod_vhost_ldap_config_t *conf =
404 (mod_vhost_ldap_config_t *) ap_get_module_config(r->server->module_config, &vhost_ldap_module);
406 const char *dn = NULL;
407 util_ldap_connection_t *ldc = NULL;
408 const char **extuservals = NULL;
410 char *ldap_webuser_attributes[] =
411 { "apacheExtConfigUserName", "apacheExtConfigUserServerName", "userPassword", 0 };
413 apr_snprintf(userfilter, FILTER_LENGTH, "(&(apacheExtConfigUserName=%s)(apacheExtConfigUserServerName=%s))", r->user,r->server->server_hostname);
414 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " AuthUser search filter: %s", userfilter);
415 ldc = util_ldap_connection_find(r, conf->host, conf->port, conf->binddn, conf->bindpw, conf->deref,
417 result = util_ldap_cache_getuserdn(r, ldc, conf->url, conf->wucbasedn, conf->scope,
418 ldap_webuser_attributes, userfilter, &dn, &extuservals);
419 util_ldap_connection_close(ldc);
422 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, " User %s found.", r->user);
424 mod_vhost_ldap_doextuserconfig(r, ldap_webuser_attributes, extuservals, extuserreqc);
427 char **passwords = (char **) extuserreqc->webuserpassword->elts;
428 for (x = 0; x < extuserreqc->webuserpassword->nelts; x++) {
429 if ( ( apr_password_validate(sent_pw, passwords[x]) == OK) || strcasecmp(sent_pw,passwords[x]) == 0 ) {
430 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, " Authentication for user %s at %s successful.", extuserreqc->webusername, r->server->server_hostname);
437 ap_log_rerror(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, r, " User %s at %s not found", extuserreqc->webusername, r->server->server_hostname);
438 return HTTP_UNAUTHORIZED;
442 ap_note_basic_auth_failure(r);
443 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
444 ": Both a username and password must be provided : authentication for user %s at %s failed.",
445 extuserreqc->webusername, r->server->server_hostname);
446 return HTTP_UNAUTHORIZED;
448 ap_note_basic_auth_failure(r);
449 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, "Authentication for user %s at %s failed.",
450 extuserreqc->webusername, r->server->server_hostname);
451 return HTTP_UNAUTHORIZED;
454 static int check_mod_vhost_ldap_auth_require(char *user, const char *t, request_rec * r)
457 w = ap_getword(r->pool, &t, ' ');
458 if(!strcmp(w, "valid-user")) {
463 if(!strcmp(w, "user")) {
465 w = ap_getword_conf(r->pool, &t);
466 if(!strcmp(user, w)) {
472 return HTTP_UNAUTHORIZED;
476 return HTTP_INTERNAL_SERVER_ERROR;
478 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
479 ": %s : Reached end of check_mod_vhost_ldap_auth_require!", r->server->server_hostname);
480 return HTTP_INTERNAL_SERVER_ERROR;
482 static int mod_vhost_ldap_check_auth(request_rec * r)
484 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
485 ": mod_vhost_ldap_check_auth, parsing existing ap_requires for %s at %s ", r->user, r->server->server_hostname);
486 char *user = r->user;
490 const apr_array_header_t *reqs_arr = ap_requires(r);
493 reqs = (require_line *) reqs_arr->elts;
495 for (x = 0; x < reqs_arr->nelts; x++) {
496 t = reqs[x].requirement;
497 if((rv = check_mod_vhost_ldap_auth_require(user, t, r)) != HTTP_UNAUTHORIZED) {
503 ap_note_basic_auth_failure(r);
504 return HTTP_UNAUTHORIZED;
506 static void *mod_vhost_ldap_create_server_config(apr_pool_t * p, server_rec * s)
509 mod_vhost_ldap_config_t *conf = (mod_vhost_ldap_config_t *) apr_pcalloc(p, sizeof(mod_vhost_ldap_config_t));
510 conf->enabled = MVL_UNSET;
511 conf->have_ldap_url = 0;
512 conf->have_deref = 0;
515 conf->deref = always;
516 conf->wlcbasedn = NULL;
517 conf->wucbasedn = NULL;
521 static void *mod_vhost_ldap_merge_server_config(apr_pool_t * p, void *parentv, void *childv)
523 mod_vhost_ldap_config_t *parent = (mod_vhost_ldap_config_t *) parentv;
524 mod_vhost_ldap_config_t *child = (mod_vhost_ldap_config_t *) childv;
525 mod_vhost_ldap_config_t *conf = (mod_vhost_ldap_config_t *) apr_pcalloc(p, sizeof(mod_vhost_ldap_config_t));
527 conf->enabled = (child->enabled == MVL_UNSET ? parent->enabled : child->enabled);
529 if(child->have_ldap_url) {
530 conf->have_ldap_url = child->have_ldap_url;
531 conf->url = child->url;
532 conf->host = child->host;
533 conf->port = child->port;
534 conf->basedn = child->basedn;
535 conf->scope = child->scope;
536 conf->filter = child->filter;
537 conf->secure = child->secure;
538 conf->wlcbasedn = child->wlcbasedn;
539 conf->wucbasedn = child->wucbasedn;
542 conf->have_ldap_url = parent->have_ldap_url;
543 conf->url = parent->url;
544 conf->host = parent->host;
545 conf->port = parent->port;
546 conf->basedn = parent->basedn;
547 conf->scope = parent->scope;
548 conf->filter = parent->filter;
549 conf->secure = parent->secure;
550 conf->wlcbasedn = parent->wlcbasedn;
551 conf->wucbasedn = parent->wucbasedn;
554 if(child->have_deref) {
555 conf->have_deref = child->have_deref;
556 conf->deref = child->deref;
559 conf->have_deref = parent->have_deref;
560 conf->deref = parent->deref;
563 conf->binddn = (child->binddn ? child->binddn : parent->binddn);
564 conf->bindpw = (child->bindpw ? child->bindpw : parent->bindpw);
567 static const char *mod_vhost_ldap_parse_url(cmd_parms * cmd, void *dummy, const char *url)
570 apr_ldap_url_desc_t *urld;
572 mod_vhost_ldap_config_t *conf =
573 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
575 result = apr_ldap_url_parse(url, &(urld));
577 if(result != LDAP_SUCCESS) {
579 case LDAP_URL_ERR_NOTLDAP:
580 return "LDAP URL does not begin with ldap://";
581 case LDAP_URL_ERR_NODN:
582 return "LDAP URL does not have a DN";
583 case LDAP_URL_ERR_BADSCOPE:
584 return "LDAP URL has an invalid scope";
585 case LDAP_URL_ERR_MEM:
586 return "Out of memory parsing LDAP URL";
588 return "Could not parse LDAP URL";
591 conf->url = apr_pstrdup(cmd->pool, url);
593 /* Set all the values, or at least some sane defaults */
595 char *p = apr_palloc(cmd->pool, strlen(conf->host) + strlen(urld->lud_host) + 2);
596 strcpy(p, urld->lud_host);
598 strcat(p, conf->host);
602 conf->host = urld->lud_host ? apr_pstrdup(cmd->pool, urld->lud_host) : "localhost";
604 conf->basedn = urld->lud_dn ? apr_pstrdup(cmd->pool, urld->lud_dn) : "";
606 conf->scope = urld->lud_scope == LDAP_SCOPE_ONELEVEL ? LDAP_SCOPE_ONELEVEL : LDAP_SCOPE_SUBTREE;
608 if(urld->lud_filter) {
609 if(urld->lud_filter[0] == '(') {
611 * Get rid of the surrounding parens; later on when generating the
612 * filter, they'll be put back.
614 conf->filter = apr_pstrdup(cmd->pool, urld->lud_filter + 1);
615 conf->filter[strlen(conf->filter) - 1] = '\0';
618 conf->filter = apr_pstrdup(cmd->pool, urld->lud_filter);
622 conf->filter = "objectClass=apacheConfig";
625 /* "ldaps" indicates secure ldap connections desired
627 if(strncasecmp(url, "ldaps", 5) == 0) {
629 conf->port = urld->lud_port ? urld->lud_port : LDAPS_PORT;
634 conf->port = urld->lud_port ? urld->lud_port : LDAP_PORT;
637 conf->have_ldap_url = 1;
638 apr_ldap_free_urldesc(urld);
641 static const char *mod_vhost_ldap_set_enabled(cmd_parms * cmd, void *dummy, int enabled)
643 mod_vhost_ldap_config_t *conf =
644 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
645 conf->enabled = (enabled) ? MVL_ENABLED : MVL_DISABLED;
648 static const char *mod_vhost_ldap_set_binddn(cmd_parms * cmd, void *dummy, const char *binddn)
650 mod_vhost_ldap_config_t *conf =
651 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
652 conf->binddn = apr_pstrdup(cmd->pool, binddn);
655 static const char *mod_vhost_ldap_set_wucbasedn(cmd_parms * cmd, void *dummy, const char *wucbasedn)
657 mod_vhost_ldap_config_t *conf =
658 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
659 conf->wucbasedn = apr_pstrdup(cmd->pool, wucbasedn);
662 static const char *mod_vhost_ldap_set_wlcbasedn(cmd_parms * cmd, void *dummy, const char *wlcbasedn)
665 mod_vhost_ldap_config_t *conf =
666 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
667 conf->wlcbasedn = apr_pstrdup(cmd->pool, wlcbasedn);
670 static const char *mod_vhost_ldap_set_bindpw(cmd_parms * cmd, void *dummy, const char *bindpw)
672 mod_vhost_ldap_config_t *conf =
673 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
674 conf->bindpw = apr_pstrdup(cmd->pool, bindpw);
677 static const char *mod_vhost_ldap_set_deref(cmd_parms * cmd, void *dummy, const char *deref)
679 mod_vhost_ldap_config_t *conf =
680 (mod_vhost_ldap_config_t *) ap_get_module_config(cmd->server->module_config, &vhost_ldap_module);
682 if(strcmp(deref, "never") == 0 || strcmp(deref, "searching") == 0 || strcmp(deref, "finding") == 0
683 || strcmp(deref, "always") == 0) {
684 conf->deref = *deref;
685 conf->have_deref = 1;
688 return "Unrecognized value for VhostLDAPAliasDereference directive";
696 static int mod_vhost_ldap_translate_name(request_rec * r)
698 char filtbuf[FILTER_LENGTH];
699 char extconffiltbuf[FILTER_LENGTH];
702 mod_vhost_ldap_config_t *conf;
703 mod_vhost_ldap_request_t *reqc;
704 mod_vhost_ldap_extconfig_object_t *extreqc;
705 core_server_config *core;
707 util_ldap_connection_t *ldc = NULL;
708 const char **vals, **extconfvals = NULL;
709 const char *dn = NULL;
710 const char *hostname = NULL;
716 * more info about attributes in typedefs definitions and schema desc
717 * PS. Did You ever wonder why they used this damned weird "\0", instead,
718 * let's say, '$' or 'EndOfWord' or whatever ??
721 char *attributes[] = { "apacheServerName", "apacheServerAlias", "apacheDocumentRoot",
722 "apacheSuexecUid", "apacheSuexecGid", "apacheServerAdmin",
723 "apacheExtConfigHasRequireLine", "apacheLocationOptionsDn",
727 char *extconfigattributes[] = { "apacheExtConfigUri", "apacheExtConfigRequireValidUser",
728 "apacheExtConfigServerName", "apacheExtConfigObjectName",
729 "apacheExtConfigUserDn",
733 //we assume we're in trouble, and will change it, if not.
734 result = LDAP_SERVER_DOWN;
736 top = r->main ? r->main : r;
737 hostname = r->hostname;
739 //get our module config options
740 conf = (mod_vhost_ldap_config_t *) ap_get_module_config(r->server->module_config, &vhost_ldap_module);
742 //a reference to core config
743 core = (core_server_config *) ap_get_module_config(r->server->module_config, &core_module);
745 //and some variable initialization
746 reqc = (mod_vhost_ldap_request_t *) apr_pcalloc(r->pool, sizeof(mod_vhost_ldap_request_t));
747 extreqc = (mod_vhost_ldap_extconfig_object_t *) apr_pcalloc(r->pool, sizeof(mod_vhost_ldap_extconfig_object_t));
749 //current request config get set
750 ap_set_module_config(r->request_config, &vhost_ldap_module, reqc);
752 // mod_vhost_ldap is disabled or we don't have LDAP Url
753 if((conf->enabled != MVL_ENABLED) || (!conf->have_ldap_url)) {
759 apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(|(apacheServerName=%s)(apacheServerAlias=%s)))",
760 conf->filter, hostname, hostname);
762 while(failures++ <= 5 && result == LDAP_SERVER_DOWN) {
763 //searching for connection
764 ldc = util_ldap_connection_find(r, conf->host, conf->port, conf->binddn, conf->bindpw,
765 conf->deref, conf->secure);
766 result = util_ldap_cache_getuserdn(r, ldc, conf->url, conf->basedn, conf->scope, attributes,
767 filtbuf, &dn, &vals);
768 util_ldap_connection_close(ldc);
775 if(result != LDAP_SUCCESS) {
779 reqc->dn = apr_pstrdup(r->pool, dn);
782 //this translate_name function we're in is long enough, don't You think?
783 //we set all into reqc struct
784 mod_vhost_ldap_dovhostconfig(r, attributes, vals, reqc);
787 if((reqc->name == NULL) || (reqc->docroot == NULL)) {
792 if(r->uri[0] == '/') {
793 r->filename = apr_pstrcat(r->pool, reqc->docroot, r->uri, NULL);
799 if(reqc->has_reqlines == 1 && reqc->rqlocationlines) {
801 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
802 "This vhost has access control configured, need to check if it's enabled for current uri");
806 //we have mercy, and do not open a connection for each uri search ;)
808 ldc = util_ldap_connection_find(r, conf->host, conf->port, conf->binddn, conf->bindpw, conf->deref,
811 //ask Your programming teacher, what's all about with
812 // these "NO, TRY _VERY_ HARD, AND THEN TRY AGAIN _NOT_ TO USE BREAK FOR LOOP LEAVING" ;)
813 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, "Entering extConfig Objects search");
814 while(i <= strlen(apr_pstrdup(r->pool, r->uri)) && !extconfvals) {
816 char *buff = apr_pstrndup(r->pool, r->uri, i);
817 //ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,"Searching for hostname %s and URI %s, origname is %s", hostname, buff, reqc->name);
818 //uncomment this, if You'd like to see in log how uri gets checked
819 //ap_log_error(APLOG_MARK,APLOG_DEBUG,OK,NULL,"%s", buff);
821 //well, we must had been connecting already, so we don't do more ldap server connection checks,
822 //and we're doing a search with cache_getuser instead of using extConfigObject dn apacheConfig object attribute value(s),
823 //because there's no convenient function in apr api.
824 //vhost location RDN attribute is used actually by some GUI to make things easier
825 //TODO: use some generic ldap functions (?) classic search or implement more ldap routines for apr
827 //so, we do a search below locationDnBase for config object with matches current hostname and uri..
828 //note, that we took our current uri, and we're searching starting from / adding one by one chararacter
829 //to match config object - access config is always the same as first matching upper url access config.
830 //and more - if someone defined accessobject for /main and /main/subdir, the first one is used.
831 //when upper is deleted - next below is returned, and so far..
832 //and more - if there are two or more extConfig object for the same combination of server/uri,
833 //then first found is returned and search isn't processed further.
835 //we do a search based on original reqc->name instead of current hostname, to apply rules even if we're accessing
836 //site via ServerAlias name
837 apr_snprintf(extconffiltbuf, FILTER_LENGTH,
838 "(&(apacheExtConfigServerName=%s)(apacheExtConfigUri=%s))", reqc->name, buff);
840 result = util_ldap_cache_getuserdn(r, ldc, conf->url, conf->wlcbasedn, conf->scope,
841 extconfigattributes, extconffiltbuf, &dn, &extconfvals);
843 //matched URI, if found, is returned anyway with extconfvals as ldap attribute value.
845 //ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, "Closing LDAP Connection");
846 util_ldap_connection_close(ldc);
848 if(result != LDAP_SUCCESS) {
849 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
850 "This vhost has access control, but probably not for this URI, access config entry not found");
851 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
852 "Tried with ldap search filter: %s", extconffiltbuf);
855 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
856 "This uri has access control, configuration object is found");
859 //this translate_name function we're in is long enough, don't You think?
860 //we set all into extreqc struct
861 //ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, "Entering extconfig buffer fill");
862 mod_vhost_ldap_doextconfig(r, extconfigattributes, extconfvals, extreqc);
865 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL, "Entering ap_requires generation process");
867 core_dir_config *coredirconf =
868 (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module);
869 coredirconf->ap_auth_name = extreqc->extconfname;
870 coredirconf->ap_auth_type = (char *) "basic";
871 char *userlist = "user nobody";
873 if(extreqc->extusers) {
874 mod_vhost_ldap_webuser_t *extuserreqc;
876 (mod_vhost_ldap_webuser_t *) apr_pcalloc(r->pool,
877 sizeof(mod_vhost_ldap_webuser_t));
878 char *ldap_webuser_attributes[] =
879 { "apacheExtConfigUserName", "apacheExtConfigUserServerName", "userPassword",
882 ldc = util_ldap_connection_find(r, conf->host, conf->port, conf->binddn, conf->bindpw,
883 conf->deref, conf->secure);
885 for (i = 0; i < extreqc->extusers->nelts; i++) {
886 char userfilter[FILTER_LENGTH];
888 const char **extuservals = NULL;
890 apr_snprintf(userfilter, FILTER_LENGTH,
891 "(&(objectClass=apacheExtendedConfigUserObject)(apacheExtConfigUserServerName=%s))",
893 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
894 "User search filter: %s", userfilter);
895 result = util_ldap_cache_getuserdn(r, ldc, conf->url, conf->wucbasedn,
896 conf->scope, ldap_webuser_attributes,
897 userfilter, &dn, &extuservals);
899 mod_vhost_ldap_doextuserconfig(r, ldap_webuser_attributes, extuservals,
902 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
903 "current username: %s", extuserreqc->webusername);
905 userlist = apr_pstrcat(r->pool, userlist, " ", extuserreqc->webusername, NULL);
907 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
908 "current userlist: %s", userlist);
911 util_ldap_connection_close(ldc);
913 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r, "final userlist: %s ", userlist);
916 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL, "AuthName set to %s", coredirconf->ap_auth_name);
917 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL, "AuthType set to %s", coredirconf->ap_auth_type);
918 ap_log_error(APLOG_MARK, APLOG_DEBUG, OK, NULL, "Preparing access control line");
919 coredirconf->ap_requires =
920 (apr_array_header_t *) get_ap_reqs(r->pool, extreqc, reqc->name, userlist);
925 ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
926 "This vhost is not configured for access control, or it is disabled via apacheExtConfigHasRequireLine = FALSE skipping..");
929 top->server->server_hostname = apr_pstrdup(top->pool, reqc->name);
933 top->server->server_admin = apr_pstrdup(top->pool, reqc->admin);
936 // set environment variables
937 e = top->subprocess_env;
938 apr_table_addn(e, "SERVER_ROOT", reqc->docroot);
939 core->ap_document_root = apr_pstrdup(top->pool, reqc->docroot);
943 #ifdef HAVE_UNIX_SUEXEC
944 static ap_unix_identity_t *mod_vhost_ldap_get_suexec_id_doer(const request_rec * r)
946 ap_unix_identity_t *ugid = NULL;
947 mod_vhost_ldap_config_t *conf =
948 (mod_vhost_ldap_config_t *) ap_get_module_config(r->server->module_config, &vhost_ldap_module);
949 mod_vhost_ldap_request_t *req =
950 (mod_vhost_ldap_request_t *) ap_get_module_config(r->request_config, &vhost_ldap_module);
955 // mod_vhost_ldap is disabled or we don't have LDAP Url
956 if((conf->enabled != MVL_ENABLED) || (!conf->have_ldap_url)) {
960 if((req == NULL) || (req->uid == NULL) || (req->gid == NULL)) {
964 if((ugid = apr_palloc(r->pool, sizeof(ap_unix_identity_t))) == NULL) {
968 uid = (uid_t) atoll(req->uid);
969 gid = (gid_t) atoll(req->gid);
971 if((uid < MIN_UID) || (gid < MIN_GID)) {
983 static int mod_vhost_ldap_post_config(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * s)
985 /* make sure that mod_ldap (util_ldap) is loaded */
986 if(ap_find_linked_module("util_ldap.c") == NULL) {
987 return HTTP_INTERNAL_SERVER_ERROR;
990 ap_add_version_component(p, MOD_VHOST_LDAP_VERSION);
994 static void mod_vhost_ldap_register_hooks(apr_pool_t * p)
996 ap_hook_post_config(mod_vhost_ldap_post_config, NULL, NULL, APR_HOOK_MIDDLE);
997 ap_hook_translate_name(mod_vhost_ldap_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
998 #ifdef HAVE_UNIX_SUEXEC
999 ap_hook_get_suexec_identity(mod_vhost_ldap_get_suexec_id_doer, NULL, NULL, APR_HOOK_MIDDLE);
1001 ap_hook_check_user_id(mod_vhost_ldap_authenticate_basic_user, NULL, NULL, APR_HOOK_MIDDLE);
1002 ap_hook_auth_checker(mod_vhost_ldap_check_auth, NULL, NULL, APR_HOOK_MIDDLE);
1005 static const command_rec mod_vhost_ldap_cmds[] = {
1007 AP_INIT_TAKE1("VhostLDAPURL", mod_vhost_ldap_parse_url, NULL, RSRC_CONF,
1008 "URL to define LDAP connection. This should be an RFC 2255 compliant\n"
1009 "URL of the form ldap://host[:port]/basedn[?attrib[?scope[?filter]]].\n"
1011 "<li>Host is the name of the LDAP server. Use a space separated list of hosts \n"
1012 "to specify redundant servers.\n"
1013 "<li>Port is optional, and specifies the port to connect to.\n"
1014 "<li>basedn specifies the base DN to start searches from\n" "</ul>\n"),
1016 AP_INIT_TAKE1("VhostLDAPBindDN", mod_vhost_ldap_set_binddn, NULL, RSRC_CONF,
1017 "DN to use to bind to LDAP server. If not provided, will do an anonymous bind."),
1019 AP_INIT_TAKE1("VhostLDAPBindPassword", mod_vhost_ldap_set_bindpw, NULL, RSRC_CONF,
1020 "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
1022 AP_INIT_FLAG("VhostLDAPEnabled", mod_vhost_ldap_set_enabled, NULL, RSRC_CONF,
1023 "Set to off to disable vhost_ldap, even if it's been enabled in a higher tree"),
1025 AP_INIT_TAKE1("VhostLDAPDereferenceAliases", mod_vhost_ldap_set_deref, NULL, RSRC_CONF,
1026 "Determines how aliases are handled during a search. Can be one of the"
1027 "values \"never\", \"searching\", \"finding\", or \"always\". " "Defaults to always."),
1029 AP_INIT_TAKE1("VhostLDAPWebLocationConfigBaseDn", mod_vhost_ldap_set_wlcbasedn, NULL, RSRC_CONF,
1030 "Base DN to do all location config searches."),
1032 AP_INIT_TAKE1("VhostLDAPWebUsersBaseDn", mod_vhost_ldap_set_wucbasedn, NULL, RSRC_CONF,
1033 "Base DN to do all location config searches"),
1038 module AP_MODULE_DECLARE_DATA vhost_ldap_module = {
1039 STANDARD20_MODULE_STUFF, // jakas lista
1040 NULL, // create per-directory config structure
1041 NULL, // merge per-directory config structures, default is to override
1042 mod_vhost_ldap_create_server_config, // called when module configuration data needs to be created/allocated.
1043 mod_vhost_ldap_merge_server_config, // merge per-server config structures
1044 mod_vhost_ldap_cmds, // Here we pass in the list of new configuration directives.
1045 mod_vhost_ldap_register_hooks, // register me in apache core