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

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