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