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