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