18 buffer
*auth_ldap_hostname
;
19 buffer
*auth_ldap_basedn
;
20 buffer
*auth_ldap_binddn
;
21 buffer
*auth_ldap_bindpw
;
22 buffer
*auth_ldap_filter
;
23 buffer
*auth_ldap_cafile
;
24 buffer
*auth_ldap_groupmember
;
25 unsigned short auth_ldap_starttls
;
26 unsigned short auth_ldap_allow_empty_pw
;
31 plugin_config
**config_storage
;
32 plugin_config conf
, *anon_conf
; /* this is only used as long as no handler_ctx is setup */
37 static handler_t
mod_authn_ldap_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
);
39 INIT_FUNC(mod_authn_ldap_init
) {
40 static http_auth_backend_t http_auth_backend_ldap
=
41 { "ldap", mod_authn_ldap_basic
, NULL
, NULL
};
42 plugin_data
*p
= calloc(1, sizeof(*p
));
43 p
->ldap_filter
= buffer_init();
45 /* register http_auth_backend_ldap */
46 http_auth_backend_ldap
.p_d
= p
;
47 http_auth_backend_set(&http_auth_backend_ldap
);
52 FREE_FUNC(mod_authn_ldap_free
) {
57 if (!p
) return HANDLER_GO_ON
;
59 buffer_free(p
->ldap_filter
);
61 if (p
->config_storage
) {
63 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
64 plugin_config
*s
= p
->config_storage
[i
];
66 if (NULL
== s
) continue;
68 buffer_free(s
->auth_ldap_hostname
);
69 buffer_free(s
->auth_ldap_basedn
);
70 buffer_free(s
->auth_ldap_binddn
);
71 buffer_free(s
->auth_ldap_bindpw
);
72 buffer_free(s
->auth_ldap_filter
);
73 buffer_free(s
->auth_ldap_cafile
);
74 buffer_free(s
->auth_ldap_groupmember
);
76 if (NULL
!= s
->ldap
) ldap_unbind_ext_s(s
->ldap
, NULL
, NULL
);
79 free(p
->config_storage
);
87 /*(copied from mod_vhostdb_ldap.c)*/
88 static void mod_authn_add_scheme (server
*srv
, buffer
*host
)
90 if (!buffer_string_is_empty(host
)) {
91 /* reformat hostname(s) as LDAP URIs (scheme://host:port) */
92 static const char *schemes
[] = {
93 "ldap://", "ldaps://", "ldapi://", "cldap://"
95 char *b
, *e
= host
->ptr
;
96 buffer_clear(srv
->tmp_buf
);
99 while (*b
==' '||*b
=='\t'||*b
=='\r'||*b
=='\n'||*b
==',') ++b
;
100 if (*b
== '\0') break;
102 while (*e
!=' '&&*e
!='\t'&&*e
!='\r'&&*e
!='\n'&&*e
!=','&&*e
!='\0')
104 if (!buffer_string_is_empty(srv
->tmp_buf
))
105 buffer_append_string_len(srv
->tmp_buf
, CONST_STR_LEN(","));
106 for (j
= 0; j
< sizeof(schemes
)/sizeof(char *); ++j
) {
107 if (0 == strncasecmp(b
, schemes
[j
], strlen(schemes
[j
]))) {
111 if (j
== sizeof(schemes
)/sizeof(char *))
112 buffer_append_string_len(srv
->tmp_buf
,
113 CONST_STR_LEN("ldap://"));
114 buffer_append_string_len(srv
->tmp_buf
, b
, (size_t)(e
- b
));
116 buffer_copy_buffer(host
, srv
->tmp_buf
);
120 SETDEFAULTS_FUNC(mod_authn_ldap_set_defaults
) {
121 plugin_data
*p
= p_d
;
123 config_values_t cv
[] = {
124 { "auth.backend.ldap.hostname", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
125 { "auth.backend.ldap.base-dn", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
126 { "auth.backend.ldap.filter", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
127 { "auth.backend.ldap.ca-file", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 3 */
128 { "auth.backend.ldap.starttls", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 4 */
129 { "auth.backend.ldap.bind-dn", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 5 */
130 { "auth.backend.ldap.bind-pw", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 6 */
131 { "auth.backend.ldap.allow-empty-pw", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 7 */
132 { "auth.backend.ldap.groupmember", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 8 */
133 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
136 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
138 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
139 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
142 s
= calloc(1, sizeof(plugin_config
));
144 s
->auth_ldap_hostname
= buffer_init();
145 s
->auth_ldap_basedn
= buffer_init();
146 s
->auth_ldap_binddn
= buffer_init();
147 s
->auth_ldap_bindpw
= buffer_init();
148 s
->auth_ldap_filter
= buffer_init();
149 s
->auth_ldap_cafile
= buffer_init();
150 s
->auth_ldap_groupmember
= buffer_init_string("memberUid");
151 s
->auth_ldap_starttls
= 0;
154 cv
[0].destination
= s
->auth_ldap_hostname
;
155 cv
[1].destination
= s
->auth_ldap_basedn
;
156 cv
[2].destination
= s
->auth_ldap_filter
;
157 cv
[3].destination
= s
->auth_ldap_cafile
;
158 cv
[4].destination
= &(s
->auth_ldap_starttls
);
159 cv
[5].destination
= s
->auth_ldap_binddn
;
160 cv
[6].destination
= s
->auth_ldap_bindpw
;
161 cv
[7].destination
= &(s
->auth_ldap_allow_empty_pw
);
162 cv
[8].destination
= s
->auth_ldap_groupmember
;
164 p
->config_storage
[i
] = s
;
166 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
167 return HANDLER_ERROR
;
170 if (!buffer_string_is_empty(s
->auth_ldap_filter
)) {
171 if (*s
->auth_ldap_filter
->ptr
!= ',') {
172 /*(translate '$' to '?' for consistency with other modules)*/
173 char *d
= s
->auth_ldap_filter
->ptr
;
174 for (; NULL
!= (d
= strchr(d
, '$')); ++d
) *d
= '?';
175 if (NULL
== strchr(s
->auth_ldap_filter
->ptr
, '?')) {
176 log_error_write(srv
, __FILE__
, __LINE__
, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '?'");
177 return HANDLER_ERROR
;
182 mod_authn_add_scheme(srv
, s
->auth_ldap_hostname
);
185 return HANDLER_GO_ON
;
190 static int mod_authn_ldap_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
192 plugin_config
*s
= p
->config_storage
[0];
194 PATCH(auth_ldap_hostname
);
195 PATCH(auth_ldap_basedn
);
196 PATCH(auth_ldap_binddn
);
197 PATCH(auth_ldap_bindpw
);
198 PATCH(auth_ldap_filter
);
199 PATCH(auth_ldap_cafile
);
200 PATCH(auth_ldap_starttls
);
201 PATCH(auth_ldap_allow_empty_pw
);
202 PATCH(auth_ldap_groupmember
);
205 /* skip the first, the global context */
206 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
207 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
208 s
= p
->config_storage
[i
];
210 /* condition didn't match */
211 if (!config_check_cond(srv
, con
, dc
)) continue;
214 for (j
= 0; j
< dc
->value
->used
; j
++) {
215 data_unset
*du
= dc
->value
->data
[j
];
217 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.hostname"))) {
218 PATCH(auth_ldap_hostname
);
220 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.base-dn"))) {
221 PATCH(auth_ldap_basedn
);
222 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.filter"))) {
223 PATCH(auth_ldap_filter
);
224 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.ca-file"))) {
225 PATCH(auth_ldap_cafile
);
226 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.starttls"))) {
227 PATCH(auth_ldap_starttls
);
228 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) {
229 PATCH(auth_ldap_binddn
);
230 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) {
231 PATCH(auth_ldap_bindpw
);
232 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) {
233 PATCH(auth_ldap_allow_empty_pw
);
234 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.ldap.groupmember"))) {
235 PATCH(auth_ldap_groupmember
);
244 static void mod_authn_ldap_err(server
*srv
, const char *file
, unsigned long line
, const char *fn
, int err
)
246 log_error_write(srv
,file
,line
,"sSss","ldap:",fn
,":",ldap_err2string(err
));
249 static void mod_authn_ldap_opt_err(server
*srv
, const char *file
, unsigned long line
, const char *fn
, LDAP
*ld
)
252 ldap_get_option(ld
, LDAP_OPT_ERROR_NUMBER
, &err
);
253 mod_authn_ldap_err(srv
, file
, line
, fn
, err
);
256 static void mod_authn_append_ldap_dn_escape(buffer
* const filter
, const buffer
* const raw
) {
257 /* [RFC4514] 2.4 Converting an AttributeValue from ASN.1 to a String
259 * https://www.ldap.com/ldap-dns-and-rdns
260 * http://social.technet.microsoft.com/wiki/contents/articles/5312.active-directory-characters-to-escape.aspx
262 const char * const b
= raw
->ptr
;
263 const size_t rlen
= buffer_string_length(raw
);
264 if (0 == rlen
) return;
266 if (b
[0] == ' ') { /* || b[0] == '#' handled below for MS Active Directory*/
267 /* escape leading ' ' */
268 buffer_append_string_len(filter
, CONST_STR_LEN("\\"));
271 for (size_t i
= 0; i
< rlen
; ++i
) {
275 /* encode all UTF-8 chars with high bit set
276 * (instead of validating UTF-8 and escaping only invalid UTF-8) */
277 if (((unsigned char *)b
)[len
] > 0x7f)
282 case '"': case '+': case ',': case ';': case '\\':
284 case '=': case '#': /* (for MS Active Directory) */
291 } while (++len
< rlen
);
295 buffer_append_string_len(filter
, b
+i
, len
);
296 if ((i
+= len
) == rlen
) break;
300 buffer_append_string_len(filter
, CONST_STR_LEN("\\"));
301 buffer_append_string_len(filter
, b
+i
, 1);
304 /* escape NUL ('\0') (and all UTF-8 chars with high bit set) */
306 buffer_string_prepare_append(filter
, 3);
307 f
= filter
->ptr
+ buffer_string_length(filter
);
309 f
[1] = "0123456789abcdef"[(((unsigned char *)b
)[i
] >> 4) & 0xf];
310 f
[2] = "0123456789abcdef"[(((unsigned char *)b
)[i
] ) & 0xf];
311 buffer_commit(filter
, 3);
315 if (rlen
> 1 && b
[rlen
-1] == ' ') {
316 /* escape trailing ' ' */
317 filter
->ptr
[buffer_string_length(filter
)-1] = '\\';
318 buffer_append_string_len(filter
, CONST_STR_LEN(" "));
322 static void mod_authn_append_ldap_filter_escape(buffer
* const filter
, const buffer
* const raw
) {
323 /* [RFC4515] 3. String Search Filter Definition
327 * The <valueencoding> rule ensures that the entire filter string is a
328 * valid UTF-8 string and provides that the octets that represent the
329 * ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII
330 * 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a
331 * backslash "\" (ASCII 0x5c) followed by the two hexadecimal digits
332 * representing the value of the encoded octet.
336 * As indicated by the <valueencoding> rule, implementations MUST escape
337 * all octets greater than 0x7F that are not part of a valid UTF-8
338 * encoding sequence when they generate a string representation of a
339 * search filter. Implementations SHOULD accept as input strings that
340 * are not valid UTF-8 strings. This is necessary because RFC 2254 did
341 * not clearly define the term "string representation" (and in
342 * particular did not mention that the string representation of an LDAP
343 * search filter is a string of UTF-8-encoded Unicode characters).
346 * https://www.ldap.com/ldap-filters
347 * Although not required, you may escape any other characters that you want
348 * in the assertion value (or substring component) of a filter. This may be
349 * accomplished by prefixing the hexadecimal representation of each byte of
350 * the UTF-8 encoding of the character to escape with a backslash character.
352 const char * const b
= raw
->ptr
;
353 const size_t rlen
= buffer_string_length(raw
);
354 for (size_t i
= 0; i
< rlen
; ++i
) {
358 /* encode all UTF-8 chars with high bit set
359 * (instead of validating UTF-8 and escaping only invalid UTF-8) */
360 if (((unsigned char *)b
)[len
] > 0x7f)
365 case '\0': case '(': case ')': case '*': case '\\':
369 } while (++len
< rlen
);
373 buffer_append_string_len(filter
, b
+i
, len
);
374 if ((i
+= len
) == rlen
) break;
377 /* escape * ( ) \ NUL ('\0') (and all UTF-8 chars with high bit set) */
378 buffer_string_prepare_append(filter
, 3);
379 f
= filter
->ptr
+ buffer_string_length(filter
);
381 f
[1] = "0123456789abcdef"[(((unsigned char *)b
)[i
] >> 4) & 0xf];
382 f
[2] = "0123456789abcdef"[(((unsigned char *)b
)[i
] ) & 0xf];
383 buffer_commit(filter
, 3);
387 static LDAP
* mod_authn_ldap_host_init(server
*srv
, plugin_config
*s
) {
391 if (buffer_string_is_empty(s
->auth_ldap_hostname
)) return NULL
;
393 if (LDAP_SUCCESS
!= ldap_initialize(&ld
, s
->auth_ldap_hostname
->ptr
)) {
394 log_error_write(srv
, __FILE__
, __LINE__
, "sss", "ldap:",
395 "ldap_initialize():", strerror(errno
));
400 ret
= ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &ret
);
401 if (LDAP_OPT_SUCCESS
!= ret
) {
402 mod_authn_ldap_err(srv
, __FILE__
, __LINE__
, "ldap_set_option()", ret
);
407 /* restart ldap functions if interrupted by a signal, e.g. SIGCHLD */
408 ldap_set_option(ld
, LDAP_OPT_RESTART
, LDAP_OPT_ON
);
410 if (s
->auth_ldap_starttls
) {
411 /* if no CA file is given, it is ok, as we will use encryption
412 * if the server requires a CAfile it will tell us */
413 if (!buffer_string_is_empty(s
->auth_ldap_cafile
)) {
414 ret
= ldap_set_option(NULL
, LDAP_OPT_X_TLS_CACERTFILE
,
415 s
->auth_ldap_cafile
->ptr
);
416 if (LDAP_OPT_SUCCESS
!= ret
) {
417 mod_authn_ldap_err(srv
, __FILE__
, __LINE__
,
418 "ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE)",
425 ret
= ldap_start_tls_s(ld
, NULL
, NULL
);
426 if (LDAP_OPT_SUCCESS
!= ret
) {
427 mod_authn_ldap_err(srv
,__FILE__
,__LINE__
,"ldap_start_tls_s()",ret
);
436 static int mod_authn_ldap_bind(server
*srv
, LDAP
*ld
, const char *dn
, const char *pw
) {
441 *((const char **)&creds
.bv_val
) = pw
; /*(cast away const)*/
442 creds
.bv_len
= strlen(pw
);
448 /* RFE: add functionality: LDAP_SASL_EXTERNAL (or GSS-SPNEGO, etc.) */
450 ret
= ldap_sasl_bind_s(ld
,dn
,LDAP_SASL_SIMPLE
,&creds
,NULL
,NULL
,NULL
);
451 if (ret
!= LDAP_SUCCESS
) {
452 mod_authn_ldap_err(srv
, __FILE__
, __LINE__
, "ldap_sasl_bind_s()", ret
);
458 static int mod_authn_ldap_rebind_proc (LDAP
*ld
, LDAP_CONST
char *url
, ber_tag_t ldap_request
, ber_int_t msgid
, void *params
) {
459 plugin_config
*s
= (plugin_config
*)params
;
461 UNUSED(ldap_request
);
463 return !buffer_string_is_empty(s
->auth_ldap_binddn
)
464 ? mod_authn_ldap_bind(s
->srv
, ld
,
465 s
->auth_ldap_binddn
->ptr
,
466 s
->auth_ldap_bindpw
->ptr
)
467 : mod_authn_ldap_bind(s
->srv
, ld
, NULL
, NULL
);
470 static LDAPMessage
* mod_authn_ldap_search(server
*srv
, plugin_config
*s
, char *base
, char *filter
) {
471 LDAPMessage
*lm
= NULL
;
472 char *attrs
[] = { LDAP_NO_ATTRS
, NULL
};
476 * 1. connect anonymously (if not already connected)
477 * (ldap connection is kept open unless connection-level error occurs)
478 * 2. issue search using filter
481 if (s
->ldap
!= NULL
) {
482 ret
= ldap_search_ext_s(s
->ldap
, base
, LDAP_SCOPE_SUBTREE
, filter
,
483 attrs
, 0, NULL
, NULL
, NULL
, 0, &lm
);
484 if (LDAP_SUCCESS
== ret
) {
486 } else if (LDAP_SERVER_DOWN
!= ret
) {
487 /* try again (or initial request);
488 * ldap lib sometimes fails for the first call but reconnects */
489 ret
= ldap_search_ext_s(s
->ldap
, base
, LDAP_SCOPE_SUBTREE
, filter
,
490 attrs
, 0, NULL
, NULL
, NULL
, 0, &lm
);
491 if (LDAP_SUCCESS
== ret
) {
496 ldap_unbind_ext_s(s
->ldap
, NULL
, NULL
);
499 s
->ldap
= mod_authn_ldap_host_init(srv
, s
);
500 if (NULL
== s
->ldap
) {
504 ldap_set_rebind_proc(s
->ldap
, mod_authn_ldap_rebind_proc
, s
);
505 ret
= mod_authn_ldap_rebind_proc(s
->ldap
, NULL
, 0, 0, s
);
506 if (LDAP_SUCCESS
!= ret
) {
507 ldap_destroy(s
->ldap
);
512 ret
= ldap_search_ext_s(s
->ldap
, base
, LDAP_SCOPE_SUBTREE
, filter
,
513 attrs
, 0, NULL
, NULL
, NULL
, 0, &lm
);
514 if (LDAP_SUCCESS
!= ret
) {
515 log_error_write(srv
, __FILE__
, __LINE__
, "sSss",
516 "ldap:", ldap_err2string(ret
), "; filter:", filter
);
517 ldap_unbind_ext_s(s
->ldap
, NULL
, NULL
);
525 static char * mod_authn_ldap_get_dn(server
*srv
, plugin_config
*s
, char *base
, char *filter
) {
527 LDAPMessage
*lm
, *first
;
531 lm
= mod_authn_ldap_search(srv
, s
, base
, filter
);
536 ld
= s
->ldap
; /*(must be after mod_authn_ldap_search(); might reconnect)*/
538 count
= ldap_count_entries(ld
, lm
);
539 if (0 == count
) { /*(no entires found)*/
542 } else if (count
> 1) {
543 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
544 "ldap:", "more than one record returned. "
545 "you might have to refine the filter:", filter
);
548 if (NULL
== (first
= ldap_first_entry(ld
, lm
))) {
549 mod_authn_ldap_opt_err(srv
,__FILE__
,__LINE__
,"ldap_first_entry()",ld
);
554 if (NULL
== (dn
= ldap_get_dn(ld
, first
))) {
555 mod_authn_ldap_opt_err(srv
,__FILE__
,__LINE__
,"ldap_get_dn()",ld
);
564 static handler_t
mod_authn_ldap_memberOf(server
*srv
, plugin_config
*s
, const http_auth_require_t
*require
, const buffer
*username
, const char *userdn
) {
565 array
*groups
= require
->group
;
566 buffer
*filter
= buffer_init();
567 handler_t rc
= HANDLER_ERROR
;
569 buffer_copy_string_len(filter
, CONST_STR_LEN("("));
570 buffer_append_string_buffer(filter
, s
->auth_ldap_groupmember
);
571 buffer_append_string_len(filter
, CONST_STR_LEN("="));
572 if (buffer_is_equal_string(s
->auth_ldap_groupmember
,
573 CONST_STR_LEN("member"))) {
574 buffer_append_string(filter
, userdn
);
575 } else { /*(assume "memberUid"; consider validating in SETDEFAULTS_FUNC)*/
576 mod_authn_append_ldap_filter_escape(filter
, username
);
578 buffer_append_string_len(filter
, CONST_STR_LEN(")"));
580 for (size_t i
= 0; i
< groups
->used
; ++i
) {
581 char *base
= groups
->data
[i
]->key
->ptr
;
582 LDAPMessage
*lm
= mod_authn_ldap_search(srv
, s
, base
, filter
->ptr
);
584 int count
= ldap_count_entries(s
->ldap
, lm
);
597 static handler_t
mod_authn_ldap_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
) {
598 plugin_data
*p
= (plugin_data
*)p_d
;
604 mod_authn_ldap_patch_connection(srv
, con
, p
);
605 p
->anon_conf
->srv
= srv
;
608 if (pw
[0] == '\0' && !p
->conf
.auth_ldap_allow_empty_pw
)
609 return HANDLER_ERROR
;
611 template = p
->conf
.auth_ldap_filter
;
612 if (buffer_string_is_empty(template)) {
613 return HANDLER_ERROR
;
616 /* build filter to get DN for uid = username */
617 buffer_clear(p
->ldap_filter
);
618 if (*template->ptr
== ',') {
619 /* special-case filter template beginning with ',' to be explicit DN */
620 buffer_append_string_len(p
->ldap_filter
, CONST_STR_LEN("uid="));
621 mod_authn_append_ldap_dn_escape(p
->ldap_filter
, username
);
622 buffer_append_string_buffer(p
->ldap_filter
, template);
623 dn
= p
->ldap_filter
->ptr
;
625 for (char *b
= template->ptr
, *d
; *b
; b
= d
+1) {
626 if (NULL
!= (d
= strchr(b
, '?'))) {
627 buffer_append_string_len(p
->ldap_filter
, b
, (size_t)(d
- b
));
628 mod_authn_append_ldap_filter_escape(p
->ldap_filter
, username
);
630 d
= template->ptr
+ buffer_string_length(template);
631 buffer_append_string_len(p
->ldap_filter
, b
, (size_t)(d
- b
));
636 /* ldap_search for DN (synchronous; blocking) */
637 dn
= mod_authn_ldap_get_dn(srv
, p
->anon_conf
,
638 p
->conf
.auth_ldap_basedn
->ptr
,
639 p
->ldap_filter
->ptr
);
641 return HANDLER_ERROR
;
645 /* auth against LDAP server (synchronous; blocking) */
647 ld
= mod_authn_ldap_host_init(srv
, &p
->conf
);
649 if (dn
!= p
->ldap_filter
->ptr
) ldap_memfree(dn
);
650 return HANDLER_ERROR
;
653 /* Disable referral tracking. Target user should be in provided scope */
655 int ret
= ldap_set_option(ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
656 if (LDAP_OPT_SUCCESS
!= ret
) {
657 mod_authn_ldap_err(srv
,__FILE__
,__LINE__
,"ldap_set_option()",ret
);
659 if (dn
!= p
->ldap_filter
->ptr
) ldap_memfree(dn
);
660 return HANDLER_ERROR
;
664 if (LDAP_SUCCESS
!= mod_authn_ldap_bind(srv
, ld
, dn
, pw
)) {
666 if (dn
!= p
->ldap_filter
->ptr
) ldap_memfree(dn
);
667 return HANDLER_ERROR
;
670 ldap_unbind_ext_s(ld
, NULL
, NULL
); /* disconnect */
672 if (http_auth_match_rules(require
, username
->ptr
, NULL
, NULL
)) {
673 rc
= HANDLER_GO_ON
; /* access granted */
676 if (require
->group
->used
) {
677 /*(must not re-use p->ldap_filter, since it might be used for dn)*/
678 rc
= mod_authn_ldap_memberOf(srv
, &p
->conf
, require
, username
, dn
);
682 if (dn
!= p
->ldap_filter
->ptr
) ldap_memfree(dn
);
686 int mod_authn_ldap_plugin_init(plugin
*p
);
687 int mod_authn_ldap_plugin_init(plugin
*p
) {
688 p
->version
= LIGHTTPD_VERSION_ID
;
689 p
->name
= buffer_init_string("authn_ldap");
690 p
->init
= mod_authn_ldap_init
;
691 p
->set_defaults
= mod_authn_ldap_set_defaults
;
692 p
->cleanup
= mod_authn_ldap_free
;