[BACK]Return to ldap.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Annotation of src/usr.bin/sudo/ldap.c, Revision 1.2

1.1       millert     1: /*
                      2:  * Copyright (c) 2003-2005 Todd C. Miller <Todd.Miller@courtesan.com>
                      3:  *
                      4:  * This code is derived from software contributed by Aaron Spangler.
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <config.h>
                     20:
                     21: #include <sys/types.h>
                     22: #include <sys/time.h>
                     23: #include <sys/param.h>
                     24: #include <sys/stat.h>
                     25: #include <stdio.h>
                     26: #ifdef STDC_HEADERS
                     27: # include <stdlib.h>
                     28: # include <stddef.h>
                     29: #else
                     30: # ifdef HAVE_STDLIB_H
                     31: #  include <stdlib.h>
                     32: # endif
                     33: #endif /* STDC_HEADERS */
                     34: #ifdef HAVE_STRING_H
                     35: # include <string.h>
                     36: #else
                     37: # ifdef HAVE_STRINGS_H
                     38: #  include <strings.h>
                     39: # endif
                     40: #endif /* HAVE_STRING_H */
                     41: #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
                     42: # include <malloc.h>
                     43: #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
                     44: #ifdef HAVE_UNISTD_H
                     45: # include <unistd.h>
                     46: #endif /* HAVE_UNISTD_H */
                     47: #include <ctype.h>
                     48: #include <pwd.h>
                     49: #include <grp.h>
                     50: #include <netinet/in.h>
                     51: #include <arpa/inet.h>
                     52: #include <netdb.h>
                     53: #ifdef HAVE_ERR_H
                     54: # include <err.h>
                     55: #else
                     56: # include "emul/err.h"
                     57: #endif /* HAVE_ERR_H */
                     58: #include <errno.h>
                     59: #ifdef HAVE_LBER_H
                     60: # include <lber.h>
                     61: #endif
                     62: #include <ldap.h>
                     63:
                     64: #include "sudo.h"
                     65: #include "parse.h"
                     66:
                     67: #ifndef lint
