Annotation of src/usr.bin/sudo/ldap.c, Revision 1.5
1.1 millert 1: /*
2: * Copyright (c) 2003-2005 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: #else
37: # ifdef HAVE_STRINGS_H
38: # include <strings.h>
39: # endif
40: #endif /* HAVE_STRING_H */
41: #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
42: # include <malloc.h>
43: #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
44: #ifdef HAVE_UNISTD_H
45: # include <unistd.h>
46: #endif /* HAVE_UNISTD_H */
47: #include <ctype.h>
48: #include <pwd.h>
49: #include <grp.h>
50: #include <netinet/in.h>
51: #include <arpa/inet.h>
52: #include <netdb.h>
53: #ifdef HAVE_ERR_H
54: # include <err.h>
55: #else
56: # include "emul/err.h"
57: #endif /* HAVE_ERR_H */
58: #include <errno.h>
59: #ifdef HAVE_LBER_H
60: # include <lber.h>
61: #endif
62: #include <ldap.h>
63:
64: #include "sudo.h"
65: #include "parse.h"
66:
67: #ifndef lint
1.5 ! millert 68: __unused static const char rcsid[] = "$Sudo: ldap.c,v 1.11.2.28 2007/12/19 19:29:32 millert Exp $";
1.1 millert 69: #endif /* lint */
70:
71: #ifndef LINE_MAX
72: # define LINE_MAX 2048
73: #endif
74:
75: #ifndef LDAP_OPT_SUCCESS
76: # define LDAP_OPT_SUCCESS LDAP_SUCCESS
77: #endif
78:
1.5 ! millert 79: #define DPRINTF(args, level) if (ldap_conf.debug >= level) warnx args
1.1 millert 80:
1.5 ! millert 81: #define CONF_BOOL 0
! 82: #define CONF_INT 1
! 83: #define CONF_STR 2
! 84:
! 85: #define SUDO_LDAP_SSL 1
! 86: #define SUDO_LDAP_STARTTLS 2
! 87:
! 88: struct ldap_config_table {
! 89: const char *conf_str; /* config file string */
! 90: short type; /* CONF_BOOL, CONF_INT, CONF_STR */
! 91: short connected; /* connection-specific value? */
! 92: int opt_val; /* LDAP_OPT_* (or -1 for sudo internal) */
! 93: void *valp; /* pointer into ldap_conf */
! 94: };
1.1 millert 95:
96: /* ldap configuration structure */
97: struct ldap_config {
98: int port;
99: int version;
100: int debug;
1.5 ! millert 101: int ldap_debug;
1.1 millert 102: int tls_checkpeer;
103: int timelimit;
104: int bind_timelimit;
1.5 ! millert 105: int ssl_mode;
1.1 millert 106: char *host;
107: char *uri;
108: char *binddn;
109: char *bindpw;
110: char *rootbinddn;
111: char *base;
112: char *ssl;
113: char *tls_cacertfile;
114: char *tls_cacertdir;
115: char *tls_random_file;
116: char *tls_cipher_suite;
117: char *tls_certfile;
118: char *tls_keyfile;
119: } ldap_conf;
120:
1.5 ! millert 121: struct ldap_config_table ldap_conf_table[] = {
! 122: { "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug },
! 123: { "host", CONF_STR, FALSE, -1, &ldap_conf.host },
! 124: { "port", CONF_INT, FALSE, -1, &ldap_conf.port },
! 125: { "ssl", CONF_STR, FALSE, -1, &ldap_conf.ssl },
! 126: { "sslpath", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
! 127: { "uri", CONF_STR, FALSE, -1, &ldap_conf.uri },
! 128: #ifdef LDAP_OPT_DEBUG_LEVEL
! 129: { "debug", CONF_INT, FALSE, LDAP_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug },
! 130: #endif
! 131: #ifdef LDAP_OPT_PROTOCOL_VERSION
! 132: { "ldap_version", CONF_INT, TRUE, LDAP_OPT_PROTOCOL_VERSION,
! 133: &ldap_conf.version },
! 134: #endif
! 135: #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
! 136: { "tls_checkpeer", CONF_BOOL, FALSE, LDAP_OPT_X_TLS_REQUIRE_CERT,
! 137: &ldap_conf.tls_checkpeer },
! 138: #endif
! 139: #ifdef LDAP_OPT_X_TLS_CACERTFILE
! 140: { "tls_cacertfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTFILE,
! 141: &ldap_conf.tls_cacertfile },
! 142: #endif
! 143: #ifdef LDAP_OPT_X_TLS_CACERTDIR
! 144: { "tls_cacertdir", CONF_STR, FALSE, LDAP_OPT_X_TLS_CACERTDIR,
! 145: &ldap_conf.tls_cacertdir },
! 146: #endif
! 147: #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
! 148: { "tls_randfile", CONF_STR, FALSE, LDAP_OPT_X_TLS_RANDOM_FILE,
! 149: &ldap_conf.tls_random_file },
! 150: #endif
! 151: #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
! 152: { "tls_ciphers", CONF_STR, FALSE, LDAP_OPT_X_TLS_CIPHER_SUITE,
! 153: &ldap_conf.tls_cipher_suite },
! 154: #endif
! 155: #ifdef LDAP_OPT_X_TLS_CERTFILE
! 156: { "tls_cert", CONF_STR, FALSE, LDAP_OPT_X_TLS_CERTFILE,
! 157: &ldap_conf.tls_certfile },
! 158: #else
! 159: { "tls_cert", CONF_STR, FALSE, -1, &ldap_conf.tls_certfile },
! 160: #endif
! 161: #ifdef LDAP_OPT_X_TLS_KEYFILE
! 162: { "tls_key", CONF_STR, FALSE, LDAP_OPT_X_TLS_KEYFILE,
! 163: &ldap_conf.tls_keyfile },
! 164: #else
! 165: { "tls_key", CONF_STR, FALSE, -1, &ldap_conf.tls_keyfile },
! 166: #endif
! 167: #ifdef LDAP_OPT_NETWORK_TIMEOUT
! 168: { "bind_timelimit", CONF_INT, TRUE, -1 /* needs timeval, set manually */,
! 169: &ldap_conf.bind_timelimit },
! 170: #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
! 171: { "bind_timelimit", CONF_INT, TRUE, LDAP_X_OPT_CONNECT_TIMEOUT,
! 172: &ldap_conf.bind_timelimit },
! 173: #endif
! 174: { "timelimit", CONF_INT, TRUE, LDAP_OPT_TIMELIMIT, &ldap_conf.timelimit },
! 175: { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn },
! 176: { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw },
! 177: { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn },
! 178: { "sudoers_base", CONF_STR, FALSE, -1, &ldap_conf.base },
! 179: { NULL }
! 180: };
! 181:
1.1 millert 182: static void sudo_ldap_update_defaults __P((LDAP *));
183: static void sudo_ldap_close __P((LDAP *));
184: static LDAP *sudo_ldap_open __P((void));
185:
186: /*
187: * Walk through search results and return TRUE if we have a matching
188: * netgroup, else FALSE.
189: */
190: int
191: sudo_ldap_check_user_netgroup(ld, entry)
192: LDAP *ld;
193: LDAPMessage *entry;
194: {
195: char **v = NULL, **p = NULL;
196: int ret = FALSE;
197:
198: if (!entry)
199: return(ret);
200:
201: /* get the values from the entry */
202: v = ldap_get_values(ld, entry, "sudoUser");
203:
204: /* walk through values */
205: for (p = v; p && *p && !ret; p++) {
206: /* match any */
207: if (netgr_matches(*p, NULL, NULL, user_name))
208: ret = TRUE;
209: DPRINTF(("ldap sudoUser netgroup '%s' ... %s", *p,
210: ret ? "MATCH!" : "not"), 2);
211: }
212:
213: if (v)
214: ldap_value_free(v); /* cleanup */
215:
216: return(ret);
217: }
218:
219: /*
220: * Walk through search results and return TRUE if we have a
221: * host match, else FALSE.
222: */
223: int
224: sudo_ldap_check_host(ld, entry)
225: LDAP *ld;
226: LDAPMessage *entry;
227: {
228: char **v = NULL, **p = NULL;
229: int ret = FALSE;
230:
231: if (!entry)
232: return(ret);
233:
234: /* get the values from the entry */
235: v = ldap_get_values(ld, entry, "sudoHost");
236:
237: /* walk through values */
238: for (p = v; p && *p && !ret; p++) {
239: /* match any or address or netgroup or hostname */
1.4 millert 240: if (!strcmp(*p, "ALL") || addr_matches(*p) ||
1.1 millert 241: netgr_matches(*p, user_host, user_shost, NULL) ||
242: !hostname_matches(user_shost, user_host, *p))
243: ret = TRUE;
244: DPRINTF(("ldap sudoHost '%s' ... %s", *p,
245: ret ? "MATCH!" : "not"), 2);
246: }
247:
248: if (v)
249: ldap_value_free(v); /* cleanup */
250:
251: return(ret);
252: }
253:
254: /*
255: * Walk through search results and return TRUE if we have a runas match,
256: * else FALSE.
257: * Since the runas directive in /etc/sudoers is optional, so is sudoRunAs.
258: */
259: int
260: sudo_ldap_check_runas(ld, entry)
261: LDAP *ld;
262: LDAPMessage *entry;
263: {
264: char **v = NULL, **p = NULL;
265: int ret = FALSE;
266:
267: if (!entry)
268: return(ret);
269:
270: /* get the values from the entry */
271: v = ldap_get_values(ld, entry, "sudoRunAs");
272:
273: /*
274: * BUG:
275: *
276: * if runas is not specified on the command line, the only information
277: * as to which user to run as is in the runas_default option. We should
278: * check to see if we have the local option present. Unfortunately we
279: * don't parse these options until after this routine says yes or no.
280: * The query has already returned, so we could peek at the attribute
281: * values here though.
282: *
283: * For now just require users to always use -u option unless its set
284: * in the global defaults. This behaviour is no different than the global
285: * /etc/sudoers.
286: *
287: * Sigh - maybe add this feature later
288: *
289: */
290:
291: /*
292: * If there are no runas entries, match runas_default against
293: * what the user specified on the command line.
294: */
295: if (!v)
1.2 millert 296: ret = !strcasecmp(runas_pw->pw_name, def_runas_default);
1.1 millert 297:
298: /* walk through values returned, looking for a match */
299: for (p = v; p && *p && !ret; p++) {
1.2 millert 300: switch (*p[0]) {
301: case '+':
302: if (netgr_matches(*p, NULL, NULL, runas_pw->pw_name))
303: ret = TRUE;
304: break;
305: case '%':
306: if (usergr_matches(*p, runas_pw->pw_name, runas_pw))
307: ret = TRUE;
308: break;
309: case 'A':
310: if (strcmp(*p, "ALL") == 0) {
311: ret = TRUE;
312: break;
313: }
314: /* FALLTHROUGH */
315: default:
316: if (strcasecmp(*p, runas_pw->pw_name) == 0)
317: ret = TRUE;
318: break;
319: }
1.1 millert 320: DPRINTF(("ldap sudoRunAs '%s' ... %s", *p,
321: ret ? "MATCH!" : "not"), 2);
322: }
323:
324: if (v)
325: ldap_value_free(v); /* cleanup */
326:
327: return(ret);
328: }
329:
330: /*
331: * Walk through search results and return TRUE if we have a command match.
332: */
333: int
1.4 millert 334: sudo_ldap_check_command(ld, entry, setenv_implied)
1.1 millert 335: LDAP *ld;
336: LDAPMessage *entry;
1.4 millert 337: int *setenv_implied;
1.1 millert 338: {
339: char *allowed_cmnd, *allowed_args, **v = NULL, **p = NULL;
340: int foundbang, ret = FALSE;
341:
342: if (!entry)
343: return(ret);
344:
345: v = ldap_get_values(ld, entry, "sudoCommand");
346:
347: /* get_first_entry */
348: for (p = v; p && *p && ret >= 0; p++) {
349: /* Match against ALL ? */
1.4 millert 350: if (!strcmp(*p, "ALL")) {
1.1 millert 351: ret = TRUE;
1.4 millert 352: if (setenv_implied != NULL)
353: *setenv_implied = TRUE;
1.1 millert 354: DPRINTF(("ldap sudoCommand '%s' ... MATCH!", *p), 2);
355: continue;
356: }
357:
358: /* check for !command */
359: if (**p == '!') {
360: foundbang = TRUE;
361: allowed_cmnd = estrdup(1 + *p); /* !command */
362: } else {
363: foundbang = FALSE;
364: allowed_cmnd = estrdup(*p); /* command */
365: }
366:
367: /* split optional args away from command */
368: allowed_args = strchr(allowed_cmnd, ' ');
369: if (allowed_args)
370: *allowed_args++ = '\0';
371:
372: /* check the command like normal */
373: if (command_matches(allowed_cmnd, allowed_args)) {
374: /*
375: * If allowed (no bang) set ret but keep on checking.
376: * If disallowed (bang), exit loop.
377: */
378: ret = foundbang ? -1 : TRUE;
379: }
380: DPRINTF(("ldap sudoCommand '%s' ... %s", *p,
381: ret == TRUE ? "MATCH!" : "not"), 2);
382:
383: efree(allowed_cmnd); /* cleanup */
384: }
385:
386: if (v)
387: ldap_value_free(v); /* more cleanup */
388:
389: /* return TRUE if we found at least one ALLOW and no DENY */
390: return(ret > 0);
391: }
392:
393: /*
394: * Read sudoOption and modify the defaults as we go. This is used once
395: * from the cn=defaults entry and also once when a final sudoRole is matched.
396: */
397: void
398: sudo_ldap_parse_options(ld, entry)
399: LDAP *ld;
400: LDAPMessage *entry;
401: {
402: char op, *var, *val, **v = NULL, **p = NULL;
403:
404: if (!entry)
405: return;
406:
407: v = ldap_get_values(ld, entry, "sudoOption");
408:
409: /* walk through options */
410: for (p = v; p && *p; p++) {
411:
412: DPRINTF(("ldap sudoOption: '%s'", *p), 2);
413: var = estrdup(*p);
414:
415: /* check for equals sign past first char */
416: val = strchr(var, '=');
417: if (val > var) {
418: *val++ = '\0'; /* split on = and truncate var */
419: op = *(val - 2); /* peek for += or -= cases */
420: if (op == '+' || op == '-') {
421: *(val - 2) = '\0'; /* found, remove extra char */
422: /* case var+=val or var-=val */
423: set_default(var, val, (int) op);
424: } else {
425: /* case var=val */
426: set_default(var, val, TRUE);
427: }
428: } else if (*var == '!') {
429: /* case !var Boolean False */
430: set_default(var + 1, NULL, FALSE);
431: } else {
432: /* case var Boolean True */
433: set_default(var, NULL, TRUE);
434: }
435: efree(var);
436: }
437:
438: if (v)
439: ldap_value_free(v);
440: }
441:
442: /*
443: * Concatenate strings, dynamically growing them as necessary.
444: * Strings can be arbitrarily long and are allocated/reallocated on
445: * the fly. Make sure to free them when you are done.
446: *
447: * Usage:
448: *
449: * char *s=NULL;
450: * size_t sz;
451: *
452: * ncat(&s,&sz,"This ");
453: * ncat(&s,&sz,"is ");
454: * ncat(&s,&sz,"an ");
455: * ncat(&s,&sz,"arbitrarily ");
456: * ncat(&s,&sz,"long ");
457: * ncat(&s,&sz,"string!");
458: *
459: * printf("String Value='%s', but has %d bytes allocated\n",s,sz);
460: *
461: */
462: void
463: ncat(s, sz, src)
464: char **s;
465: size_t *sz;
466: char *src;
467: {
468: size_t nsz;
469:
470: /* handle initial alloc */
471: if (*s == NULL) {
472: *s = estrdup(src);
473: *sz = strlen(src) + 1;
474: return;
475: }
476: /* handle realloc */
477: nsz = strlen(*s) + strlen(src) + 1;
478: if (*sz < nsz)
479: *s = erealloc((void *) *s, *sz = nsz * 2);
480: strlcat(*s, src, *sz);
481: }
482:
483: /*
484: * builds together a filter to check against ldap
485: */
486: char *
487: sudo_ldap_build_pass1()
488: {
489: struct group *grp;
490: size_t sz;
491: char *b = NULL;
492: int i;
493:
494: /* global OR */
495: ncat(&b, &sz, "(|");
496:
497: /* build filter sudoUser=user_name */
498: ncat(&b, &sz, "(sudoUser=");
499: ncat(&b, &sz, user_name);
500: ncat(&b, &sz, ")");
501:
502: /* Append primary group */
503: grp = getgrgid(user_gid);
504: if (grp != NULL) {
505: ncat(&b, &sz, "(sudoUser=%");
506: ncat(&b, &sz, grp -> gr_name);
507: ncat(&b, &sz, ")");
508: }
509:
510: /* Append supplementary groups */
511: for (i = 0; i < user_ngroups; i++) {
512: if ((grp = getgrgid(user_groups[i])) != NULL) {
513: ncat(&b, &sz, "(sudoUser=%");
514: ncat(&b, &sz, grp -> gr_name);
515: ncat(&b, &sz, ")");
516: }
517: }
518:
519: /* Add ALL to list */
520: ncat(&b, &sz, "(sudoUser=ALL)");
521:
522: /* End of OR List */
523: ncat(&b, &sz, ")");
524:
525: return(b);
526: }
527:
528: /*
529: * Map yes/true/on to TRUE, no/false/off to FALSE, else -1
530: */
531: int
532: _atobool(s)
533: const char *s;
534: {
535: switch (*s) {
536: case 'y':
537: case 'Y':
538: if (strcasecmp(s, "yes") == 0)
539: return(TRUE);
540: break;
541: case 't':
542: case 'T':
543: if (strcasecmp(s, "true") == 0)
544: return(TRUE);
545: break;
546: case 'o':
547: case 'O':
548: if (strcasecmp(s, "on") == 0)
549: return(TRUE);
550: if (strcasecmp(s, "off") == 0)
551: return(FALSE);
552: break;
553: case 'n':
554: case 'N':
555: if (strcasecmp(s, "no") == 0)
556: return(FALSE);
557: break;
558: case 'f':
559: case 'F':
560: if (strcasecmp(s, "false") == 0)
561: return(FALSE);
562: break;
563: }
564: return(-1);
565: }
566:
567: int
568: sudo_ldap_read_config()
569: {
570: FILE *f;
571: char buf[LINE_MAX], *c, *keyword, *value;
1.5 ! millert 572: struct ldap_config_table *cur;
1.1 millert 573:
574: /* defaults */
575: ldap_conf.version = 3;
1.5 ! millert 576: ldap_conf.port = -1;
1.1 millert 577: ldap_conf.tls_checkpeer = -1;
578: ldap_conf.timelimit = -1;
579: ldap_conf.bind_timelimit = -1;
580:
581: if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL)
582: return(FALSE);
1.5 ! millert 583:
1.1 millert 584: while (fgets(buf, sizeof(buf), f)) {
585: /* ignore text after comment character */
586: if ((c = strchr(buf, '#')) != NULL)
587: *c = '\0';
588:
589: /* skip leading whitespace */
590: for (c = buf; isspace((unsigned char) *c); c++)
591: /* nothing */;
592:
593: if (*c == '\0' || *c == '\n')
594: continue; /* skip empty line */
595:
596: /* properly terminate keyword string */
597: keyword = c;
598: while (*c && !isspace((unsigned char) *c))
599: c++;
600: if (*c)
601: *c++ = '\0'; /* terminate keyword */
602:
603: /* skip whitespace before value */
604: while (isspace((unsigned char) *c))
605: c++;
606: value = c;
607:
608: /* trim whitespace after value */
609: while (*c)
610: c++; /* wind to end */
611: while (--c > value && isspace((unsigned char) *c))
612: *c = '\0';
613:
1.5 ! millert 614: /* Look up keyword in config table. */
! 615: for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
! 616: if (strcasecmp(keyword, cur->conf_str) == 0) {
! 617: switch (cur->type) {
! 618: case CONF_BOOL:
! 619: *(int *)(cur->valp) = _atobool(value);
! 620: break;
! 621: case CONF_INT:
! 622: *(int *)(cur->valp) = atoi(value);
! 623: break;
! 624: case CONF_STR:
! 625: efree(*(char **)(cur->valp));
! 626: *(char **)(cur->valp) = estrdup(value);
! 627: break;
! 628: }
! 629: break;
! 630: }
1.1 millert 631: }
632: }
633: fclose(f);
634:
635: if (!ldap_conf.host)
1.5 ! millert 636: ldap_conf.host = "localhost";
1.1 millert 637:
638: if (ldap_conf.bind_timelimit > 0)
639: ldap_conf.bind_timelimit *= 1000; /* convert to ms */
640:
641: if (ldap_conf.debug > 1) {
642: fprintf(stderr, "LDAP Config Summary\n");
643: fprintf(stderr, "===================\n");
644: #ifdef HAVE_LDAP_INITIALIZE
645: if (ldap_conf.uri) {
646: fprintf(stderr, "uri %s\n", ldap_conf.uri);
647: } else
648: #endif
649: {
650: fprintf(stderr, "host %s\n", ldap_conf.host ?
651: ldap_conf.host : "(NONE)");
652: fprintf(stderr, "port %d\n", ldap_conf.port);
653: }
654: fprintf(stderr, "ldap_version %d\n", ldap_conf.version);
655:
656: fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ?
657: ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
658: fprintf(stderr, "binddn %s\n", ldap_conf.binddn ?
659: ldap_conf.binddn : "(anonymous)");
660: fprintf(stderr, "bindpw %s\n", ldap_conf.bindpw ?
661: ldap_conf.bindpw : "(anonymous)");
1.5 ! millert 662: if (ldap_conf.bind_timelimit > 0)
! 663: fprintf(stderr, "bind_timelimit %d\n", ldap_conf.bind_timelimit);
! 664: if (ldap_conf.timelimit > 0)
! 665: fprintf(stderr, "timelimit %d\n", ldap_conf.timelimit);
1.1 millert 666: fprintf(stderr, "ssl %s\n", ldap_conf.ssl ?
667: ldap_conf.ssl : "(no)");
1.5 ! millert 668: if (ldap_conf.tls_checkpeer != -1)
! 669: fprintf(stderr, "tls_checkpeer %s\n", ldap_conf.tls_checkpeer ?
! 670: "(yes)" : "(no)");
! 671: if (ldap_conf.tls_cacertfile != NULL)
! 672: fprintf(stderr, "tls_cacertfile %s\n", ldap_conf.tls_cacertfile);
! 673: if (ldap_conf.tls_cacertdir != NULL)
! 674: fprintf(stderr, "tls_cacertdir %s\n", ldap_conf.tls_cacertdir);
! 675: if (ldap_conf.tls_random_file != NULL)
! 676: fprintf(stderr, "tls_random_file %s\n", ldap_conf.tls_random_file);
! 677: if (ldap_conf.tls_cipher_suite != NULL)
! 678: fprintf(stderr, "tls_cipher_suite %s\n", ldap_conf.tls_cipher_suite);
! 679: if (ldap_conf.tls_certfile != NULL)
! 680: fprintf(stderr, "tls_certfile %s\n", ldap_conf.tls_certfile);
! 681: if (ldap_conf.tls_keyfile != NULL)
! 682: fprintf(stderr, "tls_keyfile %s\n", ldap_conf.tls_keyfile);
1.1 millert 683: fprintf(stderr, "===================\n");
684: }
685: if (!ldap_conf.base)
686: return(FALSE); /* if no base is defined, ignore LDAP */
687:
1.5 ! millert 688: /*
! 689: * Interpret SSL option
! 690: */
! 691: if (ldap_conf.ssl != NULL) {
! 692: if (strcasecmp(ldap_conf.ssl, "start_tls") == 0)
! 693: ldap_conf.ssl_mode = SUDO_LDAP_STARTTLS;
! 694: else if (_atobool(ldap_conf.ssl))
! 695: ldap_conf.ssl_mode = SUDO_LDAP_SSL;
! 696: }
! 697:
! 698: /* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */
! 699: if (ldap_conf.port < 0)
! 700: ldap_conf.port =
! 701: ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT;
! 702:
1.1 millert 703: /* If rootbinddn set, read in /etc/ldap.secret if it exists. */
704: if (ldap_conf.rootbinddn) {
705: if ((f = fopen(_PATH_LDAP_SECRET, "r")) != NULL) {
706: if (fgets(buf, sizeof(buf), f) != NULL) {
707: /* removing trailing newlines */
708: for (c = buf; *c != '\0'; c++)
709: continue;
710: while (--c > buf && *c == '\n')
711: *c = '\0';
712: /* copy to bindpw and binddn */
713: efree(ldap_conf.bindpw);
714: ldap_conf.bindpw = estrdup(buf);
715: efree(ldap_conf.binddn);
716: ldap_conf.binddn = ldap_conf.rootbinddn;
717: ldap_conf.rootbinddn = NULL;
718: }
719: fclose(f);
720: }
721: }
722: return(TRUE);
723: }
724:
725: /*
726: * like perl's join(sep,@ARGS)
727: */
728: char *
729: _ldap_join_values(sep, v)
730: char *sep;
731: char **v;
732: {
733: char *b = NULL, **p = NULL;
734: size_t sz = 0;
735:
736: /* paste values together */
737: for (p = v; p && *p; p++) {
738: if (p != v && sep != NULL)
1.3 martynas 739: ncat(&b, &sz, sep); /* append separator */
1.1 millert 740: ncat(&b, &sz, *p); /* append value */
741: }
742:
743: /* sanity check */
744: if (b[0] == '\0') {
745: /* something went wrong, put something here */
746: ncat(&b, &sz, "(empty list)"); /* append value */
747: }
748:
749: return(b);
750: }
751:
752: char *sudo_ldap_cm_list = NULL;
753: size_t sudo_ldap_cm_list_size;
754:
755: #define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
756: /*
757: * Walks through search result and returns TRUE if we have a
758: * command match
759: */
760: int
761: sudo_ldap_add_match(ld, entry, pwflag)
762: LDAP *ld;
763: LDAPMessage *entry;
764: int pwflag;
765: {
766: char *dn, **edn, **v = NULL;
767:
768: /* if we are not collecting matches, then don't save them */
769: if (pwflag != I_LISTPW)
770: return(TRUE);
771:
772: /* collect the dn, only show the rdn */
773: dn = ldap_get_dn(ld, entry);
774: edn = dn ? ldap_explode_dn(dn, 1) : NULL;
775: SAVE_LIST("\nLDAP Role: ");
776: SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
777: SAVE_LIST("\n");
778: if (dn)
779: ldap_memfree(dn);
780: if (edn)
781: ldap_value_free(edn);
782:
783: /* get the Runas Values from the entry */
784: v = ldap_get_values(ld, entry, "sudoRunAs");
785: if (v && *v) {
786: SAVE_LIST(" RunAs: (");
787: SAVE_LIST(_ldap_join_values(", ", v));
788: SAVE_LIST(")\n");
789: }
790: if (v)
791: ldap_value_free(v);
792:
793: /* get the Command Values from the entry */
794: v = ldap_get_values(ld, entry, "sudoCommand");
795: if (v && *v) {
796: SAVE_LIST(" Commands:\n ");
797: SAVE_LIST(_ldap_join_values("\n ", v));
798: SAVE_LIST("\n");
799: } else {
800: SAVE_LIST(" Commands: NONE\n");
801: }
802: if (v)
803: ldap_value_free(v);
804:
805: return(FALSE); /* Don't stop at the first match */
806: }
807: #undef SAVE_LIST
808:
809: void
810: sudo_ldap_list_matches()
811: {
812: if (sudo_ldap_cm_list != NULL)
813: printf("%s", sudo_ldap_cm_list);
814: }
815:
816: /*
1.5 ! millert 817: * Set LDAP options based on the config table.
1.1 millert 818: */
1.5 ! millert 819: int
! 820: sudo_ldap_set_options(ld)
! 821: LDAP *ld;
1.1 millert 822: {
1.5 ! millert 823: struct ldap_config_table *cur;
1.1 millert 824: int rc;
825:
1.5 ! millert 826: /* Set ber options */
! 827: #ifdef LBER_OPT_DEBUG_LEVEL
! 828: if (ldap_conf.ldap_debug)
! 829: ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_conf.ldap_debug);
! 830: #endif
1.1 millert 831:
1.5 ! millert 832: /* Set simple LDAP options */
! 833: for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) {
! 834: LDAP *conn;
! 835: int ival;
! 836: char *sval;
1.1 millert 837:
1.5 ! millert 838: if (cur->opt_val == -1)
! 839: continue;
1.1 millert 840:
1.5 ! millert 841: conn = cur->connected ? ld : NULL;
! 842: switch (cur->type) {
! 843: case CONF_BOOL:
! 844: case CONF_INT:
! 845: ival = *(int *)(cur->valp);
! 846: if (ival >= 0) {
! 847: rc = ldap_set_option(conn, cur->opt_val, &ival);
! 848: if (rc != LDAP_OPT_SUCCESS) {
! 849: warnx("ldap_set_option: %s -> %d: %s",
! 850: cur->conf_str, ival, ldap_err2string(rc));
! 851: return(-1);
! 852: }
! 853: DPRINTF(("ldap_set_option: %s -> %d", cur->conf_str, ival), 1);
! 854: }
! 855: break;
! 856: case CONF_STR:
! 857: sval = *(char **)(cur->valp);
! 858: if (sval != NULL) {
! 859: rc = ldap_set_option(conn, cur->opt_val, sval);
! 860: if (rc != LDAP_OPT_SUCCESS) {
! 861: warnx("ldap_set_option: %s -> %s: %s",
! 862: cur->conf_str, sval, ldap_err2string(rc));
! 863: return(-1);
! 864: }
! 865: DPRINTF(("ldap_set_option: %s -> %s", cur->conf_str, sval), 1);
! 866: }
! 867: break;
! 868: }
! 869: }
1.1 millert 870:
871: #ifdef LDAP_OPT_NETWORK_TIMEOUT
1.5 ! millert 872: /* Convert bind_timelimit to a timeval */
1.1 millert 873: if (ldap_conf.bind_timelimit > 0) {
874: struct timeval tv;
875: tv.tv_sec = ldap_conf.bind_timelimit / 1000;
876: tv.tv_usec = 0;
877: rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
878: if (rc != LDAP_OPT_SUCCESS) {
1.5 ! millert 879: warnx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s",
! 880: (long)tv.tv_sec, ldap_err2string(rc));
! 881: return(-1);
! 882: }
! 883: DPRINTF(("ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT, %ld)\n",
! 884: (long)tv.tv_sec), 1);
! 885: }
! 886: #endif
! 887:
! 888: #if defined(LDAP_OPT_X_TLS) && !defined(HAVE_LDAPSSL_INIT)
! 889: if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
! 890: int val = LDAP_OPT_X_TLS_HARD;
! 891: rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val);
! 892: if (rc != LDAP_SUCCESS) {
! 893: warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s",
! 894: ldap_err2string(rc));
! 895: return(-1);
! 896: }
! 897:
! 898: }
! 899: #endif
! 900: return(0);
! 901: }
! 902:
! 903: /*
! 904: * Open a connection to the LDAP server.
! 905: */
! 906: static LDAP *
! 907: sudo_ldap_open()
! 908: {
! 909: LDAP *ld = NULL;
! 910: int rc;
! 911:
! 912: if (!sudo_ldap_read_config())
! 913: return(NULL);
! 914:
! 915: #ifdef HAVE_LDAPSSL_INIT
! 916: if (ldap_conf.ssl_mode == SUDO_LDAP_SSL) {
! 917: DPRINTF(("ldapssl_clientauth_init(%s, %s)",
! 918: ldap_conf.tls_certfile ? ldap_conf.tls_certfile : "NULL",
! 919: ldap_conf.tls_keyfile ? ldap_conf.tls_keyfile : "NULL"), 2);
! 920: rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL,
! 921: ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL);
! 922: if (rc != LDAP_SUCCESS) {
! 923: warnx("unable to initialize SSL cert and key db: %s",
! 924: ldapssl_err2string(rc));
1.1 millert 925: return(NULL);
926: }
927: }
1.5 ! millert 928: #endif /* HAVE_LDAPSSL_INIT */
1.1 millert 929:
1.5 ! millert 930: /* Connect to LDAP server */
1.1 millert 931: #ifdef HAVE_LDAP_INITIALIZE
932: if (ldap_conf.uri) {
1.5 ! millert 933: DPRINTF(("ldap_initialize(ld, %s)", ldap_conf.uri), 2);
1.1 millert 934: rc = ldap_initialize(&ld, ldap_conf.uri);
1.5 ! millert 935: if (rc != LDAP_SUCCESS) {
! 936: warnx("unable to initialize LDAP: %s", ldap_err2string(rc));
1.1 millert 937: return(NULL);
938: }
939: } else
940: #endif /* HAVE_LDAP_INITIALIZE */
1.5 ! millert 941: {
! 942: #ifdef HAVE_LDAPSSL_INIT
! 943: DPRINTF(("ldapssl_init(%s, %d, %d)", ldap_conf.host, ldap_conf.port,
! 944: ldap_conf.ssl_mode == SUDO_LDAP_SSL), 2);
! 945: ld = ldapssl_init(ldap_conf.host, ldap_conf.port,
! 946: ldap_conf.ssl_mode == SUDO_LDAP_SSL);
! 947: #else
! 948: DPRINTF(("ldap_init(%s, %d)", ldap_conf.host, ldap_conf.port), 2);
! 949: ld = ldap_init(ldap_conf.host, ldap_conf.port);
! 950: #endif /* HAVE_LDAPSSL_INIT */
! 951: if (ld == NULL) {
! 952: warn("unable to initialize LDAP");
1.1 millert 953: return(NULL);
954: }
955: }
956:
1.5 ! millert 957: /* Set LDAP options */
! 958: if (sudo_ldap_set_options(ld) < 0)
! 959: return(NULL);
1.1 millert 960:
1.5 ! millert 961: if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) {
1.1 millert 962: #ifdef HAVE_LDAP_START_TLS_S
963: rc = ldap_start_tls_s(ld, NULL, NULL);
964: if (rc != LDAP_SUCCESS) {
1.5 ! millert 965: warnx("ldap_start_tls_s(): %s", ldap_err2string(rc));
1.1 millert 966: ldap_unbind(ld);
967: return(NULL);
968: }
969: DPRINTF(("ldap_start_tls_s() ok"), 1);
1.5 ! millert 970: #else
! 971: warnx("start_tls specified but LDAP libs do not support ldap_start_tls_s()");
! 972: #endif /* HAVE_LDAP_START_TLS_S */
1.1 millert 973: }
974:
975: /* Actually connect */
976: if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) {
1.5 ! millert 977: warnx("ldap_simple_bind_s: %s", ldap_err2string(rc));
1.1 millert 978: return(NULL);
979: }
1.5 ! millert 980: DPRINTF(("ldap_simple_bind_s() ok"), 1);
1.1 millert 981:
982: return(ld);
983: }
984:
985: static void
986: sudo_ldap_update_defaults(ld)
987: LDAP *ld;
988: {
989: LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
990: int rc; /* temp return value */
991:
992: rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE,
993: "cn=defaults", NULL, 0, &result);
1.5 ! millert 994: if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
1.1 millert 995: DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
996: sudo_ldap_parse_options(ld, entry);
997: } else
998: DPRINTF(("no default options found!"), 1);
999:
1000: if (result)
1001: ldap_msgfree(result);
1002: }
1003:
1004: /*
1005: * like sudoers_lookup() - only LDAP style
1006: */
1007: int
1008: sudo_ldap_check(pwflag)
1009: int pwflag;
1010: {
1011: LDAP *ld;
1012: LDAPMessage *entry = NULL, *result = NULL; /* used for searches */
1013: char *filt; /* used to parse attributes */
1014: int rc, ret = FALSE, do_netgr; /* temp/final return values */
1.4 millert 1015: int setenv_implied;
1.1 millert 1016: int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */
1017:
1018: /* Open a connection to the LDAP server. */
1019: if ((ld = sudo_ldap_open()) == NULL)
1020: return(VALIDATE_ERROR);
1021:
1022: /* Parse Default options. */
1023: sudo_ldap_update_defaults(ld);
1024:
1025: /*
1026: * Okay - time to search for anything that matches this user
1027: * Lets limit it to only two queries of the LDAP server
1028: *
1029: * The first pass will look by the username, groups, and
1030: * the keyword ALL. We will then inspect the results that
1031: * came back from the query. We don't need to inspect the
1032: * sudoUser in this pass since the LDAP server already scanned
1033: * it for us.
1034: *
1035: * The second pass will return all the entries that contain
1036: * user netgroups. Then we take the netgroups returned and
1037: * try to match them against the username.
1038: */
1.4 millert 1039: setenv_implied = FALSE;
1.1 millert 1040: for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) {
1041: filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1();
1042: DPRINTF(("ldap search '%s'", filt), 1);
1043: rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt,
1044: NULL, 0, &result);
1.5 ! millert 1045: if (rc != LDAP_SUCCESS)
1.1 millert 1046: DPRINTF(("nothing found for '%s'", filt), 1);
1047: efree(filt);
1048:
1049: /* parse each entry returned from this most recent search */
1050: entry = rc ? NULL : ldap_first_entry(ld, result);
1051: while (entry != NULL) {
1052: DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
1053: if (
1054: /* first verify user netgroup matches - only if in pass 2 */
1055: (!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) &&
1056: /* remember that user matched */
1057: (ldap_user_matches = -1) &&
1058: /* verify host match */
1059: sudo_ldap_check_host(ld, entry) &&
1060: /* remember that host matched */
1061: (ldap_host_matches = -1) &&
1062: /* add matches for listing later */
1063: sudo_ldap_add_match(ld, entry, pwflag) &&
1064: /* verify command match */
1.4 millert 1065: sudo_ldap_check_command(ld, entry, &setenv_implied) &&
1.1 millert 1066: /* verify runas match */
1067: sudo_ldap_check_runas(ld, entry)
1068: ) {
1069: /* We have a match! */
1070: DPRINTF(("Perfect Matched!"), 1);
1071: /* pick up any options */
1.4 millert 1072: if (setenv_implied)
1073: def_setenv = TRUE;
1.1 millert 1074: sudo_ldap_parse_options(ld, entry);
1075: /* make sure we don't reenter loop */
1076: ret = VALIDATE_OK;
1077: /* break from inside for loop */
1078: break;
1079: }
1080: entry = ldap_next_entry(ld, entry);
1081: }
1082: if (result)
1083: ldap_msgfree(result);
1084: result = NULL;
1085: }
1086:
1087: sudo_ldap_close(ld); /* shut down connection */
1088:
1089: DPRINTF(("user_matches=%d", ldap_user_matches), 1);
1090: DPRINTF(("host_matches=%d", ldap_host_matches), 1);
1091:
1092: /* Check for special case for -v, -k, -l options */
1093: if (pwflag && ldap_user_matches && ldap_host_matches) {
1094: /*
1095: * Handle verifypw & listpw
1096: *
1097: * To be extra paranoid, since we haven't read any NOPASSWD options
1098: * in /etc/sudoers yet, but we have to make the decission now, lets
1099: * assume the worst and prefer to prompt for password unless the setting
1100: * is "never". (example verifypw=never or listpw=never)
1101: *
1102: */
1103: ret = VALIDATE_OK;
1104: if (pwflag == -1) {
1105: SET(ret, FLAG_NOPASS); /* -k or -K */
1106: } else {
1107: switch (sudo_defs_table[pwflag].sd_un.tuple) {
1108: case never:
1109: SET(ret, FLAG_NOPASS);
1110: break;
1111: case always:
1112: if (def_authenticate)
1113: SET(ret, FLAG_CHECK_USER);
1114: break;
1115: default:
1116: break;
1117: }
1118: }
1119: }
1120: if (ISSET(ret, VALIDATE_OK)) {
1121: /* we have a match, should we check the password? */
1122: if (!def_authenticate)
1123: SET(ret, FLAG_NOPASS);
1124: if (def_noexec)
1125: SET(ret, FLAG_NOEXEC);
1126: if (def_setenv)
1127: SET(ret, FLAG_SETENV);
1128: } else {
1129: /* we do not have a match */
1130: ret = VALIDATE_NOT_OK;
1131: if (pwflag)
1132: SET(ret, FLAG_NO_CHECK);
1133: else if (!ldap_user_matches)
1134: SET(ret, FLAG_NO_USER);
1135: else if (!ldap_host_matches)
1136: SET(ret, FLAG_NO_HOST);
1137: }
1138: DPRINTF(("sudo_ldap_check(%d)=0x%02x", pwflag, ret), 1);
1139:
1140: return(ret);
1141: }
1142:
1143: /*
1144: * shut down LDAP connection
1145: */
1146: static void
1147: sudo_ldap_close(LDAP *ld)
1148: {
1149: if (ld)
1150: ldap_unbind_s(ld);
1151: }