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