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