1 /*
2 * Copyright (c) 2001-2009 Proofpoint, Inc. and its suppliers.
3 * All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 */
9
10 /* some "deprecated" calls are used, e.g., ldap_get_values() */
11 #define LDAP_DEPRECATED 1
12
13 #include <sm/gen.h>
14 SM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
15
16 #if LDAPMAP
17 # include <sys/types.h>
18 # include <errno.h>
19 # include <setjmp.h>
20 # include <stdlib.h>
21 # include <unistd.h>
22
23 # include <sm/bitops.h>
24 # include <sm/clock.h>
25 # include <sm/conf.h>
26 # include <sm/debug.h>
27 # include <sm/errstring.h>
28 # include <sm/ldap.h>
29 # include <sm/string.h>
30 # ifdef EX_OK
31 # undef EX_OK /* for SVr4.2 SMP */
32 # endif /* EX_OK */
33 # include <sm/sysexits.h>
34
35 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
36 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
37
38 static void ldaptimeout __P((int));
39 static bool sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *));
40 static SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **, char *, int, SM_RPOOL_T *));
41
42 /*
43 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
44 **
45 ** Parameters:
46 ** lmap -- pointer to SM_LDAP_STRUCT to clear
47 **
48 ** Returns:
49 ** None.
50 **
51 */
52
53 #if _FFR_LDAP_VERSION
54 # if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
55 ERROR FFR_LDAP_VERSION > _LDAP_VERSION_MAX
56 # endif /* defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX */
57 # if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
58 ERROR FFR_LDAP_VERSION < _LDAP_VERSION_MIN
59 # endif /* defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN */
60 # define SM_LDAP_VERSION_DEFAULT _FFR_LDAP_VERSION
61 #else /* _FFR_LDAP_VERSION */
62 # define SM_LDAP_VERSION_DEFAULT 0
63 #endif /* _FFR_LDAP_VERSION */
64
65 void
sm_ldap_clear(lmap)66 sm_ldap_clear(lmap)
67 SM_LDAP_STRUCT *lmap;
68 {
69 if (lmap == NULL)
70 return;
71
72 lmap->ldap_host = NULL;
73 lmap->ldap_port = LDAP_PORT;
74 lmap->ldap_uri = NULL;
75 lmap->ldap_version = SM_LDAP_VERSION_DEFAULT;
76 lmap->ldap_deref = LDAP_DEREF_NEVER;
77 lmap->ldap_timelimit = LDAP_NO_LIMIT;
78 lmap->ldap_sizelimit = LDAP_NO_LIMIT;
79 # ifdef LDAP_REFERRALS
80 lmap->ldap_options = LDAP_OPT_REFERRALS;
81 # else /* LDAP_REFERRALS */
82 lmap->ldap_options = 0;
83 # endif /* LDAP_REFERRALS */
84 lmap->ldap_attrsep = '\0';
85 lmap->ldap_binddn = NULL;
86 lmap->ldap_secret = NULL;
87 lmap->ldap_method = LDAP_AUTH_SIMPLE;
88 lmap->ldap_base = NULL;
89 lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
90 lmap->ldap_attrsonly = LDAPMAP_FALSE;
91 lmap->ldap_timeout.tv_sec = 0;
92 lmap->ldap_timeout.tv_usec = 0;
93 lmap->ldap_ld = NULL;
94 lmap->ldap_filter = NULL;
95 lmap->ldap_attr[0] = NULL;
96 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
97 lmap->ldap_attr_needobjclass[0] = NULL;
98 lmap->ldap_res = NULL;
99 lmap->ldap_next = NULL;
100 lmap->ldap_pid = 0;
101 lmap->ldap_multi_args = false;
102 }
103
104 /*
105 ** SM_LDAP_START -- actually connect to an LDAP server
106 **
107 ** Parameters:
108 ** name -- name of map for debug output.
109 ** lmap -- the LDAP map being opened.
110 **
111 ** Returns:
112 ** true if connection is successful, false otherwise.
113 **
114 ** Side Effects:
115 ** Populates lmap->ldap_ld.
116 */
117
118 static jmp_buf LDAPTimeout;
119
120 #define SM_LDAP_SETTIMEOUT(to) \
121 do \
122 { \
123 if (to != 0) \
124 { \
125 if (setjmp(LDAPTimeout) != 0) \
126 { \
127 errno = ETIMEDOUT; \
128 return false; \
129 } \
130 ev = sm_setevent(to, ldaptimeout, 0); \
131 } \
132 } while (0)
133
134 #define SM_LDAP_CLEARTIMEOUT() \
135 do \
136 { \
137 if (ev != NULL) \
138 sm_clrevent(ev); \
139 } while (0)
140
141 bool
sm_ldap_start(name,lmap)142 sm_ldap_start(name, lmap)
143 char *name;
144 SM_LDAP_STRUCT *lmap;
145 {
146 int bind_result;
147 int save_errno = 0;
148 char *id;
149 SM_EVENT *ev = NULL;
150 LDAP *ld = NULL;
151
152 if (sm_debug_active(&SmLDAPTrace, 2))
153 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
154
155 if (lmap->ldap_host != NULL)
156 id = lmap->ldap_host;
157 else if (lmap->ldap_uri != NULL)
158 id = lmap->ldap_uri;
159 else
160 id = "localhost";
161
162 if (sm_debug_active(&SmLDAPTrace, 9))
163 {
164 /* Don't print a port number for LDAP URIs */
165 if (lmap->ldap_uri != NULL)
166 sm_dprintf("ldapmap_start(%s)\n", id);
167 else
168 sm_dprintf("ldapmap_start(%s, %d)\n", id,
169 lmap->ldap_port);
170 }
171
172 if (lmap->ldap_uri != NULL)
173 {
174 #if SM_CONF_LDAP_INITIALIZE
175 /* LDAP server supports URIs so use them directly */
176 save_errno = ldap_initialize(&ld, lmap->ldap_uri);
177 #else /* SM_CONF_LDAP_INITIALIZE */
178 int err;
179 LDAPURLDesc *ludp = NULL;
180
181 /* Blast apart URL and use the ldap_init/ldap_open below */
182 err = ldap_url_parse(lmap->ldap_uri, &ludp);
183 if (err != 0)
184 {
185 errno = err + E_LDAPURLBASE;
186 return false;
187 }
188 lmap->ldap_host = sm_strdup_x(ludp->lud_host);
189 if (lmap->ldap_host == NULL)
190 {
191 save_errno = errno;
192 ldap_free_urldesc(ludp);
193 errno = save_errno;
194 return false;
195 }
196 lmap->ldap_port = ludp->lud_port;
197 ldap_free_urldesc(ludp);
198 #endif /* SM_CONF_LDAP_INITIALIZE */
199 }
200
201 if (ld == NULL)
202 {
203 # if USE_LDAP_INIT
204 ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
205 save_errno = errno;
206 # else /* USE_LDAP_INIT */
207 /*
208 ** If using ldap_open(), the actual connection to the server
209 ** happens now so we need the timeout here. For ldap_init(),
210 ** the connection happens at bind time.
211 */
212
213 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
214 ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
215 save_errno = errno;
216
217 /* clear the event if it has not sprung */
218 SM_LDAP_CLEARTIMEOUT();
219 # endif /* USE_LDAP_INIT */
220 }
221
222 errno = save_errno;
223 if (ld == NULL)
224 return false;
225
226 sm_ldap_setopts(ld, lmap);
227
228 # if USE_LDAP_INIT
229 /*
230 ** If using ldap_init(), the actual connection to the server
231 ** happens at ldap_bind_s() so we need the timeout here.
232 */
233
234 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
235 # endif /* USE_LDAP_INIT */
236
237 # ifdef LDAP_AUTH_KRBV4
238 if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
239 lmap->ldap_secret != NULL)
240 {
241 /*
242 ** Need to put ticket in environment here instead of
243 ** during parseargs as there may be different tickets
244 ** for different LDAP connections.
245 */
246
247 (void) putenv(lmap->ldap_secret);
248 }
249 # endif /* LDAP_AUTH_KRBV4 */
250
251 bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
252 lmap->ldap_secret, lmap->ldap_method);
253
254 # if USE_LDAP_INIT
255 /* clear the event if it has not sprung */
256 SM_LDAP_CLEARTIMEOUT();
257 # endif /* USE_LDAP_INIT */
258
259 if (bind_result != LDAP_SUCCESS)
260 {
261 errno = bind_result + E_LDAPBASE;
262 return false;
263 }
264
265 /* Save PID to make sure only this PID closes the LDAP connection */
266 lmap->ldap_pid = getpid();
267 lmap->ldap_ld = ld;
268 return true;
269 }
270
271 /* ARGSUSED */
272 static void
ldaptimeout(unused)273 ldaptimeout(unused)
274 int unused;
275 {
276 /*
277 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
278 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
279 ** DOING.
280 */
281
282 errno = ETIMEDOUT;
283 longjmp(LDAPTimeout, 1);
284 }
285
286 /*
287 ** SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
288 **
289 ** Initiate an LDAP search, return the msgid.
290 ** The calling function must collect the results.
291 **
292 ** Parameters:
293 ** lmap -- LDAP map information
294 ** argv -- key vector of substitutions in LDAP filter
295 ** NOTE: argv must have SM_LDAP_ARGS elements to prevent
296 ** out of bound array references
297 **
298 ** Returns:
299 ** <0 on failure (SM_LDAP_ERR*), msgid on success
300 **
301 */
302
303 int
sm_ldap_search_m(lmap,argv)304 sm_ldap_search_m(lmap, argv)
305 SM_LDAP_STRUCT *lmap;
306 char **argv;
307 {
308 int msgid;
309 char *fp, *p, *q;
310 char filter[LDAPMAP_MAX_FILTER + 1];
311
312 SM_REQUIRE(lmap != NULL);
313 SM_REQUIRE(argv != NULL);
314 SM_REQUIRE(argv[0] != NULL);
315
316 memset(filter, '\0', sizeof filter);
317 fp = filter;
318 p = lmap->ldap_filter;
319 while ((q = strchr(p, '%')) != NULL)
320 {
321 char *key;
322
323 if (lmap->ldap_multi_args)
324 {
325 #if SM_LDAP_ARGS < 10
326 # ERROR _SM_LDAP_ARGS must be 10
327 #endif /* SM_LDAP_ARGS < 10 */
328 if (q[1] == 's')
329 key = argv[0];
330 else if (q[1] >= '0' && q[1] <= '9')
331 {
332 key = argv[q[1] - '0'];
333 if (key == NULL)
334 {
335 # if SM_LDAP_ERROR_ON_MISSING_ARGS
336 return SM_LDAP_ERR_ARG_MISS;
337 # else /* SM_LDAP_ERROR_ON_MISSING_ARGS */
338 key = "";
339 # endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
340 }
341 }
342 else
343 key = NULL;
344 }
345 else
346 key = argv[0];
347
348 if (q[1] == 's')
349 {
350 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
351 "%.*s%s", (int) (q - p), p, key);
352 fp += strlen(fp);
353 p = q + 2;
354 }
355 else if (q[1] == '0' ||
356 (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
357 {
358 char *k = key;
359
360 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
361 "%.*s", (int) (q - p), p);
362 fp += strlen(fp);
363 p = q + 2;
364
365 /* Properly escape LDAP special characters */
366 while (SPACELEFT(filter, fp) > 0 &&
367 *k != '\0')
368 {
369 if (*k == '*' || *k == '(' ||
370 *k == ')' || *k == '\\')
371 {
372 (void) sm_strlcat(fp,
373 (*k == '*' ? "\\2A" :
374 (*k == '(' ? "\\28" :
375 (*k == ')' ? "\\29" :
376 (*k == '\\' ? "\\5C" :
377 "\00")))),
378 SPACELEFT(filter, fp));
379 fp += strlen(fp);
380 k++;
381 }
382 else
383 *fp++ = *k++;
384 }
385 }
386 else
387 {
388 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
389 "%.*s", (int) (q - p + 1), p);
390 p = q + (q[1] == '%' ? 2 : 1);
391 fp += strlen(fp);
392 }
393 }
394 (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
395 if (sm_debug_active(&SmLDAPTrace, 20))
396 sm_dprintf("ldap search filter=%s\n", filter);
397
398 lmap->ldap_res = NULL;
399 msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
400 lmap->ldap_scope, filter,
401 (lmap->ldap_attr[0] == NULL ? NULL :
402 lmap->ldap_attr),
403 lmap->ldap_attrsonly);
404 return msgid;
405 }
406
407 /*
408 ** SM_LDAP_SEARCH -- initiate LDAP search
409 **
410 ** Initiate an LDAP search, return the msgid.
411 ** The calling function must collect the results.
412 ** Note this is just a wrapper into sm_ldap_search_m()
413 **
414 ** Parameters:
415 ** lmap -- LDAP map information
416 ** key -- key to substitute in LDAP filter
417 **
418 ** Returns:
419 ** <0 on failure, msgid on success
420 **
421 */
422
423 int
sm_ldap_search(lmap,key)424 sm_ldap_search(lmap, key)
425 SM_LDAP_STRUCT *lmap;
426 char *key;
427 {
428 char *argv[SM_LDAP_ARGS];
429
430 memset(argv, '\0', sizeof argv);
431 argv[0] = key;
432 return sm_ldap_search_m(lmap, argv);
433 }
434
435 /*
436 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
437 ** particular objectClass
438 **
439 ** Parameters:
440 ** lmap -- pointer to SM_LDAP_STRUCT in use
441 ** entry -- current LDAP entry struct
442 ** ocvalue -- particular objectclass in question.
443 ** may be of form (fee|foo|fum) meaning
444 ** any entry can be part of either fee,
445 ** foo or fum objectclass
446 **
447 ** Returns:
448 ** true if item has that objectClass
449 */
450
451 static bool
sm_ldap_has_objectclass(lmap,entry,ocvalue)452 sm_ldap_has_objectclass(lmap, entry, ocvalue)
453 SM_LDAP_STRUCT *lmap;
454 LDAPMessage *entry;
455 char *ocvalue;
456 {
457 char **vals = NULL;
458 int i;
459
460 if (ocvalue == NULL)
461 return false;
462
463 vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
464 if (vals == NULL)
465 return false;
466
467 for (i = 0; vals[i] != NULL; i++)
468 {
469 char *p;
470 char *q;
471
472 p = q = ocvalue;
473 while (*p != '\0')
474 {
475 while (*p != '\0' && *p != '|')
476 p++;
477
478 if ((p - q) == strlen(vals[i]) &&
479 sm_strncasecmp(vals[i], q, p - q) == 0)
480 {
481 ldap_value_free(vals);
482 return true;
483 }
484
485 while (*p == '|')
486 p++;
487 q = p;
488 }
489 }
490
491 ldap_value_free(vals);
492 return false;
493 }
494
495 /*
496 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
497 **
498 ** Parameters:
499 ** lmap -- pointer to SM_LDAP_STRUCT in use
500 ** msgid -- msgid returned by sm_ldap_search()
501 ** flags -- flags for the lookup
502 ** delim -- delimiter for result concatenation
503 ** rpool -- memory pool for storage
504 ** result -- return string
505 ** recurse -- recursion list
506 **
507 ** Returns:
508 ** status (sysexit)
509 */
510
511 # define SM_LDAP_ERROR_CLEANUP() \
512 { \
513 if (lmap->ldap_res != NULL) \
514 { \
515 ldap_msgfree(lmap->ldap_res); \
516 lmap->ldap_res = NULL; \
517 } \
518 (void) ldap_abandon(lmap->ldap_ld, msgid); \
519 }
520
521 static SM_LDAP_RECURSE_ENTRY *
sm_ldap_add_recurse(top,item,type,rpool)522 sm_ldap_add_recurse(top, item, type, rpool)
523 SM_LDAP_RECURSE_LIST **top;
524 char *item;
525 int type;
526 SM_RPOOL_T *rpool;
527 {
528 int n;
529 int m;
530 int p;
531 int insertat;
532 int moveb;
533 int oldsizeb;
534 int rc;
535 SM_LDAP_RECURSE_ENTRY *newe;
536 SM_LDAP_RECURSE_ENTRY **olddata;
537
538 /*
539 ** This code will maintain a list of
540 ** SM_LDAP_RECURSE_ENTRY structures
541 ** in ascending order.
542 */
543
544 if (*top == NULL)
545 {
546 /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
547 *top = sm_rpool_malloc_x(rpool, sizeof **top);
548 (*top)->lrl_cnt = 0;
549 (*top)->lrl_size = 0;
550 (*top)->lrl_data = NULL;
551 }
552
553 if ((*top)->lrl_cnt >= (*top)->lrl_size)
554 {
555 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
556 olddata = (*top)->lrl_data;
557 if ((*top)->lrl_size == 0)
558 {
559 oldsizeb = 0;
560 (*top)->lrl_size = 256;
561 }
562 else
563 {
564 oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
565 (*top)->lrl_size *= 2;
566 }
567 (*top)->lrl_data = sm_rpool_malloc_x(rpool,
568 (*top)->lrl_size * sizeof *((*top)->lrl_data));
569 if (oldsizeb > 0)
570 memcpy((*top)->lrl_data, olddata, oldsizeb);
571 }
572
573 /*
574 ** Binary search/insert item:type into list.
575 ** Return current entry pointer if already exists.
576 */
577
578 n = 0;
579 m = (*top)->lrl_cnt - 1;
580 if (m < 0)
581 insertat = 0;
582 else
583 insertat = -1;
584
585 while (insertat == -1)
586 {
587 p = (m + n) / 2;
588
589 rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
590 if (rc == 0)
591 rc = type - (*top)->lrl_data[p]->lr_type;
592
593 if (rc < 0)
594 m = p - 1;
595 else if (rc > 0)
596 n = p + 1;
597 else
598 return (*top)->lrl_data[p];
599
600 if (m == -1)
601 insertat = 0;
602 else if (n >= (*top)->lrl_cnt)
603 insertat = (*top)->lrl_cnt;
604 else if (m < n)
605 insertat = m + 1;
606 }
607
608 /*
609 ** Not found in list, make room
610 ** at insert point and add it.
611 */
612
613 newe = sm_rpool_malloc_x(rpool, sizeof *newe);
614 if (newe != NULL)
615 {
616 moveb = ((*top)->lrl_cnt - insertat) * sizeof *((*top)->lrl_data);
617 if (moveb > 0)
618 memmove(&((*top)->lrl_data[insertat + 1]),
619 &((*top)->lrl_data[insertat]),
620 moveb);
621
622 newe->lr_search = sm_rpool_strdup_x(rpool, item);
623 newe->lr_type = type;
624 newe->lr_ludp = NULL;
625 newe->lr_attrs = NULL;
626 newe->lr_done = false;
627
628 ((*top)->lrl_data)[insertat] = newe;
629 (*top)->lrl_cnt++;
630 }
631 return newe;
632 }
633
634 int
sm_ldap_results(lmap,msgid,flags,delim,rpool,result,resultln,resultsz,recurse)635 sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
636 resultln, resultsz, recurse)
637 SM_LDAP_STRUCT *lmap;
638 int msgid;
639 int flags;
640 int delim;
641 SM_RPOOL_T *rpool;
642 char **result;
643 int *resultln;
644 int *resultsz;
645 SM_LDAP_RECURSE_LIST *recurse;
646 {
647 bool toplevel;
648 int i;
649 int statp;
650 int vsize;
651 int ret;
652 int save_errno;
653 char *p;
654 SM_LDAP_RECURSE_ENTRY *rl;
655
656 /* Are we the top top level of the search? */
657 toplevel = (recurse == NULL);
658
659 /* Get results */
660 statp = EX_NOTFOUND;
661 while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
662 (lmap->ldap_timeout.tv_sec == 0 ? NULL :
663 &(lmap->ldap_timeout)),
664 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
665 {
666 LDAPMessage *entry;
667
668 /* If we don't want multiple values and we have one, break */
669 if ((char) delim == '\0' &&
670 !bitset(SM_LDAP_SINGLEMATCH, flags) &&
671 *result != NULL)
672 break;
673
674 /* Cycle through all entries */
675 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
676 entry != NULL;
677 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
678 {
679 BerElement *ber;
680 char *attr;
681 char **vals = NULL;
682 char *dn;
683
684 /*
685 ** If matching only and found an entry,
686 ** no need to spin through attributes
687 */
688
689 if (bitset(SM_LDAP_MATCHONLY, flags))
690 {
691 statp = EX_OK;
692 continue;
693 }
694
695 #if _FFR_LDAP_SINGLEDN
696 if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
697 {
698 /* only wanted one match */
699 SM_LDAP_ERROR_CLEANUP();
700 errno = ENOENT;
701 return EX_NOTFOUND;
702 }
703 #endif /* _FFR_LDAP_SINGLEDN */
704
705 /* record completed DN's to prevent loops */
706 dn = ldap_get_dn(lmap->ldap_ld, entry);
707 if (dn == NULL)
708 {
709 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
710 save_errno += E_LDAPBASE;
711 SM_LDAP_ERROR_CLEANUP();
712 errno = save_errno;
713 return EX_TEMPFAIL;
714 }
715
716 rl = sm_ldap_add_recurse(&recurse, dn,
717 SM_LDAP_ATTR_DN,
718 rpool);
719
720 if (rl == NULL)
721 {
722 ldap_memfree(dn);
723 SM_LDAP_ERROR_CLEANUP();
724 errno = ENOMEM;
725 return EX_OSERR;
726 }
727 else if (rl->lr_done)
728 {
729 /* already on list, skip it */
730 ldap_memfree(dn);
731 continue;
732 }
733 ldap_memfree(dn);
734
735 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
736 /*
737 ** Reset value to prevent lingering
738 ** LDAP_DECODING_ERROR due to
739 ** OpenLDAP 1.X's hack (see below)
740 */
741
742 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
743 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
744
745 for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
746 &ber);
747 attr != NULL;
748 attr = ldap_next_attribute(lmap->ldap_ld, entry,
749 ber))
750 {
751 char *tmp, *vp_tmp;
752 int type;
753 char *needobjclass = NULL;
754
755 type = SM_LDAP_ATTR_NONE;
756 for (i = 0; lmap->ldap_attr[i] != NULL; i++)
757 {
758 if (sm_strcasecmp(lmap->ldap_attr[i],
759 attr) == 0)
760 {
761 type = lmap->ldap_attr_type[i];
762 needobjclass = lmap->ldap_attr_needobjclass[i];
763 break;
764 }
765 }
766
767 if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
768 type == SM_LDAP_ATTR_NONE)
769 {
770 /* URL lookups specify attrs to use */
771 type = SM_LDAP_ATTR_NORMAL;
772 needobjclass = NULL;
773 }
774
775 if (type == SM_LDAP_ATTR_NONE)
776 {
777 /* attribute not requested */
778 ldap_memfree(attr);
779 SM_LDAP_ERROR_CLEANUP();
780 errno = EFAULT;
781 return EX_SOFTWARE;
782 }
783
784 /*
785 ** For recursion on a particular attribute,
786 ** we may need to see if this entry is
787 ** part of a particular objectclass.
788 ** Also, ignore objectClass attribute.
789 ** Otherwise we just ignore this attribute.
790 */
791
792 if (type == SM_LDAP_ATTR_OBJCLASS ||
793 (needobjclass != NULL &&
794 !sm_ldap_has_objectclass(lmap, entry,
795 needobjclass)))
796 {
797 ldap_memfree(attr);
798 continue;
799 }
800
801 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
802 {
803 vals = ldap_get_values(lmap->ldap_ld,
804 entry,
805 attr);
806 if (vals == NULL)
807 {
808 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
809 if (save_errno == LDAP_SUCCESS)
810 {
811 ldap_memfree(attr);
812 continue;
813 }
814
815 /* Must be an error */
816 save_errno += E_LDAPBASE;
817 ldap_memfree(attr);
818 SM_LDAP_ERROR_CLEANUP();
819 errno = save_errno;
820 return EX_TEMPFAIL;
821 }
822 }
823
824 statp = EX_OK;
825
826 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
827 /*
828 ** Reset value to prevent lingering
829 ** LDAP_DECODING_ERROR due to
830 ** OpenLDAP 1.X's hack (see below)
831 */
832
833 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
834 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
835
836 /*
837 ** If matching only,
838 ** no need to spin through entries
839 */
840
841 if (bitset(SM_LDAP_MATCHONLY, flags))
842 {
843 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
844 ldap_value_free(vals);
845 ldap_memfree(attr);
846 continue;
847 }
848
849 /*
850 ** If we don't want multiple values,
851 ** return first found.
852 */
853
854 if ((char) delim == '\0')
855 {
856 if (*result != NULL)
857 {
858 /* already have a value */
859 if (bitset(SM_LDAP_SINGLEMATCH,
860 flags))
861 {
862 /* only wanted one match */
863 SM_LDAP_ERROR_CLEANUP();
864 errno = ENOENT;
865 return EX_NOTFOUND;
866 }
867 break;
868 }
869
870 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
871 {
872 *result = sm_rpool_strdup_x(rpool,
873 attr);
874 ldap_memfree(attr);
875 break;
876 }
877
878 if (vals[0] == NULL)
879 {
880 ldap_value_free(vals);
881 ldap_memfree(attr);
882 continue;
883 }
884
885 vsize = strlen(vals[0]) + 1;
886 if (lmap->ldap_attrsep != '\0')
887 vsize += strlen(attr) + 1;
888 *result = sm_rpool_malloc_x(rpool,
889 vsize);
890 if (lmap->ldap_attrsep != '\0')
891 sm_snprintf(*result, vsize,
892 "%s%c%s",
893 attr,
894 lmap->ldap_attrsep,
895 vals[0]);
896 else
897 sm_strlcpy(*result, vals[0],
898 vsize);
899 ldap_value_free(vals);
900 ldap_memfree(attr);
901 break;
902 }
903
904 /* attributes only */
905 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
906 {
907 if (*result == NULL)
908 *result = sm_rpool_strdup_x(rpool,
909 attr);
910 else
911 {
912 if (bitset(SM_LDAP_SINGLEMATCH,
913 flags) &&
914 *result != NULL)
915 {
916 /* only wanted one match */
917 SM_LDAP_ERROR_CLEANUP();
918 errno = ENOENT;
919 return EX_NOTFOUND;
920 }
921
922 vsize = strlen(*result) +
923 strlen(attr) + 2;
924 tmp = sm_rpool_malloc_x(rpool,
925 vsize);
926 (void) sm_snprintf(tmp,
927 vsize, "%s%c%s",
928 *result, (char) delim,
929 attr);
930 *result = tmp;
931 }
932 ldap_memfree(attr);
933 continue;
934 }
935
936 /*
937 ** If there is more than one, munge then
938 ** into a map_coldelim separated string.
939 ** If we are recursing we may have an entry
940 ** with no 'normal' values to put in the
941 ** string.
942 ** This is not an error.
943 */
944
945 if (type == SM_LDAP_ATTR_NORMAL &&
946 bitset(SM_LDAP_SINGLEMATCH, flags) &&
947 *result != NULL)
948 {
949 /* only wanted one match */
950 SM_LDAP_ERROR_CLEANUP();
951 errno = ENOENT;
952 return EX_NOTFOUND;
953 }
954
955 vsize = 0;
956 for (i = 0; vals[i] != NULL; i++)
957 {
958 if (type == SM_LDAP_ATTR_DN ||
959 type == SM_LDAP_ATTR_FILTER ||
960 type == SM_LDAP_ATTR_URL)
961 {
962 /* add to recursion */
963 if (sm_ldap_add_recurse(&recurse,
964 vals[i],
965 type,
966 rpool) == NULL)
967 {
968 SM_LDAP_ERROR_CLEANUP();
969 errno = ENOMEM;
970 return EX_OSERR;
971 }
972 continue;
973 }
974
975 vsize += strlen(vals[i]) + 1;
976 if (lmap->ldap_attrsep != '\0')
977 vsize += strlen(attr) + 1;
978 }
979
980 /*
981 ** Create/Append to string any normal
982 ** attribute values. Otherwise, just free
983 ** memory and move on to the next
984 ** attribute in this entry.
985 */
986
987 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
988 {
989 char *pe;
990
991 /* Grow result string if needed */
992 if ((*resultln + vsize) >= *resultsz)
993 {
994 while ((*resultln + vsize) >= *resultsz)
995 {
996 if (*resultsz == 0)
997 *resultsz = 1024;
998 else
999 *resultsz *= 2;
1000 }
1001
1002 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
1003 *vp_tmp = '\0';
1004
1005 if (*result != NULL)
1006 sm_strlcpy(vp_tmp,
1007 *result,
1008 *resultsz);
1009 *result = vp_tmp;
1010 }
1011
1012 p = *result + *resultln;
1013 pe = *result + *resultsz;
1014
1015 for (i = 0; vals[i] != NULL; i++)
1016 {
1017 if (*resultln > 0 &&
1018 p < pe)
1019 *p++ = (char) delim;
1020
1021 if (lmap->ldap_attrsep != '\0')
1022 {
1023 p += sm_strlcpy(p, attr,
1024 pe - p);
1025 if (p < pe)
1026 *p++ = lmap->ldap_attrsep;
1027 }
1028
1029 p += sm_strlcpy(p, vals[i],
1030 pe - p);
1031 *resultln = p - (*result);
1032 if (p >= pe)
1033 {
1034 /* Internal error: buffer too small for LDAP values */
1035 SM_LDAP_ERROR_CLEANUP();
1036 errno = ENOMEM;
1037 return EX_OSERR;
1038 }
1039 }
1040 }
1041
1042 ldap_value_free(vals);
1043 ldap_memfree(attr);
1044 }
1045 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1046
1047 /*
1048 ** We check save_errno != LDAP_DECODING_ERROR since
1049 ** OpenLDAP 1.X has a very ugly *undocumented*
1050 ** hack of returning this error code from
1051 ** ldap_next_attribute() if the library freed the
1052 ** ber attribute. See:
1053 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
1054 */
1055
1056 if (save_errno != LDAP_SUCCESS &&
1057 save_errno != LDAP_DECODING_ERROR)
1058 {
1059 /* Must be an error */
1060 save_errno += E_LDAPBASE;
1061 SM_LDAP_ERROR_CLEANUP();
1062 errno = save_errno;
1063 return EX_TEMPFAIL;
1064 }
1065
1066 /* mark this DN as done */
1067 rl->lr_done = true;
1068 if (rl->lr_ludp != NULL)
1069 {
1070 ldap_free_urldesc(rl->lr_ludp);
1071 rl->lr_ludp = NULL;
1072 }
1073 if (rl->lr_attrs != NULL)
1074 {
1075 free(rl->lr_attrs);
1076 rl->lr_attrs = NULL;
1077 }
1078
1079 /* We don't want multiple values and we have one */
1080 if ((char) delim == '\0' &&
1081 !bitset(SM_LDAP_SINGLEMATCH, flags) &&
1082 *result != NULL)
1083 break;
1084 }
1085 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1086 if (save_errno != LDAP_SUCCESS &&
1087 save_errno != LDAP_DECODING_ERROR)
1088 {
1089 /* Must be an error */
1090 save_errno += E_LDAPBASE;
1091 SM_LDAP_ERROR_CLEANUP();
1092 errno = save_errno;
1093 return EX_TEMPFAIL;
1094 }
1095 ldap_msgfree(lmap->ldap_res);
1096 lmap->ldap_res = NULL;
1097 }
1098
1099 if (ret == 0)
1100 save_errno = ETIMEDOUT;
1101 else if (ret == LDAP_RES_SEARCH_RESULT)
1102 {
1103 /*
1104 ** We may have gotten an LDAP_RES_SEARCH_RESULT response
1105 ** with an error inside it, so we have to extract that
1106 ** with ldap_parse_result(). This can happen when talking
1107 ** to an LDAP proxy whose backend has gone down.
1108 */
1109
1110 if (lmap->ldap_res == NULL)
1111 save_errno = LDAP_UNAVAILABLE;
1112 else
1113 {
1114 int rc;
1115
1116 save_errno = ldap_parse_result(lmap->ldap_ld,
1117 lmap->ldap_res, &rc, NULL, NULL,
1118 NULL, NULL, 0);
1119 if (save_errno == LDAP_SUCCESS)
1120 save_errno = rc;
1121 }
1122 }
1123 else
1124 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1125 if (save_errno != LDAP_SUCCESS)
1126 {
1127 statp = EX_TEMPFAIL;
1128 switch (save_errno)
1129 {
1130 #ifdef LDAP_SERVER_DOWN
1131 case LDAP_SERVER_DOWN:
1132 #endif /* LDAP_SERVER_DOWN */
1133 case LDAP_TIMEOUT:
1134 case ETIMEDOUT:
1135 case LDAP_UNAVAILABLE:
1136
1137 /*
1138 ** server disappeared,
1139 ** try reopen on next search
1140 */
1141
1142 statp = EX_RESTART;
1143 break;
1144 }
1145 if (ret != 0)
1146 save_errno += E_LDAPBASE;
1147 SM_LDAP_ERROR_CLEANUP();
1148 errno = save_errno;
1149 return statp;
1150 }
1151
1152 if (lmap->ldap_res != NULL)
1153 {
1154 ldap_msgfree(lmap->ldap_res);
1155 lmap->ldap_res = NULL;
1156 }
1157
1158 if (toplevel)
1159 {
1160 int rlidx;
1161
1162 /*
1163 ** Spin through the built-up recurse list at the top
1164 ** of the recursion. Since new items are added at the
1165 ** end of the shared list, we actually only ever get
1166 ** one level of recursion before things pop back to the
1167 ** top. Any items added to the list during that recursion
1168 ** will be expanded by the top level.
1169 */
1170
1171 for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1172 rlidx++)
1173 {
1174 int newflags;
1175 int sid;
1176 int status;
1177
1178 rl = recurse->lrl_data[rlidx];
1179
1180 newflags = flags;
1181 if (rl->lr_done)
1182 {
1183 /* already expanded */
1184 continue;
1185 }
1186
1187 if (rl->lr_type == SM_LDAP_ATTR_DN)
1188 {
1189 /* do DN search */
1190 sid = ldap_search(lmap->ldap_ld,
1191 rl->lr_search,
1192 lmap->ldap_scope,
1193 "(objectClass=*)",
1194 (lmap->ldap_attr[0] == NULL ?
1195 NULL : lmap->ldap_attr),
1196 lmap->ldap_attrsonly);
1197 }
1198 else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1199 {
1200 /* do new search */
1201 sid = ldap_search(lmap->ldap_ld,
1202 lmap->ldap_base,
1203 lmap->ldap_scope,
1204 rl->lr_search,
1205 (lmap->ldap_attr[0] == NULL ?
1206 NULL : lmap->ldap_attr),
1207 lmap->ldap_attrsonly);
1208 }
1209 else if (rl->lr_type == SM_LDAP_ATTR_URL)
1210 {
1211 /* Parse URL */
1212 sid = ldap_url_parse(rl->lr_search,
1213 &rl->lr_ludp);
1214
1215 if (sid != 0)
1216 {
1217 errno = sid + E_LDAPURLBASE;
1218 return EX_TEMPFAIL;
1219 }
1220
1221 /* We need to add objectClass */
1222 if (rl->lr_ludp->lud_attrs != NULL)
1223 {
1224 int attrnum = 0;
1225
1226 while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1227 {
1228 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1229 "objectClass") == 0)
1230 {
1231 /* already requested */
1232 attrnum = -1;
1233 break;
1234 }
1235 attrnum++;
1236 }
1237
1238 if (attrnum >= 0)
1239 {
1240 int i;
1241
1242 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1243 if (rl->lr_attrs == NULL)
1244 {
1245 save_errno = errno;
1246 ldap_free_urldesc(rl->lr_ludp);
1247 errno = save_errno;
1248 return EX_TEMPFAIL;
1249 }
1250 for (i = 0 ; i < attrnum; i++)
1251 {
1252 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1253 }
1254 rl->lr_attrs[i++] = "objectClass";
1255 rl->lr_attrs[i++] = NULL;
1256 }
1257 }
1258
1259 /*
1260 ** Use the existing connection
1261 ** for this search. It really
1262 ** should use lud_scheme://lud_host:lud_port/
1263 ** instead but that would require
1264 ** opening a new connection.
1265 ** This should be fixed ASAP.
1266 */
1267
1268 sid = ldap_search(lmap->ldap_ld,
1269 rl->lr_ludp->lud_dn,
1270 rl->lr_ludp->lud_scope,
1271 rl->lr_ludp->lud_filter,
1272 rl->lr_attrs,
1273 lmap->ldap_attrsonly);
1274
1275 /* Use the attributes specified by URL */
1276 newflags |= SM_LDAP_USE_ALLATTR;
1277 }
1278 else
1279 {
1280 /* unknown or illegal attribute type */
1281 errno = EFAULT;
1282 return EX_SOFTWARE;
1283 }
1284
1285 /* Collect results */
1286 if (sid == -1)
1287 {
1288 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1289 statp = EX_TEMPFAIL;
1290 switch (save_errno)
1291 {
1292 #ifdef LDAP_SERVER_DOWN
1293 case LDAP_SERVER_DOWN:
1294 #endif /* LDAP_SERVER_DOWN */
1295 case LDAP_TIMEOUT:
1296 case ETIMEDOUT:
1297 case LDAP_UNAVAILABLE:
1298
1299 /*
1300 ** server disappeared,
1301 ** try reopen on next search
1302 */
1303
1304 statp = EX_RESTART;
1305 break;
1306 }
1307 errno = save_errno + E_LDAPBASE;
1308 return statp;
1309 }
1310
1311 status = sm_ldap_results(lmap, sid, newflags, delim,
1312 rpool, result, resultln,
1313 resultsz, recurse);
1314 save_errno = errno;
1315 if (status != EX_OK && status != EX_NOTFOUND)
1316 {
1317 errno = save_errno;
1318 return status;
1319 }
1320
1321 /* Mark as done */
1322 rl->lr_done = true;
1323 if (rl->lr_ludp != NULL)
1324 {
1325 ldap_free_urldesc(rl->lr_ludp);
1326 rl->lr_ludp = NULL;
1327 }
1328 if (rl->lr_attrs != NULL)
1329 {
1330 free(rl->lr_attrs);
1331 rl->lr_attrs = NULL;
1332 }
1333
1334 /* Reset rlidx as new items may have been added */
1335 rlidx = -1;
1336 }
1337 }
1338 return statp;
1339 }
1340
1341 /*
1342 ** SM_LDAP_CLOSE -- close LDAP connection
1343 **
1344 ** Parameters:
1345 ** lmap -- LDAP map information
1346 **
1347 ** Returns:
1348 ** None.
1349 **
1350 */
1351
1352 void
sm_ldap_close(lmap)1353 sm_ldap_close(lmap)
1354 SM_LDAP_STRUCT *lmap;
1355 {
1356 if (lmap->ldap_ld == NULL)
1357 return;
1358
1359 if (lmap->ldap_pid == getpid())
1360 ldap_unbind(lmap->ldap_ld);
1361 lmap->ldap_ld = NULL;
1362 lmap->ldap_pid = 0;
1363 }
1364
1365 /*
1366 ** SM_LDAP_SETOPTS -- set LDAP options
1367 **
1368 ** Parameters:
1369 ** ld -- LDAP session handle
1370 ** lmap -- LDAP map information
1371 **
1372 ** Returns:
1373 ** None.
1374 **
1375 */
1376
1377 void
sm_ldap_setopts(ld,lmap)1378 sm_ldap_setopts(ld, lmap)
1379 LDAP *ld;
1380 SM_LDAP_STRUCT *lmap;
1381 {
1382 # if USE_LDAP_SET_OPTION
1383 if (lmap->ldap_version != 0)
1384 {
1385 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
1386 &lmap->ldap_version);
1387 }
1388 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
1389 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
1390 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
1391 else
1392 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1393 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
1394 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1395 # if _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
1396 if (lmap->ldap_networktmo > 0)
1397 {
1398 struct timeval tmo;
1399
1400 tmo.tv_sec = lmap->ldap_networktmo;
1401 tmo.tv_usec = 0;
1402 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo);
1403 }
1404 # endif /* _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
1405 # ifdef LDAP_OPT_RESTART
1406 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1407 # endif /* LDAP_OPT_RESTART */
1408 # else /* USE_LDAP_SET_OPTION */
1409 /* From here on in we can use ldap internal timelimits */
1410 ld->ld_deref = lmap->ldap_deref;
1411 ld->ld_options = lmap->ldap_options;
1412 ld->ld_sizelimit = lmap->ldap_sizelimit;
1413 ld->ld_timelimit = lmap->ldap_timelimit;
1414 # endif /* USE_LDAP_SET_OPTION */
1415 }
1416
1417 /*
1418 ** SM_LDAP_GETERRNO -- get ldap errno value
1419 **
1420 ** Parameters:
1421 ** ld -- LDAP session handle
1422 **
1423 ** Returns:
1424 ** LDAP errno.
1425 **
1426 */
1427
1428 int
sm_ldap_geterrno(ld)1429 sm_ldap_geterrno(ld)
1430 LDAP *ld;
1431 {
1432 int err = LDAP_SUCCESS;
1433
1434 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1435 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
1436 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1437 # ifdef LDAP_OPT_SIZELIMIT
1438 err = ldap_get_lderrno(ld, NULL, NULL);
1439 # else /* LDAP_OPT_SIZELIMIT */
1440 err = ld->ld_errno;
1441
1442 /*
1443 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1444 ** OpenLDAP 1.X's hack (see above)
1445 */
1446
1447 ld->ld_errno = LDAP_SUCCESS;
1448 # endif /* LDAP_OPT_SIZELIMIT */
1449 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1450 return err;
1451 }
1452 # endif /* LDAPMAP */
1453