source: trunk/third/sendmail/libsm/ldap.c @ 19204

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