Annotation of embedaddon/sudo/plugins/sudoers/ldap.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 2003-2011 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: #endif /* HAVE_STRING_H */
! 37: #ifdef HAVE_STRINGS_H
! 38: # include <strings.h>
! 39: #endif /* HAVE_STRINGS_H */
! 40: #ifdef HAVE_UNISTD_H
! 41: # include <unistd.h>
! 42: #endif /* HAVE_UNISTD_H */
! 43: #if TIME_WITH_SYS_TIME
! 44: # include <time.h>
! 45: #endif
! 46: #include <ctype.h>
! 47: #include <pwd.h>
! 48: #include <grp.h>
! 49: #include <netinet/in.h>
! 50: #include <arpa/inet.h>
! 51: #include <netdb.h>
! 52: #ifdef HAVE_LBER_H
! 53: # include <lber.h>
! 54: #endif
! 55: #include <ldap.h>
! 56: #if defined(HAVE_LDAP_SSL_H)
! 57: # include <ldap_ssl.h>
! 58: #elif defined(HAVE_MPS_LDAP_SSL_H)
! 59: # include <mps/ldap_ssl.h>
! 60: #endif
! 61: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 62: # ifdef HAVE_SASL_SASL_H
! 63: # include <sasl/sasl.h>
! 64: # else
! 65: # include <sasl.h>
! 66: # endif
! 67: # if HAVE_GSS_KRB5_CCACHE_NAME
! 68: # if defined(HAVE_GSSAPI_GSSAPI_KRB5_H)
! 69: # include <gssapi/gssapi.h>
! 70: # include <gssapi/gssapi_krb5.h>
! 71: # elif defined(HAVE_GSSAPI_GSSAPI_H)
! 72: # include <gssapi/gssapi.h>
! 73: # else
! 74: # include <gssapi.h>
! 75: # endif
! 76: # endif
! 77: #endif
! 78:
! 79: #include "sudoers.h"
! 80: #include "parse.h"
! 81: #include "lbuf.h"
! 82:
! 83: #ifndef LDAP_OPT_SUCCESS
! 84: # define LDAP_OPT_SUCCESS LDAP_SUCCESS
! 85: #endif
! 86:
! 87: #ifndef LDAPS_PORT
! 88: # define LDAPS_PORT 636
! 89: #endif
! 90:
! 91: #if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET)
! 92: # define LDAP_SASL_QUIET 0
! 93: #endif
! 94:
! 95: #ifndef HAVE_LDAP_UNBIND_EXT_S
! 96: #define ldap_unbind_ext_s(a, b, c) ldap_unbind_s(a)
! 97: #endif
! 98:
! 99: #ifndef HAVE_LDAP_SEARCH_EXT_S
! 100: # ifdef HAVE_LDAP_SEARCH_ST
! 101: # define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k) \
! 102: ldap_search_st(a, b, c, d, e, f, i, k)
! 103: # else
! 104: # define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k) \
! 105: ldap_search_s(a, b, c, d, e, f, k)
! 106: # endif
! 107: #endif
! 108:
! 109: #define LDAP_FOREACH(var, ld, res) \
! 110: for ((var) = ldap_first_entry((ld), (res)); \
! 111: (var) != NULL; \
! 112: (var) = ldap_next_entry((ld), (var)))
! 113:
! 114: #define DPRINTF(args, level) if (ldap_conf.debug >= level) warningx args
! 115:
! 116: #define CONF_BOOL 0
! 117: #define CONF_INT 1
! 118: #define CONF_STR 2
! 119: #define CONF_LIST_STR 4
! 120: #define CONF_DEREF_VAL 5
! 121:
! 122: #define SUDO_LDAP_SSL 1
! 123: #define SUDO_LDAP_STARTTLS 2
! 124:
! 125: /* The TIMEFILTER_LENGTH includes the filter itself plus the global AND
! 126: wrapped around the user filter and the time filter when timed entries
! 127: are used. The length is computed as follows:
! 128: 85 for the filter
! 129: + 2 * 13 for the now timestamp
! 130: + 3 for the global AND
! 131: */
! 132: #define TIMEFILTER_LENGTH 114
! 133:
! 134: /*
! 135: * The ldap_search structure implements a linked list of ldap and
! 136: * search result pointers, which allows us to remove them after
! 137: * all search results have been combined in memory.
! 138: * XXX - should probably be a tailq since we do appends
! 139: */
! 140: struct ldap_search_list {
! 141: LDAP *ldap;
! 142: LDAPMessage *searchresult;
! 143: struct ldap_search_list *next;
! 144: };
! 145:
! 146: /*
! 147: * The ldap_entry_wrapper structure is used to implement sorted result entries.
! 148: * A double is used for the order to allow for insertion of new entries
! 149: * without having to renumber everything.
! 150: * Note: there is no standard floating point type in LDAP.
! 151: * As a result, some LDAP servers will only allow an integer.
! 152: */
! 153: struct ldap_entry_wrapper {
! 154: LDAPMessage *entry;
! 155: double order;
! 156: };
! 157:
! 158: /*
! 159: * The ldap_result structure contains the list of matching searches as
! 160: * well as an array of all result entries sorted by the sudoOrder attribute.
! 161: */
! 162: struct ldap_result {
! 163: struct ldap_search_list *searches;
! 164: struct ldap_entry_wrapper *entries;
! 165: int allocated_entries;
! 166: int nentries;
! 167: int user_matches;
! 168: int host_matches;
! 169: };
! 170: #define ALLOCATION_INCREMENT 100
! 171:
! 172: struct ldap_config_table {
! 173: const char *conf_str; /* config file string */
! 174: short type; /* CONF_BOOL, CONF_INT, CONF_STR */
! 175: short connected; /* connection-specific value? */
! 176: int opt_val; /* LDAP_OPT_* (or -1 for sudo internal) */
! 177: void *valp; /* pointer into ldap_conf */
! 178: };
! 179:
! 180: struct ldap_config_list_str {
! 181: struct ldap_config_list_str *next;
! 182: char val[1];
! 183: };
! 184:
! 185: /* LDAP configuration structure */
! 186: static struct ldap_config {
! 187: int port;
! 188: int version;
! 189: int debug;
! 190: int ldap_debug;
! 191: int tls_checkpeer;
! 192: int timelimit;
! 193: int timeout;
! 194: int bind_timelimit;
! 195: int use_sasl;
! 196: int rootuse_sasl;
! 197: int ssl_mode;
! 198: int timed;
! 199: int deref;
! 200: char *host;
! 201: struct ldap_config_list_str *uri;
! 202: char *binddn;
! 203: char *bindpw;
! 204: char *rootbinddn;
! 205: struct ldap_config_list_str *base;
! 206: char *search_filter;
! 207: char *ssl;
! 208: char *tls_cacertfile;
! 209: char *tls_cacertdir;
! 210: char *tls_random_file;
! 211: char *tls_cipher_suite;
! 212: char *tls_certfile;
! 213: char *tls_keyfile;
! 214: char *sasl_auth_id;
! 215: char *rootsasl_auth_id;
! 216: char *sasl_secprops;
! 217: char *krb5_ccname;
! 218: } ldap_conf;
! 219:
! 220: static struct ldap_config_table ldap_conf_table[] = {
! 221: { "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug },
! 222: { "host", CONF_STR, FALSE, -1, &ldap_conf.host },
! 223: { "port", CONF_INT, FALSE, -1, &ldap_conf.port },
! 224: { "ssl", CONF_STR, FALSE, -1, &ldap_conf.ssl },
! 225: { "sslpath", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
! 226: { "uri", CONF_LIST_STR, FALSE, -1, &ldap_conf.uri },
! 227: #ifdef LDAP_OPT_DEBUG_LEVEL
! 228: { "debug", CONF_INT, FALSE, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
! 229: #endif
! 230: #ifdef LDAP_OPT_PROTOCOL_VERSION
! 231: { "ldap_version", CONF_INT, TRUE, LDAP_OPT_PROTOCOL_VERSION,
! 232: &ldap_conf.version },
! 233: #endif
! 234: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
! 235: { "tls_checkpeer", CONF_BOOL, FALSE, LDAP_OPT_X_TLS_REQUIRE_CERT,
! 236: &ldap_conf.tls_checkpeer },
! 237: #else
! 238: { "tls_checkpeer", CONF_BOOL, FALSE, -1, &ldap_conf.tls_checkpeer },
! 239: #endif
! 240: #ifdef LDAP_OPT_X_TLS_CACERTFILE
! 241: { "tls_cacertfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
! 242: &ldap_conf.tls_cacertfile },
! 243: { "tls_cacert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
! 244: &ldap_conf.tls_cacertfile },
! 245: #endif
! 246: #ifdef LDAP_OPT_X_TLS_CACERTDIR
! 247: { "tls_cacertdir", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTDIR,
! 248: &ldap_conf.tls_cacertdir },
! 249: #endif
! 250: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
! 251: { "tls_randfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_RANDOM_FILE,
! 252: &ldap_conf.tls_random_file },
! 253: #endif
! 254: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
! 255: { "tls_ciphers", CONF_STR, FALSE, LDAP_OPT_X_TLS_CIPHER_SUITE,
! 256: &ldap_conf.tls_cipher_suite },
! 257: #endif
! 258: #ifdef LDAP_OPT_X_TLS_CERTFILE
! 259: { "tls_cert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CERTFILE,
! 260: &ldap_conf.tls_certfile },
! 261: #else
! 262: { "tls_cert", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
! 263: #endif
! 264: #ifdef LDAP_OPT_X_TLS_KEYFILE
! 265: { "tls_key", CONF_STR, FALSE, LDAP_OPT_X_TLS_KEYFILE,
! 266: &ldap_conf.tls_keyfile },
! 267: #else
! 268: { "tls_key", CONF_STR, FALSE, -1, &ldap_conf.tls_keyfile },
! 269: #endif
! 270: #ifdef LDAP_OPT_NETWORK_TIMEOUT
! 271: { "bind_timelimit", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
! 272: &ldap_conf.bind_timelimit },
! 273: { "network_timeout", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
! 274: &ldap_conf.bind_timelimit },
! 275: #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
! 276: { "bind_timelimit", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
! 277: &ldap_conf.bind_timelimit },
! 278: { "network_timeout", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
! 279: &ldap_conf.bind_timelimit },
! 280: #endif
! 281: { "timelimit", CONF_INT, TRUE, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
! 282: #ifdef LDAP_OPT_TIMEOUT
! 283: { "timeout", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
! 284: &ldap_conf.timeout },
! 285: #endif
! 286: #ifdef LDAP_OPT_DEREF
! 287: { "deref", CONF_DEREF_VAL, TRUE, LDAP_OPT_DEREF, &ldap_conf.deref },
! 288: #endif
! 289: { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn },
! 290: { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw },
! 291: { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
! 292: { "sudoers_base", CONF_LIST_STR, FALSE, -1, &ldap_conf.base },
! 293: { "sudoers_timed", CONF_BOOL, FALSE, -1, &ldap_conf.timed },
! 294: { "sudoers_search_filter", CONF_STR, FALSE, -1, &ldap_conf.search_filter },
! 295: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 296: { "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl },
! 297: { "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id },
! 298: { "rootuse_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.rootuse_sasl },
! 299: { "rootsasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.rootsasl_auth_id },
! 300: # ifdef LDAP_OPT_X_SASL_SECPROPS
! 301: { "sasl_secprops", CONF_STR, TRUE, LDAP_OPT_X_SASL_SECPROPS,
! 302: &ldap_conf.sasl_secprops },
! 303: # endif
! 304: { "krb5_ccname", CONF_STR, FALSE, -1, &ldap_conf.krb5_ccname },
! 305: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
! 306: { NULL }
! 307: };
! 308:
! 309: /* sudo_nss implementation */
! 310: static int sudo_ldap_open(struct sudo_nss *nss);
! 311: static int sudo_ldap_close(struct sudo_nss *nss);
! 312: static int sudo_ldap_parse(struct sudo_nss *nss);
! 313: static int sudo_ldap_setdefs(struct sudo_nss *nss);
! 314: static int sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag);
! 315: static int sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw);
! 316: static int sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
! 317: struct lbuf *lbuf);
! 318: static int sudo_ldap_display_bound_defaults(struct sudo_nss *nss,
! 319: struct passwd *pw, struct lbuf *lbuf);
! 320: static int sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
! 321: struct lbuf *lbuf);
! 322: static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss,
! 323: struct passwd *pw);
! 324:
! 325: /*
! 326: * LDAP sudo_nss handle.
! 327: * We store the connection to the LDAP server, the cached ldap_result object
! 328: * (if any), and the name of the user the query was performed for.
! 329: * If a new query is launched with sudo_ldap_result_get() that specifies a
! 330: * different user, the old cached result is freed before the new query is run.
! 331: */
! 332: struct sudo_ldap_handle {
! 333: LDAP *ld;
! 334: struct ldap_result *result;
! 335: char *username;
! 336: struct group_list *grlist;
! 337: };
! 338:
! 339: struct sudo_nss sudo_nss_ldap = {
! 340: &sudo_nss_ldap,
! 341: NULL,
! 342: sudo_ldap_open,
! 343: sudo_ldap_close,
! 344: sudo_ldap_parse,
! 345: sudo_ldap_setdefs,
! 346: sudo_ldap_lookup,
! 347: sudo_ldap_display_cmnd,
! 348: sudo_ldap_display_defaults,
! 349: sudo_ldap_display_bound_defaults,
! 350: sudo_ldap_display_privs
! 351: };
! 352:
! 353: #ifdef HAVE_LDAP_CREATE
! 354: /*
! 355: * Rebuild the hosts list and include a specific port for each host.
! 356: * ldap_create() does not take a default port parameter so we must
! 357: * append one if we want something other than LDAP_PORT.
! 358: */
! 359: static void
! 360: sudo_ldap_conf_add_ports(void)
! 361: {
! 362:
! 363: char *host, *port, defport[13];
! 364: char hostbuf[LINE_MAX * 2];
! 365:
! 366: hostbuf[0] = '\0';
! 367: if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport))
! 368: errorx(1, _("sudo_ldap_conf_add_ports: port too large"));
! 369:
! 370: for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) {
! 371: if (hostbuf[0] != '\0') {
! 372: if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
! 373: goto toobig;
! 374: }
! 375:
! 376: if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
! 377: goto toobig;
! 378: /* Append port if there is not one already. */
! 379: if ((port = strrchr(host, ':')) == NULL ||
! 380: !isdigit((unsigned char)port[1])) {
! 381: if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf))
! 382: goto toobig;
! 383: }
! 384: }
! 385:
! 386: efree(ldap_conf.host);
! 387: ldap_conf.host = estrdup(hostbuf);
! 388: return;
! 389:
! 390: toobig:
! 391: errorx(1, _("sudo_ldap_conf_add_ports: out of space expanding hostbuf"));
! 392: }
! 393: #endif
! 394:
! 395: #ifndef HAVE_LDAP_INITIALIZE
! 396: /*
! 397: * For each uri, convert to host:port pairs. For ldaps:// enable SSL
! 398: * Accepts: uris of the form ldap:/// or ldap://hostname:portnum/
! 399: * where the trailing slash is optional.
! 400: */
! 401: static int
! 402: sudo_ldap_parse_uri(const struct ldap_config_list_str *uri_list)
! 403: {
! 404: char *buf, *uri, *host, *cp, *port;
! 405: char hostbuf[LINE_MAX];
! 406: int nldap = 0, nldaps = 0;
! 407: int rc = -1;
! 408:
! 409: do {
! 410: buf = estrdup(uri_list->val);
! 411: hostbuf[0] = '\0';
! 412: for ((uri = strtok(buf, " \t")); uri != NULL; (uri = strtok(NULL, " \t"))) {
! 413: if (strncasecmp(uri, "ldap://", 7) == 0) {
! 414: nldap++;
! 415: host = uri + 7;
! 416: } else if (strncasecmp(uri, "ldaps://", 8) == 0) {
! 417: nldaps++;
! 418: host = uri + 8;
! 419: } else {
! 420: warningx(_("unsupported LDAP uri type: %s"), uri);
! 421: goto done;
! 422: }
! 423:
! 424: /* trim optional trailing slash */
! 425: if ((cp = strrchr(host, '/')) != NULL && cp[1] == '\0') {
! 426: *cp = '\0';
! 427: }
! 428:
! 429: if (hostbuf[0] != '\0') {
! 430: if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf))
! 431: goto toobig;
! 432: }
! 433:
! 434: if (*host == '\0')
! 435: host = "localhost"; /* no host specified, use localhost */
! 436:
! 437: if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf))
! 438: goto toobig;
! 439:
! 440: /* If using SSL and no port specified, add port 636 */
! 441: if (nldaps) {
! 442: if ((port = strrchr(host, ':')) == NULL ||
! 443: !isdigit((unsigned char)port[1]))
! 444: if (strlcat(hostbuf, ":636", sizeof(hostbuf)) >= sizeof(hostbuf))
! 445: goto toobig;
! 446: }
! 447: }
! 448: if (hostbuf[0] == '\0') {
! 449: warningx(_("invalid uri: %s"), uri_list);
! 450: goto done;
! 451: }
! 452:
! 453: if (nldaps != 0) {
! 454: if (nldap != 0) {
! 455: warningx(_("unable to mix ldap and ldaps URIs"));
! 456: goto done;
! 457: }
! 458: if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
! 459: warningx(_("unable to mix ldaps and starttls"));
! 460: goto done;
! 461: }
! 462: ldap_conf.ssl_mode = SUDO_LDAP_SSL;
! 463: }
! 464:
! 465: efree(ldap_conf.host);
! 466: ldap_conf.host = estrdup(hostbuf);
! 467: efree(buf);
! 468: } while ((uri_list = uri_list->next));
! 469:
! 470: buf = NULL;
! 471: rc = 0;
! 472:
! 473: done:
! 474: efree(buf);
! 475: return rc;
! 476:
! 477: toobig:
! 478: errorx(1, _("sudo_ldap_parse_uri: out of space building hostbuf"));
! 479: }
! 480: #else
! 481: static char *
! 482: sudo_ldap_join_uri(struct ldap_config_list_str *uri_list)
! 483: {
! 484: struct ldap_config_list_str *uri;
! 485: size_t len = 0;
! 486: char *buf, *cp;
! 487:
! 488: /* Usually just a single entry. */
! 489: if (uri_list->next == NULL)
! 490: return estrdup(uri_list->val);
! 491:
! 492: for (uri = uri_list; uri != NULL; uri = uri->next) {
! 493: len += strlen(uri->val) + 1;
! 494: }
! 495: buf = cp = emalloc(len);
! 496: buf[0] = '\0';
! 497: for (uri = uri_list; uri != NULL; uri = uri->next) {
! 498: cp += strlcpy(cp, uri->val, len - (cp - buf));
! 499: *cp++ = ' ';
! 500: }
! 501: cp[-1] = '\0';
! 502: return buf;
! 503: }
! 504: #endif /* HAVE_LDAP_INITIALIZE */
! 505:
! 506: static int
! 507: sudo_ldap_init(LDAP **ldp, const char *host, int port)
! 508: {
! 509: LDAP *ld = NULL;
! 510: int rc = LDAP_CONNECT_ERROR;
! 511:
! 512: #ifdef HAVE_LDAPSSL_INIT
! 513: if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
! 514: DPRINTF(("ldapssl_clientauth_init(%s, %s)",
! 515: ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
! 516: ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
! 517: rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
! 518: ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
! 519: /*
! 520: * Mozilla-derived SDKs have a bug starting with version 5.0
! 521: * where the path can no longer be a file name and must be a dir.
! 522: */
! 523: if (rc != LDAP_SUCCESS) {
! 524: char *cp;
! 525: if (ldap_conf.tls_certfile) {
! 526: cp = strrchr(ldap_conf.tls_certfile, '/');
! 527: if (cp != NULL && strncmp(cp + 1, "cert", 4) == 0)
! 528: *cp = '\0';
! 529: }
! 530: if (ldap_conf.tls_keyfile) {
! 531: cp = strrchr(ldap_conf.tls_keyfile, '/');
! 532: if (cp != NULL && strncmp(cp + 1, "key", 3) == 0)
! 533: *cp = '\0';
! 534: }
! 535: DPRINTF(("ldapssl_clientauth_init(%s, %s)",
! 536: ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
! 537: ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
! 538: rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
! 539: ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
! 540: if (rc != LDAP_SUCCESS) {
! 541: warningx(_("unable to initialize SSL cert and key db: %s"),
! 542: ldapssl_err2string(rc));
! 543: goto done;
! 544: }
! 545: }
! 546:
! 547: DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2);
! 548: if ((ld = ldapssl_init(host, port, 1)) != NULL)
! 549: rc = LDAP_SUCCESS;
! 550: } else
! 551: #endif
! 552: {
! 553: #ifdef HAVE_LDAP_CREATE
! 554: DPRINTF(("ldap_create()"), 2);
! 555: if ((rc = ldap_create(&ld)) != LDAP_SUCCESS)
! 556: goto done;
! 557: DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2);
! 558: rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host);
! 559: #else
! 560: DPRINTF(("ldap_init(%s, %d)", host, port), 2);
! 561: if ((ld = ldap_init(host, port)) != NULL)
! 562: rc = LDAP_SUCCESS;
! 563: #endif
! 564: }
! 565:
! 566: done:
! 567: *ldp = ld;
! 568: return rc;
! 569: }
! 570:
! 571: /*
! 572: * Walk through search results and return TRUE if we have a matching
! 573: * netgroup, else FALSE.
! 574: */
! 575: static int
! 576: sudo_ldap_check_user_netgroup(LDAP *ld, LDAPMessage *entry, char *user)
! 577: {
! 578: struct berval **bv, **p;
! 579: char *val;
! 580: int ret = FALSE;
! 581:
! 582: if (!entry)
! 583: return ret;
! 584:
! 585: /* get the values from the entry */
! 586: bv = ldap_get_values_len(ld, entry, "sudoUser");
! 587: if (bv == NULL)
! 588: return ret;
! 589:
! 590: /* walk through values */
! 591: for (p = bv; *p != NULL && !ret; p++) {
! 592: val = (*p)->bv_val;
! 593: /* match any */
! 594: if (netgr_matches(val, NULL, NULL, user))
! 595: ret = TRUE;
! 596: DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val,
! 597: ret ? "MATCH!" : "not"), 2 + ((ret) ? 0 : 1));
! 598: }
! 599:
! 600: ldap_value_free_len(bv); /* cleanup */
! 601:
! 602: return ret;
! 603: }
! 604:
! 605: /*
! 606: * Walk through search results and return TRUE if we have a
! 607: * host match, else FALSE.
! 608: */
! 609: static int
! 610: sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry)
! 611: {
! 612: struct berval **bv, **p;
! 613: char *val;
! 614: int ret = FALSE;
! 615:
! 616: if (!entry)
! 617: return ret;
! 618:
! 619: /* get the values from the entry */
! 620: bv = ldap_get_values_len(ld, entry, "sudoHost");
! 621: if (bv == NULL)
! 622: return ret;
! 623:
! 624: /* walk through values */
! 625: for (p = bv; *p != NULL && !ret; p++) {
! 626: val = (*p)->bv_val;
! 627: /* match any or address or netgroup or hostname */
! 628: if (!strcmp(val, "ALL") || addr_matches(val) ||
! 629: netgr_matches(val, user_host, user_shost, NULL) ||
! 630: hostname_matches(user_shost, user_host, val))
! 631: ret = TRUE;
! 632: DPRINTF(("ldap sudoHost '%s' ... %s", val,
! 633: ret ? "MATCH!" : "not"), 2);
! 634: }
! 635:
! 636: ldap_value_free_len(bv); /* cleanup */
! 637:
! 638: return ret;
! 639: }
! 640:
! 641: static int
! 642: sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry)
! 643: {
! 644: struct berval **bv, **p;
! 645: char *val;
! 646: int ret = FALSE;
! 647:
! 648: if (!runas_pw)
! 649: return UNSPEC;
! 650:
! 651: /* get the runas user from the entry */
! 652: bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
! 653: if (bv == NULL)
! 654: bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */
! 655:
! 656: /*
! 657: * BUG:
! 658: *
! 659: * if runas is not specified on the command line, the only information
! 660: * as to which user to run as is in the runas_default option. We should
! 661: * check to see if we have the local option present. Unfortunately we
! 662: * don't parse these options until after this routine says yes or no.
! 663: * The query has already returned, so we could peek at the attribute
! 664: * values here though.
! 665: *
! 666: * For now just require users to always use -u option unless its set
! 667: * in the global defaults. This behaviour is no different than the global
! 668: * /etc/sudoers.
! 669: *
! 670: * Sigh - maybe add this feature later
! 671: */
! 672:
! 673: /*
! 674: * If there are no runas entries, match runas_default against
! 675: * what the user specified on the command line.
! 676: */
! 677: if (bv == NULL)
! 678: return !strcasecmp(runas_pw->pw_name, def_runas_default);
! 679:
! 680: /* walk through values returned, looking for a match */
! 681: for (p = bv; *p != NULL && !ret; p++) {
! 682: val = (*p)->bv_val;
! 683: switch (val[0]) {
! 684: case '+':
! 685: if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
! 686: ret = TRUE;
! 687: break;
! 688: case '%':
! 689: if (usergr_matches(val, runas_pw->pw_name, runas_pw))
! 690: ret = TRUE;
! 691: break;
! 692: case 'A':
! 693: if (strcmp(val, "ALL") == 0) {
! 694: ret = TRUE;
! 695: break;
! 696: }
! 697: /* FALLTHROUGH */
! 698: default:
! 699: if (strcasecmp(val, runas_pw->pw_name) == 0)
! 700: ret = TRUE;
! 701: break;
! 702: }
! 703: DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val,
! 704: ret ? "MATCH!" : "not"), 2);
! 705: }
! 706:
! 707: ldap_value_free_len(bv); /* cleanup */
! 708:
! 709: return ret;
! 710: }
! 711:
! 712: static int
! 713: sudo_ldap_check_runas_group(LDAP *ld, LDAPMessage *entry)
! 714: {
! 715: struct berval **bv, **p;
! 716: char *val;
! 717: int ret = FALSE;
! 718:
! 719: /* runas_gr is only set if the user specified the -g flag */
! 720: if (!runas_gr)
! 721: return UNSPEC;
! 722:
! 723: /* get the values from the entry */
! 724: bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
! 725: if (bv == NULL)
! 726: return ret;
! 727:
! 728: /* walk through values returned, looking for a match */
! 729: for (p = bv; *p != NULL && !ret; p++) {
! 730: val = (*p)->bv_val;
! 731: if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
! 732: ret = TRUE;
! 733: DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val,
! 734: ret ? "MATCH!" : "not"), 2);
! 735: }
! 736:
! 737: ldap_value_free_len(bv); /* cleanup */
! 738:
! 739: return ret;
! 740: }
! 741:
! 742: /*
! 743: * Walk through search results and return TRUE if we have a runas match,
! 744: * else FALSE. RunAs info is optional.
! 745: */
! 746: static int
! 747: sudo_ldap_check_runas(LDAP *ld, LDAPMessage *entry)
! 748: {
! 749: int ret;
! 750:
! 751: if (!entry)
! 752: return FALSE;
! 753:
! 754: ret = sudo_ldap_check_runas_user(ld, entry) != FALSE &&
! 755: sudo_ldap_check_runas_group(ld, entry) != FALSE;
! 756:
! 757: return ret;
! 758: }
! 759:
! 760: /*
! 761: * Walk through search results and return TRUE if we have a command match,
! 762: * FALSE if disallowed and UNSPEC if not matched.
! 763: */
! 764: static int
! 765: sudo_ldap_check_command(LDAP *ld, LDAPMessage *entry, int *setenv_implied)
! 766: {
! 767: struct berval **bv, **p;
! 768: char *allowed_cmnd, *allowed_args, *val;
! 769: int foundbang, ret = UNSPEC;
! 770:
! 771: if (!entry)
! 772: return ret;
! 773:
! 774: bv = ldap_get_values_len(ld, entry, "sudoCommand");
! 775: if (bv == NULL)
! 776: return ret;
! 777:
! 778: for (p = bv; *p != NULL && ret != FALSE; p++) {
! 779: val = (*p)->bv_val;
! 780: /* Match against ALL ? */
! 781: if (!strcmp(val, "ALL")) {
! 782: ret = TRUE;
! 783: if (setenv_implied != NULL)
! 784: *setenv_implied = TRUE;
! 785: DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2);
! 786: continue;
! 787: }
! 788:
! 789: /* check for !command */
! 790: if (*val == '!') {
! 791: foundbang = TRUE;
! 792: allowed_cmnd = estrdup(1 + val); /* !command */
! 793: } else {
! 794: foundbang = FALSE;
! 795: allowed_cmnd = estrdup(val); /* command */
! 796: }
! 797:
! 798: /* split optional args away from command */
! 799: allowed_args = strchr(allowed_cmnd, ' ');
! 800: if (allowed_args)
! 801: *allowed_args++ = '\0';
! 802:
! 803: /* check the command like normal */
! 804: if (command_matches(allowed_cmnd, allowed_args)) {
! 805: /*
! 806: * If allowed (no bang) set ret but keep on checking.
! 807: * If disallowed (bang), exit loop.
! 808: */
! 809: ret = foundbang ? FALSE : TRUE;
! 810: }
! 811: DPRINTF(("ldap sudoCommand '%s' ... %s", val,
! 812: ret == TRUE ? "MATCH!" : "not"), 2);
! 813:
! 814: efree(allowed_cmnd); /* cleanup */
! 815: }
! 816:
! 817: ldap_value_free_len(bv); /* more cleanup */
! 818:
! 819: return ret;
! 820: }
! 821:
! 822: /*
! 823: * Search for boolean "option" in sudoOption.
! 824: * Returns TRUE if found and allowed, FALSE if negated, else UNSPEC.
! 825: */
! 826: static int
! 827: sudo_ldap_check_bool(LDAP *ld, LDAPMessage *entry, char *option)
! 828: {
! 829: struct berval **bv, **p;
! 830: char ch, *var;
! 831: int ret = UNSPEC;
! 832:
! 833: if (entry == NULL)
! 834: return UNSPEC;
! 835:
! 836: bv = ldap_get_values_len(ld, entry, "sudoOption");
! 837: if (bv == NULL)
! 838: return ret;
! 839:
! 840: /* walk through options */
! 841: for (p = bv; *p != NULL; p++) {
! 842: var = (*p)->bv_val;;
! 843: DPRINTF(("ldap sudoOption: '%s'", var), 2);
! 844:
! 845: if ((ch = *var) == '!')
! 846: var++;
! 847: if (strcmp(var, option) == 0)
! 848: ret = (ch != '!');
! 849: }
! 850:
! 851: ldap_value_free_len(bv);
! 852:
! 853: return ret;
! 854: }
! 855:
! 856: /*
! 857: * Read sudoOption and modify the defaults as we go. This is used once
! 858: * from the cn=defaults entry and also once when a final sudoRole is matched.
! 859: */
! 860: static void
! 861: sudo_ldap_parse_options(LDAP *ld, LDAPMessage *entry)
! 862: {
! 863: struct berval **bv, **p;
! 864: char op, *var, *val;
! 865:
! 866: if (entry == NULL)
! 867: return;
! 868:
! 869: bv = ldap_get_values_len(ld, entry, "sudoOption");
! 870: if (bv == NULL)
! 871: return;
! 872:
! 873: /* walk through options */
! 874: for (p = bv; *p != NULL; p++) {
! 875: var = estrdup((*p)->bv_val);
! 876: DPRINTF(("ldap sudoOption: '%s'", var), 2);
! 877:
! 878: /* check for equals sign past first char */
! 879: val = strchr(var, '=');
! 880: if (val > var) {
! 881: *val++ = '\0'; /* split on = and truncate var */
! 882: op = *(val - 2); /* peek for += or -= cases */
! 883: if (op == '+' || op == '-') {
! 884: *(val - 2) = '\0'; /* found, remove extra char */
! 885: /* case var+=val or var-=val */
! 886: set_default(var, val, (int) op);
! 887: } else {
! 888: /* case var=val */
! 889: set_default(var, val, TRUE);
! 890: }
! 891: } else if (*var == '!') {
! 892: /* case !var Boolean False */
! 893: set_default(var + 1, NULL, FALSE);
! 894: } else {
! 895: /* case var Boolean True */
! 896: set_default(var, NULL, TRUE);
! 897: }
! 898: efree(var);
! 899: }
! 900:
! 901: ldap_value_free_len(bv);
! 902: }
! 903:
! 904: /*
! 905: * Build an LDAP timefilter.
! 906: *
! 907: * Stores a filter in the buffer that makes sure only entries
! 908: * are selected that have a sudoNotBefore in the past and a
! 909: * sudoNotAfter in the future, i.e. a filter of the following
! 910: * structure (spaced out a little more for better readability:
! 911: *
! 912: * (&
! 913: * (|
! 914: * (!(sudoNotAfter=*))
! 915: * (sudoNotAfter>__now__)
! 916: * )
! 917: * (|
! 918: * (!(sudoNotBefore=*))
! 919: * (sudoNotBefore<__now__)
! 920: * )
! 921: * )
! 922: *
! 923: * If either the sudoNotAfter or sudoNotBefore attributes are missing,
! 924: * no time restriction shall be imposed.
! 925: */
! 926: static int
! 927: sudo_ldap_timefilter(char *buffer, size_t buffersize)
! 928: {
! 929: struct tm *tp;
! 930: time_t now;
! 931: char timebuffer[16];
! 932: int bytes = 0;
! 933:
! 934: /* Make sure we have a formatted timestamp for __now__. */
! 935: time(&now);
! 936: if ((tp = gmtime(&now)) == NULL) {
! 937: warning(_("unable to get GMT time"));
! 938: goto done;
! 939: }
! 940:
! 941: /* Format the timestamp according to the RFC. */
! 942: if (strftime(timebuffer, sizeof(timebuffer), "%Y%m%d%H%M%SZ", tp) == 0) {
! 943: warning(_("unable to format timestamp"));
! 944: goto done;
! 945: }
! 946:
! 947: /* Build filter. */
! 948: bytes = snprintf(buffer, buffersize, "(&(|(!(sudoNotAfter=*))(sudoNotAfter>=%s))(|(!(sudoNotBefore=*))(sudoNotBefore<=%s)))",
! 949: timebuffer, timebuffer);
! 950: if (bytes < 0 || bytes >= buffersize) {
! 951: warning(_("unable to build time filter"));
! 952: bytes = 0;
! 953: }
! 954:
! 955: done:
! 956: return bytes;
! 957: }
! 958:
! 959: /*
! 960: * Builds up a filter to search for default settings
! 961: */
! 962: static char *
! 963: sudo_ldap_build_default_filter()
! 964: {
! 965: char *filt;
! 966:
! 967: if (ldap_conf.search_filter)
! 968: easprintf(&filt, "(&%s(cn=defaults))", ldap_conf.search_filter);
! 969: else
! 970: filt = estrdup("cn=defaults");
! 971: return filt;
! 972: }
! 973:
! 974: /*
! 975: * Builds up a filter to check against LDAP.
! 976: */
! 977: static char *
! 978: sudo_ldap_build_pass1(struct passwd *pw)
! 979: {
! 980: struct group *grp;
! 981: char *buf, timebuffer[TIMEFILTER_LENGTH];
! 982: struct group_list *grlist;
! 983: size_t sz = 0;
! 984: int i;
! 985:
! 986: /* Start with LDAP search filter length + 3 */
! 987: if (ldap_conf.search_filter)
! 988: sz += strlen(ldap_conf.search_filter) + 3;
! 989:
! 990: /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */
! 991: sz += 29 + strlen(pw->pw_name);
! 992:
! 993: /* Add space for primary and supplementary groups */
! 994: if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
! 995: sz += 12 + strlen(grp->gr_name);
! 996: }
! 997: if ((grlist = get_group_list(pw)) != NULL) {
! 998: for (i = 0; i < grlist->ngroups; i++) {
! 999: if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
! 1000: continue;
! 1001: sz += 12 + strlen(grlist->groups[i]);
! 1002: }
! 1003: }
! 1004:
! 1005: /* If timed, add space for time limits. */
! 1006: if (ldap_conf.timed)
! 1007: sz += TIMEFILTER_LENGTH;
! 1008: buf = emalloc(sz);
! 1009: *buf = '\0';
! 1010:
! 1011: /*
! 1012: * If timed or using a search filter, start a global AND clause to
! 1013: * contain the search filter, search criteria, and time restriction.
! 1014: */
! 1015: if (ldap_conf.timed || ldap_conf.search_filter)
! 1016: (void) strlcpy(buf, "(&", sz);
! 1017:
! 1018: if (ldap_conf.search_filter)
! 1019: (void) strlcat(buf, ldap_conf.search_filter, sz);
! 1020:
! 1021: /* Global OR + sudoUser=user_name filter */
! 1022: (void) strlcat(buf, "(|(sudoUser=", sz);
! 1023: (void) strlcat(buf, pw->pw_name, sz);
! 1024: (void) strlcat(buf, ")", sz);
! 1025:
! 1026: /* Append primary group */
! 1027: if (grp != NULL) {
! 1028: (void) strlcat(buf, "(sudoUser=%", sz);
! 1029: (void) strlcat(buf, grp->gr_name, sz);
! 1030: (void) strlcat(buf, ")", sz);
! 1031: }
! 1032:
! 1033: /* Append supplementary groups */
! 1034: if (grlist != NULL) {
! 1035: for (i = 0; i < grlist->ngroups; i++) {
! 1036: if (grp != NULL && strcasecmp(grlist->groups[i], grp->gr_name) == 0)
! 1037: continue;
! 1038: (void) strlcat(buf, "(sudoUser=%", sz);
! 1039: (void) strlcat(buf, grlist->groups[i], sz);
! 1040: (void) strlcat(buf, ")", sz);
! 1041: }
! 1042: }
! 1043:
! 1044: /* Done with groups. */
! 1045: if (grlist != NULL)
! 1046: grlist_delref(grlist);
! 1047: if (grp != NULL)
! 1048: gr_delref(grp);
! 1049:
! 1050: /* Add ALL to list and end the global OR */
! 1051: if (strlcat(buf, "(sudoUser=ALL)", sz) >= sz)
! 1052: errorx(1, _("sudo_ldap_build_pass1 allocation mismatch"));
! 1053:
! 1054: /* Add the time restriction, or simply end the global OR. */
! 1055: if (ldap_conf.timed) {
! 1056: strlcat(buf, ")", sz); /* closes the global OR */
! 1057: sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
! 1058: strlcat(buf, timebuffer, sz);
! 1059: } else if (ldap_conf.search_filter) {
! 1060: strlcat(buf, ")", sz); /* closes the global OR */
! 1061: }
! 1062: strlcat(buf, ")", sz); /* closes the global OR or the global AND */
! 1063:
! 1064: return buf;
! 1065: }
! 1066:
! 1067: /*
! 1068: * Builds up a filter to check against netgroup entries in LDAP.
! 1069: */
! 1070: static char *
! 1071: sudo_ldap_build_pass2(void)
! 1072: {
! 1073: char *filt, timebuffer[TIMEFILTER_LENGTH];
! 1074:
! 1075: if (ldap_conf.timed)
! 1076: sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
! 1077:
! 1078: /*
! 1079: * Match all sudoUsers beginning with a '+'.
! 1080: * If a search filter or time restriction is specified,
! 1081: * those get ANDed in to the expression.
! 1082: */
! 1083: easprintf(&filt, "%s%s(sudoUser=+*)%s%s",
! 1084: (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
! 1085: ldap_conf.search_filter ? ldap_conf.search_filter : "",
! 1086: ldap_conf.timed ? timebuffer : "",
! 1087: (ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
! 1088:
! 1089: return filt;
! 1090: }
! 1091:
! 1092: static void
! 1093: sudo_ldap_read_secret(const char *path)
! 1094: {
! 1095: FILE *fp;
! 1096: char buf[LINE_MAX], *cp;
! 1097:
! 1098: if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
! 1099: if (fgets(buf, sizeof(buf), fp) != NULL) {
! 1100: if ((cp = strchr(buf, '\n')) != NULL)
! 1101: *cp = '\0';
! 1102: /* copy to bindpw and binddn */
! 1103: efree(ldap_conf.bindpw);
! 1104: ldap_conf.bindpw = estrdup(buf);
! 1105: efree(ldap_conf.binddn);
! 1106: ldap_conf.binddn = ldap_conf.rootbinddn;
! 1107: ldap_conf.rootbinddn = NULL;
! 1108: }
! 1109: fclose(fp);
! 1110: }
! 1111: }
! 1112:
! 1113: static int
! 1114: sudo_ldap_read_config(void)
! 1115: {
! 1116: FILE *fp;
! 1117: char *cp, *keyword, *value;
! 1118: struct ldap_config_table *cur;
! 1119:
! 1120: /* defaults */
! 1121: ldap_conf.version = 3;
! 1122: ldap_conf.port = -1;
! 1123: ldap_conf.tls_checkpeer = -1;
! 1124: ldap_conf.timelimit = -1;
! 1125: ldap_conf.timeout = -1;
! 1126: ldap_conf.bind_timelimit = -1;
! 1127: ldap_conf.use_sasl = -1;
! 1128: ldap_conf.rootuse_sasl = -1;
! 1129: ldap_conf.deref = -1;
! 1130:
! 1131: if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL)
! 1132: return FALSE;
! 1133:
! 1134: while ((cp = sudo_parseln(fp)) != NULL) {
! 1135: if (*cp == '\0')
! 1136: continue; /* skip empty line */
! 1137:
! 1138: /* split into keyword and value */
! 1139: keyword = cp;
! 1140: while (*cp && !isblank((unsigned char) *cp))
! 1141: cp++;
! 1142: if (*cp)
! 1143: *cp++ = '\0'; /* terminate keyword */
! 1144:
! 1145: /* skip whitespace before value */
! 1146: while (isblank((unsigned char) *cp))
! 1147: cp++;
! 1148: value = cp;
! 1149:
! 1150: /* Look up keyword in config table. */
! 1151: for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
! 1152: if (strcasecmp(keyword, cur->conf_str) == 0) {
! 1153: switch (cur->type) {
! 1154: case CONF_DEREF_VAL:
! 1155: if (strcasecmp(value, "searching") == 0)
! 1156: *(int *)(cur->valp) = LDAP_DEREF_SEARCHING;
! 1157: else if (strcasecmp(value, "finding") == 0)
! 1158: *(int *)(cur->valp) = LDAP_DEREF_FINDING;
! 1159: else if (strcasecmp(value, "always") == 0)
! 1160: *(int *)(cur->valp) = LDAP_DEREF_ALWAYS;
! 1161: else
! 1162: *(int *)(cur->valp) = LDAP_DEREF_NEVER;
! 1163: break;
! 1164: case CONF_BOOL:
! 1165: *(int *)(cur->valp) = atobool(value) == TRUE;
! 1166: break;
! 1167: case CONF_INT:
! 1168: *(int *)(cur->valp) = atoi(value);
! 1169: break;
! 1170: case CONF_STR:
! 1171: efree(*(char **)(cur->valp));
! 1172: *(char **)(cur->valp) = estrdup(value);
! 1173: break;
! 1174: case CONF_LIST_STR:
! 1175: {
! 1176: struct ldap_config_list_str **p;
! 1177: size_t len = strlen(value);
! 1178:
! 1179: if (len > 0) {
! 1180: p = (struct ldap_config_list_str **)cur->valp;
! 1181: while (*p != NULL)
! 1182: p = &(*p)->next;
! 1183: *p = emalloc(sizeof(struct ldap_config_list_str) + len);
! 1184: memcpy((*p)->val, value, len + 1);
! 1185: (*p)->next = NULL;
! 1186: }
! 1187: }
! 1188: break;
! 1189: }
! 1190: break;
! 1191: }
! 1192: }
! 1193: }
! 1194: fclose(fp);
! 1195:
! 1196: if (!ldap_conf.host)
! 1197: ldap_conf.host = estrdup("localhost");
! 1198:
! 1199: if (ldap_conf.debug > 1) {
! 1200: sudo_printf(SUDO_CONV_ERROR_MSG, "LDAP Config Summary\n");
! 1201: sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
! 1202: if (ldap_conf.uri) {
! 1203: struct ldap_config_list_str *uri = ldap_conf.uri;
! 1204:
! 1205: do {
! 1206: sudo_printf(SUDO_CONV_ERROR_MSG, "uri %s\n",
! 1207: uri->val);
! 1208: } while ((uri = uri->next) != NULL);
! 1209: } else {
! 1210: sudo_printf(SUDO_CONV_ERROR_MSG, "host %s\n",
! 1211: ldap_conf.host ? ldap_conf.host : "(NONE)");
! 1212: sudo_printf(SUDO_CONV_ERROR_MSG, "port %d\n",
! 1213: ldap_conf.port);
! 1214: }
! 1215: sudo_printf(SUDO_CONV_ERROR_MSG, "ldap_version %d\n",
! 1216: ldap_conf.version);
! 1217:
! 1218: if (ldap_conf.base) {
! 1219: struct ldap_config_list_str *base = ldap_conf.base;
! 1220: do {
! 1221: sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
! 1222: base->val);
! 1223: } while ((base = base->next) != NULL);
! 1224: } else {
! 1225: sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
! 1226: "(NONE: LDAP disabled)");
! 1227: }
! 1228: if (ldap_conf.search_filter) {
! 1229: sudo_printf(SUDO_CONV_ERROR_MSG, "search_filter %s\n",
! 1230: ldap_conf.search_filter);
! 1231: }
! 1232: sudo_printf(SUDO_CONV_ERROR_MSG, "binddn %s\n",
! 1233: ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
! 1234: sudo_printf(SUDO_CONV_ERROR_MSG, "bindpw %s\n",
! 1235: ldap_conf.bindpw ? ldap_conf.bindpw : "(anonymous)");
! 1236: if (ldap_conf.bind_timelimit > 0) {
! 1237: sudo_printf(SUDO_CONV_ERROR_MSG, "bind_timelimit %d\n",
! 1238: ldap_conf.bind_timelimit);
! 1239: }
! 1240: if (ldap_conf.timelimit > 0) {
! 1241: sudo_printf(SUDO_CONV_ERROR_MSG, "timelimit %d\n",
! 1242: ldap_conf.timelimit);
! 1243: }
! 1244: if (ldap_conf.deref != -1) {
! 1245: sudo_printf(SUDO_CONV_ERROR_MSG, "deref %d\n",
! 1246: ldap_conf.deref);
! 1247: }
! 1248: sudo_printf(SUDO_CONV_ERROR_MSG, "ssl %s\n",
! 1249: ldap_conf.ssl ? ldap_conf.ssl : "(no)");
! 1250: if (ldap_conf.tls_checkpeer != -1) {
! 1251: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_checkpeer %s\n",
! 1252: ldap_conf.tls_checkpeer ? "(yes)" : "(no)");
! 1253: }
! 1254: if (ldap_conf.tls_cacertfile != NULL) {
! 1255: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertfile %s\n",
! 1256: ldap_conf.tls_cacertfile);
! 1257: }
! 1258: if (ldap_conf.tls_cacertdir != NULL) {
! 1259: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cacertdir %s\n",
! 1260: ldap_conf.tls_cacertdir);
! 1261: }
! 1262: if (ldap_conf.tls_random_file != NULL) {
! 1263: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_random_file %s\n",
! 1264: ldap_conf.tls_random_file);
! 1265: }
! 1266: if (ldap_conf.tls_cipher_suite != NULL) {
! 1267: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_cipher_suite %s\n",
! 1268: ldap_conf.tls_cipher_suite);
! 1269: }
! 1270: if (ldap_conf.tls_certfile != NULL) {
! 1271: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_certfile %s\n",
! 1272: ldap_conf.tls_certfile);
! 1273: }
! 1274: if (ldap_conf.tls_keyfile != NULL) {
! 1275: sudo_printf(SUDO_CONV_ERROR_MSG, "tls_keyfile %s\n",
! 1276: ldap_conf.tls_keyfile);
! 1277: }
! 1278: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 1279: if (ldap_conf.use_sasl != -1) {
! 1280: sudo_printf(SUDO_CONV_ERROR_MSG, "use_sasl %s\n",
! 1281: ldap_conf.use_sasl ? "yes" : "no");
! 1282: sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_auth_id %s\n",
! 1283: ldap_conf.sasl_auth_id ? ldap_conf.sasl_auth_id : "(NONE)");
! 1284: sudo_printf(SUDO_CONV_ERROR_MSG, "rootuse_sasl %d\n",
! 1285: ldap_conf.rootuse_sasl);
! 1286: sudo_printf(SUDO_CONV_ERROR_MSG, "rootsasl_auth_id %s\n",
! 1287: ldap_conf.rootsasl_auth_id ? ldap_conf.rootsasl_auth_id : "(NONE)");
! 1288: sudo_printf(SUDO_CONV_ERROR_MSG, "sasl_secprops %s\n",
! 1289: ldap_conf.sasl_secprops ? ldap_conf.sasl_secprops : "(NONE)");
! 1290: sudo_printf(SUDO_CONV_ERROR_MSG, "krb5_ccname %s\n",
! 1291: ldap_conf.krb5_ccname ? ldap_conf.krb5_ccname : "(NONE)");
! 1292: }
! 1293: #endif
! 1294: sudo_printf(SUDO_CONV_ERROR_MSG, "===================\n");
! 1295: }
! 1296: if (!ldap_conf.base)
! 1297: return FALSE; /* if no base is defined, ignore LDAP */
! 1298:
! 1299: if (ldap_conf.bind_timelimit > 0)
! 1300: ldap_conf.bind_timelimit *= 1000; /* convert to ms */
! 1301:
! 1302: /*
! 1303: * Interpret SSL option
! 1304: */
! 1305: if (ldap_conf.ssl != NULL) {
! 1306: if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
! 1307: ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
! 1308: else if (atobool(ldap_conf.ssl) == TRUE)
! 1309: ldap_conf.ssl_mode = SUDO_LDAP_SSL;
! 1310: }
! 1311:
! 1312: #if defined(HAVE_LDAPSSL_SET_STRENGTH) && !defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
! 1313: if (ldap_conf.tls_checkpeer != -1) {
! 1314: ldapssl_set_strength(NULL,
! 1315: ldap_conf.tls_checkpeer ? LDAPSSL_AUTH_CERT : LDAPSSL_AUTH_WEAK);
! 1316: }
! 1317: #endif
! 1318:
! 1319: #ifndef HAVE_LDAP_INITIALIZE
! 1320: /* Convert uri list to host list if no ldap_initialize(). */
! 1321: if (ldap_conf.uri) {
! 1322: struct ldap_config_list_str *uri = ldap_conf.uri;
! 1323: if (sudo_ldap_parse_uri(uri) != 0)
! 1324: return FALSE;
! 1325: do {
! 1326: ldap_conf.uri = uri->next;
! 1327: efree(uri);
! 1328: } while ((uri = ldap_conf.uri));
! 1329: ldap_conf.port = LDAP_PORT;
! 1330: }
! 1331: #endif
! 1332:
! 1333: if (!ldap_conf.uri) {
! 1334: /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
! 1335: if (ldap_conf.port < 0)
! 1336: ldap_conf.port =
! 1337: ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
! 1338:
! 1339: #ifdef HAVE_LDAP_CREATE
! 1340: /*
! 1341: * Cannot specify port directly to ldap_create(), each host must
! 1342: * include :port to override the default.
! 1343: */
! 1344: if (ldap_conf.port != LDAP_PORT)
! 1345: sudo_ldap_conf_add_ports();
! 1346: #endif
! 1347: }
! 1348:
! 1349: /* If search filter is not parenthesized, make it so. */
! 1350: if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') {
! 1351: size_t len = strlen(ldap_conf.search_filter);
! 1352: cp = ldap_conf.search_filter;
! 1353: ldap_conf.search_filter = emalloc(len + 3);
! 1354: ldap_conf.search_filter[0] = '(';
! 1355: memcpy(ldap_conf.search_filter + 1, cp, len);
! 1356: ldap_conf.search_filter[len + 1] = ')';
! 1357: ldap_conf.search_filter[len + 2] = '\0';
! 1358: efree(cp);
! 1359: }
! 1360:
! 1361: /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
! 1362: if (ldap_conf.rootbinddn)
! 1363: sudo_ldap_read_secret(_PATH_LDAP_SECRET);
! 1364:
! 1365: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 1366: /*
! 1367: * Make sure we can open the file specified by krb5_ccname.
! 1368: */
! 1369: if (ldap_conf.krb5_ccname != NULL) {
! 1370: if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 ||
! 1371: strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) {
! 1372: value = ldap_conf.krb5_ccname +
! 1373: (ldap_conf.krb5_ccname[4] == ':' ? 5 : 7);
! 1374: if ((fp = fopen(value, "r")) != NULL) {
! 1375: DPRINTF(("using krb5 credential cache: %s", value), 1);
! 1376: fclose(fp);
! 1377: } else {
! 1378: /* Can't open it, just ignore the entry. */
! 1379: DPRINTF(("unable to open krb5 credential cache: %s", value), 1);
! 1380: efree(ldap_conf.krb5_ccname);
! 1381: ldap_conf.krb5_ccname = NULL;
! 1382: }
! 1383: }
! 1384: }
! 1385: #endif
! 1386: return TRUE;
! 1387: }
! 1388:
! 1389: /*
! 1390: * Extract the dn from an entry and return the first rdn from it.
! 1391: */
! 1392: static char *
! 1393: sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
! 1394: {
! 1395: #ifdef HAVE_LDAP_STR2DN
! 1396: char *dn, *rdn = NULL;
! 1397: LDAPDN tmpDN;
! 1398:
! 1399: if ((dn = ldap_get_dn(ld, entry)) == NULL)
! 1400: return NULL;
! 1401: if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
! 1402: ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
! 1403: ldap_dnfree(tmpDN);
! 1404: }
! 1405: ldap_memfree(dn);
! 1406: return rdn;
! 1407: #else
! 1408: char *dn, **edn;
! 1409:
! 1410: if ((dn = ldap_get_dn(ld, entry)) == NULL)
! 1411: return NULL;
! 1412: edn = ldap_explode_dn(dn, 1);
! 1413: ldap_memfree(dn);
! 1414: return edn ? edn[0] : NULL;
! 1415: #endif
! 1416: }
! 1417:
! 1418: /*
! 1419: * Fetch and display the global Options.
! 1420: */
! 1421: static int
! 1422: sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
! 1423: struct lbuf *lbuf)
! 1424: {
! 1425: struct berval **bv, **p;
! 1426: struct timeval tv, *tvp = NULL;
! 1427: struct ldap_config_list_str *base;
! 1428: struct sudo_ldap_handle *handle = nss->handle;
! 1429: LDAP *ld;
! 1430: LDAPMessage *entry, *result;
! 1431: char *prefix, *filt;
! 1432: int rc, count = 0;
! 1433:
! 1434: if (handle == NULL || handle->ld == NULL)
! 1435: goto done;
! 1436: ld = handle->ld;
! 1437:
! 1438: filt = sudo_ldap_build_default_filter();
! 1439: for (base = ldap_conf.base; base != NULL; base = base->next) {
! 1440: if (ldap_conf.timeout > 0) {
! 1441: tv.tv_sec = ldap_conf.timeout;
! 1442: tv.tv_usec = 0;
! 1443: tvp = &tv;
! 1444: }
! 1445: result = NULL;
! 1446: rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
! 1447: filt, NULL, 0, NULL, NULL, tvp, 0, &result);
! 1448: if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
! 1449: bv = ldap_get_values_len(ld, entry, "sudoOption");
! 1450: if (bv != NULL) {
! 1451: if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
! 1452: prefix = " ";
! 1453: else
! 1454: prefix = ", ";
! 1455: for (p = bv; *p != NULL; p++) {
! 1456: lbuf_append(lbuf, "%s%s", prefix, (*p)->bv_val);
! 1457: prefix = ", ";
! 1458: count++;
! 1459: }
! 1460: ldap_value_free_len(bv);
! 1461: }
! 1462: }
! 1463: if (result)
! 1464: ldap_msgfree(result);
! 1465: }
! 1466: efree(filt);
! 1467: done:
! 1468: return count;
! 1469: }
! 1470:
! 1471: /*
! 1472: * STUB
! 1473: */
! 1474: static int
! 1475: sudo_ldap_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
! 1476: struct lbuf *lbuf)
! 1477: {
! 1478: return 0;
! 1479: }
! 1480:
! 1481: /*
! 1482: * Print a record in the short form, ala file sudoers.
! 1483: */
! 1484: static int
! 1485: sudo_ldap_display_entry_short(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
! 1486: {
! 1487: struct berval **bv, **p;
! 1488: int count = 0;
! 1489:
! 1490: lbuf_append(lbuf, " (");
! 1491:
! 1492: /* get the RunAsUser Values from the entry */
! 1493: bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
! 1494: if (bv == NULL)
! 1495: bv = ldap_get_values_len(ld, entry, "sudoRunAs");
! 1496: if (bv != NULL) {
! 1497: for (p = bv; *p != NULL; p++) {
! 1498: lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
! 1499: }
! 1500: ldap_value_free_len(bv);
! 1501: } else
! 1502: lbuf_append(lbuf, "%s", def_runas_default);
! 1503:
! 1504: /* get the RunAsGroup Values from the entry */
! 1505: bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
! 1506: if (bv != NULL) {
! 1507: lbuf_append(lbuf, " : ");
! 1508: for (p = bv; *p != NULL; p++) {
! 1509: lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
! 1510: }
! 1511: ldap_value_free_len(bv);
! 1512: }
! 1513: lbuf_append(lbuf, ") ");
! 1514:
! 1515: /* get the Option Values from the entry */
! 1516: bv = ldap_get_values_len(ld, entry, "sudoOption");
! 1517: if (bv != NULL) {
! 1518: for (p = bv; *p != NULL; p++) {
! 1519: char *cp = (*p)->bv_val;
! 1520: if (*cp == '!')
! 1521: cp++;
! 1522: if (strcmp(cp, "authenticate") == 0)
! 1523: lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
! 1524: "NOPASSWD: " : "PASSWD: ");
! 1525: else if (strcmp(cp, "noexec") == 0)
! 1526: lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
! 1527: "EXEC: " : "NOEXEC: ");
! 1528: else if (strcmp(cp, "setenv") == 0)
! 1529: lbuf_append(lbuf, (*p)->bv_val[0] == '!' ?
! 1530: "NOSETENV: " : "SETENV: ");
! 1531: }
! 1532: ldap_value_free_len(bv);
! 1533: }
! 1534:
! 1535: /* get the Command Values from the entry */
! 1536: bv = ldap_get_values_len(ld, entry, "sudoCommand");
! 1537: if (bv != NULL) {
! 1538: for (p = bv; *p != NULL; p++) {
! 1539: lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
! 1540: count++;
! 1541: }
! 1542: ldap_value_free_len(bv);
! 1543: }
! 1544: lbuf_append(lbuf, "\n");
! 1545:
! 1546: return count;
! 1547: }
! 1548:
! 1549: /*
! 1550: * Print a record in the long form.
! 1551: */
! 1552: static int
! 1553: sudo_ldap_display_entry_long(LDAP *ld, LDAPMessage *entry, struct lbuf *lbuf)
! 1554: {
! 1555: struct berval **bv, **p;
! 1556: char *rdn;
! 1557: int count = 0;
! 1558:
! 1559: /* extract the dn, only show the first rdn */
! 1560: rdn = sudo_ldap_get_first_rdn(ld, entry);
! 1561: if (rdn != NULL)
! 1562: lbuf_append(lbuf, _("\nLDAP Role: %s\n"), rdn);
! 1563: else
! 1564: lbuf_append(lbuf, _("\nLDAP Role: UNKNOWN\n"));
! 1565: if (rdn)
! 1566: ldap_memfree(rdn);
! 1567:
! 1568: /* get the RunAsUser Values from the entry */
! 1569: lbuf_append(lbuf, " RunAsUsers: ");
! 1570: bv = ldap_get_values_len(ld, entry, "sudoRunAsUser");
! 1571: if (bv == NULL)
! 1572: bv = ldap_get_values_len(ld, entry, "sudoRunAs");
! 1573: if (bv != NULL) {
! 1574: for (p = bv; *p != NULL; p++) {
! 1575: lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
! 1576: }
! 1577: ldap_value_free_len(bv);
! 1578: } else
! 1579: lbuf_append(lbuf, "%s", def_runas_default);
! 1580: lbuf_append(lbuf, "\n");
! 1581:
! 1582: /* get the RunAsGroup Values from the entry */
! 1583: bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup");
! 1584: if (bv != NULL) {
! 1585: lbuf_append(lbuf, " RunAsGroups: ");
! 1586: for (p = bv; *p != NULL; p++) {
! 1587: lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
! 1588: }
! 1589: ldap_value_free_len(bv);
! 1590: lbuf_append(lbuf, "\n");
! 1591: }
! 1592:
! 1593: /* get the Option Values from the entry */
! 1594: bv = ldap_get_values_len(ld, entry, "sudoOption");
! 1595: if (bv != NULL) {
! 1596: lbuf_append(lbuf, " Options: ");
! 1597: for (p = bv; *p != NULL; p++) {
! 1598: lbuf_append(lbuf, "%s%s", p != bv ? ", " : "", (*p)->bv_val);
! 1599: }
! 1600: ldap_value_free_len(bv);
! 1601: lbuf_append(lbuf, "\n");
! 1602: }
! 1603:
! 1604: /*
! 1605: * Display order attribute if present. This attribute is single valued,
! 1606: * so there is no need for a loop.
! 1607: */
! 1608: bv = ldap_get_values_len(ld, entry, "sudoOrder");
! 1609: if (bv != NULL) {
! 1610: if (*bv != NULL) {
! 1611: lbuf_append(lbuf, _(" Order: %s\n"), (*bv)->bv_val);
! 1612: }
! 1613: ldap_value_free_len(bv);
! 1614: }
! 1615:
! 1616: /* Get the command values from the entry. */
! 1617: bv = ldap_get_values_len(ld, entry, "sudoCommand");
! 1618: if (bv != NULL) {
! 1619: lbuf_append(lbuf, _(" Commands:\n"));
! 1620: for (p = bv; *p != NULL; p++) {
! 1621: lbuf_append(lbuf, "\t%s\n", (*p)->bv_val);
! 1622: count++;
! 1623: }
! 1624: ldap_value_free_len(bv);
! 1625: }
! 1626:
! 1627: return count;
! 1628: }
! 1629:
! 1630: /*
! 1631: * Like sudo_ldap_lookup(), except we just print entries.
! 1632: */
! 1633: static int
! 1634: sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
! 1635: struct lbuf *lbuf)
! 1636: {
! 1637: struct sudo_ldap_handle *handle = nss->handle;
! 1638: LDAP *ld;
! 1639: struct ldap_result *lres;
! 1640: LDAPMessage *entry;
! 1641: int i, count = 0;
! 1642:
! 1643: if (handle == NULL || handle->ld == NULL)
! 1644: goto done;
! 1645: ld = handle->ld;
! 1646:
! 1647: DPRINTF(("ldap search for command list"), 1);
! 1648: lres = sudo_ldap_result_get(nss, pw);
! 1649:
! 1650: /* Display all matching entries. */
! 1651: for (i = 0; i < lres->nentries; i++) {
! 1652: entry = lres->entries[i].entry;
! 1653: if (long_list)
! 1654: count += sudo_ldap_display_entry_long(ld, entry, lbuf);
! 1655: else
! 1656: count += sudo_ldap_display_entry_short(ld, entry, lbuf);
! 1657: }
! 1658:
! 1659: done:
! 1660: return count;
! 1661: }
! 1662:
! 1663: static int
! 1664: sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
! 1665: {
! 1666: struct sudo_ldap_handle *handle = nss->handle;
! 1667: LDAP *ld;
! 1668: struct ldap_result *lres;
! 1669: LDAPMessage *entry;
! 1670: int i, found = FALSE;
! 1671:
! 1672: if (handle == NULL || handle->ld == NULL)
! 1673: goto done;
! 1674: ld = handle->ld;
! 1675:
! 1676: /*
! 1677: * The sudo_ldap_result_get() function returns all nodes that match
! 1678: * the user and the host.
! 1679: */
! 1680: DPRINTF(("ldap search for command list"), 1);
! 1681: lres = sudo_ldap_result_get(nss, pw);
! 1682: for (i = 0; i < lres->nentries; i++) {
! 1683: entry = lres->entries[i].entry;
! 1684: if (sudo_ldap_check_command(ld, entry, NULL) &&
! 1685: sudo_ldap_check_runas(ld, entry)) {
! 1686: found = TRUE;
! 1687: goto done;
! 1688: }
! 1689: }
! 1690:
! 1691: done:
! 1692: if (found)
! 1693: printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
! 1694: user_args ? " " : "", user_args ? user_args : "");
! 1695: return !found;
! 1696: }
! 1697:
! 1698: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 1699: static int
! 1700: sudo_ldap_sasl_interact(LDAP *ld, unsigned int flags, void *_auth_id,
! 1701: void *_interact)
! 1702: {
! 1703: char *auth_id = (char *)_auth_id;
! 1704: sasl_interact_t *interact = (sasl_interact_t *)_interact;
! 1705:
! 1706: for (; interact->id != SASL_CB_LIST_END; interact++) {
! 1707: if (interact->id != SASL_CB_USER)
! 1708: return LDAP_PARAM_ERROR;
! 1709:
! 1710: if (auth_id != NULL)
! 1711: interact->result = auth_id;
! 1712: else if (interact->defresult != NULL)
! 1713: interact->result = interact->defresult;
! 1714: else
! 1715: interact->result = "";
! 1716:
! 1717: interact->len = strlen(interact->result);
! 1718: #if SASL_VERSION_MAJOR < 2
! 1719: interact->result = estrdup(interact->result);
! 1720: #endif /* SASL_VERSION_MAJOR < 2 */
! 1721: }
! 1722: return LDAP_SUCCESS;
! 1723: }
! 1724: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
! 1725:
! 1726: /*
! 1727: * Set LDAP options based on the config table.
! 1728: */
! 1729: static int
! 1730: sudo_ldap_set_options(LDAP *ld)
! 1731: {
! 1732: struct ldap_config_table *cur;
! 1733: int rc;
! 1734:
! 1735: /* Set ber options */
! 1736: #ifdef LBER_OPT_DEBUG_LEVEL
! 1737: if (ldap_conf.ldap_debug)
! 1738: ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
! 1739: #endif
! 1740:
! 1741: /* Set simple LDAP options */
! 1742: for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
! 1743: LDAP *conn;
! 1744: int ival;
! 1745: char *sval;
! 1746:
! 1747: if (cur->opt_val == -1)
! 1748: continue;
! 1749:
! 1750: conn = cur->connected ? ld : NULL;
! 1751: switch (cur->type) {
! 1752: case CONF_BOOL:
! 1753: case CONF_INT:
! 1754: ival = *(int *)(cur->valp);
! 1755: if (ival >= 0) {
! 1756: rc = ldap_set_option(conn, cur->opt_val, &ival);
! 1757: if (rc != LDAP_OPT_SUCCESS) {
! 1758: warningx("ldap_set_option: %s -> %d: %s",
! 1759: cur->conf_str, ival, ldap_err2string(rc));
! 1760: return -1;
! 1761: }
! 1762: DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
! 1763: }
! 1764: break;
! 1765: case CONF_STR:
! 1766: sval = *(char **)(cur->valp);
! 1767: if (sval != NULL) {
! 1768: rc = ldap_set_option(conn, cur->opt_val, sval);
! 1769: if (rc != LDAP_OPT_SUCCESS) {
! 1770: warningx("ldap_set_option: %s -> %s: %s",
! 1771: cur->conf_str, sval, ldap_err2string(rc));
! 1772: return -1;
! 1773: }
! 1774: DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
! 1775: }
! 1776: break;
! 1777: }
! 1778: }
! 1779:
! 1780: #ifdef LDAP_OPT_TIMEOUT
! 1781: /* Convert timeout to a timeval */
! 1782: if (ldap_conf.timeout > 0) {
! 1783: struct timeval tv;
! 1784: tv.tv_sec = ldap_conf.timeout;
! 1785: tv.tv_usec = 0;
! 1786: rc = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv);
! 1787: if (rc != LDAP_OPT_SUCCESS) {
! 1788: warningx("ldap_set_option(TIMEOUT, %ld): %s",
! 1789: (long)tv.tv_sec, ldap_err2string(rc));
! 1790: return -1;
! 1791: }
! 1792: DPRINTF(("ldap_set_option(LDAP_OPT_TIMEOUT, %ld)",
! 1793: (long)tv.tv_sec), 1);
! 1794: }
! 1795: #endif
! 1796: #ifdef LDAP_OPT_NETWORK_TIMEOUT
! 1797: /* Convert bind_timelimit to a timeval */
! 1798: if (ldap_conf.bind_timelimit > 0) {
! 1799: struct timeval tv;
! 1800: tv.tv_sec = ldap_conf.bind_timelimit / 1000;
! 1801: tv.tv_usec = 0;
! 1802: rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
! 1803: if (rc != LDAP_OPT_SUCCESS) {
! 1804: warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
! 1805: (long)tv.tv_sec, ldap_err2string(rc));
! 1806: return -1;
! 1807: }
! 1808: DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)",
! 1809: (long)tv.tv_sec), 1);
! 1810: }
! 1811: #endif
! 1812:
! 1813: #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
! 1814: if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
! 1815: int val = LDAP_OPT_X_TLS_HARD;
! 1816: rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
! 1817: if (rc != LDAP_SUCCESS) {
! 1818: warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
! 1819: ldap_err2string(rc));
! 1820: return -1;
! 1821: }
! 1822: DPRINTF(("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD)"), 1);
! 1823: }
! 1824: #endif
! 1825: return 0;
! 1826: }
! 1827:
! 1828: /*
! 1829: * Create a new sudo_ldap_result structure.
! 1830: */
! 1831: static struct ldap_result *
! 1832: sudo_ldap_result_alloc(void)
! 1833: {
! 1834: struct ldap_result *result;
! 1835:
! 1836: result = emalloc(sizeof(*result));
! 1837: result->searches = NULL;
! 1838: result->nentries = 0;
! 1839: result->entries = NULL;
! 1840: result->allocated_entries = 0;
! 1841: result->user_matches = FALSE;
! 1842: result->host_matches = FALSE;
! 1843: return result;
! 1844: }
! 1845:
! 1846: /*
! 1847: * Free the ldap result structure
! 1848: */
! 1849: static void
! 1850: sudo_ldap_result_free(struct ldap_result *lres)
! 1851: {
! 1852: struct ldap_search_list *s;
! 1853:
! 1854: if (lres != NULL) {
! 1855: if (lres->nentries) {
! 1856: efree(lres->entries);
! 1857: lres->entries = NULL;
! 1858: }
! 1859: if (lres->searches) {
! 1860: while ((s = lres->searches) != NULL) {
! 1861: ldap_msgfree(s->searchresult);
! 1862: lres->searches = s->next;
! 1863: efree(s);
! 1864: }
! 1865: }
! 1866: efree(lres);
! 1867: }
! 1868: }
! 1869:
! 1870: /*
! 1871: * Add a search result to the ldap_result structure.
! 1872: */
! 1873: static struct ldap_search_list *
! 1874: sudo_ldap_result_add_search(struct ldap_result *lres, LDAP *ldap,
! 1875: LDAPMessage *searchresult)
! 1876: {
! 1877: struct ldap_search_list *s, *news;
! 1878:
! 1879: news = emalloc(sizeof(struct ldap_search_list));
! 1880: news->next = NULL;
! 1881: news->ldap = ldap;
! 1882: news->searchresult = searchresult;
! 1883:
! 1884: /* Add entry to the end of the chain (XXX - tailq instead?). */
! 1885: if (lres->searches) {
! 1886: for (s = lres->searches; s->next != NULL; s = s->next)
! 1887: continue;
! 1888: s->next = news;
! 1889: } else {
! 1890: lres->searches = news;
! 1891: }
! 1892: return news;
! 1893: }
! 1894:
! 1895: /*
! 1896: * Connect to the LDAP server specified by ld
! 1897: */
! 1898: static int
! 1899: sudo_ldap_bind_s(LDAP *ld)
! 1900: {
! 1901: int rc;
! 1902: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 1903: const char *old_ccname = user_ccname;
! 1904: # ifdef HAVE_GSS_KRB5_CCACHE_NAME
! 1905: unsigned int status;
! 1906: # endif
! 1907: #endif
! 1908:
! 1909: #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
! 1910: if (ldap_conf.rootuse_sasl == TRUE ||
! 1911: (ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE)) {
! 1912: void *auth_id = ldap_conf.rootsasl_auth_id ?
! 1913: ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id;
! 1914:
! 1915: if (ldap_conf.krb5_ccname != NULL) {
! 1916: # ifdef HAVE_GSS_KRB5_CCACHE_NAME
! 1917: if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname)
! 1918: != GSS_S_COMPLETE) {
! 1919: old_ccname = NULL;
! 1920: DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
! 1921: }
! 1922: # else
! 1923: setenv("KRB5CCNAME", ldap_conf.krb5_ccname, TRUE);
! 1924: # endif
! 1925: }
! 1926: rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
! 1927: NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id);
! 1928: if (ldap_conf.krb5_ccname != NULL) {
! 1929: # ifdef HAVE_GSS_KRB5_CCACHE_NAME
! 1930: if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE)
! 1931: DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
! 1932: # else
! 1933: if (old_ccname != NULL)
! 1934: setenv("KRB5CCNAME", old_ccname, TRUE);
! 1935: else
! 1936: unsetenv("KRB5CCNAME");
! 1937: # endif
! 1938: }
! 1939: if (rc != LDAP_SUCCESS) {
! 1940: warningx("ldap_sasl_interactive_bind_s(): %s",
! 1941: ldap_err2string(rc));
! 1942: return -1;
! 1943: }
! 1944: DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1);
! 1945: } else
! 1946: #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
! 1947: #ifdef HAVE_LDAP_SASL_BIND_S
! 1948: {
! 1949: struct berval bv;
! 1950:
! 1951: bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : "";
! 1952: bv.bv_len = strlen(bv.bv_val);
! 1953:
! 1954: rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv,
! 1955: NULL, NULL, NULL);
! 1956: if (rc != LDAP_SUCCESS) {
! 1957: warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc));
! 1958: return -1;
! 1959: }
! 1960: DPRINTF(("ldap_sasl_bind_s() ok"), 1);
! 1961: }
! 1962: #else
! 1963: {
! 1964: rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw);
! 1965: if (rc != LDAP_SUCCESS) {
! 1966: warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc));
! 1967: return -1;
! 1968: }
! 1969: DPRINTF(("ldap_simple_bind_s() ok"), 1);
! 1970: }
! 1971: #endif
! 1972: return 0;
! 1973: }
! 1974:
! 1975: /*
! 1976: * Open a connection to the LDAP server.
! 1977: * Returns 0 on success and non-zero on failure.
! 1978: */
! 1979: static int
! 1980: sudo_ldap_open(struct sudo_nss *nss)
! 1981: {
! 1982: LDAP *ld;
! 1983: int rc, ldapnoinit = FALSE;
! 1984: struct sudo_ldap_handle *handle;
! 1985:
! 1986: if (!sudo_ldap_read_config())
! 1987: return -1;
! 1988:
! 1989: /* Prevent reading of user ldaprc and system defaults. */
! 1990: if (getenv("LDAPNOINIT") == NULL) {
! 1991: ldapnoinit = TRUE;
! 1992: setenv("LDAPNOINIT", "1", TRUE);
! 1993: }
! 1994:
! 1995: /* Connect to LDAP server */
! 1996: #ifdef HAVE_LDAP_INITIALIZE
! 1997: if (ldap_conf.uri != NULL) {
! 1998: char *buf = sudo_ldap_join_uri(ldap_conf.uri);
! 1999: DPRINTF(("ldap_initialize(ld, %s)", buf), 2);
! 2000: rc = ldap_initialize(&ld, buf);
! 2001: efree(buf);
! 2002: } else
! 2003: #endif
! 2004: rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port);
! 2005: if (rc != LDAP_SUCCESS) {
! 2006: warningx(_("unable to initialize LDAP: %s"), ldap_err2string(rc));
! 2007: return -1;
! 2008: }
! 2009:
! 2010: if (ldapnoinit)
! 2011: unsetenv("LDAPNOINIT");
! 2012:
! 2013: /* Set LDAP options */
! 2014: if (sudo_ldap_set_options(ld) < 0)
! 2015: return -1;
! 2016:
! 2017: if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
! 2018: #if defined(HAVE_LDAP_START_TLS_S)
! 2019: rc = ldap_start_tls_s(ld, NULL, NULL);
! 2020: if (rc != LDAP_SUCCESS) {
! 2021: warningx("ldap_start_tls_s(): %s", ldap_err2string(rc));
! 2022: return -1;
! 2023: }
! 2024: DPRINTF(("ldap_start_tls_s() ok"), 1);
! 2025: #elif defined(HAVE_LDAP_SSL_CLIENT_INIT) && defined(HAVE_LDAP_START_TLS_S_NP)
! 2026: if (ldap_ssl_client_init(NULL, NULL, 0, &rc) != LDAP_SUCCESS) {
! 2027: warningx("ldap_ssl_client_init(): %s", ldap_err2string(rc));
! 2028: return -1;
! 2029: }
! 2030: rc = ldap_start_tls_s_np(ld, NULL);
! 2031: if (rc != LDAP_SUCCESS) {
! 2032: warningx("ldap_start_tls_s_np(): %s", ldap_err2string(rc));
! 2033: return -1;
! 2034: }
! 2035: DPRINTF(("ldap_start_tls_s_np() ok"), 1);
! 2036: #else
! 2037: warningx(_("start_tls specified but LDAP libs do not support ldap_start_tls_s() or ldap_start_tls_s_np()"));
! 2038: #endif /* !HAVE_LDAP_START_TLS_S && !HAVE_LDAP_START_TLS_S_NP */
! 2039: }
! 2040:
! 2041: /* Actually connect */
! 2042: if (sudo_ldap_bind_s(ld) != 0)
! 2043: return -1;
! 2044:
! 2045: /* Create a handle container. */
! 2046: handle = emalloc(sizeof(struct sudo_ldap_handle));
! 2047: handle->ld = ld;
! 2048: handle->result = NULL;
! 2049: handle->username = NULL;
! 2050: handle->grlist = NULL;
! 2051: nss->handle = handle;
! 2052:
! 2053: return 0;
! 2054: }
! 2055:
! 2056: static int
! 2057: sudo_ldap_setdefs(struct sudo_nss *nss)
! 2058: {
! 2059: struct ldap_config_list_str *base;
! 2060: struct sudo_ldap_handle *handle = nss->handle;
! 2061: struct timeval tv, *tvp = NULL;
! 2062: LDAP *ld;
! 2063: LDAPMessage *entry, *result;
! 2064: char *filt;
! 2065: int rc;
! 2066:
! 2067: if (handle == NULL || handle->ld == NULL)
! 2068: return -1;
! 2069: ld = handle->ld;
! 2070:
! 2071: filt = sudo_ldap_build_default_filter();
! 2072: DPRINTF(("Looking for cn=defaults: %s", filt), 1);
! 2073:
! 2074: for (base = ldap_conf.base; base != NULL; base = base->next) {
! 2075: if (ldap_conf.timeout > 0) {
! 2076: tv.tv_sec = ldap_conf.timeout;
! 2077: tv.tv_usec = 0;
! 2078: tvp = &tv;
! 2079: }
! 2080: result = NULL;
! 2081: rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE,
! 2082: filt, NULL, 0, NULL, NULL, tvp, 0, &result);
! 2083: if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
! 2084: DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
! 2085: sudo_ldap_parse_options(ld, entry);
! 2086: } else
! 2087: DPRINTF(("no default options found in %s", base->val), 1);
! 2088:
! 2089: if (result)
! 2090: ldap_msgfree(result);
! 2091: }
! 2092: efree(filt);
! 2093:
! 2094: return 0;
! 2095: }
! 2096:
! 2097: /*
! 2098: * like sudoers_lookup() - only LDAP style
! 2099: */
! 2100: static int
! 2101: sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
! 2102: {
! 2103: struct sudo_ldap_handle *handle = nss->handle;
! 2104: LDAP *ld;
! 2105: LDAPMessage *entry;
! 2106: int i, rc, setenv_implied;
! 2107: struct ldap_result *lres = NULL;
! 2108:
! 2109: if (handle == NULL || handle->ld == NULL)
! 2110: return ret;
! 2111: ld = handle->ld;
! 2112:
! 2113: /* Fetch list of sudoRole entries that match user and host. */
! 2114: lres = sudo_ldap_result_get(nss, sudo_user.pw);
! 2115:
! 2116: /*
! 2117: * The following queries are only determine whether or not a
! 2118: * password is required, so the order of the entries doesn't matter.
! 2119: */
! 2120: if (pwflag) {
! 2121: int doauth = UNSPEC;
! 2122: int matched = UNSPEC;
! 2123: enum def_tuple pwcheck =
! 2124: (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
! 2125:
! 2126: DPRINTF(("perform search for pwflag %d", pwflag), 1);
! 2127: for (i = 0; i < lres->nentries; i++) {
! 2128: entry = lres->entries[i].entry;
! 2129: if ((pwcheck == any && doauth != FALSE) ||
! 2130: (pwcheck == all && doauth == FALSE)) {
! 2131: doauth = sudo_ldap_check_bool(ld, entry, "authenticate");
! 2132: }
! 2133: /* Only check the command when listing another user. */
! 2134: if (user_uid == 0 || list_pw == NULL ||
! 2135: user_uid == list_pw->pw_uid ||
! 2136: sudo_ldap_check_command(ld, entry, NULL)) {
! 2137: matched = TRUE;
! 2138: break;
! 2139: }
! 2140: }
! 2141: if (matched || user_uid == 0) {
! 2142: SET(ret, VALIDATE_OK);
! 2143: CLR(ret, VALIDATE_NOT_OK);
! 2144: if (def_authenticate) {
! 2145: switch (pwcheck) {
! 2146: case always:
! 2147: SET(ret, FLAG_CHECK_USER);
! 2148: break;
! 2149: case all:
! 2150: case any:
! 2151: if (doauth == FALSE)
! 2152: def_authenticate = FALSE;
! 2153: break;
! 2154: case never:
! 2155: def_authenticate = FALSE;
! 2156: break;
! 2157: default:
! 2158: break;
! 2159: }
! 2160: }
! 2161: }
! 2162: goto done;
! 2163: }
! 2164:
! 2165: DPRINTF(("searching LDAP for sudoers entries"), 1);
! 2166:
! 2167: setenv_implied = FALSE;
! 2168: for (i = 0; i < lres->nentries; i++) {
! 2169: entry = lres->entries[i].entry;
! 2170: if (!sudo_ldap_check_runas(ld, entry))
! 2171: continue;
! 2172: rc = sudo_ldap_check_command(ld, entry, &setenv_implied);
! 2173: if (rc != UNSPEC) {
! 2174: /* We have a match. */
! 2175: DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
! 2176: if (rc == TRUE) {
! 2177: DPRINTF(("LDAP entry: %p", entry), 1);
! 2178: /* Apply entry-specific options. */
! 2179: if (setenv_implied)
! 2180: def_setenv = TRUE;
! 2181: sudo_ldap_parse_options(ld, entry);
! 2182: #ifdef HAVE_SELINUX
! 2183: /* Set role and type if not specified on command line. */
! 2184: if (user_role == NULL)
! 2185: user_role = def_role;
! 2186: if (user_type == NULL)
! 2187: user_type = def_type;
! 2188: #endif /* HAVE_SELINUX */
! 2189: SET(ret, VALIDATE_OK);
! 2190: CLR(ret, VALIDATE_NOT_OK);
! 2191: } else {
! 2192: SET(ret, VALIDATE_NOT_OK);
! 2193: CLR(ret, VALIDATE_OK);
! 2194: }
! 2195: break;
! 2196: }
! 2197: }
! 2198:
! 2199: done:
! 2200: DPRINTF(("done with LDAP searches"), 1);
! 2201: DPRINTF(("user_matches=%d", lres->user_matches), 1);
! 2202: DPRINTF(("host_matches=%d", lres->host_matches), 1);
! 2203:
! 2204: if (!ISSET(ret, VALIDATE_OK)) {
! 2205: /* No matching entries. */
! 2206: if (pwflag && list_pw == NULL)
! 2207: SET(ret, FLAG_NO_CHECK);
! 2208: }
! 2209: if (lres->user_matches)
! 2210: CLR(ret, FLAG_NO_USER);
! 2211: if (lres->host_matches)
! 2212: CLR(ret, FLAG_NO_HOST);
! 2213: DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1);
! 2214:
! 2215: return ret;
! 2216: }
! 2217:
! 2218: /*
! 2219: * Comparison function for ldap_entry_wrapper structures, descending order.
! 2220: */
! 2221: static int
! 2222: ldap_entry_compare(const void *a, const void *b)
! 2223: {
! 2224: const struct ldap_entry_wrapper *aw = a;
! 2225: const struct ldap_entry_wrapper *bw = b;
! 2226:
! 2227: return bw->order < aw->order ? -1 :
! 2228: (bw->order > aw->order ? 1 : 0);
! 2229: }
! 2230:
! 2231: /*
! 2232: * Find the last entry in the list of searches, usually the
! 2233: * one currently being used to add entries.
! 2234: * XXX - use a tailq instead?
! 2235: */
! 2236: static struct ldap_search_list *
! 2237: sudo_ldap_result_last_search(struct ldap_result *lres)
! 2238: {
! 2239: struct ldap_search_list *result = lres->searches;
! 2240:
! 2241: if (result) {
! 2242: while (result->next)
! 2243: result = result->next;
! 2244: }
! 2245: return result;
! 2246: }
! 2247:
! 2248: /*
! 2249: * Add an entry to the result structure.
! 2250: */
! 2251: static struct ldap_entry_wrapper *
! 2252: sudo_ldap_result_add_entry(struct ldap_result *lres, LDAPMessage *entry)
! 2253: {
! 2254: struct ldap_search_list *last;
! 2255: struct berval **bv;
! 2256: double order = 0.0;
! 2257: char *ep;
! 2258:
! 2259: /* Determine whether the entry has the sudoOrder attribute. */
! 2260: last = sudo_ldap_result_last_search(lres);
! 2261: bv = ldap_get_values_len(last->ldap, entry, "sudoOrder");
! 2262: if (bv != NULL) {
! 2263: if (ldap_count_values_len(bv) > 0) {
! 2264: /* Get the value of this attribute, 0 if not present. */
! 2265: DPRINTF(("order attribute raw: %s", (*bv)->bv_val), 1);
! 2266: order = strtod((*bv)->bv_val, &ep);
! 2267: if (ep == (*bv)->bv_val || *ep != '\0') {
! 2268: warningx(_("invalid sudoOrder attribute: %s"), (*bv)->bv_val);
! 2269: order = 0.0;
! 2270: }
! 2271: DPRINTF(("order attribute: %f", order), 1);
! 2272: }
! 2273: ldap_value_free_len(bv);
! 2274: }
! 2275:
! 2276: /*
! 2277: * Enlarge the array of entry wrappers as needed, preallocating blocks
! 2278: * of 100 entries to save on allocation time.
! 2279: */
! 2280: if (++lres->nentries > lres->allocated_entries) {
! 2281: lres->allocated_entries += ALLOCATION_INCREMENT;
! 2282: lres->entries = erealloc3(lres->entries, lres->allocated_entries,
! 2283: sizeof(lres->entries[0]));
! 2284: }
! 2285:
! 2286: /* Fill in the new entry and return it. */
! 2287: lres->entries[lres->nentries - 1].entry = entry;
! 2288: lres->entries[lres->nentries - 1].order = order;
! 2289:
! 2290: return &lres->entries[lres->nentries - 1];
! 2291: }
! 2292:
! 2293: /*
! 2294: * Free the ldap result structure in the sudo_nss handle.
! 2295: */
! 2296: static void
! 2297: sudo_ldap_result_free_nss(struct sudo_nss *nss)
! 2298: {
! 2299: struct sudo_ldap_handle *handle = nss->handle;
! 2300:
! 2301: if (handle->result != NULL) {
! 2302: DPRINTF(("removing reusable search result"), 1);
! 2303: sudo_ldap_result_free(handle->result);
! 2304: if (handle->username) {
! 2305: efree(handle->username);
! 2306: handle->username = NULL;
! 2307: }
! 2308: handle->grlist = NULL;
! 2309: handle->result = NULL;
! 2310: }
! 2311: }
! 2312:
! 2313: /*
! 2314: * Perform the LDAP query for the user or return a cached query if
! 2315: * there is one for this user.
! 2316: */
! 2317: static struct ldap_result *
! 2318: sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw)
! 2319: {
! 2320: struct sudo_ldap_handle *handle = nss->handle;
! 2321: struct ldap_config_list_str *base;
! 2322: struct ldap_result *lres;
! 2323: struct timeval tv, *tvp = NULL;
! 2324: LDAPMessage *entry, *result;
! 2325: LDAP *ld = handle->ld;
! 2326: int do_netgr, rc;
! 2327: char *filt;
! 2328:
! 2329: /*
! 2330: * If we already have a cached result, return it so we don't have to
! 2331: * have to contact the LDAP server again.
! 2332: */
! 2333: if (handle->result) {
! 2334: if (handle->grlist == user_group_list &&
! 2335: strcmp(pw->pw_name, handle->username) == 0) {
! 2336: DPRINTF(("reusing previous result (user %s) with %d entries",
! 2337: handle->username, handle->result->nentries), 1);
! 2338: return handle->result;
! 2339: }
! 2340: /* User mismatch, cached result cannot be used. */
! 2341: DPRINTF(("removing result (user %s), new search (user %s)",
! 2342: handle->username, pw->pw_name), 1);
! 2343: sudo_ldap_result_free_nss(nss);
! 2344: }
! 2345:
! 2346: /*
! 2347: * Okay - time to search for anything that matches this user
! 2348: * Lets limit it to only two queries of the LDAP server
! 2349: *
! 2350: * The first pass will look by the username, groups, and
! 2351: * the keyword ALL. We will then inspect the results that
! 2352: * came back from the query. We don't need to inspect the
! 2353: * sudoUser in this pass since the LDAP server already scanned
! 2354: * it for us.
! 2355: *
! 2356: * The second pass will return all the entries that contain
! 2357: * user netgroups. Then we take the netgroups returned and
! 2358: * try to match them against the username.
! 2359: *
! 2360: * Since we have to sort the possible entries before we make a
! 2361: * decision, we perform the queries and store all of the results in
! 2362: * an ldap_result object. The results are then sorted by sudoOrder.
! 2363: */
! 2364: lres = sudo_ldap_result_alloc();
! 2365: for (do_netgr = 0; do_netgr < 2; do_netgr++) {
! 2366: filt = do_netgr ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
! 2367: DPRINTF(("ldap search '%s'", filt), 1);
! 2368: for (base = ldap_conf.base; base != NULL; base = base->next) {
! 2369: DPRINTF(("searching from base '%s'", base->val), 1);
! 2370: if (ldap_conf.timeout > 0) {
! 2371: tv.tv_sec = ldap_conf.timeout;
! 2372: tv.tv_usec = 0;
! 2373: tvp = &tv;
! 2374: }
! 2375: result = NULL;
! 2376: rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
! 2377: NULL, 0, NULL, NULL, tvp, 0, &result);
! 2378: if (rc != LDAP_SUCCESS) {
! 2379: DPRINTF(("nothing found for '%s'", filt), 1);
! 2380: continue;
! 2381: }
! 2382: lres->user_matches = TRUE;
! 2383:
! 2384: /* Add the seach result to list of search results. */
! 2385: DPRINTF(("adding search result"), 1);
! 2386: sudo_ldap_result_add_search(lres, ld, result);
! 2387: LDAP_FOREACH(entry, ld, result) {
! 2388: if ((!do_netgr ||
! 2389: sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) &&
! 2390: sudo_ldap_check_host(ld, entry)) {
! 2391: lres->host_matches = TRUE;
! 2392: sudo_ldap_result_add_entry(lres, entry);
! 2393: }
! 2394: }
! 2395: DPRINTF(("result now has %d entries", lres->nentries), 1);
! 2396: }
! 2397: efree(filt);
! 2398: }
! 2399:
! 2400: /* Sort the entries by the sudoOrder attribute. */
! 2401: DPRINTF(("sorting remaining %d entries", lres->nentries), 1);
! 2402: qsort(lres->entries, lres->nentries, sizeof(lres->entries[0]),
! 2403: ldap_entry_compare);
! 2404:
! 2405: /* Store everything in the sudo_nss handle. */
! 2406: handle->result = lres;
! 2407: handle->username = estrdup(pw->pw_name);
! 2408: handle->grlist = user_group_list;
! 2409:
! 2410: return lres;
! 2411: }
! 2412:
! 2413: /*
! 2414: * Shut down the LDAP connection.
! 2415: */
! 2416: static int
! 2417: sudo_ldap_close(struct sudo_nss *nss)
! 2418: {
! 2419: struct sudo_ldap_handle *handle = nss->handle;
! 2420:
! 2421: if (handle != NULL) {
! 2422: /* Free the result before unbinding; it may use the LDAP connection. */
! 2423: sudo_ldap_result_free_nss(nss);
! 2424:
! 2425: /* Unbind and close the LDAP connection. */
! 2426: if (handle->ld != NULL) {
! 2427: ldap_unbind_ext_s(handle->ld, NULL, NULL);
! 2428: handle->ld = NULL;
! 2429: }
! 2430:
! 2431: /* Free the handle container. */
! 2432: efree(nss->handle);
! 2433: nss->handle = NULL;
! 2434: }
! 2435: return 0;
! 2436: }
! 2437:
! 2438: /*
! 2439: * STUB
! 2440: */
! 2441: static int
! 2442: sudo_ldap_parse(struct sudo_nss *nss)
! 2443: {
! 2444: return 0;
! 2445: }
! 2446:
! 2447: #if 0
! 2448: /*
! 2449: * Create an ldap_result from an LDAP search result.
! 2450: *
! 2451: * This function is currently not used anywhere, it is left here as
! 2452: * an example of how to use the cached searches.
! 2453: */
! 2454: static struct ldap_result *
! 2455: sudo_ldap_result_from_search(LDAP *ldap, LDAPMessage *searchresult)
! 2456: {
! 2457: /*
! 2458: * An ldap_result is built from several search results, which are
! 2459: * organized in a list. The head of the list is maintained in the
! 2460: * ldap_result structure, together with the wrappers that point
! 2461: * to individual entries, this has to be initialized first.
! 2462: */
! 2463: struct ldap_result *result = sudo_ldap_result_alloc();
! 2464:
! 2465: /*
! 2466: * Build a new list node for the search result, this creates the
! 2467: * list node.
! 2468: */
! 2469: struct ldap_search_list *last = sudo_ldap_result_add_search(result,
! 2470: ldap, searchresult);
! 2471:
! 2472: /*
! 2473: * Now add each entry in the search result to the array of of entries
! 2474: * in the ldap_result object.
! 2475: */
! 2476: LDAPMessage *entry;
! 2477: LDAP_FOREACH(entry, last->ldap, last->searchresult) {
! 2478: sudo_ldap_result_add_entry(result, entry);
! 2479: }
! 2480: DPRINTF(("sudo_ldap_result_from_search: %d entries found",
! 2481: result->nentries), 2);
! 2482: return result;
! 2483: }
! 2484: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>