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