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