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