fb7e9f203f4374fccb176f22ca779d23c7f33b9c
[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.16 2007/09/04 14:58:46 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 (!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)
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)
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 }