[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.4

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.4     ! millert    68: __unused static const char rcsid[] = "$Sudo: ldap.c,v 1.11.2.20 2007/11/27 17:06:54 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 */
1.4     ! millert   166:        if (!strcmp(*p, "ALL") || addr_matches(*p) ||
1.1       millert   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
1.4     ! millert   260: sudo_ldap_check_command(ld, entry, setenv_implied)
1.1       millert   261:     LDAP *ld;
                    262:     LDAPMessage *entry;
1.4     ! millert   263:     int *setenv_implied;
1.1       millert   264: {
                    265:     char *allowed_cmnd, *allowed_args, **v = NULL, **p = NULL;
                    266:     int foundbang, ret = FALSE;
                    267:
                    268:     if (!entry)
                    269:        return(ret);
                    270:
                    271:     v = ldap_get_values(ld, entry, "sudoCommand");
                    272:
                    273:     /* get_first_entry */
                    274:     for (p = v; p && *p && ret >= 0; p++) {
                    275:        /* Match against ALL ? */
1.4     ! millert   276:        if (!strcmp(*p, "ALL")) {
1.1       millert   277:            ret = TRUE;
1.4     ! millert   278:            if (setenv_implied != NULL)
        !           279:                *setenv_implied = TRUE;
1.1       millert   280:            DPRINTF(("ldap sudoCommand '%s' ... MATCH!", *p), 2);
                    281:            continue;
                    282:        }
                    283:
                    284:        /* check for !command */
                    285:        if (**p == '!') {
                    286:            foundbang = TRUE;
                    287:            allowed_cmnd = estrdup(1 + *p);     /* !command */
                    288:        } else {
                    289:            foundbang = FALSE;
                    290:            allowed_cmnd = estrdup(*p);         /* command */
                    291:        }
                    292:
                    293:        /* split optional args away from command */
                    294:        allowed_args = strchr(allowed_cmnd, ' ');
                    295:        if (allowed_args)
                    296:            *allowed_args++ = '\0';
                    297:
                    298:        /* check the command like normal */
                    299:        if (command_matches(allowed_cmnd, allowed_args)) {
                    300:            /*
                    301:             * If allowed (no bang) set ret but keep on checking.
                    302:             * If disallowed (bang), exit loop.
                    303:             */
                    304:            ret = foundbang ? -1 : TRUE;
                    305:        }
                    306:        DPRINTF(("ldap sudoCommand '%s' ... %s", *p,
                    307:            ret == TRUE ? "MATCH!" : "not"), 2);
                    308:
                    309:        efree(allowed_cmnd);    /* cleanup */
                    310:     }
                    311:
                    312:     if (v)
                    313:        ldap_value_free(v);     /* more cleanup */
                    314:
                    315:     /* return TRUE if we found at least one ALLOW and no DENY */
                    316:     return(ret > 0);
                    317: }
                    318:
                    319: /*
                    320:  * Read sudoOption and modify the defaults as we go.  This is used once
                    321:  * from the cn=defaults entry and also once when a final sudoRole is matched.
                    322:  */
                    323: void
                    324: sudo_ldap_parse_options(ld, entry)
                    325:     LDAP *ld;
                    326:     LDAPMessage *entry;
                    327: {
                    328:     char op, *var, *val, **v = NULL, **p = NULL;
                    329:
                    330:     if (!entry)
                    331:        return;
                    332:
                    333:     v = ldap_get_values(ld, entry, "sudoOption");
                    334:
                    335:     /* walk through options */
                    336:     for (p = v; p && *p; p++) {
                    337:
                    338:        DPRINTF(("ldap sudoOption: '%s'", *p), 2);
                    339:        var = estrdup(*p);
                    340:
                    341:        /* check for equals sign past first char */
                    342:        val = strchr(var, '=');
                    343:        if (val > var) {
                    344:            *val++ = '\0';      /* split on = and truncate var */
                    345:            op = *(val - 2);    /* peek for += or -= cases */
                    346:            if (op == '+' || op == '-') {
                    347:                *(val - 2) = '\0';      /* found, remove extra char */
                    348:                /* case var+=val or var-=val */
                    349:                set_default(var, val, (int) op);
                    350:            } else {
                    351:                /* case var=val */
                    352:                set_default(var, val, TRUE);
                    353:            }
                    354:        } else if (*var == '!') {
                    355:            /* case !var Boolean False */
                    356:            set_default(var + 1, NULL, FALSE);
                    357:        } else {
                    358:            /* case var Boolean True */
                    359:            set_default(var, NULL, TRUE);
                    360:        }
                    361:        efree(var);
                    362:     }
                    363:
                    364:     if (v)
                    365:        ldap_value_free(v);
                    366: }
                    367:
                    368: /*
                    369:  * Concatenate strings, dynamically growing them as necessary.
                    370:  * Strings can be arbitrarily long and are allocated/reallocated on
                    371:  * the fly.  Make sure to free them when you are done.
                    372:  *
                    373:  * Usage:
                    374:  *
                    375:  * char *s=NULL;
                    376:  * size_t sz;
                    377:  *
                    378:  * ncat(&s,&sz,"This ");
                    379:  * ncat(&s,&sz,"is ");
                    380:  * ncat(&s,&sz,"an ");
                    381:  * ncat(&s,&sz,"arbitrarily ");
                    382:  * ncat(&s,&sz,"long ");
                    383:  * ncat(&s,&sz,"string!");
                    384:  *
                    385:  * printf("String Value='%s', but has %d bytes allocated\n",s,sz);
                    386:  *
                    387:  */
                    388: void
                    389: ncat(s, sz, src)
                    390:     char **s;
                    391:     size_t *sz;
                    392:     char *src;
                    393: {
                    394:     size_t nsz;
                    395:
                    396:     /* handle initial alloc */
                    397:     if (*s == NULL) {
                    398:        *s = estrdup(src);
                    399:        *sz = strlen(src) + 1;
                    400:        return;
                    401:     }
                    402:     /* handle realloc */
                    403:     nsz = strlen(*s) + strlen(src) + 1;
                    404:     if (*sz < nsz)
                    405:        *s = erealloc((void *) *s, *sz = nsz * 2);
                    406:     strlcat(*s, src, *sz);
                    407: }
                    408:
                    409: /*
                    410:  * builds together a filter to check against ldap
                    411:  */
                    412: char *
                    413: sudo_ldap_build_pass1()
                    414: {
                    415:     struct group *grp;
                    416:     size_t sz;
                    417:     char *b = NULL;
                    418:     int i;
                    419:
                    420:     /* global OR */
                    421:     ncat(&b, &sz, "(|");
                    422:
                    423:     /* build filter sudoUser=user_name */
                    424:     ncat(&b, &sz, "(sudoUser=");
                    425:     ncat(&b, &sz, user_name);
                    426:     ncat(&b, &sz, ")");
                    427:
                    428:     /* Append primary group */
                    429:     grp = getgrgid(user_gid);
                    430:     if (grp != NULL) {
                    431:        ncat(&b, &sz, "(sudoUser=%");
                    432:        ncat(&b, &sz, grp -> gr_name);
                    433:        ncat(&b, &sz, ")");
                    434:     }
                    435:
                    436:     /* Append supplementary groups */
                    437:     for (i = 0; i < user_ngroups; i++) {
                    438:        if ((grp = getgrgid(user_groups[i])) != NULL) {
                    439:            ncat(&b, &sz, "(sudoUser=%");
                    440:            ncat(&b, &sz, grp -> gr_name);
                    441:            ncat(&b, &sz, ")");
                    442:        }
                    443:     }
                    444:
                    445:     /* Add ALL to list */
                    446:     ncat(&b, &sz, "(sudoUser=ALL)");
                    447:
                    448:     /* End of OR List */
                    449:     ncat(&b, &sz, ")");
                    450:
                    451:     return(b);
                    452: }
                    453:
                    454: /*
                    455:  * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
                    456:  */
                    457: int
                    458: _atobool(s)
                    459:     const char *s;
                    460: {
                    461:     switch (*s) {
                    462:        case 'y':
                    463:        case 'Y':
                    464:            if (strcasecmp(s, "yes") == 0)
                    465:                return(TRUE);
                    466:            break;
                    467:        case 't':
                    468:        case 'T':
                    469:            if (strcasecmp(s, "true") == 0)
                    470:                return(TRUE);
                    471:            break;
                    472:        case 'o':
                    473:        case 'O':
                    474:            if (strcasecmp(s, "on") == 0)
                    475:                return(TRUE);
                    476:            if (strcasecmp(s, "off") == 0)
                    477:                return(FALSE);
                    478:            break;
                    479:        case 'n':
                    480:        case 'N':
                    481:            if (strcasecmp(s, "no") == 0)
                    482:                return(FALSE);
                    483:            break;
                    484:        case 'f':
                    485:        case 'F':
                    486:            if (strcasecmp(s, "false") == 0)
                    487:                return(FALSE);
                    488:            break;
                    489:     }
                    490:     return(-1);
                    491: }
                    492:
                    493: int
                    494: sudo_ldap_read_config()
                    495: {
                    496:     FILE *f;
                    497:     char buf[LINE_MAX], *c, *keyword, *value;
                    498:
                    499:     /* defaults */
                    500:     ldap_conf.version = 3;
                    501:     ldap_conf.port = 389;
                    502:     ldap_conf.tls_checkpeer = -1;
                    503:     ldap_conf.timelimit = -1;
                    504:     ldap_conf.bind_timelimit = -1;
                    505:
                    506:     if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL)
                    507:        return(FALSE);
                    508:     while (fgets(buf, sizeof(buf), f)) {
                    509:        /* ignore text after comment character */
                    510:        if ((c = strchr(buf, '#')) != NULL)
                    511:            *c = '\0';
                    512:
                    513:        /* skip leading whitespace */
                    514:        for (c = buf; isspace((unsigned char) *c); c++)
                    515:            /* nothing */;
                    516:
                    517:        if (*c == '\0' || *c == '\n')
                    518:            continue;           /* skip empty line */
                    519:
                    520:        /* properly terminate keyword string */
                    521:        keyword = c;
                    522:        while (*c && !isspace((unsigned char) *c))
                    523:            c++;
                    524:        if (*c)
                    525:            *c++ = '\0';        /* terminate keyword */
                    526:
                    527:        /* skip whitespace before value */
                    528:        while (isspace((unsigned char) *c))
                    529:            c++;
                    530:        value = c;
                    531:
                    532:        /* trim whitespace after value */
                    533:        while (*c)
                    534:            c++;                /* wind to end */
                    535:        while (--c > value && isspace((unsigned char) *c))
                    536:            *c = '\0';
                    537:
                    538:        /* The following macros make the code much more readable */
                    539:
                    540: #define MATCH_S(x,y) if (!strcasecmp(keyword,x)) \
                    541:     { efree(y); y=estrdup(value); }
                    542: #define MATCH_I(x,y) if (!strcasecmp(keyword,x)) { y=atoi(value); }
                    543: #define MATCH_B(x,y) if (!strcasecmp(keyword,x)) { y=_atobool(value); }
                    544:
                    545:        /*
                    546:         * Parse values using a continues chain of if else if else if else if
                    547:         * else ...
                    548:         */
                    549:        MATCH_S("host", ldap_conf.host)
                    550:            else
                    551:        MATCH_I("port", ldap_conf.port)
                    552:            else
                    553:        MATCH_S("ssl", ldap_conf.ssl)
                    554:            else
                    555:        MATCH_B("tls_checkpeer", ldap_conf.tls_checkpeer)
                    556:            else
                    557:        MATCH_S("tls_cacertfile", ldap_conf.tls_cacertfile)
                    558:            else
                    559:        MATCH_S("tls_cacertdir", ldap_conf.tls_cacertdir)
                    560:            else
                    561:        MATCH_S("tls_randfile", ldap_conf.tls_random_file)
                    562:            else
                    563:        MATCH_S("tls_ciphers", ldap_conf.tls_cipher_suite)
                    564:            else
                    565:        MATCH_S("tls_cert", ldap_conf.tls_certfile)
                    566:            else
                    567:        MATCH_S("tls_key", ldap_conf.tls_keyfile)
                    568:            else
                    569:        MATCH_I("ldap_version", ldap_conf.version)
                    570:            else
                    571:        MATCH_I("bind_timelimit", ldap_conf.bind_timelimit)
                    572:            else
                    573:        MATCH_I("timelimit", ldap_conf.timelimit)
                    574:            else
                    575:        MATCH_S("uri", ldap_conf.uri)
                    576:            else
                    577:        MATCH_S("binddn", ldap_conf.binddn)
                    578:            else
                    579:        MATCH_S("bindpw", ldap_conf.bindpw)
                    580:            else
                    581:        MATCH_S("rootbinddn", ldap_conf.rootbinddn)
                    582:            else
                    583:        MATCH_S("sudoers_base", ldap_conf.base)
                    584:            else
                    585:        MATCH_I("sudoers_debug", ldap_conf.debug)
                    586:            else {
                    587:
                    588:            /*
                    589:             * The keyword was unrecognized.  Since this config file is
                    590:             * shared by multiple programs, it is appropriate to silently
                    591:             * ignore options this program does not understand
                    592:             */
                    593:        }
                    594:
                    595:     }
                    596:     fclose(f);
                    597:
                    598:     if (!ldap_conf.host)
                    599:        ldap_conf.host = estrdup("localhost");
                    600:
                    601:     if (ldap_conf.bind_timelimit > 0)
                    602:        ldap_conf.bind_timelimit *= 1000;       /* convert to ms */
                    603:
                    604:     if (ldap_conf.debug > 1) {
                    605:        fprintf(stderr, "LDAP Config Summary\n");
                    606:        fprintf(stderr, "===================\n");
                    607: #ifdef HAVE_LDAP_INITIALIZE
                    608:        if (ldap_conf.uri) {
                    609:            fprintf(stderr, "uri          %s\n", ldap_conf.uri);
                    610:        } else
                    611: #endif
                    612:        {
                    613:            fprintf(stderr, "host         %s\n", ldap_conf.host ?
                    614:                ldap_conf.host : "(NONE)");
                    615:            fprintf(stderr, "port         %d\n", ldap_conf.port);
                    616:        }
                    617:        fprintf(stderr, "ldap_version %d\n", ldap_conf.version);
                    618:
                    619:        fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ?
                    620:            ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
                    621:        fprintf(stderr, "binddn       %s\n", ldap_conf.binddn ?
                    622:            ldap_conf.binddn : "(anonymous)");
                    623:        fprintf(stderr, "bindpw       %s\n", ldap_conf.bindpw ?
                    624:            ldap_conf.bindpw : "(anonymous)");
                    625:        fprintf(stderr, "bind_timelimit  %d\n", ldap_conf.bind_timelimit);
                    626:        fprintf(stderr, "timelimit    %d\n", ldap_conf.timelimit);
                    627: #ifdef HAVE_LDAP_START_TLS_S
                    628:        fprintf(stderr, "ssl          %s\n", ldap_conf.ssl ?
                    629:            ldap_conf.ssl : "(no)");
                    630: #endif
                    631:        fprintf(stderr, "===================\n");
                    632:     }
                    633:     if (!ldap_conf.base)
                    634:        return(FALSE);          /* if no base is defined, ignore LDAP */
                    635:
                    636:     /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
                    637:     if (ldap_conf.rootbinddn) {
                    638:        if ((f = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
                    639:            if (fgets(buf, sizeof(buf), f) != NULL) {
                    640:                /* removing trailing newlines */
                    641:                for (c = buf; *c != '\0'; c++)
                    642:                    continue;
                    643:                while (--c > buf && *c == '\n')
                    644:                    *c = '\0';
                    645:                /* copy to bindpw and binddn */
                    646:                efree(ldap_conf.bindpw);
                    647:                ldap_conf.bindpw = estrdup(buf);
                    648:                efree(ldap_conf.binddn);
                    649:                ldap_conf.binddn = ldap_conf.rootbinddn;
                    650:                ldap_conf.rootbinddn = NULL;
                    651:            }
                    652:            fclose(f);
                    653:        }
                    654:     }
                    655:     return(TRUE);
                    656: }
                    657:
                    658: /*
                    659:  * like perl's join(sep,@ARGS)
                    660:  */
                    661: char *
                    662:  _ldap_join_values(sep, v)
                    663:     char *sep;
                    664:     char **v;
                    665: {
                    666:     char *b = NULL, **p = NULL;
                    667:     size_t sz = 0;
                    668:
                    669:     /* paste values together */
                    670:     for (p = v; p && *p; p++) {
                    671:        if (p != v && sep != NULL)
1.3       martynas  672:            ncat(&b, &sz, sep); /* append separator */
1.1       millert   673:        ncat(&b, &sz, *p);      /* append value */
                    674:     }
                    675:
                    676:     /* sanity check */
                    677:     if (b[0] == '\0') {
                    678:        /* something went wrong, put something here */
                    679:        ncat(&b, &sz, "(empty list)");  /* append value */
                    680:     }
                    681:
                    682:     return(b);
                    683: }
                    684:
                    685: char *sudo_ldap_cm_list = NULL;
                    686: size_t sudo_ldap_cm_list_size;
                    687:
                    688: #define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
                    689: /*
                    690:  * Walks through search result and returns TRUE if we have a
                    691:  * command match
                    692:  */
                    693: int
                    694: sudo_ldap_add_match(ld, entry, pwflag)
                    695:     LDAP *ld;
                    696:     LDAPMessage *entry;
                    697:     int pwflag;
                    698: {
                    699:     char *dn, **edn, **v = NULL;
                    700:
                    701:     /* if we are not collecting matches, then don't save them */
                    702:     if (pwflag != I_LISTPW)
                    703:        return(TRUE);
                    704:
                    705:     /* collect the dn, only show the rdn */
                    706:     dn = ldap_get_dn(ld, entry);
                    707:     edn = dn ? ldap_explode_dn(dn, 1) : NULL;
                    708:     SAVE_LIST("\nLDAP Role: ");
                    709:     SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
                    710:     SAVE_LIST("\n");
                    711:     if (dn)
                    712:        ldap_memfree(dn);
                    713:     if (edn)
                    714:        ldap_value_free(edn);
                    715:
                    716:     /* get the Runas Values from the entry */
                    717:     v = ldap_get_values(ld, entry, "sudoRunAs");
                    718:     if (v && *v) {
                    719:        SAVE_LIST("  RunAs: (");
                    720:        SAVE_LIST(_ldap_join_values(", ", v));
                    721:        SAVE_LIST(")\n");
                    722:     }
                    723:     if (v)
                    724:        ldap_value_free(v);
                    725:
                    726:     /* get the Command Values from the entry */
                    727:     v = ldap_get_values(ld, entry, "sudoCommand");
                    728:     if (v && *v) {
                    729:        SAVE_LIST("  Commands:\n    ");
                    730:        SAVE_LIST(_ldap_join_values("\n    ", v));
                    731:        SAVE_LIST("\n");
                    732:     } else {
                    733:        SAVE_LIST("  Commands: NONE\n");
                    734:     }
                    735:     if (v)
                    736:        ldap_value_free(v);
                    737:
                    738:     return(FALSE);             /* Don't stop at the first match */
                    739: }
                    740: #undef SAVE_LIST
                    741:
                    742: void
                    743: sudo_ldap_list_matches()
                    744: {
                    745:     if (sudo_ldap_cm_list != NULL)
                    746:        printf("%s", sudo_ldap_cm_list);
                    747: }
                    748:
                    749: /* macros to set option, error on failure plus consistent debugging */
                    750: #define SET_OPTS(opt, val) do { \
                    751:     if (ldap_conf.val != NULL) { \
                    752:        if (ldap_conf.debug > 1) \
                    753:            fprintf(stderr, \
                    754:                "ldap_set_option(LDAP_OPT_%s, \"%s\")\n", #opt, ldap_conf.val);\
                    755:        rc = ldap_set_option(ld, LDAP_OPT_ ## opt, ldap_conf.val); \
                    756:        if (rc != LDAP_OPT_SUCCESS) { \
                    757:            fprintf(stderr,"ldap_set_option(LDAP_OPT_%s, \"%s\")=%d: %s\n", \
                    758:                #opt, ldap_conf.val, rc, ldap_err2string(rc)); \
                    759:            return(NULL); \
                    760:        } \
                    761:     } \
                    762: } while(0)
                    763: #define SET_OPTI(opt, val) do { \
                    764:     if (ldap_conf.val >= 0) { \
                    765:        if (ldap_conf.debug > 1) \
                    766:            fprintf(stderr, \
                    767:                "ldap_set_option(LDAP_OPT_%s, %d)\n", #opt, ldap_conf.val); \
                    768:        rc = ldap_set_option(ld, LDAP_OPT_ ## opt, &ldap_conf.val); \
                    769:        if (rc != LDAP_OPT_SUCCESS) { \
                    770:            fprintf(stderr,"ldap_set_option(LDAP_OPT_%s, %d)=%d: %s\n", \
                    771:                #opt, ldap_conf.val, rc, ldap_err2string(rc)); \
                    772:            return(NULL); \
                    773:        } \
                    774:     } \
                    775: } while(0)
                    776:
                    777: /*
                    778:  * Open a connection to the LDAP server.
                    779:  */
                    780: static LDAP *
                    781: sudo_ldap_open()
                    782: {
                    783:     LDAP *ld = NULL;
                    784:     int rc;
                    785:
                    786:     if (!sudo_ldap_read_config())
                    787:        return(NULL);
                    788:
                    789:     /* attempt to setup ssl options */
                    790: #ifdef LDAP_OPT_X_TLS_CACERTFILE
                    791:     SET_OPTS(X_TLS_CACERTFILE, tls_cacertfile);
                    792: #endif /* LDAP_OPT_X_TLS_CACERTFILE */
                    793:
                    794: #ifdef LDAP_OPT_X_TLS_CACERTDIR
                    795:     SET_OPTS(X_TLS_CACERTDIR, tls_cacertdir);
                    796: #endif /* LDAP_OPT_X_TLS_CACERTDIR */
                    797:
                    798: #ifdef LDAP_OPT_X_TLS_CERTFILE
                    799:     SET_OPTS(X_TLS_CERTFILE, tls_certfile);
                    800: #endif /* LDAP_OPT_X_TLS_CERTFILE */
                    801:
                    802: #ifdef LDAP_OPT_X_TLS_KEYFILE
                    803:     SET_OPTS(X_TLS_KEYFILE, tls_keyfile);
                    804: #endif /* LDAP_OPT_X_TLS_KEYFILE */
                    805:
                    806: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
                    807:     SET_OPTS(X_TLS_CIPHER_SUITE, tls_cipher_suite);
                    808: #endif /* LDAP_OPT_X_TLS_CIPHER_SUITE */
                    809:
                    810: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
                    811:     SET_OPTS(X_TLS_RANDOM_FILE, tls_random_file);
                    812: #endif /* LDAP_OPT_X_TLS_RANDOM_FILE */
                    813:
                    814: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
                    815:     /* check the server certificate? */
                    816:     SET_OPTI(X_TLS_REQUIRE_CERT, tls_checkpeer);
                    817: #endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
                    818:
                    819:     /* set timelimit options */
                    820:     SET_OPTI(TIMELIMIT, timelimit);
                    821:
                    822: #ifdef LDAP_OPT_NETWORK_TIMEOUT
                    823:     if (ldap_conf.bind_timelimit > 0) {
                    824:        struct timeval tv;
                    825:        tv.tv_sec = ldap_conf.bind_timelimit / 1000;
                    826:        tv.tv_usec = 0;
                    827:        if (ldap_conf.debug > 1)
                    828:            fprintf(stderr, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)\n",
                    829:            tv.tv_sec);
                    830:        rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
                    831:        if (rc != LDAP_OPT_SUCCESS) {
                    832:            fprintf(stderr,"ldap_set_option(NETWORK_TIMEOUT, %ld)=%d: %s\n",
                    833:            tv.tv_sec, rc, ldap_err2string(rc));
                    834:            return(NULL);
                    835:        }
                    836:     }
                    837: #endif
                    838:
                    839:     /* attempt connect */
                    840: #ifdef HAVE_LDAP_INITIALIZE
                    841:     if (ldap_conf.uri) {
                    842:
                    843:        DPRINTF(("ldap_initialize(ld,%s)", ldap_conf.uri), 2);
                    844:
                    845:        rc = ldap_initialize(&ld, ldap_conf.uri);
                    846:        if (rc) {
                    847:            fprintf(stderr, "ldap_initialize()=%d : %s\n",
                    848:                rc, ldap_err2string(rc));
                    849:            return(NULL);
                    850:        }
                    851:     } else
                    852: #endif /* HAVE_LDAP_INITIALIZE */
                    853:     if (ldap_conf.host) {
                    854:
                    855:        DPRINTF(("ldap_init(%s,%d)", ldap_conf.host, ldap_conf.port), 2);
                    856:
                    857:        if ((ld = ldap_init(ldap_conf.host, ldap_conf.port)) == NULL) {
                    858:            fprintf(stderr, "ldap_init(): errno=%d : %s\n",
                    859:                errno, strerror(errno));
                    860:            return(NULL);
                    861:        }
                    862:     }
                    863: #ifdef LDAP_OPT_PROTOCOL_VERSION
                    864:
                    865:     /* Set the LDAP Protocol version */
                    866:     SET_OPTI(PROTOCOL_VERSION, version);
                    867:
                    868: #endif /* LDAP_OPT_PROTOCOL_VERSION */
                    869:
                    870: #ifdef HAVE_LDAP_START_TLS_S
                    871:     /* Turn on TLS */
                    872:     if (ldap_conf.ssl && !strcasecmp(ldap_conf.ssl, "start_tls")) {
                    873:        rc = ldap_start_tls_s(ld, NULL, NULL);
                    874:        if (rc != LDAP_SUCCESS) {
                    875:            fprintf(stderr, "ldap_start_tls_s(): %d: %s\n", rc,
                    876:                ldap_err2string(rc));
                    877:            ldap_unbind(ld);
                    878:            return(NULL);
                    879:        }
                    880:        DPRINTF(("ldap_start_tls_s() ok"), 1);
                    881:     }
                    882: #endif /* HAVE_LDAP_START_TLS_S */
                    883:
                    884:     /* Actually connect */
                    885:     if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) {
                    886:        fprintf(stderr, "ldap_simple_bind_s()=%d : %s\n",
                    887:            rc, ldap_err2string(rc));
                    888:        return(NULL);
                    889:     }
                    890:     DPRINTF(("ldap_bind() ok"), 1);
                    891:
                    892:     return(ld);
                    893: }
                    894:
                    895: static void
                    896: sudo_ldap_update_defaults(ld)
                    897:     LDAP *ld;
                    898: {
                    899:     LDAPMessage *entry = NULL, *result = NULL;  /* used for searches */
                    900:     int rc;                                     /* temp return value */
                    901:
                    902:     rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
                    903:        "cn=defaults", NULL, 0, &result);
                    904:     if (!rc && (entry = ldap_first_entry(ld, result))) {
                    905:        DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                    906:        sudo_ldap_parse_options(ld, entry);
                    907:     } else
                    908:        DPRINTF(("no default options found!"), 1);
                    909:
                    910:     if (result)
                    911:        ldap_msgfree(result);
                    912: }
                    913:
                    914: /*
                    915:  * like sudoers_lookup() - only LDAP style
                    916:  */
                    917: int
                    918: sudo_ldap_check(pwflag)
                    919:     int pwflag;
                    920: {
                    921:     LDAP *ld;
                    922:     LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
                    923:     char *filt;                                        /* used to parse attributes */
                    924:     int rc, ret = FALSE, do_netgr;             /* temp/final return values */
1.4     ! millert   925:     int setenv_implied;
1.1       millert   926:     int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */
                    927:
                    928:     /* Open a connection to the LDAP server. */
                    929:     if ((ld = sudo_ldap_open()) == NULL)
                    930:        return(VALIDATE_ERROR);
                    931:
                    932:     /* Parse Default options. */
                    933:     sudo_ldap_update_defaults(ld);
                    934:
                    935:     /*
                    936:      * Okay - time to search for anything that matches this user
                    937:      * Lets limit it to only two queries of the LDAP server
                    938:      *
                    939:      * The first pass will look by the username, groups, and
                    940:      * the keyword ALL.  We will then inspect the results that
                    941:      * came back from the query.  We don't need to inspect the
                    942:      * sudoUser in this pass since the LDAP server already scanned
                    943:      * it for us.
                    944:      *
                    945:      * The second pass will return all the entries that contain
                    946:      * user netgroups.  Then we take the netgroups returned and
                    947:      * try to match them against the username.
                    948:      */
1.4     ! millert   949:     setenv_implied = FALSE;
1.1       millert   950:     for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
                    951:        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1();
                    952:        DPRINTF(("ldap search '%s'", filt), 1);
                    953:        rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
                    954:            NULL, 0, &result);
                    955:        if (rc)
                    956:            DPRINTF(("nothing found for '%s'", filt), 1);
                    957:        efree(filt);
                    958:
                    959:        /* parse each entry returned from this most recent search */
                    960:        entry = rc ? NULL : ldap_first_entry(ld, result);
                    961:        while (entry != NULL) {
                    962:            DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
                    963:            if (
                    964:            /* first verify user netgroup matches - only if in pass 2 */
                    965:                (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) &&
                    966:            /* remember that user matched */
                    967:                (ldap_user_matches = -1) &&
                    968:            /* verify host match */
                    969:                sudo_ldap_check_host(ld, entry) &&
                    970:            /* remember that host matched */
                    971:                (ldap_host_matches = -1) &&
                    972:            /* add matches for listing later */
                    973:                sudo_ldap_add_match(ld, entry, pwflag) &&
                    974:            /* verify command match */
1.4     ! millert   975:                sudo_ldap_check_command(ld, entry, &setenv_implied) &&
1.1       millert   976:            /* verify runas match */
                    977:                sudo_ldap_check_runas(ld, entry)
                    978:                ) {
                    979:                /* We have a match! */
                    980:                DPRINTF(("Perfect Matched!"), 1);
                    981:                /* pick up any options */
1.4     ! millert   982:                if (setenv_implied)
        !           983:                    def_setenv = TRUE;
1.1       millert   984:                sudo_ldap_parse_options(ld, entry);
                    985:                /* make sure we don't reenter loop */
                    986:                ret = VALIDATE_OK;
                    987:                /* break from inside for loop */
                    988:                break;
                    989:            }
                    990:            entry = ldap_next_entry(ld, entry);
                    991:        }
                    992:        if (result)
                    993:            ldap_msgfree(result);
                    994:        result = NULL;
                    995:     }
                    996:
                    997:     sudo_ldap_close(ld);               /* shut down connection */
                    998:
                    999:     DPRINTF(("user_matches=%d", ldap_user_matches), 1);
                   1000:     DPRINTF(("host_matches=%d", ldap_host_matches), 1);
                   1001:
                   1002:     /* Check for special case for -v, -k, -l options */
                   1003:     if (pwflag && ldap_user_matches && ldap_host_matches) {
                   1004:        /*
                   1005:          * Handle verifypw & listpw
                   1006:          *
                   1007:          * To be extra paranoid, since we haven't read any NOPASSWD options
                   1008:          * in /etc/sudoers yet, but we have to make the decission now, lets
                   1009:          * assume the worst and prefer to prompt for password unless the setting
                   1010:          * is "never". (example verifypw=never or listpw=never)
                   1011:          *
                   1012:          */
                   1013:        ret = VALIDATE_OK;
                   1014:        if (pwflag == -1) {
                   1015:            SET(ret, FLAG_NOPASS);              /* -k or -K */
                   1016:        } else {
                   1017:            switch (sudo_defs_table[pwflag].sd_un.tuple) {
                   1018:            case never:
                   1019:                SET(ret, FLAG_NOPASS);
                   1020:                break;
                   1021:            case always:
                   1022:                if (def_authenticate)
                   1023:                    SET(ret, FLAG_CHECK_USER);
                   1024:                break;
                   1025:            default:
                   1026:                break;
                   1027:            }
                   1028:        }
                   1029:     }
                   1030:     if (ISSET(ret, VALIDATE_OK)) {
                   1031:        /* we have a match, should we check the password? */
                   1032:        if (!def_authenticate)
                   1033:            SET(ret, FLAG_NOPASS);
                   1034:        if (def_noexec)
                   1035:            SET(ret, FLAG_NOEXEC);
                   1036:        if (def_setenv)
                   1037:            SET(ret, FLAG_SETENV);
                   1038:     } else {
                   1039:        /* we do not have a match */
                   1040:        ret = VALIDATE_NOT_OK;
                   1041:        if (pwflag)
                   1042:            SET(ret, FLAG_NO_CHECK);
                   1043:        else if (!ldap_user_matches)
                   1044:            SET(ret, FLAG_NO_USER);
                   1045:        else if (!ldap_host_matches)
                   1046:            SET(ret, FLAG_NO_HOST);
                   1047:     }
                   1048:     DPRINTF(("sudo_ldap_check(%d)=0x%02x", pwflag, ret), 1);
                   1049:
                   1050:     return(ret);
                   1051: }
                   1052:
                   1053: /*
                   1054:  * shut down LDAP connection
                   1055:  */
                   1056: static void
                   1057: sudo_ldap_close(LDAP *ld)
                   1058: {
                   1059:     if (ld)
                   1060:        ldap_unbind_s(ld);
                   1061: }