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