1.2     ! millert    68: __unused static const char rcsid[] = "$Sudo: ldap.c,v 1.11.2.16 2007/09/04 14:58:46 millert Exp $";
1.1       millert    69: #endif /* lint */
                     70:
                     71: #ifndef LINE_MAX
                     72: # define LINE_MAX 2048
                     73: #endif
                     74:
                     75: #ifndef LDAP_OPT_SUCCESS
                     76: # define LDAP_OPT_SUCCESS LDAP_SUCCESS
                     77: #endif
                     78:
                     79: #if defined(LDAP_X_OPT_CONNECT_TIMEOUT) && !defined(LDAP_OPT_X_CONNECT_TIMEOUT)
                     80: #define LDAP_OPT_X_CONNECT_TIMEOUT     LDAP_OPT_X_CONNECT_TIMEOUT
                     81: #endif
                     82:
                     83: #define        DPRINTF(args, level)    if (ldap_conf.debug >= level) warnx args
                     84:
                     85: /* ldap configuration structure */
                     86: struct ldap_config {
                     87:     int port;
                     88:     int version;
                     89:     int debug;
                     90:     int tls_checkpeer;
                     91:     int timelimit;
                     92:     int bind_timelimit;
                     93:     char *host;
                     94:     char *uri;
                     95:     char *binddn;
                     96:     char *bindpw;
                     97:     char *rootbinddn;
                     98:     char *base;
                     99:     char *ssl;
                    100:     char *tls_cacertfile;
                    101:     char *tls_cacertdir;
                    102:     char *tls_random_file;
                    103:     char *tls_cipher_suite;
                    104:     char *tls_certfile;
                    105:     char *tls_keyfile;
                    106: } ldap_conf;
                    107:
                    108: static void sudo_ldap_update_defaults __P((LDAP *));
                    109: static void sudo_ldap_close __P((LDAP *));
                    110: static LDAP *sudo_ldap_open __P((void));
                    111:
                    112: /*
                    113:  * Walk through search results and return TRUE if we have a matching
                    114:  * netgroup, else FALSE.
                    115:  */
                    116: int
                    117: sudo_ldap_check_user_netgroup(ld, entry)
                    118:     LDAP *ld;
                    119:     LDAPMessage *entry;
                    120: {
                    121:     char **v = NULL, **p = NULL;
                    122:     int ret = FALSE;
                    123:
                    124:     if (!entry)
                    125:        return(ret);
                    126:
                    127:     /* get the values from the entry */
                    128:     v = ldap_get_values(ld, entry, "sudoUser");
                    129:
                    130:     /* walk through values */
                    131:     for (p = v; p && *p && !ret; p++) {
                    132:        /* match any */
                    133:        if (netgr_matches(*p, NULL, NULL, user_name))
                    134:            ret = TRUE;
                    135:        DPRINTF(("ldap sudoUser netgroup '%s' ... %s", *p,
                    136:            ret ? "MATCH!" : "not"), 2);
                    137:     }
                    138:
                    139:     if (v)
                    140:        ldap_value_free(v);     /* cleanup */
                    141:
                    142:     return(ret);
                    143: }
                    144:
                    145: /*
                    146:  * Walk through search results and return TRUE if we have a
                    147:  * host match, else FALSE.
                    148:  */
                    149: int
                    150: sudo_ldap_check_host(ld, entry)
                    151:     LDAP *ld;
                    152:     LDAPMessage *entry;
                    153: {
                    154:     char **v = NULL, **p = NULL;
                    155:     int ret = FALSE;
                    156:
                    157:     if (!entry)
                    158:        return(ret);
                    159:
                    160:     /* get the values from the entry */
                    161:     v = ldap_get_values(ld, entry, "sudoHost");
                    162:
                    163:     /* walk through values */
                    164:     for (p = v; p && *p && !ret; p++) {
                    165:        /* match any or address or netgroup or hostname */
                    166:        if (!strcasecmp(*p, "ALL") || addr_matches(*p) ||
                    167:            netgr_matches(*p, user_host, user_shost, NULL) ||
                    168:            !hostname_matches(user_shost, user_host, *p))
                    169:            ret = TRUE;
                    170:        DPRINTF(("ldap sudoHost '%s' ... %s", *p,
                    171:            ret ? "MATCH!" : "not"), 2);
                    172:     }
                    173:
                    174:     if (v)
                    175:        ldap_value_free(v);     /* cleanup */
                    176:
                    177:     return(ret);
                    178: }
                    179:
                    180: /*
                    181:  * Walk through search results and return TRUE if we have a runas match,
                    182:  * else FALSE.
                    183:  * Since the runas directive in /etc/sudoers is optional, so is sudoRunAs.
                    184:  */
                    185: int
                    186: sudo_ldap_check_runas(ld, entry)
                    187:     LDAP *ld;
                    188:     LDAPMessage *entry;
                    189: {
                    190:     char **v = NULL, **p = NULL;
                    191:     int ret = FALSE;
                    192:
                    193:     if (!entry)
                    194:        return(ret);
                    195:
                    196:     /* get the values from the entry */
                    197:     v = ldap_get_values(ld, entry, "sudoRunAs");
                    198:
                    199:     /*
                    200:      * BUG:
                    201:      *
                    202:      * if runas is not specified on the command line, the only information
                    203:      * as to which user to run as is in the runas_default option.  We should
                    204:      * check to see if we have the local option present.  Unfortunately we
                    205:      * don't parse these options until after this routine says yes or no.
                    206:      * The query has already returned, so we could peek at the attribute
                    207:      * values here though.
                    208:      *
                    209:      * For now just require users to always use -u option unless its set
                    210:      * in the global defaults. This behaviour is no different than the global
                    211:      * /etc/sudoers.
                    212:      *
                    213:      * Sigh - maybe add this feature later
                    214:      *
                    215:      */
                    216:
                    217:     /*
                    218:      * If there are no runas entries, match runas_default against
                    219:      * what the user specified on the command line.
                    220:      */
                    221:     if (!v)
1.2     ! millert   222:        ret = !strcasecmp(runas_pw->pw_name, def_runas_default);
1.1       millert   223:
                    224:     /* walk through values returned, looking for a match */
                    225:     for (p = v; p && *p && !ret; p++) {
1.2     ! millert   226:        switch (*p[0]) {
        !           227:        case '+':
        !           228:            if (netgr_matches(*p, NULL, NULL, runas_pw->pw_name))
        !           229:                ret = TRUE;
        !           230:            break;
        !           231:        case '%':
        !           232:            if (usergr_matches(*p, runas_pw->pw_name, runas_pw))
        !           233:                ret = TRUE;
        !           234:            break;
        !           235:        case 'A':
        !           236:            if (strcmp(*p, "ALL") == 0) {
        !           237:                ret = TRUE;
        !           238:                break;
        !           239:            }
        !           240:            /* FALLTHROUGH */
        !           241:        default:
        !           242:            if (strcasecmp(*p, runas_pw->pw_name) == 0)
        !           243:                ret = TRUE;
        !           244:            break;
        !           245:        }
1.1       millert   246:        DPRINTF(("ldap sudoRunAs '%s' ... %s", *p,
                    247:            ret ? "MATCH!" : "not"), 2);
                    248:     }
                    249:
                    250:     if (v)
                    251:        ldap_value_free(v);     /* cleanup */
                    252:
                    253:     return(ret);
                    254: }
                    255:
                    256: /*
                    257:  * Walk through search results and return TRUE if we have a command match.
                    258:  */
                    259: int
                    260: sudo_ldap_check_command(ld, entry)
                    261:     LDAP *ld;
                    262:     LDAPMessage *entry;
                    263: {
                    264:     char *allowed_cmnd, *allowed_args, **v = NULL, **p = NULL;
                    265:     int foundbang, ret = FALSE;
                    266:
                    267:     if (!entry)
                    268:        return(ret);
                    269:
                    270:     v = ldap_get_values(ld, entry, "sudoCommand");
                    271:
                    272:     /* get_first_entry */
                    273:     for (p = v; p && *p && ret >= 0; p++) {
                    274:        /* Match against ALL ? */
                    275:        if (!strcasecmp(*p, "ALL")) {
                    276:            ret = TRUE;
                    277:            DPRINTF(("ldap sudoCommand '%s' ... MATCH!", *p), 2);
                    278:            continue;
                    279:        }
                    280:
                    281:        /* check for !command */
                    282:        if (**p == '!') {
                    283:            foundbang = TRUE;
                    284:            allowed_cmnd = estrdup(1 + *p);     /* !command */
                    285:        } else {
                    286:            foundbang = FALSE;
                    287:            allowed_cmnd = estrdup(*p);         /* command */
                    288:        }
                    289:
                    290:        /* split optional args away from command */
                    291:        allowed_args = strchr(allowed_cmnd, ' ');
                    292:        if (allowed_args)
                    293:            *allowed_args++ = '\0';
                    294:
                    295:        /* check the command like normal */
                    296:        if (command_matches(allowed_cmnd, allowed_args)) {
                    297:            /*
                    298:             * If allowed (no bang) set ret but keep on checking.
                    299:             * If disallowed (bang), exit loop.
                    300:             */
                    301:            ret = foundbang ? -1 : TRUE;
                    302:        }
                    303:        DPRINTF(("ldap sudoCommand '%s' ... %s", *p,
                    304:            ret == TRUE ? "MATCH!" : "not"), 2);
                    305:
                    306:        efree(allowed_cmnd);    /* cleanup */
                    307:     }
                    308:
                    309:     if (v)
                    310:        ldap_value_free(v);     /* more cleanup */
                    311:
                    312:     /* return TRUE if we found at least one ALLOW and no DENY */
                    313:     return(ret > 0);
                    314: }
                    315:
                    316: /*
                    317:  * Read sudoOption and modify the defaults as we go.  This is used once
                    318:  * from the cn=defaults entry and also once when a final sudoRole is matched.
                    319:  */
                    320: void
                    321: sudo_ldap_parse_options(ld, entry)
                    322:     LDAP *ld;
                    323:     LDAPMessage *entry;
                    324: {
                    325:     char op, *var, *val, **v = NULL, **p = NULL;
                    326:
                    327:     if (!entry)
                    328:        return;
                    329:
                    330:     v = ldap_get_values(ld, entry, "sudoOption");
                    331:
                    332:     /* walk through options */
                    333:     for (p = v; p && *p; p++) {
                    334:
                    335:        DPRINTF(("ldap sudoOption: '%s'", *p), 2);
                    336:        var = estrdup(*p);
                    337:
                    338:        /* check for equals sign past first char */
                    339:        val = strchr(var, '=');
                    340:        if (val > var) {
                    341:            *val++ = '\0';      /* split on = and truncate var */
                    342:            op = *(val - 2);    /* peek for += or -= cases */
                    343:            if (op == '+' || op == '-') {
                    344:                *(val - 2) = '\0';      /* found, remove extra char */
                    345:                /* case var+=val or var-=val */
                    346:                set_default(var, val, (int) op);
                    347:            } else {
                    348:                /* case var=val */
                    349:                set_default(var, val, TRUE);
                    350:            }
                    351:        } else if (*var == '!') {
                    352:            /* case !var Boolean False */
                    353:            set_default(var + 1, NULL, FALSE);
                    354:        } else {
                    355:            /* case var Boolean True */
                    356:            set_default(var, NULL, TRUE);
                    357:        }
                    358:        efree(var);
                    359:     }
                    360:
                    361:     if (v)
                    362:        ldap_value_free(v);
                    363: }
                    364:
                    365: /*
                    366:  * Concatenate strings, dynamically growing them as necessary.
                    367:  * Strings can be arbitrarily long and are allocated/reallocated on
                    368:  * the fly.  Make sure to free them when you are done.
                    369:  *
                    370:  * Usage:
                    371:  *
                    372:  * char *s=NULL;
                    373:  * size_t sz;
                    374:  *
                    375:  * ncat(&s,&sz,"This ");
                    376:  * ncat(&s,&sz,"is ");
                    377:  * ncat(&s,&sz,"an ");
                    378:  * ncat(&s,&sz,"arbitrarily ");
                    379:  * ncat(&s,&sz,"long ");
                    380:  * ncat(&s,&sz,"string!");
                    381:  *
                    382:  * printf("String Value='%s', but has %d bytes allocated\n",s,sz);
                    383:  *
                    384:  */
                    385: void
                    386: ncat(s, sz, src)
                    387:     char **s;
                    388:     size_t *sz;
                    389:     char *src;
                    390: {
                    391:     size_t nsz;
                    392:
                    393:     /* handle initial alloc */
                    394:     if (*s == NULL) {
                    395:        *s = estrdup(src);
                    396:        *sz = strlen(src) + 1;
                    397:        return;
                    398:     }
                    399:     /* handle realloc */
                    400:     nsz = strlen(*s) + strlen(src) + 1;
                    401:     if (*sz < nsz)
                    402:        *s = erealloc((void *) *s, *sz = nsz * 2);
                    403:     strlcat(*s, src, *sz);
                    404: }
                    405:
                    406: /*
                    407:  * builds together a filter to check against ldap
                    408:  */
                    409: char *
                    410: sudo_ldap_build_pass1()
                    411: {
                    412:     struct group *grp;
                    413:     size_t sz;
                    414:     char *b = NULL;
                    415:     int i;
                    416:
                    417:     /* global OR */
                    418:     ncat(&b, &sz, "(|");
                    419:
                    420:     /* build filter sudoUser=user_name */
                    421:     ncat(&b, &sz, "(sudoUser=");
                    422:     ncat(&b, &sz, user_name);
                    423:     ncat(&b, &sz, ")");
                    424:
                    425:     /* Append primary group */
                    426:     grp = getgrgid(user_gid);
                    427:     if (grp != NULL) {
                    428:        ncat(&b, &sz, "(sudoUser=%");
                    429:        ncat(&b, &sz, grp -> gr_name);
                    430:        ncat(&b, &sz, ")");
                    431:     }
                    432:
                    433:     /* Append supplementary groups */
                    434:     for (i = 0; i < user_ngroups; i++) {
                    435:        if ((grp = getgrgid(user_groups[i])) != NULL) {
                    436:            ncat(&b, &sz, "(sudoUser=%");
                    437:            ncat(&b, &sz, grp -> gr_name);
                    438:            ncat(&b, &sz, ")");
                    439:        }
                    440:     }
                    441:
                    442:     /* Add ALL to list */
                    443:     ncat(&b, &sz, "(sudoUser=ALL)");
                    444:
                    445:     /* End of OR List */
                    446:     ncat(&b, &sz, ")");
                    447:
                    448:     return(b);
                    449: }
                    450:
                    451: /*
                    452:  * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
                    453:  */
                    454: int
                    455: _atobool(s)
                    456:     const char *s;
                    457: {
                    458:     switch (*s) {
                    459:        case 'y':
                    460:        case 'Y':
                    461:            if (strcasecmp(s, "yes") == 0)
                    462:                return(TRUE);
                    463:            break;
                    464:        case 't':
                    465:        case 'T':
                    466:            if (strcasecmp(s, "true") == 0)
                    467:                return(TRUE);
                    468:            break;
                    469:        case 'o':
                    470:        case 'O':
                    471:            if (strcasecmp(s, "on") == 0)
                    472:                return(TRUE);
                    473:            if (strcasecmp(s, "off") == 0)
                    474:                return(FALSE);
                    475:            break;
                    476:        case 'n':
                    477:        case 'N':
                    478:            if (strcasecmp(s, "no") == 0)
                    479:                return(FALSE);
                    480:            break;
                    481:        case 'f':
                    482:        case 'F':
                    483:            if (strcasecmp(s, "false") == 0)
                    484:                return(FALSE);
                    485:            break;
                    486:     }
                    487:     return(-1);
                    488: }
                    489:
                    490: int
                    491: sudo_ldap_read_config()
                    492: {
                    493:     FILE *f;
                    494:     char buf[LINE_MAX], *c, *keyword, *value;
                    495:
                    496:     /* defaults */
                    497:     ldap_conf.version = 3;
                    498:     ldap_conf.port = 389;
                    499:     ldap_conf.tls_checkpeer = -1;
                    500:     ldap_conf.timelimit = -1;
                    501:     ldap_conf.bind_timelimit = -1;
                    502:
                    503:     if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL)
                    504:        return(FALSE);
                    505:     while (fgets(buf, sizeof(buf), f)) {
                    506:        /* ignore text after comment character */
                    507:        if ((c = strchr(buf, '#')) != NULL)
                    508:            *c = '\0';
                    509:
                    510:        /* skip leading whitespace */
                    511:        for (c = buf; isspace((unsigned char) *c); c++)
                    512:            /* nothing */;
                    513:
                    514:        if (*c == '\0' || *c == '\n')
                    515:            continue;           /* skip empty line */
                    516:
                    517:        /* properly terminate keyword string */
                    518:        keyword = c;
                    519:        while (*c && !isspace((unsigned char) *c))
                    520:            c++;
                    521:        if (*c)
                    522:            *c++ = '\0';        /* terminate keyword */
                    523:
                    524:        /* skip whitespace before value */
                    525:        while (isspace((unsigned char) *c))
                    526:            c++;
                    527:        value = c;
                    528:
                    529:        /* trim whitespace after value */
                    530:        while (*c)
                    531:            c++;                /* wind to end */
                    532:        while (--c > value && isspace((unsigned char) *c))
                    533:            *c = '\0';
                    534:
                    535:        /* The following macros make the code much more readable */
                    536:
                    537: #define MATCH_S(x,y) if (!strcasecmp(keyword,x)) \
                    538:     { efree(y); y=estrdup(value); }
                    539: #define MATCH_I(x,y) if (!strcasecmp(keyword,x)) { y=atoi(value); }
                    540: #define MATCH_B(x,y) if (!strcasecmp(keyword,x)) { y=_atobool(value); }
                    541:
                    542:        /*
                    543:         * Parse values using a continues chain of if else if else if else if
                    544:         * else ...
                    545:         */
                    546:        MATCH_S("host", ldap_conf.host)
                    547:            else
                    548:        MATCH_I("port", ldap_conf.port)
                    549:            else
                    550:        MATCH_S("ssl", ldap_conf.ssl)
                    551:            else
                    552:        MATCH_B("tls_checkpeer", ldap_conf.tls_checkpeer)
                    553:            else
                    554:        MATCH_S("tls_cacertfile", ldap_conf.tls_cacertfile)
                    555:            else
                    556:        MATCH_S("tls_cacertdir", ldap_conf.tls_cacertdir)
                    557:            else
                    558:        MATCH_S("tls_randfile", ldap_conf.tls_random_file)
                    559:            else
                    560:        MATCH_S("tls_ciphers", ldap_conf.tls_cipher_suite)
                    561:            else
                    562:        MATCH_S("tls_cert", ldap_conf.tls_certfile)
                    563:            else
                    564:        MATCH_S("tls_key", ldap_conf.tls_keyfile)
                    565:            else
                    566:        MATCH_I("ldap_version", ldap_conf.version)
                    567:            else
                    568:        MATCH_I("bind_timelimit", ldap_conf.bind_timelimit)
                    569:            else
                    570:        MATCH_I("timelimit", ldap_conf.timelimit)
                    571:            else
                    572:        MATCH_S("uri", ldap_conf.uri)
                    573:            else
                    574:        MATCH_S("binddn", ldap_conf.binddn)
                    575:            else
                    576:        MATCH_S("bindpw", ldap_conf.bindpw)
                    577:            else
                    578:        MATCH_S("rootbinddn", ldap_conf.rootbinddn)
                    579:            else
                    580:        MATCH_S("sudoers_base", ldap_conf.base)
                    581:            else
                    582:        MATCH_I("sudoers_debug", ldap_conf.debug)
                    583:            else {
                    584:
                    585:            /*
                    586:             * The keyword was unrecognized.  Since this config file is
                    587:             * shared by multiple programs, it is appropriate to silently
                    588:             * ignore options this program does not understand
                    589:             */
                    590:        }
                    591:
                    592:     }
                    593:     fclose(f);
                    594:
                    595:     if (!ldap_conf.host)
                    596:        ldap_conf.host = estrdup("localhost");
                    597:
                    598:     if (ldap_conf.bind_timelimit > 0)
                    599:        ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
                    600:
                    601:     if (ldap_conf.debug > 1) {
                    602:        fprintf(stderr, "LDAP Config Summary\n");
                    603:        fprintf(stderr, "===================\n");
                    604: #ifdef HAVE_LDAP_INITIALIZE
                    605:        if (ldap_conf.uri) {
                    606:            fprintf(stderr, "uri          %s\n", ldap_conf.uri);
                    607:        } else
                    608: #endif
                    609:        {
                    610:            fprintf(stderr, "host         %s\n", ldap_conf.host ?
                    611:                ldap_conf.host : "(NONE)");
                    612:            fprintf(stderr, "port         %d\n", ldap_conf.port);
                    613:        }
                    614:        fprintf(stderr, "ldap_version %d\n", ldap_conf.version);
                    615:
                    616:        fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ?
                    617:            ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
                    618:        fprintf(stderr, "binddn       %s\n", ldap_conf.binddn ?
                    619:            ldap_conf.binddn : "(anonymous)");
                    620:        fprintf(stderr, "bindpw       %s\n", ldap_conf.bindpw ?
                    621:            ldap_conf.bindpw : "(anonymous)");
                    622:        fprintf(stderr, "bind_timelimit  %d\n", ldap_conf.bind_timelimit);
                    623:        fprintf(stderr, "timelimit    %d\n", ldap_conf.timelimit);
                    624: #ifdef HAVE_LDAP_START_TLS_S
                    625:        fprintf(stderr, "ssl          %s\n", ldap_conf.ssl ?
                    626:            ldap_conf.ssl : "(no)");
                    627: #endif
                    628:        fprintf(stderr, "===================\n");
                    629:     }
                    630:     if (!ldap_conf.base)
                    631:        return(FALSE);          /* if no base is defined, ignore LDAP */
                    632:
                    633:     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
                    634:     if (ldap_conf.rootbinddn) {
                    635:        if ((f = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
                    636:            if (fgets(buf, sizeof(buf), f) != NULL) {
                    637:                /* removing trailing newlines */
                    638:                for (c = buf; *c != '\0'; c++)
                    639:                    continue;
                    640:                while (--c > buf && *c == '\n')
                    641:                    *c = '\0';
                    642:                /* copy to bindpw and binddn */
                    643:                efree(ldap_conf.bindpw);
                    644:                ldap_conf.bindpw = estrdup(buf);
                    645:                efree(ldap_conf.binddn);
                    646:                ldap_conf.binddn = ldap_conf.rootbinddn;
                    647:                ldap_conf.rootbinddn = NULL;
                    648:            }
                    649:            fclose(f);
                    650:        }
                    651:     }
                    652:     return(TRUE);
                    653: }
                    654:
                    655: /*
                    656:  * like perl's join(sep,@ARGS)
                    657:  */
                    658: char *
                    659:  _ldap_join_values(sep, v)
                    660:     char *sep;
                    661:     char **v;
                    662: {
                    663:     char *b = NULL, **p = NULL;
                    664:     size_t sz = 0;
                    665:
                    666:     /* paste values together */
                    667:     for (p = v; p && *p; p++) {
                    668:        if (p != v && sep != NULL)
                    669:            ncat(&b, &sz, sep); /* append seperator */
                    670:        ncat(&b, &sz, *p);      /* append value */
                    671:     }
                    672:
                    673:     /* sanity check */
                    674:     if (b[0] == '\0') {
                    675:        /* something went wrong, put something here */
                    676:        ncat(&b, &sz, "(empty list)");  /* append value */
                    677:     }
                    678:
                    679:     return(b);
                    680: }
                    681:
                    682: char *sudo_ldap_cm_list = NULL;
                    683: size_t sudo_ldap_cm_list_size;
                    684:
                    685: #define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
                    686: /*
                    687:  * Walks through search result and returns TRUE if we have a
                    688:  * command match
                    689:  */
                    690: int
                    691: sudo_ldap_add_match(ld, entry, pwflag)
                    692:     LDAP *ld;
                    693:     LDAPMessage *entry;
                    694:     int pwflag;
                    695: {
                    696:     char *dn, **edn, **v = NULL;
                    697:
                    698:     /* if we are not collecting matches, then don't save them */
                    699:     if (pwflag != I_LISTPW)
                    700:        return(TRUE);
                    701:
                    702:     /* collect the dn, only show the rdn */
                    703:     dn = ldap_get_dn(ld, entry);
                    704:     edn = dn ? ldap_explode_dn(dn, 1) : NULL;
                    705:     SAVE_LIST("\nLDAP Role: ");
                    706:     SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
                    707:     SAVE_LIST("\n");
                    708:     if (dn)
                    709:        ldap_memfree(dn);
                    710:     if (edn)
                    711:        ldap_value_free(edn);
                    712:
                    713:     /* get the Runas Values from the entry */
                    714:     v = ldap_get_values(ld, entry, "sudoRunAs");
                    715:     if (v && *v) {
                    716:        SAVE_LIST("  RunAs: (");
                    717:        SAVE_LIST(_ldap_join_values(", ", v));
                    718:        SAVE_LIST(")\n");
                    719:     }
                    720:     if (v)
                    721:        ldap_value_free(v);
                    722:
                    723:     /* get the Command Values from the entry */
                    724:     v = ldap_get_values(ld, entry, "sudoCommand");
                    725:     if (v && *v) {
                    726:        SAVE_LIST("  Commands:\n    ");
                    727:        SAVE_LIST(_ldap_join_values("\n    ", v));
                    728:        SAVE_LIST("\n");
                    729:     } else {
                    730:        SAVE_LIST("  Commands: NONE\n");
                    731:     }
                    732:     if (v)
                    733:        ldap_value_free(v);
                    734:
                    735:     return(FALSE);             /* Don't stop at the first match */
                    736: }
                    737: #undef SAVE_LIST
                    738:
                    739: void
                    740: sudo_ldap_list_matches()
                    741: {
                    742:     if (sudo_ldap_cm_list != NULL)
                    743:        printf("%s", sudo_ldap_cm_list);
                    744: }
                    745:
                    746: /* macros to set option, error on failure plus consistent debugging */
                    747: #define SET_OPTS(opt, val) do { \
                    748:     if (ldap_conf.val != NULL) { \
                    749:        if (ldap_conf.debug > 1) \
                    750:            fprintf(stderr, \
                    751:                "ldap_set_option(LDAP_OPT_%s, \"%s\")\n", #opt, ldap_conf.val);\
                    752:        rc = ldap_set_option(ld, LDAP_OPT_ ## opt, ldap_conf.val); \
                    753:        if (rc != LDAP_OPT_SUCCESS) { \
                    754:            fprintf(stderr,"ldap_set_option(LDAP_OPT_%s, \"%s\")=%d: %s\n", \
                    755:                #opt, ldap_conf.val, rc, ldap_err2string(rc)); \
                    756:            return(NULL); \
                    757:        } \
                    758:     } \
                    759: } while(0)
                    760: #define SET_OPTI(opt, val) do { \
                    761:     if (ldap_conf.val >= 0) { \
                    762:        if (ldap_conf.debug > 1) \
                    763:            fprintf(stderr, \
                    764:                "ldap_set_option(LDAP_OPT_%s, %d)\n", #opt, ldap_conf.val); \
                    765:        rc = ldap_set_option(ld, LDAP_OPT_ ## opt, &ldap_conf.val); \
                    766:        if (rc != LDAP_OPT_SUCCESS) { \
                    767:            fprintf(stderr,"ldap_set_option(LDAP_OPT_%s, %d)=%d: %s\n", \
                    768:                #opt, ldap_conf.val, rc, ldap_err2string(rc)); \
                    769:            return(NULL); \
                    770:        } \
                    771:     } \
                    772: } while(0)
                    773:
                    774: /*
                    775:  * Open a connection to the LDAP server.
                    776:  */
                    777: static LDAP *
                    778: sudo_ldap_open()
                    779: {
                    780:     LDAP *ld = NULL;
                    781:     int rc;
                    782:
                    783:     if (!sudo_ldap_read_config())
                    784:        return(NULL);
                    785:
                    786:     /* attempt to setup ssl options */
                    787: #ifdef LDAP_OPT_X_TLS_CACERTFILE
                    788:     SET_OPTS(X_TLS_CACERTFILE, tls_cacertfile);
                    789: #endif /* LDAP_OPT_X_TLS_CACERTFILE */
                    790:
                    791: #ifdef LDAP_OPT_X_TLS_CACERTDIR
                    792:     SET_OPTS(X_TLS_CACERTDIR, tls_cacertdir);
                    793: #endif /* LDAP_OPT_X_TLS_CACERTDIR */
                    794:
                    795: #ifdef LDAP_OPT_X_TLS_CERTFILE
                    796:     SET_OPTS(X_TLS_CERTFILE, tls_certfile);
                    797: #endif /* LDAP_OPT_X_TLS_CERTFILE */
                    798:
                    799: #ifdef LDAP_OPT_X_TLS_KEYFILE
                    800:     SET_OPTS(X_TLS_KEYFILE, tls_keyfile);
                    801: #endif /* LDAP_OPT_X_TLS_KEYFILE */
                    802:
                    803: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
                    804:     SET_OPTS(X_TLS_CIPHER_SUITE, tls_cipher_suite);
                    805: #endif /* LDAP_OPT_X_TLS_CIPHER_SUITE */
                    806:
                    807: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
                    808:     SET_OPTS(X_TLS_RANDOM_FILE, tls_random_file);
                    809: #endif /* LDAP_OPT_X_TLS_RANDOM_FILE */
                    810:
                    811: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
                    812:     /* check the server certificate? */
                    813:     SET_OPTI(X_TLS_REQUIRE_CERT, tls_checkpeer);
                    814: #endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
                    815:
                    816:     /* set timelimit options */
                    817:     SET_OPTI(TIMELIMIT, timelimit);
                    818:
                    819: #ifdef LDAP_OPT_NETWORK_TIMEOUT
                    820:     if (ldap_conf.bind_timelimit > 0) {
                    821:        struct timeval tv;
                    822:        tv.tv_sec = ldap_conf.bind_timelimit / 1000;
                    823:        tv.tv_usec = 0;
                    824:        if (ldap_conf.debug > 1)
                    825:            fprintf(stderr, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)\n",
                    826:            tv.tv_sec);
                    827:        rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
                    828:        if (rc != LDAP_OPT_SUCCESS) {
                    829:            fprintf(stderr,"ldap_set_option(NETWORK_TIMEOUT, %ld)=%d: %s\n",
                    830:            tv.tv_sec, rc, ldap_err2string(rc));
                    831:            return(NULL);
                    832:        }
                    833:     }
                    834: #endif
                    835:
                    836:     /* attempt connect */
                    837: #ifdef HAVE_LDAP_INITIALIZE
                    838:     if (ldap_conf.uri) {
                    839:
                    840:        DPRINTF(("ldap_initialize(ld,%s)", ldap_conf.uri), 2);
                    841:
                    842:        rc = ldap_initialize(&ld, ldap_conf.uri);
                    843:        if (rc) {
                    844:            fprintf(stderr, "ldap_initialize()=%d : %s\n",
                    845:                rc, ldap_err2string(rc));
                    846:            return(NULL);
                    847:        }
                    848:     } else
                    849: #endif /* HAVE_LDAP_INITIALIZE */
                    850:     if (ldap_conf.host) {
                    851:
                    852:        DPRINTF(("ldap_init(%s,%d)", ldap_conf.host, ldap_conf.port), 2);
                    853:
                    854:        if ((ld = ldap_init(ldap_conf.host, ldap_conf.port)) == NULL) {
                    855:            fprintf(stderr, "ldap_init(): errno=%d : %s\n",
                    856:                errno, strerror(errno));
                    857:            return(NULL);
                    858:        }
                    859:     }
                    860: #ifdef LDAP_OPT_PROTOCOL_VERSION
                    861:
                    862:     /* Set the LDAP Protocol version */
                    863:     SET_OPTI(PROTOCOL_VERSION, version);
                    864:
                    865: #endif /* LDAP_OPT_PROTOCOL_VERSION */
                    866:
                    867: #ifdef HAVE_LDAP_START_TLS_S
                    868:     /* Turn on TLS */
                    869:     if (ldap_conf.ssl && !strcasecmp(ldap_conf.ssl, "start_tls")) {
                    870:        rc = ldap_start_tls_s(ld, NULL, NULL);
                    871:        if (rc != LDAP_SUCCESS) {
                    872:            fprintf(stderr, "ldap_start_tls_s(): %d: %s\n", rc,
                    873:                ldap_err2string(rc));
                    874:            ldap_unbind(ld);
                    875:            return(NULL);
                    876:        }
                    877:        DPRINTF(("ldap_start_tls_s() ok"), 1);
                    878:     }
                    879: #endif /* HAVE_LDAP_START_TLS_S */
                    880:
                    881:     /* Actually connect */
                    882:     if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) {
                    883:        fprintf(stderr, "ldap_simple_bind_s()=%d : %s\n",
                    884:            rc, ldap_err2string(rc));
                    885:        return(NULL);
                    886:     }
                    887:     DPRINTF(("ldap_bind() ok"), 1);
                    888:
                    889:     return(ld);
                    890: }
                    891:
                    892: static void
                    893: sudo_ldap_update_defaults(ld)
                    894:     LDAP *ld;
                    895: {
                    896:     LDAPMessage *entry = NULL, *result = NULL;  /* used for searches */
                    897:     int rc;                                     /* temp return value */
                    898:
                    899:     rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
                    900:        "cn=defaults", NULL, 0, &result);
                    901:     if (!rc && (entry = ldap_first_entry(ld, result))) {
                    902:        DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                    903:        sudo_ldap_parse_options(ld, entry);
                    904:     } else
                    905:        DPRINTF(("no default options found!"), 1);
                    906:
                    907:     if (result)
                    908:        ldap_msgfree(result);
                    909: }
                    910:
                    911: /*
                    912:  * like sudoers_lookup() - only LDAP style
                    913:  */
                    914: int
                    915: sudo_ldap_check(pwflag)
                    916:     int pwflag;
                    917: {
                    918:     LDAP *ld;
                    919:     LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
                    920:     char *filt;                                        /* used to parse attributes */
                    921:     int rc, ret = FALSE, do_netgr;             /* temp/final return values */
                    922:     int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */
                    923:
                    924:     /* Open a connection to the LDAP server. */
                    925:     if ((ld = sudo_ldap_open()) == NULL)
                    926:        return(VALIDATE_ERROR);
                    927:
                    928:     /* Parse Default options. */
                    929:     sudo_ldap_update_defaults(ld);
                    930:
                    931:     /*
                    932:      * Okay - time to search for anything that matches this user
                    933:      * Lets limit it to only two queries of the LDAP server
                    934:      *
                    935:      * The first pass will look by the username, groups, and
                    936:      * the keyword ALL.  We will then inspect the results that
                    937:      * came back from the query.  We don't need to inspect the
                    938:      * sudoUser in this pass since the LDAP server already scanned
                    939:      * it for us.
                    940:      *
                    941:      * The second pass will return all the entries that contain
                    942:      * user netgroups.  Then we take the netgroups returned and
                    943:      * try to match them against the username.
                    944:      */
                    945:
                    946:     for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
                    947:        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1();
                    948:        DPRINTF(("ldap search '%s'", filt), 1);
                    949:        rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
                    950:            NULL, 0, &result);
                    951:        if (rc)
                    952:            DPRINTF(("nothing found for '%s'", filt), 1);
                    953:        efree(filt);
                    954:
                    955:        /* parse each entry returned from this most recent search */
                    956:        entry = rc ? NULL : ldap_first_entry(ld, result);
                    957:        while (entry != NULL) {
                    958:            DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                    959:            if (
                    960:            /* first verify user netgroup matches - only if in pass 2 */
                    961:                (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) &&
                    962:            /* remember that user matched */
                    963:                (ldap_user_matches = -1) &&
                    964:            /* verify host match */
                    965:                sudo_ldap_check_host(ld, entry) &&
                    966:            /* remember that host matched */
                    967:                (ldap_host_matches = -1) &&
                    968:            /* add matches for listing later */
                    969:                sudo_ldap_add_match(ld, entry, pwflag) &&
                    970:            /* verify command match */
                    971:                sudo_ldap_check_command(ld, entry) &&
                    972:            /* verify runas match */
                    973:                sudo_ldap_check_runas(ld, entry)
                    974:                ) {
                    975:                /* We have a match! */
                    976:                DPRINTF(("Perfect Matched!"), 1);
                    977:                /* pick up any options */
                    978:                sudo_ldap_parse_options(ld, entry);
                    979:                /* make sure we don't reenter loop */
                    980:                ret = VALIDATE_OK;
                    981:                /* break from inside for loop */
                    982:                break;
                    983:            }
                    984:            entry = ldap_next_entry(ld, entry);
                    985:        }
                    986:        if (result)
                    987:            ldap_msgfree(result);
                    988:        result = NULL;
                    989:     }
                    990:
                    991:     sudo_ldap_close(ld);               /* shut down connection */
                    992:
                    993:     DPRINTF(("user_matches=%d", ldap_user_matches), 1);
                    994:     DPRINTF(("host_matches=%d", ldap_host_matches), 1);
                    995:
                    996:     /* Check for special case for -v, -k, -l options */
                    997:     if (pwflag && ldap_user_matches && ldap_host_matches) {
                    998:        /*
                    999:          * Handle verifypw & listpw
                   1000:          *
                   1001:          * To be extra paranoid, since we haven't read any NOPASSWD options
                   1002:          * in /etc/sudoers yet, but we have to make the decission now, lets
                   1003:          * assume the worst and prefer to prompt for password unless the setting
                   1004:          * is "never". (example verifypw=never or listpw=never)
                   1005:          *
                   1006:          */
                   1007:        ret = VALIDATE_OK;
                   1008:        if (pwflag == -1) {
                   1009:            SET(ret, FLAG_NOPASS);              /* -k or -K */
                   1010:        } else {
                   1011:            switch (sudo_defs_table[pwflag].sd_un.tuple) {
                   1012:            case never:
                   1013:                SET(ret, FLAG_NOPASS);
                   1014:                break;
                   1015:            case always:
                   1016:                if (def_authenticate)
                   1017:                    SET(ret, FLAG_CHECK_USER);
                   1018:                break;
                   1019:            default:
                   1020:                break;
                   1021:            }
                   1022:        }
                   1023:     }
                   1024:     if (ISSET(ret, VALIDATE_OK)) {
                   1025:        /* we have a match, should we check the password? */
                   1026:        if (!def_authenticate)
                   1027:            SET(ret, FLAG_NOPASS);
                   1028:        if (def_noexec)
                   1029:            SET(ret, FLAG_NOEXEC);
                   1030:        if (def_setenv)
                   1031:            SET(ret, FLAG_SETENV);
                   1032:     } else {
                   1033:        /* we do not have a match */
                   1034:        ret = VALIDATE_NOT_OK;
                   1035:        if (pwflag)
                   1036:            SET(ret, FLAG_NO_CHECK);
                   1037:        else if (!ldap_user_matches)
                   1038:            SET(ret, FLAG_NO_USER);
                   1039:        else if (!ldap_host_matches)
                   1040:            SET(ret, FLAG_NO_HOST);
                   1041:     }
                   1042:     DPRINTF(("sudo_ldap_check(%d)=0x%02x", pwflag, ret), 1);
                   1043:
                   1044:     return(ret);
                   1045: }
                   1046:
                   1047: /*
                   1048:  * shut down LDAP connection
                   1049:  */
                   1050: static void
                   1051: sudo_ldap_close(LDAP *ld)
                   1052: {
                   1053:     if (ld)
                   1054:        ldap_unbind_s(ld);
                   1055: }