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