1 /* $Id: ldapcheck.c,v 1.19 2014/02/21 08:09:53 manu Exp $ */
2
3 /*
4 * Copyright (c) 2008-2012 Emmanuel Dreyfus
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Emmanuel Dreyfus
18 *
19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "config.h"
33
34 #ifdef USE_LDAP
35
36 #ifdef HAVE_SYS_CDEFS_H
37 #include <sys/cdefs.h>
38 #ifdef __RCSID
39 __RCSID("$Id: ldapcheck.c,v 1.19 2014/02/21 08:09:53 manu Exp $");
40 #endif
41 #endif
42 #include <ctype.h>
43 #include <ldap.h>
44 #include <pthread.h>
45 #include <lber.h>
46 #include <errno.h>
47 #include <sysexits.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <syslog.h>
52 #ifdef HAVE_STRING_H
53 #include <string.h>
54 #endif
55
56 #include "conf.h"
57 #include "milter-greylist.h"
58 #include "spf.h"
59 #include "acl.h"
60 #include "prop.h"
61 #include "ldapcheck.h"
62
63 #ifndef SIMPLEQ_HEAD
64 # define SIMPLEQ_HEAD STAILQ_HEAD
65 # define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER
66 # define SIMPLEQ_ENTRY STAILQ_ENTRY
67 # define SIMPLEQ_INIT STAILQ_INIT
68 # define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD
69 # define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL
70 # define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER
71 # define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD
72 # define SIMPLEQ_FOREACH STAILQ_FOREACH
73 # define SIMPLEQ_EMPTY STAILQ_EMPTY
74 # define SIMPLEQ_FIRST STAILQ_FIRST
75 # define SIMPLEQ_NEXT STAILQ_NEXT
76 # define SIMPLEQ_REMOVE STAILQ_REMOVE
77 #endif
78
79 struct ldapconf_entry {
80 char *lc_url;
81 char *lc_dn;
82 char *lc_pwd;
83 LDAP *lc_ld;
84 int lc_refcount;
85 pthread_mutex_t lc_lock;
86 SIMPLEQ_ENTRY(ldapconf_entry) lc_list;
87 };
88
89 SIMPLEQ_HEAD(ldapconf_list, ldapconf_entry);
90 LIST_HEAD(ldapcheck_list, ldapcheck_entry);
91
92 static void ldapcheck_conf_addone(char *, char *, char *);
93 static int ldapcheck_connect(struct ldapconf_entry *);
94 static int ldapcheck_disconnect(struct ldapconf_entry *);
95 static char *url_encode_percent(char *);
96 static inline void ldapcheck_lock(struct ldapconf_entry *);
97 static inline void ldapcheck_unlock(struct ldapconf_entry *);
98
99 static struct ldapcheck_list ldapcheck_list;
100 static struct ldapconf_list ldapconf_list;
101 static struct timeval ldap_timeout;
102 #ifdef notyet
103 static char *ldap_binddn;
104 static char *ldap_bindpw;
105 #endif
106
107 int ldapcheck_gflags = 0;
108
109 void
ldapcheck_init(void)110 ldapcheck_init(void) {
111 LIST_INIT(&ldapcheck_list);
112 SIMPLEQ_INIT(&ldapconf_list);
113
114 ldapcheck_gflags = 0;
115 memset(&ldap_timeout, 0, sizeof(ldap_timeout));
116
117 return;
118 }
119
120 static void
ldapcheck_conf_addone(url,binddn,bindpw)121 ldapcheck_conf_addone(url, binddn, bindpw)
122 char *url;
123 char *binddn;
124 char *bindpw;
125 {
126 struct ldapconf_entry *lc;
127
128 if ((lc = malloc(sizeof(*lc))) == NULL) {
129 mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
130 exit(EX_OSERR);
131 }
132
133 if ((lc->lc_url = strdup(url)) == NULL) {
134 mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
135 exit(EX_OSERR);
136 }
137
138 lc->lc_dn = NULL;
139 lc->lc_pwd = NULL;
140 if ((binddn != NULL) && (lc->lc_dn = strdup(binddn)) == NULL) {
141 mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
142 exit(EX_OSERR);
143 }
144
145 if ((bindpw != NULL) && (lc->lc_pwd = strdup(bindpw)) == NULL) {
146 mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
147 exit(EX_OSERR);
148 }
149
150 lc->lc_ld = NULL;
151 lc->lc_refcount = 0;
152 if (pthread_mutex_init(&lc->lc_lock, NULL) != 0) {
153 mg_log(LOG_ERR, "pthread_mutex_init() failed: %s",
154 strerror(errno));
155 exit(EX_OSERR);
156 }
157
158 SIMPLEQ_INSERT_TAIL(&ldapconf_list, lc, lc_list);
159
160 return;
161 }
162
163 void
ldapcheck_conf_add(urls,binddn,bindpw)164 ldapcheck_conf_add(urls, binddn, bindpw)
165 char *urls;
166 char *binddn;
167 char *bindpw;
168 {
169 char *lasts = NULL;
170 char *p;
171 char *sep = "\t ";
172 if (conf.c_debug || conf.c_acldebug) {
173 mg_log(LOG_DEBUG, "bind options dn =\"%s\", pwd = \"%s\"\n",
174 binddn, bindpw);
175 }
176 if ((p = strtok_r(urls, sep, &lasts)) != NULL) {
177 ldapcheck_conf_addone(p, binddn, bindpw);
178
179 while (p)
180 if ((p = strtok_r(NULL, sep, &lasts)) != NULL)
181 ldapcheck_conf_addone(p, binddn, bindpw);
182 }
183
184 return;
185 }
186
187 void
ldapcheck_timeout_set(timeout)188 ldapcheck_timeout_set(timeout)
189 int timeout;
190 {
191 ldap_timeout.tv_sec = timeout;
192 ldap_timeout.tv_usec = 0;
193
194 return;
195 }
196
197 struct ldapcheck_entry *
ldapcheck_def_add(name,url,flags)198 ldapcheck_def_add(name, url, flags)
199 char *name;
200 char *url;
201 int flags;
202 {
203 int error;
204
205 struct ldapcheck_entry *lce;
206 LDAPURLDesc *lud;
207 char *eurl;
208
209 /*
210 * Just check
211 */
212 eurl = url_encode_percent(url);
213 if ((error = ldap_url_parse(eurl, &lud)) != 0) {
214 mg_log(LOG_ERR, "Bad LDAP URL \"%s\" at line %d",
215 eurl, conf_line - 1);
216 exit(EX_DATAERR);
217 }
218 free(eurl);
219 ldap_free_urldesc(lud);
220
221 if ((lce = malloc(sizeof(*lce))) == NULL) {
222 mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
223 exit(EX_OSERR);
224 }
225
226 strncpy(lce->lce_name, name, sizeof(lce->lce_name));
227 lce->lce_name[sizeof(lce->lce_name) - 1] = '\0';
228 strncpy(lce->lce_url, url, sizeof(lce->lce_url));
229 lce->lce_url[sizeof(lce->lce_url) - 1] = '\0';
230 lce->lce_flags = flags;
231
232 LIST_INSERT_HEAD(&ldapcheck_list, lce, lce_list);
233
234 if (conf.c_debug || conf.c_acldebug) {
235 mg_log(LOG_DEBUG, "load LDAP check \"%s\" \"%s\" %s",
236 lce->lce_name, lce->lce_url,
237 (lce->lce_flags & L_CLEARPROP) ? " clear" : "");
238 }
239
240 ldapcheck_gflags = 0;
241
242 return lce;
243 }
244
245
246 /* lc must be locked */
247 static int
ldapcheck_connect(lc)248 ldapcheck_connect(lc)
249 struct ldapconf_entry *lc;
250 {
251 int error;
252 int option;
253 int optval;
254
255 /*
256 * Already connected?
257 */
258 if (lc->lc_ld != NULL)
259 return 0;
260
261 /*
262 * Initialize connexion
263 */
264 if ((error = ldap_initialize(&lc->lc_ld, lc->lc_url)) != 0) {
265 mg_log(LOG_WARNING,
266 "ldap_initialize failed for LDAP URL \"%s\": %s",
267 lc->lc_url, ldap_err2string(error));
268 return -1;
269 }
270
271 option = LDAP_OPT_PROTOCOL_VERSION;
272 optval = LDAP_VERSION3;
273 if ((error = ldap_set_option(lc->lc_ld, option, &optval)) != 0) {
274 mg_log(LOG_WARNING,
275 "ldap_set_option failed for LDAP URL \"%s\": %s",
276 lc->lc_url, ldap_err2string(error));
277 goto bad;
278 }
279
280
281 if (ldap_timeout.tv_sec != 0) {
282 option = LDAP_OPT_TIMEOUT;
283 if ((error = ldap_set_option(lc->lc_ld,
284 option,
285 &ldap_timeout)) != 0) {
286 mg_log(LOG_WARNING,
287 "ldap_set_option failed for "
288 "LDAP URL \"%s\": %s",
289 lc->lc_url, ldap_err2string(error));
290 goto bad;
291 }
292
293 option = LDAP_OPT_NETWORK_TIMEOUT;
294 if ((error = ldap_set_option(lc->lc_ld,
295 option,
296 &ldap_timeout)) != 0) {
297 mg_log(LOG_WARNING,
298 "ldap_set_option failed for "
299 "LDAP URL \"%s\": %s",
300 lc->lc_url, ldap_err2string(error));
301 goto bad;
302 }
303 }
304
305 error = ldap_simple_bind_s(lc->lc_ld, lc->lc_dn, lc->lc_pwd);
306 if (error != LDAP_SUCCESS) {
307 mg_log(LOG_WARNING,
308 "ldap_simple_bind_s (%s/%s) failed for LDAP URL \"%s\": %s",
309 lc->lc_dn, lc->lc_pwd, lc->lc_url, ldap_err2string(error));
310 goto bad;
311 }
312
313 if (conf.c_debug)
314 mg_log(LOG_INFO, "LDAP URL \"%s\" connected", lc->lc_url);
315
316 return 0;
317
318 bad:
319 mg_log(LOG_WARNING, "LDAP URL \"%s\" unreachable", lc->lc_url);
320 return -1;
321 }
322
323 /* lc must be locked */
324 static int
ldapcheck_disconnect(lc)325 ldapcheck_disconnect(lc)
326 struct ldapconf_entry *lc;
327 {
328 int error = 0;
329
330 if (lc->lc_ld == NULL)
331 return 0;
332
333 /* Sanity check */
334 if (lc->lc_refcount < 0) {
335 mg_log(LOG_ERR, "bad refcount for LDAP URL \"%s\"", lc->lc_url);
336 exit(EX_OSERR);
337 }
338
339 /*
340 * Another thread is still using this connexion. We cannot dispose
341 * it immediatly, so we just return. If the fault is permanent,
342 * the other threads will get more errors, and the last one will
343 * be able to disconnect. If the fault is transcient, other threads
344 * may have more success, so we do not need to disconnect.
345 */
346 if (lc->lc_refcount > 0) {
347 mg_log(LOG_DEBUG, "LDAP URL \"%s\" has refcount %d",
348 lc->lc_url, lc->lc_refcount);
349 return 0;
350 }
351
352 if ((error = ldap_unbind_s(lc->lc_ld)) != 0)
353 mg_log(LOG_ERR, "ldap_unbind_s() failed: %s",
354 ldap_err2string(error));
355
356 lc->lc_ld = NULL;
357
358 if (conf.c_debug)
359 mg_log(LOG_INFO, "LDAP URL \"%s\" disconnected", lc->lc_url);
360
361 return error;
362 }
363
364 char *
ldapescape(str)365 ldapescape(str)
366 char *str;
367 {
368 char *outstr;
369 size_t origlen, len;
370 char *cp;
371 char *dp;
372
373 origlen = strlen(str) + 1;
374 len = origlen;
375 for (cp = str; *cp; cp++) {
376 if (!isgraph((int)*cp)) {
377 len += 2;
378 continue;
379 }
380
381 switch(*cp) {
382 case '"':
383 case '%':
384 case '<':
385 case '>':
386 case '?':
387 case '^':
388 case '`':
389 case '{':
390 case '|':
391 case '}':
392 len += 2;
393 break;
394 case '*':
395 case '(':
396 case ')':
397 case '\\':
398 len += 4;
399 break;
400 default:
401 break;
402 }
403 }
404
405 if (len == origlen)
406 return NULL;
407
408 if ((outstr = malloc(len)) == NULL) {
409 mg_log(LOG_ERR, "malloc failed");
410 exit (EX_OSERR);
411 }
412
413 dp = outstr;
414 for (cp = str; *cp; cp++) {
415 if (!isgraph((int)*cp)) {
416 (void)sprintf(dp, "%%%02X", *cp);
417 dp += 3;
418 continue;
419 }
420
421 switch(*cp) {
422 case '"':
423 case '%':
424 case '<':
425 case '>':
426 case '?':
427 case '^':
428 case '`':
429 case '{':
430 case '|':
431 case '}':
432 (void)sprintf(dp, "%%%02X", *cp);
433 dp += 3;
434 break;
435 case '*':
436 case '(':
437 case ')':
438 case '\\':
439 (void)sprintf(dp, "%%5c%02x", *cp);
440 dp += 5;
441 break;
442 default:
443 *dp++ = *cp;
444 break;
445 }
446 }
447
448 *dp = '\0';
449
450 return outstr;
451 }
452
453 int
ldapcheck_validate(ad,stage,ap,priv)454 ldapcheck_validate(ad, stage, ap, priv)
455 acl_data_t *ad;
456 acl_stage_t stage;
457 struct acl_param *ap;
458 struct mlfi_priv *priv;
459 {
460 struct ldapconf_entry *lc = NULL;
461 char *rcpt;
462 struct ldapcheck_entry *lce;
463 LDAPURLDesc *lud = NULL;
464 char *url = NULL;
465 struct timeval tv1, tv2, tv3;
466 LDAPMessage *res0 = NULL;
467 LDAPMessage *res = NULL;
468 int error, pushed = 0 ;
469 int msgid;
470 int retval = -1;
471 int clearprop;
472 int nmatch = 0;
473 char *(*cv)(char *);
474
475 rcpt = priv->priv_cur_rcpt;
476 lce = ad->ldapcheck;
477
478 cv = (lce->lce_flags & L_NOESCAPE) ? NULL : *ldapescape;
479 url = fstring_expand(priv, rcpt, lce->lce_url, cv);
480
481 clearprop = lce->lce_flags & L_CLEARPROP;
482
483 if (conf.c_debug) {
484 mg_log(LOG_DEBUG, "checking \"%s\"\n", url);
485 gettimeofday(&tv1, NULL);
486 }
487
488 if ((error = ldap_url_parse(url, &lud)) != 0) {
489 mg_log(LOG_ERR, "Bad expanded LDAP URL \"%s\"", url);
490 goto bad;
491 }
492
493 SIMPLEQ_FOREACH(lc, &ldapconf_list, lc_list) {
494 ldapcheck_lock(lc);
495 lc->lc_refcount++;
496 ldapcheck_unlock(lc);
497
498 if (lc->lc_ld == NULL) {
499 int error;
500
501 ldapcheck_lock(lc);
502 error = ldapcheck_connect(lc);
503 if (error != 0) {
504 lc->lc_refcount--;
505 (void)ldapcheck_disconnect(lc);
506 }
507 ldapcheck_unlock(lc);
508 if (error != 0)
509 continue;
510 }
511
512 if (conf.c_debug)
513 mg_log(LOG_DEBUG,
514 "Querying \"%s\"", lc->lc_url);
515
516 /*
517 * Perform the search
518 */
519 error = ldap_search_ext(lc->lc_ld,
520 lud->lud_dn,
521 lud->lud_scope,
522 lud->lud_filter,
523 lud->lud_attrs,
524 0, /* attrsonly */
525 NULL, /* serverctrls */
526 NULL, /* clientctrls */
527 NULL, /* timeout */
528 0, /* sizelimit */
529 &msgid);
530
531 if (error == 0)
532 break;
533
534 ldapcheck_lock(lc);
535 lc->lc_refcount--;
536 (void)ldapcheck_disconnect(lc);
537 ldapcheck_unlock(lc);
538
539 mg_log(LOG_ERR, "LDAP URL \"%s\" unreachable: %s",
540 url, ldap_err2string(error));
541 }
542
543 if ((lc == NULL) || (lc->lc_ld == NULL)) {
544 mg_log(LOG_ERR, "No LDAP URL can be reached");
545 goto bad;
546 }
547
548 error = ldap_result(lc->lc_ld, msgid, 1, &ldap_timeout, &res0);
549 switch (error) {
550 case -1:
551 mg_log(LOG_ERR, "LDAP query on \"%s\" failed", url);
552 goto bad;
553 case 0:
554 mg_log(LOG_ERR, "LDAP query timeout on \"%s\"", url);
555 goto bad;
556 default:
557 break;
558 }
559
560 /*
561 * Extract results
562 */
563 for (res = ldap_first_entry(lc->lc_ld, res0);
564 res != NULL;
565 res = ldap_next_entry(lc->lc_ld, res)) {
566 BerElement *ber = NULL;
567 char *attr = NULL;
568
569 nmatch++;
570
571 for (attr = ldap_first_attribute(lc->lc_ld, res, &ber);
572 attr != NULL;
573 attr = ldap_next_attribute(lc->lc_ld, res, ber)) {
574 char **vals = NULL;
575 char **val = NULL;
576
577 vals = ldap_get_values(lc->lc_ld, res, attr);
578 if (vals == NULL) {
579 mg_log(LOG_ERR,
580 "ldap_get_values for URL \"%s\" attr %s "
581 "returns vals = NULL", attr, url);
582 ldap_value_free(vals);
583 ldap_memfree(attr);
584 continue;
585 }
586
587 for (val = vals; *val; val++)
588 {
589 acl_modify_by_prop(attr, *val, ap);
590 prop_push(attr, *val, clearprop, priv);
591 pushed++;
592 if (conf.c_acldebug)
593 mg_log(LOG_DEBUG,
594 "acl debug: pushed prop "
595 "%s: %s", attr,*val);
596 }
597
598 ldap_value_free(vals);
599 ldap_memfree(attr);
600 }
601
602 if (ber != NULL)
603 ber_free(ber, 0);
604 }
605
606 if ((lce->lce_flags & L_DOMATCH) || conf.c_fixldapcheck)
607 retval = (nmatch != 0);
608 else
609 retval = 0;
610
611 bad:
612 if (res0)
613 ldap_msgfree(res0);
614 if (lud)
615 ldap_free_urldesc(lud);
616
617 if (lc != NULL) {
618 ldapcheck_lock(lc);
619 lc->lc_refcount--;
620 ldapcheck_unlock(lc);
621 }
622
623 if (url)
624 free(url);
625
626 if (conf.c_debug) {
627 gettimeofday(&tv2, NULL);
628 timersub(&tv2, &tv1, &tv3);
629 mg_log(LOG_DEBUG, "ldapcheck lookup performed in %ld.%06lds",
630 tv3.tv_sec, tv3.tv_usec);
631 }
632
633 return retval;
634 }
635
636 void
ldapcheck_clear(void)637 ldapcheck_clear(void) /* acllist must be write locked */
638 {
639 struct ldapcheck_entry *lce;
640 struct ldapconf_entry *lc;
641
642 while(!LIST_EMPTY(&ldapcheck_list)) {
643 lce = LIST_FIRST(&ldapcheck_list);
644 LIST_REMOVE(lce, lce_list);
645 free(lce);
646 }
647
648 while(!SIMPLEQ_EMPTY(&ldapconf_list)) {
649 lc = SIMPLEQ_FIRST(&ldapconf_list);
650 SIMPLEQ_REMOVE(&ldapconf_list, lc, ldapconf_entry, lc_list);
651
652 ldapcheck_lock(lc);
653 ldapcheck_disconnect(lc);
654 ldapcheck_unlock(lc);
655
656 if (lc->lc_url)
657 free(lc->lc_url);
658
659 if (lc->lc_dn)
660 free(lc->lc_dn);
661
662 if (lc->lc_pwd)
663 free(lc->lc_pwd);
664
665 free(lc);
666 }
667
668 ldapcheck_init();
669
670 return;
671 }
672
673 struct ldapcheck_entry *
ldapcheck_byname(name)674 ldapcheck_byname(name)
675 char *name;
676 {
677 struct ldapcheck_entry *lce = NULL;
678
679 LIST_FOREACH(lce, &ldapcheck_list, lce_list) {
680 if (strcmp(name, lce->lce_name) == 0)
681 break;
682 }
683
684 return lce;
685 }
686
687 static char *
url_encode_percent(url)688 url_encode_percent(url)
689 char *url;
690 {
691 char *cp;
692 size_t len;
693 char *out;
694 char *op;
695
696 len = 0;
697 for (cp = url; *cp; cp++) {
698 if (*cp != '%')
699 len++;
700 else
701 len += 3;
702 }
703 len++;
704
705 if ((out = malloc(len + 1)) == NULL) {
706 mg_log(LOG_ERR, "malloc(%d) failed",
707 len + 1, strerror(errno));
708 exit(EX_OSERR);
709 }
710 out[0] = '\0';
711 op = out;
712
713 for (cp = url; *cp; cp++) {
714 if (*cp != '%') {
715 *op++ = *cp;
716 } else {
717 strcpy(op, "%25");
718 op += 3;
719 }
720 }
721
722 return out;
723 }
724
725 #if 0
726 static char *
727 url_encode(url)
728 char *url;
729 {
730 char *cp;
731 size_t len;
732 char *out;
733 char *op;
734
735 len = 0;
736 for (cp = url; *cp; cp++) {
737 if (isalnum((int)*cp) ||
738 (*cp == '.') ||
739 (*cp == '-') ||
740 (*cp == '_')) {
741 len++;
742 } else {
743 len += 3;
744 }
745 }
746 len++;
747
748 if ((out = malloc(len + 1)) == NULL) {
749 mg_log(LOG_ERR, "malloc(%d) failed",
750 len + 1, strerror(errno));
751 exit(EX_OSERR);
752 }
753 out[0] = '\0';
754 op = out;
755
756 for (cp = url; *cp; cp++) {
757 if (isalnum((int)*cp) ||
758 (*cp == '.') ||
759 (*cp == '-') ||
760 (*cp == ':') ||
761 (*cp == '_')) {
762 *op++ = *cp;
763 } else {
764 int i;
765
766 *op = '\0';
767 (void)snprintf(op, 4, "%%%x", *cp);
768 for (i = 0; i < 4; i++)
769 op[i] = (char)toupper((int)op[i]);
770 op += 3;
771 }
772 }
773
774 return out;
775 }
776 #endif
777
778
779 static inline void
ldapcheck_lock(lc)780 ldapcheck_lock(lc)
781 struct ldapconf_entry *lc;
782 {
783 if (pthread_mutex_lock(&lc->lc_lock) != 0) {
784 mg_log(LOG_ERR, "pthread_mutex_lock failed "
785 "in urlcheck_clear: %s", strerror(errno));
786 exit(EX_OSERR);
787 }
788
789 return;
790 }
791
792 static inline void
ldapcheck_unlock(lc)793 ldapcheck_unlock(lc)
794 struct ldapconf_entry *lc;
795 {
796 if (pthread_mutex_unlock(&lc->lc_lock) != 0) {
797 mg_log(LOG_ERR, "pthread_mutex_unlock failed "
798 "in urlcheck_clear: %s", strerror(errno));
799 exit(EX_OSERR);
800 }
801
802 return;
803 }
804
805 #endif /* USE_LDAP */
806