"Fossies" - the Fresh Open Source Software Archive

Member "sendmail-8.15.2/libsm/mbdb.c" (12 Jun 2014, 17186 Bytes) of package /linux/misc/sendmail.8.15.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mbdb.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 8.14.9_vs_8.15.1.

    1 /*
    2  * Copyright (c) 2001-2003,2009 Proofpoint, Inc. and its suppliers.
    3  *      All rights reserved.
    4  *
    5  * By using this file, you agree to the terms and conditions set
    6  * forth in the LICENSE file which can be found at the top level of
    7  * the sendmail distribution.
    8  */
    9 
   10 #include <sm/gen.h>
   11 SM_RCSID("@(#)$Id: mbdb.c,v 1.43 2014-01-08 17:03:15 ca Exp $")
   12 
   13 #include <sys/param.h>
   14 
   15 #include <ctype.h>
   16 #include <errno.h>
   17 #include <pwd.h>
   18 #include <stdlib.h>
   19 #include <setjmp.h>
   20 #include <unistd.h>
   21 
   22 #include <sm/limits.h>
   23 #include <sm/conf.h>
   24 #include <sm/assert.h>
   25 #include <sm/bitops.h>
   26 #include <sm/errstring.h>
   27 #include <sm/heap.h>
   28 #include <sm/mbdb.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 #if LDAPMAP
   36 # if _LDAP_EXAMPLE_
   37 #  include <sm/ldap.h>
   38 # endif /* _LDAP_EXAMPLE_ */
   39 #endif /* LDAPMAP */
   40 
   41 typedef struct
   42 {
   43     char    *mbdb_typename;
   44     int (*mbdb_initialize) __P((char *));
   45     int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
   46     void    (*mbdb_terminate) __P((void));
   47 } SM_MBDB_TYPE_T;
   48 
   49 static int  mbdb_pw_initialize __P((char *));
   50 static int  mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
   51 static void mbdb_pw_terminate __P((void));
   52 
   53 #if LDAPMAP
   54 # if _LDAP_EXAMPLE_
   55 static struct sm_ldap_struct LDAPLMAP;
   56 static int  mbdb_ldap_initialize __P((char *));
   57 static int  mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
   58 static void mbdb_ldap_terminate __P((void));
   59 # endif /* _LDAP_EXAMPLE_ */
   60 #endif /* LDAPMAP */
   61 
   62 static SM_MBDB_TYPE_T SmMbdbTypes[] =
   63 {
   64     { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
   65 #if LDAPMAP
   66 # if _LDAP_EXAMPLE_
   67     { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
   68 # endif /* _LDAP_EXAMPLE_ */
   69 #endif /* LDAPMAP */
   70     { NULL, NULL, NULL, NULL }
   71 };
   72 
   73 static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
   74 
   75 /*
   76 **  SM_MBDB_INITIALIZE -- specify which mailbox database to use
   77 **
   78 **  If this function is not called, then the "pw" implementation
   79 **  is used by default; this implementation uses getpwnam().
   80 **
   81 **  Parameters:
   82 **      mbdb -- Which mailbox database to use.
   83 **          The argument has the form "name" or "name.arg".
   84 **          "pw" means use getpwnam().
   85 **
   86 **  Results:
   87 **      EX_OK on success, or an EX_* code on failure.
   88 */
   89 
   90 int
   91 sm_mbdb_initialize(mbdb)
   92     char *mbdb;
   93 {
   94     size_t namelen;
   95     int err;
   96     char *name;
   97     char *arg;
   98     SM_MBDB_TYPE_T *t;
   99 
  100     SM_REQUIRE(mbdb != NULL);
  101 
  102     name = mbdb;
  103     arg = strchr(mbdb, '.');
  104     if (arg == NULL)
  105         namelen = strlen(name);
  106     else
  107     {
  108         namelen = arg - name;
  109         ++arg;
  110     }
  111 
  112     for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
  113     {
  114         if (strlen(t->mbdb_typename) == namelen &&
  115             strncmp(name, t->mbdb_typename, namelen) == 0)
  116         {
  117             err = EX_OK;
  118             if (t->mbdb_initialize != NULL)
  119                 err = t->mbdb_initialize(arg);
  120             if (err == EX_OK)
  121                 SmMbdbType = t;
  122             return err;
  123         }
  124     }
  125     return EX_UNAVAILABLE;
  126 }
  127 
  128 /*
  129 **  SM_MBDB_TERMINATE -- terminate connection to the mailbox database
  130 **
  131 **  Because this function closes any cached file descriptors that
  132 **  are being held open for the connection to the mailbox database,
  133 **  it should be called for security reasons prior to dropping privileges
  134 **  and execing another process.
  135 **
  136 **  Parameters:
  137 **      none.
  138 **
  139 **  Results:
  140 **      none.
  141 */
  142 
  143 void
  144 sm_mbdb_terminate()
  145 {
  146     if (SmMbdbType->mbdb_terminate != NULL)
  147         SmMbdbType->mbdb_terminate();
  148 }
  149 
  150 /*
  151 **  SM_MBDB_LOOKUP -- look up a local mail recipient, given name
  152 **
  153 **  Parameters:
  154 **      name -- name of local mail recipient
  155 **      user -- pointer to structure to fill in on success
  156 **
  157 **  Results:
  158 **      On success, fill in *user and return EX_OK.
  159 **      If the user does not exist, return EX_NOUSER.
  160 **      If a temporary failure (eg, a network failure) occurred,
  161 **      return EX_TEMPFAIL.  Otherwise return EX_OSERR.
  162 */
  163 
  164 int
  165 sm_mbdb_lookup(name, user)
  166     char *name;
  167     SM_MBDB_T *user;
  168 {
  169     int ret = EX_NOUSER;
  170 
  171     if (SmMbdbType->mbdb_lookup != NULL)
  172         ret = SmMbdbType->mbdb_lookup(name, user);
  173     return ret;
  174 }
  175 
  176 /*
  177 **  SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T
  178 **
  179 **  Parameters:
  180 **      user -- destination user information structure
  181 **      pw -- source passwd structure
  182 **
  183 **  Results:
  184 **      none.
  185 */
  186 
  187 void
  188 sm_mbdb_frompw(user, pw)
  189     SM_MBDB_T *user;
  190     struct passwd *pw;
  191 {
  192     SM_REQUIRE(user != NULL);
  193     (void) sm_strlcpy(user->mbdb_name, pw->pw_name,
  194               sizeof(user->mbdb_name));
  195     user->mbdb_uid = pw->pw_uid;
  196     user->mbdb_gid = pw->pw_gid;
  197     sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
  198               sizeof(user->mbdb_fullname));
  199     (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
  200               sizeof(user->mbdb_homedir));
  201     (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
  202               sizeof(user->mbdb_shell));
  203 }
  204 
  205 /*
  206 **  SM_PWFULLNAME -- build full name of user from pw_gecos field.
  207 **
  208 **  This routine interprets the strange entry that would appear
  209 **  in the GECOS field of the password file.
  210 **
  211 **  Parameters:
  212 **      gecos -- name to build.
  213 **      user -- the login name of this user (for &).
  214 **      buf -- place to put the result.
  215 **      buflen -- length of buf.
  216 **
  217 **  Returns:
  218 **      none.
  219 */
  220 
  221 #if _FFR_HANDLE_ISO8859_GECOS
  222 static char Latin1ToASCII[128] =
  223 {
  224     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  225     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33,
  226     99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42,
  227     50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65,
  228     65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79,
  229     79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97,
  230     97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110,
  231     111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121
  232 };
  233 #endif /* _FFR_HANDLE_ISO8859_GECOS */
  234 
  235 void
  236 sm_pwfullname(gecos, user, buf, buflen)
  237     register char *gecos;
  238     char *user;
  239     char *buf;
  240     size_t buflen;
  241 {
  242     register char *p;
  243     register char *bp = buf;
  244 
  245     if (*gecos == '*')
  246         gecos++;
  247 
  248     /* copy gecos, interpolating & to be full name */
  249     for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
  250     {
  251         if (bp >= &buf[buflen - 1])
  252         {
  253             /* buffer overflow -- just use login name */
  254             (void) sm_strlcpy(buf, user, buflen);
  255             return;
  256         }
  257         if (*p == '&')
  258         {
  259             /* interpolate full name */
  260             (void) sm_strlcpy(bp, user, buflen - (bp - buf));
  261             *bp = toupper(*bp);
  262             bp += strlen(bp);
  263         }
  264         else
  265         {
  266 #if _FFR_HANDLE_ISO8859_GECOS
  267             if ((unsigned char) *p >= 128)
  268                 *bp++ = Latin1ToASCII[(unsigned char) *p - 128];
  269             else
  270 #endif /* _FFR_HANDLE_ISO8859_GECOS */
  271                 *bp++ = *p;
  272         }
  273     }
  274     *bp = '\0';
  275 }
  276 
  277 /*
  278 **  /etc/passwd implementation.
  279 */
  280 
  281 /*
  282 **  MBDB_PW_INITIALIZE -- initialize getpwnam() version
  283 **
  284 **  Parameters:
  285 **      arg -- unused.
  286 **
  287 **  Results:
  288 **      EX_OK.
  289 */
  290 
  291 /* ARGSUSED0 */
  292 static int
  293 mbdb_pw_initialize(arg)
  294     char *arg;
  295 {
  296     return EX_OK;
  297 }
  298 
  299 /*
  300 **  MBDB_PW_LOOKUP -- look up a local mail recipient, given name
  301 **
  302 **  Parameters:
  303 **      name -- name of local mail recipient
  304 **      user -- pointer to structure to fill in on success
  305 **
  306 **  Results:
  307 **      On success, fill in *user and return EX_OK.
  308 **      Failure: EX_NOUSER.
  309 */
  310 
  311 static int
  312 mbdb_pw_lookup(name, user)
  313     char *name;
  314     SM_MBDB_T *user;
  315 {
  316     struct passwd *pw;
  317 
  318 #if HESIOD && !HESIOD_ALLOW_NUMERIC_LOGIN
  319     /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
  320     {
  321         char *p;
  322 
  323         for (p = name; *p != '\0'; p++)
  324             if (!isascii(*p) || !isdigit(*p))
  325                 break;
  326         if (*p == '\0')
  327             return EX_NOUSER;
  328     }
  329 #endif /* HESIOD && !HESIOD_ALLOW_NUMERIC_LOGIN */
  330 
  331     errno = 0;
  332     pw = getpwnam(name);
  333     if (pw == NULL)
  334     {
  335 #if _FFR_USE_GETPWNAM_ERRNO
  336         /*
  337         **  Only enable this code iff
  338         **  user unknown <-> getpwnam() == NULL && errno == 0
  339         **  (i.e., errno unchanged); see the POSIX spec.
  340         */
  341 
  342         if (errno != 0)
  343             return EX_TEMPFAIL;
  344 #endif /* _FFR_USE_GETPWNAM_ERRNO */
  345         return EX_NOUSER;
  346     }
  347 
  348     sm_mbdb_frompw(user, pw);
  349     return EX_OK;
  350 }
  351 
  352 /*
  353 **  MBDB_PW_TERMINATE -- terminate connection to the mailbox database
  354 **
  355 **  Parameters:
  356 **      none.
  357 **
  358 **  Results:
  359 **      none.
  360 */
  361 
  362 static void
  363 mbdb_pw_terminate()
  364 {
  365     endpwent();
  366 }
  367 
  368 #if LDAPMAP
  369 # if _LDAP_EXAMPLE_
  370 /*
  371 **  LDAP example implementation based on RFC 2307, "An Approach for Using
  372 **  LDAP as a Network Information Service":
  373 **
  374 **  ( nisSchema.1.0 NAME 'uidNumber'
  375 **    DESC 'An integer uniquely identifying a user in an
  376 **      administrative domain'
  377 **    EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
  378 **
  379 **  ( nisSchema.1.1 NAME 'gidNumber'
  380 **    DESC 'An integer uniquely identifying a group in an
  381 **      administrative domain'
  382 **    EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
  383 **
  384 **  ( nisSchema.1.2 NAME 'gecos'
  385 **    DESC 'The GECOS field; the common name'
  386 **    EQUALITY caseIgnoreIA5Match
  387 **    SUBSTRINGS caseIgnoreIA5SubstringsMatch
  388 **    SYNTAX 'IA5String' SINGLE-VALUE )
  389 **
  390 **  ( nisSchema.1.3 NAME 'homeDirectory'
  391 **    DESC 'The absolute path to the home directory'
  392 **    EQUALITY caseExactIA5Match
  393 **    SYNTAX 'IA5String' SINGLE-VALUE )
  394 **
  395 **  ( nisSchema.1.4 NAME 'loginShell'
  396 **    DESC 'The path to the login shell'
  397 **    EQUALITY caseExactIA5Match
  398 **    SYNTAX 'IA5String' SINGLE-VALUE )
  399 **
  400 **  ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
  401 **    DESC 'Abstraction of an account with POSIX attributes'
  402 **    MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
  403 **    MAY ( userPassword $ loginShell $ gecos $ description ) )
  404 **
  405 */
  406 
  407 #  define MBDB_LDAP_LABEL       "MailboxDatabase"
  408 
  409 #  ifndef MBDB_LDAP_FILTER
  410 #   define MBDB_LDAP_FILTER     "(&(objectClass=posixAccount)(uid=%0))"
  411 #  endif /* MBDB_LDAP_FILTER */
  412 
  413 #  ifndef MBDB_DEFAULT_LDAP_BASEDN
  414 #   define MBDB_DEFAULT_LDAP_BASEDN NULL
  415 #  endif /* MBDB_DEFAULT_LDAP_BASEDN */
  416 
  417 #  ifndef MBDB_DEFAULT_LDAP_SERVER
  418 #   define MBDB_DEFAULT_LDAP_SERVER NULL
  419 #  endif /* MBDB_DEFAULT_LDAP_SERVER */
  420 
  421 /*
  422 **  MBDB_LDAP_INITIALIZE -- initialize LDAP version
  423 **
  424 **  Parameters:
  425 **      arg -- LDAP specification
  426 **
  427 **  Results:
  428 **      EX_OK on success, or an EX_* code on failure.
  429 */
  430 
  431 static int
  432 mbdb_ldap_initialize(arg)
  433     char *arg;
  434 {
  435     sm_ldap_clear(&LDAPLMAP);
  436     LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
  437     LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
  438     LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
  439 
  440     /* Only want one match */
  441     LDAPLMAP.ldap_sizelimit = 1;
  442 
  443     /* interpolate new ldap_base and ldap_host from arg if given */
  444     if (arg != NULL && *arg != '\0')
  445     {
  446         char *new;
  447         char *sep;
  448         size_t len;
  449 
  450         len = strlen(arg) + 1;
  451         new = sm_malloc(len);
  452         if (new == NULL)
  453             return EX_TEMPFAIL;
  454         (void) sm_strlcpy(new, arg, len);
  455         sep = strrchr(new, '@');
  456         if (sep != NULL)
  457         {
  458             *sep++ = '\0';
  459             LDAPLMAP.ldap_host = sep;
  460         }
  461         LDAPLMAP.ldap_base = new;
  462     }
  463     return EX_OK;
  464 }
  465 
  466 
  467 /*
  468 **  MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
  469 **
  470 **  Parameters:
  471 **      name -- name of local mail recipient
  472 **      user -- pointer to structure to fill in on success
  473 **
  474 **  Results:
  475 **      On success, fill in *user and return EX_OK.
  476 **      Failure: EX_NOUSER.
  477 */
  478 
  479 #define NEED_FULLNAME   0x01
  480 #define NEED_HOMEDIR    0x02
  481 #define NEED_SHELL  0x04
  482 #define NEED_UID    0x08
  483 #define NEED_GID    0x10
  484 
  485 static int
  486 mbdb_ldap_lookup(name, user)
  487     char *name;
  488     SM_MBDB_T *user;
  489 {
  490     int msgid;
  491     int need;
  492     int ret;
  493     int save_errno;
  494     LDAPMessage *entry;
  495     BerElement *ber;
  496     char *attr = NULL;
  497 
  498     if (strlen(name) >= sizeof(user->mbdb_name))
  499     {
  500         errno = EINVAL;
  501         return EX_NOUSER;
  502     }
  503 
  504     if (LDAPLMAP.ldap_filter == NULL)
  505     {
  506         /* map not initialized, but don't have arg here */
  507         errno = EFAULT;
  508         return EX_TEMPFAIL;
  509     }
  510 
  511     if (LDAPLMAP.ldap_pid != getpid())
  512     {
  513         /* re-open map in this child process */
  514         LDAPLMAP.ldap_ld = NULL;
  515     }
  516 
  517     if (LDAPLMAP.ldap_ld == NULL)
  518     {
  519         /* map not open, try to open now */
  520         if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
  521             return EX_TEMPFAIL;
  522     }
  523 
  524     sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
  525     msgid = sm_ldap_search(&LDAPLMAP, name);
  526     if (msgid == -1)
  527     {
  528         save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
  529 #  ifdef LDAP_SERVER_DOWN
  530         if (errno == LDAP_SERVER_DOWN)
  531         {
  532             /* server disappeared, try reopen on next search */
  533             sm_ldap_close(&LDAPLMAP);
  534         }
  535 #  endif /* LDAP_SERVER_DOWN */
  536         errno = save_errno;
  537         return EX_TEMPFAIL;
  538     }
  539 
  540     /* Get results */
  541     ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
  542               (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
  543                &(LDAPLMAP.ldap_timeout)),
  544               &(LDAPLMAP.ldap_res));
  545 
  546     if (ret != LDAP_RES_SEARCH_RESULT &&
  547         ret != LDAP_RES_SEARCH_ENTRY)
  548     {
  549         if (ret == 0)
  550             errno = ETIMEDOUT;
  551         else
  552             errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
  553         ret = EX_TEMPFAIL;
  554         goto abort;
  555     }
  556 
  557     entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
  558     if (entry == NULL)
  559     {
  560         int rc;
  561 
  562         /*  
  563         **  We may have gotten an LDAP_RES_SEARCH_RESULT response
  564         **  with an error inside it, so we have to extract that
  565         **  with ldap_parse_result().  This can happen when talking
  566         **  to an LDAP proxy whose backend has gone down.
  567         */
  568 
  569         save_errno = ldap_parse_result(LDAPLMAP.ldap_ld,
  570                            LDAPLMAP.ldap_res, &rc, NULL,
  571                            NULL, NULL, NULL, 0);
  572         if (save_errno == LDAP_SUCCESS)
  573             save_errno = rc;
  574         if (save_errno == LDAP_SUCCESS)
  575         {
  576             errno = ENOENT;
  577             ret = EX_NOUSER;
  578         }
  579         else
  580         {
  581             errno = save_errno;
  582             ret = EX_TEMPFAIL;
  583         }
  584         goto abort;
  585     }
  586 
  587 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
  588     /*
  589     **  Reset value to prevent lingering
  590     **  LDAP_DECODING_ERROR due to
  591     **  OpenLDAP 1.X's hack (see below)
  592     */
  593 
  594     LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
  595 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
  596 
  597     ret = EX_OK;
  598     need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
  599     for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
  600          attr != NULL;
  601          attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
  602     {
  603         char **vals;
  604 
  605         vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
  606         if (vals == NULL)
  607         {
  608             errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
  609             if (errno == LDAP_SUCCESS)
  610             {
  611                 ldap_memfree(attr);
  612                 continue;
  613             }
  614 
  615             /* Must be an error */
  616             errno += E_LDAPBASE;
  617             ret = EX_TEMPFAIL;
  618             goto abort;
  619         }
  620 
  621 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
  622         /*
  623         **  Reset value to prevent lingering
  624         **  LDAP_DECODING_ERROR due to
  625         **  OpenLDAP 1.X's hack (see below)
  626         */
  627 
  628         LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
  629 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
  630 
  631         if (vals[0] == NULL || vals[0][0] == '\0')
  632             goto skip;
  633 
  634         if (strcasecmp(attr, "gecos") == 0)
  635         {
  636             if (!bitset(NEED_FULLNAME, need) ||
  637                 strlen(vals[0]) >= sizeof(user->mbdb_fullname))
  638                 goto skip;
  639 
  640             sm_pwfullname(vals[0], name, user->mbdb_fullname,
  641                       sizeof(user->mbdb_fullname));
  642             need &= ~NEED_FULLNAME;
  643         }
  644         else if (strcasecmp(attr, "homeDirectory") == 0)
  645         {
  646             if (!bitset(NEED_HOMEDIR, need) ||
  647                 strlen(vals[0]) >= sizeof(user->mbdb_homedir))
  648                 goto skip;
  649 
  650             (void) sm_strlcpy(user->mbdb_homedir, vals[0],
  651                       sizeof(user->mbdb_homedir));
  652             need &= ~NEED_HOMEDIR;
  653         }
  654         else if (strcasecmp(attr, "loginShell") == 0)
  655         {
  656             if (!bitset(NEED_SHELL, need) ||
  657                 strlen(vals[0]) >= sizeof(user->mbdb_shell))
  658                 goto skip;
  659 
  660             (void) sm_strlcpy(user->mbdb_shell, vals[0],
  661                       sizeof(user->mbdb_shell));
  662             need &= ~NEED_SHELL;
  663         }
  664         else if (strcasecmp(attr, "uidNumber") == 0)
  665         {
  666             char *p;
  667 
  668             if (!bitset(NEED_UID, need))
  669                 goto skip;
  670 
  671             for (p = vals[0]; *p != '\0'; p++)
  672             {
  673                 /* allow negative numbers */
  674                 if (p == vals[0] && *p == '-')
  675                 {
  676                     /* but not simply '-' */
  677                     if (*(p + 1) == '\0')
  678                         goto skip;
  679                 }
  680                 else if (!isascii(*p) || !isdigit(*p))
  681                     goto skip;
  682             }
  683             user->mbdb_uid = atoi(vals[0]);
  684             need &= ~NEED_UID;
  685         }
  686         else if (strcasecmp(attr, "gidNumber") == 0)
  687         {
  688             char *p;
  689 
  690             if (!bitset(NEED_GID, need))
  691                 goto skip;
  692 
  693             for (p = vals[0]; *p != '\0'; p++)
  694             {
  695                 /* allow negative numbers */
  696                 if (p == vals[0] && *p == '-')
  697                 {
  698                     /* but not simply '-' */
  699                     if (*(p + 1) == '\0')
  700                         goto skip;
  701                 }
  702                 else if (!isascii(*p) || !isdigit(*p))
  703                     goto skip;
  704             }
  705             user->mbdb_gid = atoi(vals[0]);
  706             need &= ~NEED_GID;
  707         }
  708 
  709 skip:
  710         ldap_value_free(vals);
  711         ldap_memfree(attr);
  712     }
  713 
  714     errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
  715 
  716     /*
  717     **  We check errno != LDAP_DECODING_ERROR since
  718     **  OpenLDAP 1.X has a very ugly *undocumented*
  719     **  hack of returning this error code from
  720     **  ldap_next_attribute() if the library freed the
  721     **  ber attribute.  See:
  722     **  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
  723     */
  724 
  725     if (errno != LDAP_SUCCESS &&
  726         errno != LDAP_DECODING_ERROR)
  727     {
  728         /* Must be an error */
  729         errno += E_LDAPBASE;
  730         ret = EX_TEMPFAIL;
  731         goto abort;
  732     }
  733 
  734  abort:
  735     save_errno = errno;
  736     if (attr != NULL)
  737     {
  738         ldap_memfree(attr);
  739         attr = NULL;
  740     }
  741     if (LDAPLMAP.ldap_res != NULL)
  742     {
  743         ldap_msgfree(LDAPLMAP.ldap_res);
  744         LDAPLMAP.ldap_res = NULL;
  745     }
  746     if (ret == EX_OK)
  747     {
  748         if (need == 0)
  749         {
  750             (void) sm_strlcpy(user->mbdb_name, name,
  751                       sizeof(user->mbdb_name));
  752             save_errno = 0;
  753         }
  754         else
  755         {
  756             ret = EX_NOUSER;
  757             save_errno = EINVAL;
  758         }
  759     }
  760     errno = save_errno;
  761     return ret;
  762 }
  763 
  764 /*
  765 **  MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
  766 **
  767 **  Parameters:
  768 **      none.
  769 **
  770 **  Results:
  771 **      none.
  772 */
  773 
  774 static void
  775 mbdb_ldap_terminate()
  776 {
  777     sm_ldap_close(&LDAPLMAP);
  778 }
  779 # endif /* _LDAP_EXAMPLE_ */
  780 #endif /* LDAPMAP */