]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/sendmail/libsm/ldap.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / sendmail / 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.85 2011/04/18 22:20:20 ca 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 if (ret == LDAP_RES_SEARCH_RESULT)
1102         {
1103                 /*
1104                 **  We may have gotten an LDAP_RES_SEARCH_RESULT response
1105                 **  with an error inside it, so we have to extract that
1106                 **  with ldap_parse_result().  This can happen when talking
1107                 **  to an LDAP proxy whose backend has gone down.
1108                 */
1109
1110                 if (lmap->ldap_res == NULL)
1111                         save_errno = LDAP_UNAVAILABLE;
1112                 else
1113                 {
1114                         int rc;
1115
1116                         save_errno = ldap_parse_result(lmap->ldap_ld,
1117                                         lmap->ldap_res, &rc, NULL, NULL,
1118                                         NULL, NULL, 0);
1119                         if (save_errno == LDAP_SUCCESS)
1120                                 save_errno = rc;
1121                 }
1122         }
1123         else
1124                 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1125         if (save_errno != LDAP_SUCCESS)
1126         {
1127                 statp = EX_TEMPFAIL;
1128                 switch (save_errno)
1129                 {
1130 #ifdef LDAP_SERVER_DOWN
1131                   case LDAP_SERVER_DOWN:
1132 #endif /* LDAP_SERVER_DOWN */
1133                   case LDAP_TIMEOUT:
1134                   case ETIMEDOUT:
1135                   case LDAP_UNAVAILABLE:
1136
1137                         /*
1138                         **  server disappeared,
1139                         **  try reopen on next search
1140                         */
1141
1142                         statp = EX_RESTART;
1143                         break;
1144                 }
1145                 if (ret != 0)
1146                         save_errno += E_LDAPBASE;
1147                 SM_LDAP_ERROR_CLEANUP();
1148                 errno = save_errno;
1149                 return statp;
1150         }
1151
1152         if (lmap->ldap_res != NULL)
1153         {
1154                 ldap_msgfree(lmap->ldap_res);
1155                 lmap->ldap_res = NULL;
1156         }
1157
1158         if (toplevel)
1159         {
1160                 int rlidx;
1161
1162                 /*
1163                 **  Spin through the built-up recurse list at the top
1164                 **  of the recursion.  Since new items are added at the
1165                 **  end of the shared list, we actually only ever get
1166                 **  one level of recursion before things pop back to the
1167                 **  top.  Any items added to the list during that recursion
1168                 **  will be expanded by the top level.
1169                 */
1170
1171                 for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1172                      rlidx++)
1173                 {
1174                         int newflags;
1175                         int sid;
1176                         int status;
1177
1178                         rl = recurse->lrl_data[rlidx];
1179
1180                         newflags = flags;
1181                         if (rl->lr_done)
1182                         {
1183                                 /* already expanded */
1184                                 continue;
1185                         }
1186
1187                         if (rl->lr_type == SM_LDAP_ATTR_DN)
1188                         {
1189                                 /* do DN search */
1190                                 sid = ldap_search(lmap->ldap_ld,
1191                                                   rl->lr_search,
1192                                                   lmap->ldap_scope,
1193                                                   "(objectClass=*)",
1194                                                   (lmap->ldap_attr[0] == NULL ?
1195                                                    NULL : lmap->ldap_attr),
1196                                                   lmap->ldap_attrsonly);
1197                         }
1198                         else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1199                         {
1200                                 /* do new search */
1201                                 sid = ldap_search(lmap->ldap_ld,
1202                                                   lmap->ldap_base,
1203                                                   lmap->ldap_scope,
1204                                                   rl->lr_search,
1205                                                   (lmap->ldap_attr[0] == NULL ?
1206                                                    NULL : lmap->ldap_attr),
1207                                                   lmap->ldap_attrsonly);
1208                         }
1209                         else if (rl->lr_type == SM_LDAP_ATTR_URL)
1210                         {
1211                                 /* Parse URL */
1212                                 sid = ldap_url_parse(rl->lr_search,
1213                                                      &rl->lr_ludp);
1214
1215                                 if (sid != 0)
1216                                 {
1217                                         errno = sid + E_LDAPURLBASE;
1218                                         return EX_TEMPFAIL;
1219                                 }
1220
1221                                 /* We need to add objectClass */
1222                                 if (rl->lr_ludp->lud_attrs != NULL)
1223                                 {
1224                                         int attrnum = 0;
1225
1226                                         while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1227                                         {
1228                                                 if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1229                                                                "objectClass") == 0)
1230                                                 {
1231                                                         /* already requested */
1232                                                         attrnum = -1;
1233                                                         break;
1234                                                 }
1235                                                 attrnum++;
1236                                         }
1237
1238                                         if (attrnum >= 0)
1239                                         {
1240                                                 int i;
1241
1242                                                 rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1243                                                 if (rl->lr_attrs == NULL)
1244                                                 {
1245                                                         save_errno = errno;
1246                                                         ldap_free_urldesc(rl->lr_ludp);
1247                                                         errno = save_errno;
1248                                                         return EX_TEMPFAIL;
1249                                                 }
1250                                                 for (i = 0 ; i < attrnum; i++)
1251                                                 {
1252                                                         rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1253                                                 }
1254                                                 rl->lr_attrs[i++] = "objectClass";
1255                                                 rl->lr_attrs[i++] = NULL;
1256                                         }
1257                                 }
1258
1259                                 /*
1260                                 **  Use the existing connection
1261                                 **  for this search.  It really
1262                                 **  should use lud_scheme://lud_host:lud_port/
1263                                 **  instead but that would require
1264                                 **  opening a new connection.
1265                                 **  This should be fixed ASAP.
1266                                 */
1267
1268                                 sid = ldap_search(lmap->ldap_ld,
1269                                                   rl->lr_ludp->lud_dn,
1270                                                   rl->lr_ludp->lud_scope,
1271                                                   rl->lr_ludp->lud_filter,
1272                                                   rl->lr_attrs,
1273                                                   lmap->ldap_attrsonly);
1274
1275                                 /* Use the attributes specified by URL */
1276                                 newflags |= SM_LDAP_USE_ALLATTR;
1277                         }
1278                         else
1279                         {
1280                                 /* unknown or illegal attribute type */
1281                                 errno = EFAULT;
1282                                 return EX_SOFTWARE;
1283                         }
1284
1285                         /* Collect results */
1286                         if (sid == -1)
1287                         {
1288                                 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1289                                 statp = EX_TEMPFAIL;
1290                                 switch (save_errno)
1291                                 {
1292 #ifdef LDAP_SERVER_DOWN
1293                                   case LDAP_SERVER_DOWN:
1294 #endif /* LDAP_SERVER_DOWN */
1295                                   case LDAP_TIMEOUT:
1296                                   case ETIMEDOUT:
1297                                   case LDAP_UNAVAILABLE:
1298
1299                                         /*
1300                                         **  server disappeared,
1301                                         **  try reopen on next search
1302                                         */
1303
1304                                         statp = EX_RESTART;
1305                                         break;
1306                                 }
1307                                 errno = save_errno + E_LDAPBASE;
1308                                 return statp;
1309                         }
1310
1311                         status = sm_ldap_results(lmap, sid, newflags, delim,
1312                                                  rpool, result, resultln,
1313                                                  resultsz, recurse);
1314                         save_errno = errno;
1315                         if (status != EX_OK && status != EX_NOTFOUND)
1316                         {
1317                                 errno = save_errno;
1318                                 return status;
1319                         }
1320
1321                         /* Mark as done */
1322                         rl->lr_done = true;
1323                         if (rl->lr_ludp != NULL)
1324                         {
1325                                 ldap_free_urldesc(rl->lr_ludp);
1326                                 rl->lr_ludp = NULL;
1327                         }
1328                         if (rl->lr_attrs != NULL)
1329                         {
1330                                 free(rl->lr_attrs);
1331                                 rl->lr_attrs = NULL;
1332                         }
1333
1334                         /* Reset rlidx as new items may have been added */
1335                         rlidx = -1;
1336                 }
1337         }
1338         return statp;
1339 }
1340
1341 /*
1342 **  SM_LDAP_CLOSE -- close LDAP connection
1343 **
1344 **      Parameters:
1345 **              lmap -- LDAP map information
1346 **
1347 **      Returns:
1348 **              None.
1349 **
1350 */
1351
1352 void
1353 sm_ldap_close(lmap)
1354         SM_LDAP_STRUCT *lmap;
1355 {
1356         if (lmap->ldap_ld == NULL)
1357                 return;
1358
1359         if (lmap->ldap_pid == getpid())
1360                 ldap_unbind(lmap->ldap_ld);
1361         lmap->ldap_ld = NULL;
1362         lmap->ldap_pid = 0;
1363 }
1364
1365 /*
1366 **  SM_LDAP_SETOPTS -- set LDAP options
1367 **
1368 **      Parameters:
1369 **              ld -- LDAP session handle
1370 **              lmap -- LDAP map information
1371 **
1372 **      Returns:
1373 **              None.
1374 **
1375 */
1376
1377 void
1378 sm_ldap_setopts(ld, lmap)
1379         LDAP *ld;
1380         SM_LDAP_STRUCT *lmap;
1381 {
1382 # if USE_LDAP_SET_OPTION
1383         if (lmap->ldap_version != 0)
1384         {
1385                 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
1386                                 &lmap->ldap_version);
1387         }
1388         ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
1389         if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
1390                 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
1391         else
1392                 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1393         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
1394         ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1395 #  if _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
1396         if (lmap->ldap_networktmo > 0)
1397         {
1398                 struct timeval tmo;
1399
1400                 tmo.tv_sec = lmap->ldap_networktmo;
1401                 tmo.tv_usec = 0;
1402                 ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo);
1403         }
1404 #  endif /* _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
1405 #  ifdef LDAP_OPT_RESTART
1406         ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1407 #  endif /* LDAP_OPT_RESTART */
1408 # else /* USE_LDAP_SET_OPTION */
1409         /* From here on in we can use ldap internal timelimits */
1410         ld->ld_deref = lmap->ldap_deref;
1411         ld->ld_options = lmap->ldap_options;
1412         ld->ld_sizelimit = lmap->ldap_sizelimit;
1413         ld->ld_timelimit = lmap->ldap_timelimit;
1414 # endif /* USE_LDAP_SET_OPTION */
1415 }
1416
1417 /*
1418 **  SM_LDAP_GETERRNO -- get ldap errno value
1419 **
1420 **      Parameters:
1421 **              ld -- LDAP session handle
1422 **
1423 **      Returns:
1424 **              LDAP errno.
1425 **
1426 */
1427
1428 int
1429 sm_ldap_geterrno(ld)
1430         LDAP *ld;
1431 {
1432         int err = LDAP_SUCCESS;
1433
1434 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1435         (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
1436 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1437 #  ifdef LDAP_OPT_SIZELIMIT
1438         err = ldap_get_lderrno(ld, NULL, NULL);
1439 #  else /* LDAP_OPT_SIZELIMIT */
1440         err = ld->ld_errno;
1441
1442         /*
1443         **  Reset value to prevent lingering LDAP_DECODING_ERROR due to
1444         **  OpenLDAP 1.X's hack (see above)
1445         */
1446
1447         ld->ld_errno = LDAP_SUCCESS;
1448 #  endif /* LDAP_OPT_SIZELIMIT */
1449 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1450         return err;
1451 }
1452 # endif /* LDAPMAP */