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