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