Return to ldap.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers |
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