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