8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / sendmail / libsm / ldap.c
blob7ee57fcbb689b645b2f89e6bab5b7b4cd90946cc
1 /*
2 * Copyright (c) 2001-2009 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
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 */
10 /* some "deprecated" calls are used, e.g., ldap_get_values() */
11 #define LDAP_DEPRECATED 1
13 #include <sm/gen.h>
14 SM_RCSID("@(#)$Id: ldap.c,v 1.83 2009/06/19 22:02:26 guenther Exp $")
16 #if LDAPMAP
17 # include <sys/types.h>
18 # include <errno.h>
19 # include <setjmp.h>
20 # include <stdlib.h>
21 # include <unistd.h>
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>
35 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
36 "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
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 *));
43 ** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
45 ** Parameters:
46 ** lmap -- pointer to SM_LDAP_STRUCT to clear
48 ** Returns:
49 ** None.
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 */
65 void
66 sm_ldap_clear(lmap)
67 SM_LDAP_STRUCT *lmap;
69 if (lmap == NULL)
70 return;
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;
105 ** SM_LDAP_START -- actually connect to an LDAP server
107 ** Parameters:
108 ** name -- name of map for debug output.
109 ** lmap -- the LDAP map being opened.
111 ** Returns:
112 ** true if connection is successful, false otherwise.
114 ** Side Effects:
115 ** Populates lmap->ldap_ld.
118 static jmp_buf LDAPTimeout;
120 #define SM_LDAP_SETTIMEOUT(to) \
121 do \
123 if (to != 0) \
125 if (setjmp(LDAPTimeout) != 0) \
127 errno = ETIMEDOUT; \
128 return false; \
130 ev = sm_setevent(to, ldaptimeout, 0); \
132 } while (0)
134 #define SM_LDAP_CLEARTIMEOUT() \
135 do \
137 if (ev != NULL) \
138 sm_clrevent(ev); \
139 } while (0)
141 bool
142 sm_ldap_start(name, lmap)
143 char *name;
144 SM_LDAP_STRUCT *lmap;
146 int bind_result;
147 int save_errno = 0;
148 char *id;
149 SM_EVENT *ev = NULL;
150 LDAP *ld = NULL;
152 if (sm_debug_active(&SmLDAPTrace, 2))
153 sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
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";
162 if (sm_debug_active(&SmLDAPTrace, 9))
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);
172 if (lmap->ldap_uri != NULL)
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;
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)
185 errno = err + E_LDAPURLBASE;
186 return false;
188 lmap->ldap_host = sm_strdup_x(ludp->lud_host);
189 if (lmap->ldap_host == NULL)
191 save_errno = errno;
192 ldap_free_urldesc(ludp);
193 errno = save_errno;
194 return false;
196 lmap->ldap_port = ludp->lud_port;
197 ldap_free_urldesc(ludp);
198 #endif /* SM_CONF_LDAP_INITIALIZE */
201 if (ld == NULL)
203 # if USE_LDAP_INIT
204 ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
205 save_errno = errno;
206 # else /* USE_LDAP_INIT */
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.
213 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
214 ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
215 save_errno = errno;
217 /* clear the event if it has not sprung */
218 SM_LDAP_CLEARTIMEOUT();
219 # endif /* USE_LDAP_INIT */
222 errno = save_errno;
223 if (ld == NULL)
224 return false;
226 sm_ldap_setopts(ld, lmap);
228 # if USE_LDAP_INIT
230 ** If using ldap_init(), the actual connection to the server
231 ** happens at ldap_bind_s() so we need the timeout here.
234 SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
235 # endif /* USE_LDAP_INIT */
237 # ifdef LDAP_AUTH_KRBV4
238 if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
239 lmap->ldap_secret != NULL)
242 ** Need to put ticket in environment here instead of
243 ** during parseargs as there may be different tickets
244 ** for different LDAP connections.
247 (void) putenv(lmap->ldap_secret);
249 # endif /* LDAP_AUTH_KRBV4 */
251 bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
252 lmap->ldap_secret, lmap->ldap_method);
254 # if USE_LDAP_INIT
255 /* clear the event if it has not sprung */
256 SM_LDAP_CLEARTIMEOUT();
257 # endif /* USE_LDAP_INIT */
259 if (bind_result != LDAP_SUCCESS)
261 errno = bind_result + E_LDAPBASE;
262 return false;
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;
271 /* ARGSUSED */
272 static void
273 ldaptimeout(unused)
274 int unused;
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.
282 errno = ETIMEDOUT;
283 longjmp(LDAPTimeout, 1);
287 ** SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
289 ** Initiate an LDAP search, return the msgid.
290 ** The calling function must collect the results.
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
298 ** Returns:
299 ** <0 on failure (SM_LDAP_ERR*), msgid on success
304 sm_ldap_search_m(lmap, argv)
305 SM_LDAP_STRUCT *lmap;
306 char **argv;
308 int msgid;
309 char *fp, *p, *q;
310 char filter[LDAPMAP_MAX_FILTER + 1];
312 SM_REQUIRE(lmap != NULL);
313 SM_REQUIRE(argv != NULL);
314 SM_REQUIRE(argv[0] != NULL);
316 memset(filter, '\0', sizeof filter);
317 fp = filter;
318 p = lmap->ldap_filter;
319 while ((q = strchr(p, '%')) != NULL)
321 char *key;
323 if (lmap->ldap_multi_args)
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')
332 key = argv[q[1] - '0'];
333 if (key == NULL)
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 */
342 else
343 key = NULL;
345 else
346 key = argv[0];
348 if (q[1] == 's')
350 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
351 "%.*s%s", (int) (q - p), p, key);
352 fp += strlen(fp);
353 p = q + 2;
355 else if (q[1] == '0' ||
356 (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
358 char *k = key;
360 (void) sm_snprintf(fp, SPACELEFT(filter, fp),
361 "%.*s", (int) (q - p), p);
362 fp += strlen(fp);
363 p = q + 2;
365 /* Properly escape LDAP special characters */
366 while (SPACELEFT(filter, fp) > 0 &&
367 *k != '\0')
369 if (*k == '*' || *k == '(' ||
370 *k == ')' || *k == '\\')
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++;
382 else
383 *fp++ = *k++;
386 else
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);
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);
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;
408 ** SM_LDAP_SEARCH -- initiate LDAP search
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()
414 ** Parameters:
415 ** lmap -- LDAP map information
416 ** key -- key to substitute in LDAP filter
418 ** Returns:
419 ** <0 on failure, msgid on success
424 sm_ldap_search(lmap, key)
425 SM_LDAP_STRUCT *lmap;
426 char *key;
428 char *argv[SM_LDAP_ARGS];
430 memset(argv, '\0', sizeof argv);
431 argv[0] = key;
432 return sm_ldap_search_m(lmap, argv);
436 ** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
437 ** particular objectClass
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
447 ** Returns:
448 ** true if item has that objectClass
451 static bool
452 sm_ldap_has_objectclass(lmap, entry, ocvalue)
453 SM_LDAP_STRUCT *lmap;
454 LDAPMessage *entry;
455 char *ocvalue;
457 char **vals = NULL;
458 int i;
460 if (ocvalue == NULL)
461 return false;
463 vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
464 if (vals == NULL)
465 return false;
467 for (i = 0; vals[i] != NULL; i++)
469 char *p;
470 char *q;
472 p = q = ocvalue;
473 while (*p != '\0')
475 while (*p != '\0' && *p != '|')
476 p++;
478 if ((p - q) == strlen(vals[i]) &&
479 sm_strncasecmp(vals[i], q, p - q) == 0)
481 ldap_value_free(vals);
482 return true;
485 while (*p == '|')
486 p++;
487 q = p;
491 ldap_value_free(vals);
492 return false;
496 ** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
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
507 ** Returns:
508 ** status (sysexit)
511 # define SM_LDAP_ERROR_CLEANUP() \
513 if (lmap->ldap_res != NULL) \
515 ldap_msgfree(lmap->ldap_res); \
516 lmap->ldap_res = NULL; \
518 (void) ldap_abandon(lmap->ldap_ld, msgid); \
521 static SM_LDAP_RECURSE_ENTRY *
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;
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;
539 ** This code will maintain a list of
540 ** SM_LDAP_RECURSE_ENTRY structures
541 ** in ascending order.
544 if (*top == NULL)
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;
553 if ((*top)->lrl_cnt >= (*top)->lrl_size)
555 /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
556 olddata = (*top)->lrl_data;
557 if ((*top)->lrl_size == 0)
559 oldsizeb = 0;
560 (*top)->lrl_size = 256;
562 else
564 oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
565 (*top)->lrl_size *= 2;
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);
574 ** Binary search/insert item:type into list.
575 ** Return current entry pointer if already exists.
578 n = 0;
579 m = (*top)->lrl_cnt - 1;
580 if (m < 0)
581 insertat = 0;
582 else
583 insertat = -1;
585 while (insertat == -1)
587 p = (m + n) / 2;
589 rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
590 if (rc == 0)
591 rc = type - (*top)->lrl_data[p]->lr_type;
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];
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;
609 ** Not found in list, make room
610 ** at insert point and add it.
613 newe = sm_rpool_malloc_x(rpool, sizeof *newe);
614 if (newe != NULL)
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);
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;
628 ((*top)->lrl_data)[insertat] = newe;
629 (*top)->lrl_cnt++;
631 return newe;
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;
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;
656 /* Are we the top top level of the search? */
657 toplevel = (recurse == NULL);
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)
666 LDAPMessage *entry;
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;
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))
679 BerElement *ber;
680 char *attr;
681 char **vals = NULL;
682 char *dn;
685 ** If matching only and found an entry,
686 ** no need to spin through attributes
689 if (bitset(SM_LDAP_MATCHONLY, flags))
691 statp = EX_OK;
692 continue;
695 #if _FFR_LDAP_SINGLEDN
696 if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
698 /* only wanted one match */
699 SM_LDAP_ERROR_CLEANUP();
700 errno = ENOENT;
701 return EX_NOTFOUND;
703 #endif /* _FFR_LDAP_SINGLEDN */
705 /* record completed DN's to prevent loops */
706 dn = ldap_get_dn(lmap->ldap_ld, entry);
707 if (dn == NULL)
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;
716 rl = sm_ldap_add_recurse(&recurse, dn,
717 SM_LDAP_ATTR_DN,
718 rpool);
720 if (rl == NULL)
722 ldap_memfree(dn);
723 SM_LDAP_ERROR_CLEANUP();
724 errno = ENOMEM;
725 return EX_OSERR;
727 else if (rl->lr_done)
729 /* already on list, skip it */
730 ldap_memfree(dn);
731 continue;
733 ldap_memfree(dn);
735 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
737 ** Reset value to prevent lingering
738 ** LDAP_DECODING_ERROR due to
739 ** OpenLDAP 1.X's hack (see below)
742 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
743 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
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))
751 char *tmp, *vp_tmp;
752 int type;
753 char *needobjclass = NULL;
755 type = SM_LDAP_ATTR_NONE;
756 for (i = 0; lmap->ldap_attr[i] != NULL; i++)
758 if (sm_strcasecmp(lmap->ldap_attr[i],
759 attr) == 0)
761 type = lmap->ldap_attr_type[i];
762 needobjclass = lmap->ldap_attr_needobjclass[i];
763 break;
767 if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
768 type == SM_LDAP_ATTR_NONE)
770 /* URL lookups specify attrs to use */
771 type = SM_LDAP_ATTR_NORMAL;
772 needobjclass = NULL;
775 if (type == SM_LDAP_ATTR_NONE)
777 /* attribute not requested */
778 ldap_memfree(attr);
779 SM_LDAP_ERROR_CLEANUP();
780 errno = EFAULT;
781 return EX_SOFTWARE;
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.
792 if (type == SM_LDAP_ATTR_OBJCLASS ||
793 (needobjclass != NULL &&
794 !sm_ldap_has_objectclass(lmap, entry,
795 needobjclass)))
797 ldap_memfree(attr);
798 continue;
801 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
803 vals = ldap_get_values(lmap->ldap_ld,
804 entry,
805 attr);
806 if (vals == NULL)
808 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
809 if (save_errno == LDAP_SUCCESS)
811 ldap_memfree(attr);
812 continue;
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;
824 statp = EX_OK;
826 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
828 ** Reset value to prevent lingering
829 ** LDAP_DECODING_ERROR due to
830 ** OpenLDAP 1.X's hack (see below)
833 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
834 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
837 ** If matching only,
838 ** no need to spin through entries
841 if (bitset(SM_LDAP_MATCHONLY, flags))
843 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
844 ldap_value_free(vals);
845 ldap_memfree(attr);
846 continue;
850 ** If we don't want multiple values,
851 ** return first found.
854 if ((char) delim == '\0')
856 if (*result != NULL)
858 /* already have a value */
859 if (bitset(SM_LDAP_SINGLEMATCH,
860 flags))
862 /* only wanted one match */
863 SM_LDAP_ERROR_CLEANUP();
864 errno = ENOENT;
865 return EX_NOTFOUND;
867 break;
870 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
872 *result = sm_rpool_strdup_x(rpool,
873 attr);
874 ldap_memfree(attr);
875 break;
878 if (vals[0] == NULL)
880 ldap_value_free(vals);
881 ldap_memfree(attr);
882 continue;
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;
904 /* attributes only */
905 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
907 if (*result == NULL)
908 *result = sm_rpool_strdup_x(rpool,
909 attr);
910 else
912 if (bitset(SM_LDAP_SINGLEMATCH,
913 flags) &&
914 *result != NULL)
916 /* only wanted one match */
917 SM_LDAP_ERROR_CLEANUP();
918 errno = ENOENT;
919 return EX_NOTFOUND;
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;
932 ldap_memfree(attr);
933 continue;
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.
945 if (type == SM_LDAP_ATTR_NORMAL &&
946 bitset(SM_LDAP_SINGLEMATCH, flags) &&
947 *result != NULL)
949 /* only wanted one match */
950 SM_LDAP_ERROR_CLEANUP();
951 errno = ENOENT;
952 return EX_NOTFOUND;
955 vsize = 0;
956 for (i = 0; vals[i] != NULL; i++)
958 if (type == SM_LDAP_ATTR_DN ||
959 type == SM_LDAP_ATTR_FILTER ||
960 type == SM_LDAP_ATTR_URL)
962 /* add to recursion */
963 if (sm_ldap_add_recurse(&recurse,
964 vals[i],
965 type,
966 rpool) == NULL)
968 SM_LDAP_ERROR_CLEANUP();
969 errno = ENOMEM;
970 return EX_OSERR;
972 continue;
975 vsize += strlen(vals[i]) + 1;
976 if (lmap->ldap_attrsep != '\0')
977 vsize += strlen(attr) + 1;
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.
987 if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
989 char *pe;
991 /* Grow result string if needed */
992 if ((*resultln + vsize) >= *resultsz)
994 while ((*resultln + vsize) >= *resultsz)
996 if (*resultsz == 0)
997 *resultsz = 1024;
998 else
999 *resultsz *= 2;
1002 vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
1003 *vp_tmp = '\0';
1005 if (*result != NULL)
1006 sm_strlcpy(vp_tmp,
1007 *result,
1008 *resultsz);
1009 *result = vp_tmp;
1012 p = *result + *resultln;
1013 pe = *result + *resultsz;
1015 for (i = 0; vals[i] != NULL; i++)
1017 if (*resultln > 0 &&
1018 p < pe)
1019 *p++ = (char) delim;
1021 if (lmap->ldap_attrsep != '\0')
1023 p += sm_strlcpy(p, attr,
1024 pe - p);
1025 if (p < pe)
1026 *p++ = lmap->ldap_attrsep;
1029 p += sm_strlcpy(p, vals[i],
1030 pe - p);
1031 *resultln = p - (*result);
1032 if (p >= pe)
1034 /* Internal error: buffer too small for LDAP values */
1035 SM_LDAP_ERROR_CLEANUP();
1036 errno = ENOMEM;
1037 return EX_OSERR;
1042 ldap_value_free(vals);
1043 ldap_memfree(attr);
1045 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
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
1056 if (save_errno != LDAP_SUCCESS &&
1057 save_errno != LDAP_DECODING_ERROR)
1059 /* Must be an error */
1060 save_errno += E_LDAPBASE;
1061 SM_LDAP_ERROR_CLEANUP();
1062 errno = save_errno;
1063 return EX_TEMPFAIL;
1066 /* mark this DN as done */
1067 rl->lr_done = true;
1068 if (rl->lr_ludp != NULL)
1070 ldap_free_urldesc(rl->lr_ludp);
1071 rl->lr_ludp = NULL;
1073 if (rl->lr_attrs != NULL)
1075 free(rl->lr_attrs);
1076 rl->lr_attrs = NULL;
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;
1085 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1086 if (save_errno != LDAP_SUCCESS &&
1087 save_errno != LDAP_DECODING_ERROR)
1089 /* Must be an error */
1090 save_errno += E_LDAPBASE;
1091 SM_LDAP_ERROR_CLEANUP();
1092 errno = save_errno;
1093 return EX_TEMPFAIL;
1095 ldap_msgfree(lmap->ldap_res);
1096 lmap->ldap_res = NULL;
1099 if (ret == 0)
1100 save_errno = ETIMEDOUT;
1101 else
1103 int rc;
1106 ** We may have gotten an LDAP_RES_SEARCH_RESULT response
1107 ** with an error inside it, so we have to extract that
1108 ** with ldap_parse_result(). This can happen when talking
1109 ** to an LDAP proxy whose backend has gone down.
1112 save_errno = ldap_parse_result(lmap->ldap_ld, lmap->ldap_res,
1113 &rc, NULL, NULL, NULL, NULL, 0);
1114 if (save_errno == LDAP_SUCCESS)
1115 save_errno = rc;
1117 if (save_errno != LDAP_SUCCESS)
1119 statp = EX_TEMPFAIL;
1120 switch (save_errno)
1122 #ifdef LDAP_SERVER_DOWN
1123 case LDAP_SERVER_DOWN:
1124 #endif /* LDAP_SERVER_DOWN */
1125 case LDAP_TIMEOUT:
1126 case ETIMEDOUT:
1127 case LDAP_UNAVAILABLE:
1130 ** server disappeared,
1131 ** try reopen on next search
1134 statp = EX_RESTART;
1135 break;
1137 if (ret != 0)
1138 save_errno += E_LDAPBASE;
1139 SM_LDAP_ERROR_CLEANUP();
1140 errno = save_errno;
1141 return statp;
1144 if (lmap->ldap_res != NULL)
1146 ldap_msgfree(lmap->ldap_res);
1147 lmap->ldap_res = NULL;
1150 if (toplevel)
1152 int rlidx;
1155 ** Spin through the built-up recurse list at the top
1156 ** of the recursion. Since new items are added at the
1157 ** end of the shared list, we actually only ever get
1158 ** one level of recursion before things pop back to the
1159 ** top. Any items added to the list during that recursion
1160 ** will be expanded by the top level.
1163 for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1164 rlidx++)
1166 int newflags;
1167 int sid;
1168 int status;
1170 rl = recurse->lrl_data[rlidx];
1172 newflags = flags;
1173 if (rl->lr_done)
1175 /* already expanded */
1176 continue;
1179 if (rl->lr_type == SM_LDAP_ATTR_DN)
1181 /* do DN search */
1182 sid = ldap_search(lmap->ldap_ld,
1183 rl->lr_search,
1184 lmap->ldap_scope,
1185 "(objectClass=*)",
1186 (lmap->ldap_attr[0] == NULL ?
1187 NULL : lmap->ldap_attr),
1188 lmap->ldap_attrsonly);
1190 else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1192 /* do new search */
1193 sid = ldap_search(lmap->ldap_ld,
1194 lmap->ldap_base,
1195 lmap->ldap_scope,
1196 rl->lr_search,
1197 (lmap->ldap_attr[0] == NULL ?
1198 NULL : lmap->ldap_attr),
1199 lmap->ldap_attrsonly);
1201 else if (rl->lr_type == SM_LDAP_ATTR_URL)
1203 /* Parse URL */
1204 sid = ldap_url_parse(rl->lr_search,
1205 &rl->lr_ludp);
1207 if (sid != 0)
1209 errno = sid + E_LDAPURLBASE;
1210 return EX_TEMPFAIL;
1213 /* We need to add objectClass */
1214 if (rl->lr_ludp->lud_attrs != NULL)
1216 int attrnum = 0;
1218 while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1220 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1221 "objectClass") == 0)
1223 /* already requested */
1224 attrnum = -1;
1225 break;
1227 attrnum++;
1230 if (attrnum >= 0)
1232 int i;
1234 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1235 if (rl->lr_attrs == NULL)
1237 save_errno = errno;
1238 ldap_free_urldesc(rl->lr_ludp);
1239 errno = save_errno;
1240 return EX_TEMPFAIL;
1242 for (i = 0 ; i < attrnum; i++)
1244 rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1246 rl->lr_attrs[i++] = "objectClass";
1247 rl->lr_attrs[i++] = NULL;
1252 ** Use the existing connection
1253 ** for this search. It really
1254 ** should use lud_scheme://lud_host:lud_port/
1255 ** instead but that would require
1256 ** opening a new connection.
1257 ** This should be fixed ASAP.
1260 sid = ldap_search(lmap->ldap_ld,
1261 rl->lr_ludp->lud_dn,
1262 rl->lr_ludp->lud_scope,
1263 rl->lr_ludp->lud_filter,
1264 rl->lr_attrs,
1265 lmap->ldap_attrsonly);
1267 /* Use the attributes specified by URL */
1268 newflags |= SM_LDAP_USE_ALLATTR;
1270 else
1272 /* unknown or illegal attribute type */
1273 errno = EFAULT;
1274 return EX_SOFTWARE;
1277 /* Collect results */
1278 if (sid == -1)
1280 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1281 statp = EX_TEMPFAIL;
1282 switch (save_errno)
1284 #ifdef LDAP_SERVER_DOWN
1285 case LDAP_SERVER_DOWN:
1286 #endif /* LDAP_SERVER_DOWN */
1287 case LDAP_TIMEOUT:
1288 case ETIMEDOUT:
1289 case LDAP_UNAVAILABLE:
1292 ** server disappeared,
1293 ** try reopen on next search
1296 statp = EX_RESTART;
1297 break;
1299 errno = save_errno + E_LDAPBASE;
1300 return statp;
1303 status = sm_ldap_results(lmap, sid, newflags, delim,
1304 rpool, result, resultln,
1305 resultsz, recurse);
1306 save_errno = errno;
1307 if (status != EX_OK && status != EX_NOTFOUND)
1309 errno = save_errno;
1310 return status;
1313 /* Mark as done */
1314 rl->lr_done = true;
1315 if (rl->lr_ludp != NULL)
1317 ldap_free_urldesc(rl->lr_ludp);
1318 rl->lr_ludp = NULL;
1320 if (rl->lr_attrs != NULL)
1322 free(rl->lr_attrs);
1323 rl->lr_attrs = NULL;
1326 /* Reset rlidx as new items may have been added */
1327 rlidx = -1;
1330 return statp;
1334 ** SM_LDAP_CLOSE -- close LDAP connection
1336 ** Parameters:
1337 ** lmap -- LDAP map information
1339 ** Returns:
1340 ** None.
1344 void
1345 sm_ldap_close(lmap)
1346 SM_LDAP_STRUCT *lmap;
1348 if (lmap->ldap_ld == NULL)
1349 return;
1351 if (lmap->ldap_pid == getpid())
1352 ldap_unbind(lmap->ldap_ld);
1353 lmap->ldap_ld = NULL;
1354 lmap->ldap_pid = 0;
1358 ** SM_LDAP_SETOPTS -- set LDAP options
1360 ** Parameters:
1361 ** ld -- LDAP session handle
1362 ** lmap -- LDAP map information
1364 ** Returns:
1365 ** None.
1369 void
1370 sm_ldap_setopts(ld, lmap)
1371 LDAP *ld;
1372 SM_LDAP_STRUCT *lmap;
1374 # if USE_LDAP_SET_OPTION
1375 if (lmap->ldap_version != 0)
1377 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
1378 &lmap->ldap_version);
1380 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
1381 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
1382 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
1383 else
1384 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1385 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
1386 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1387 # if _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
1388 if (lmap->ldap_networktmo > 0)
1390 struct timeval tmo;
1392 tmo.tv_sec = lmap->ldap_networktmo;
1393 tmo.tv_usec = 0;
1394 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo);
1396 # endif /* _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
1397 # ifdef LDAP_OPT_RESTART
1398 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1399 # endif /* LDAP_OPT_RESTART */
1400 # else /* USE_LDAP_SET_OPTION */
1401 /* From here on in we can use ldap internal timelimits */
1402 ld->ld_deref = lmap->ldap_deref;
1403 ld->ld_options = lmap->ldap_options;
1404 ld->ld_sizelimit = lmap->ldap_sizelimit;
1405 ld->ld_timelimit = lmap->ldap_timelimit;
1406 # endif /* USE_LDAP_SET_OPTION */
1410 ** SM_LDAP_GETERRNO -- get ldap errno value
1412 ** Parameters:
1413 ** ld -- LDAP session handle
1415 ** Returns:
1416 ** LDAP errno.
1421 sm_ldap_geterrno(ld)
1422 LDAP *ld;
1424 int err = LDAP_SUCCESS;
1426 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1427 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
1428 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1429 # ifdef LDAP_OPT_SIZELIMIT
1430 err = ldap_get_lderrno(ld, NULL, NULL);
1431 # else /* LDAP_OPT_SIZELIMIT */
1432 err = ld->ld_errno;
1435 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
1436 ** OpenLDAP 1.X's hack (see above)
1439 ld->ld_errno = LDAP_SUCCESS;
1440 # endif /* LDAP_OPT_SIZELIMIT */
1441 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1442 return err;
1444 # endif /* LDAPMAP */