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