b5ecc44782fc0a508a7b4a4816f98bffd199d8c6
[debian/sudo] / ldap.c
1 /*
2  * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include "config.h"
18
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <stdio.h>
23 #ifdef STDC_HEADERS
24 # include <stdlib.h>
25 # include <stddef.h>
26 #else
27 # ifdef HAVE_STDLIB_H
28 #  include <stdlib.h>
29 # endif
30 #endif /* STDC_HEADERS */
31 #ifdef HAVE_STRING_H
32 # include <string.h>
33 #else
34 # ifdef HAVE_STRINGS_H
35 #  include <strings.h>
36 # endif
37 #endif /* HAVE_STRING_H */
38 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
39 # include <malloc.h>
40 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif /* HAVE_UNISTD_H */
44 #include <ctype.h>
45 #include <pwd.h>
46 #include <grp.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <netdb.h>
50 #include <errno.h>
51 #ifdef HAVE_LBER_H
52 #include <lber.h>
53 #endif
54 #include <ldap.h>
55
56 #include "sudo.h"
57 #include "parse.h"
58
59 #ifndef lint
60 static const char rcsid[] = "$Sudo: ldap.c,v 1.14 2004/09/02 04:03:25 aaron Exp $";
61 #endif /* lint */
62
63 /* LDAP code below */
64
65 #ifndef BUF_SIZ
66 #define BUF_SIZ 1024
67 #endif
68
69 #ifndef LDAP_OPT_SUCCESS
70 #define LDAP_OPT_SUCCESS LDAP_SUCCESS
71 #endif
72
73 extern int printmatches;
74
75 /* ldap configuration structure */
76 struct ldap_config {
77   char *host;
78   int  port;
79   int  version;
80   char *uri;
81   char *binddn;
82   char *bindpw;
83   char *base;
84   char *ssl;
85   int  tls_checkpeer;
86   char *tls_cacertfile;
87   char *tls_cacertdir;
88   char *tls_random_file;
89   char *tls_cipher_suite;
90   char *tls_certfile;
91   char *tls_keyfile;
92   int debug;
93 } ldap_conf;
94
95 /*
96  * Walks through search result and returns true if we have a
97  * netgroup that matches our user
98  */
99
100
101 int
102 sudo_ldap_check_user_netgroup(ld,entry)
103   LDAP *ld;
104   LDAPMessage *entry;
105 {
106   char **v=NULL;
107   char **p=NULL;
108
109   int  ret=0;
110
111   if (!entry) return ret;
112
113   /* get the values from the entry */
114   v=ldap_get_values(ld,entry,"sudoUser");
115
116   /* walk through values */
117   for (p=v; p && *p && !ret;p++)
118   {
119     if (ldap_conf.debug>1) printf("ldap sudoUser netgroup '%s' ...",*p);
120
121     /* match any */
122     if (netgr_matches(*p,NULL,NULL,user_name)) ret=1;
123
124     if (ldap_conf.debug>1) printf(" %s\n",ret ? "MATCH!" : "not");
125   }
126
127   /* cleanup */
128   if (v) ldap_value_free(v);
129
130   /* all done */
131   return ret;
132 }
133
134
135 /*
136  * Walks through search result and returns true if we have a
137  * host match
138  */
139 int
140 sudo_ldap_check_host(ld,entry)
141   LDAP *ld;
142   LDAPMessage *entry;
143 {
144   char **v=NULL;
145   char **p=NULL;
146
147   int  ret=0;
148
149   if (!entry) return ret;
150
151   /* get the values from the entry */
152   v=ldap_get_values(ld,entry,"sudoHost");
153
154   /* walk through values */
155   for (p=v; p && *p && !ret;p++)
156   {
157     if (ldap_conf.debug>1) printf("ldap sudoHost '%s' ...",*p);
158
159     /* match any or address or netgroup or hostname */
160     if (
161          !strcasecmp(*p,"ALL") ||
162          addr_matches(*p) ||
163          netgr_matches(*p,user_host,user_shost,NULL) ||
164          !hostname_matches(user_shost,user_host,*p)
165        )
166     {
167        ret=1;
168     }
169
170
171     if (ldap_conf.debug>1) printf(" %s\n",ret ? "MATCH!" : "not");
172   }
173
174   /* cleanup */
175   if (v) ldap_value_free(v);
176
177   /* all done */
178   return ret;
179 }
180
181 /*
182  * Walks through search result and returns true if we have a
183  * runas match.  Since the runas directive in /etc/sudoers is optional,
184  * so is the sudoRunAs attribute.
185  *
186  */
187
188 int sudo_ldap_check_runas(ld,entry)
189   LDAP *ld;
190   LDAPMessage *entry;
191 {
192   char **v=NULL;
193   char **p=NULL;
194
195   int  ret=0;
196
197   if (!entry) return ret;
198
199   /* get the values from the entry */
200   v=ldap_get_values(ld,entry,"sudoRunAs");
201
202   /* BUG:
203    *
204    * if runas is not specified on the command line, the only information as
205    * to which user to run as is in the runas_default option.
206    * We should check check to see if we have the local option present.
207    * Unfortunately we don't parse these options until after this routine
208    * says yes * or no.  The query has already returned, so we could peek at the
209    * attribute values here though.
210    *
211    * For now just require users to always use -u option unless its set
212    * in the global defaults. This behaviour is no different than the global
213    * /etc/sudoers.
214    *
215    * Sigh - maybe add this feature later
216    *
217    */
218
219   /* If there are no runas entries, then match the runas_default with
220    * whats on the command line
221    */
222   if (!v)
223   {
224     ret=!strcasecmp(*user_runas,def_runas_default);
225   }
226
227   /* what about the case where exactly one runas is specified in
228    * the config and the user forgets the -u option, should we
229    * switch it?  - Probably not
230    */
231
232   /* walk through values returned, looking for a match*/
233   for (p=v; p && *p && !ret;p++)
234   {
235     if (ldap_conf.debug>1) printf("ldap sudoRunAs '%s' ...",*p);
236
237     if (
238          !strcasecmp(*p,*user_runas) ||
239          !strcasecmp(*p,"ALL")
240        )
241     {
242        ret = 1;
243     }
244
245     if (ldap_conf.debug>1) printf(" %s\n",ret ? "MATCH!" : "not");
246   }
247
248   /* cleanup */
249   if (v) ldap_value_free(v);
250
251   /* all done */
252   return ret;
253 }
254
255 /*
256  * Walks through search result and returns true if we have a
257  * command match
258  */
259 int sudo_ldap_check_command(ld,entry)
260   LDAP *ld;
261   LDAPMessage *entry;
262 {
263   char **v=NULL;
264   char **p=NULL;
265   char *allowed_cmnd;
266   char *allowed_args;
267   int  ret=0;
268   int  foundbang;
269
270   if (!entry) return ret;
271
272   v=ldap_get_values(ld,entry,"sudoCommand");
273
274   /* get_first_entry */
275   for (p=v; p && *p && ret>=0;p++){
276     if (ldap_conf.debug>1) printf("ldap sudoCommand '%s' ...",*p);
277
278     /* Match against ALL ? */
279     if (!strcasecmp(*p,"ALL")) {
280       ret=1;
281       if (safe_cmnd) free (safe_cmnd);
282       safe_cmnd=estrdup(user_cmnd);
283       if (ldap_conf.debug>1) printf(" MATCH!\n");
284       continue;
285     }
286
287     /* check for !command */
288     if (**p != '!'){
289       foundbang=0;
290       allowed_cmnd=estrdup(*p);  /* command */
291     } else {
292       foundbang=1;
293       allowed_cmnd=estrdup(1+*p); /* !command */
294     }
295
296     /* split optional args away from command */
297     allowed_args=strchr(allowed_cmnd,' ');
298     if (allowed_args) *allowed_args++='\0';
299
300     /* check the command like normal */
301     if (command_matches(allowed_cmnd,allowed_args)) {
302       if (!foundbang){
303         ret=1; /* allowed, but keep checking for a deny match */
304       } else {
305         ret=-1; /* denied by match, no need to check for more */
306       }
307       if (ldap_conf.debug>1) printf(" MATCH!\n");
308     } else {
309       if (ldap_conf.debug>1) printf(" not\n");
310     }
311
312     /* cleanup */
313     free(allowed_cmnd);
314   }
315
316   /* more cleanup */
317   if (v) ldap_value_free(v);
318
319   /* all done */
320   return ret > 0; /* say true if we found at least one ALLOW and no DENY */
321 }
322
323 /*
324  * Read sudoOption, modify the defaults as we go.
325  * This is used once from the cn=defaults entry
326  * and also once when a final sudoRole is matched.
327  *
328  */
329 void
330 sudo_ldap_parse_options(ld,entry)
331   LDAP *ld;
332   LDAPMessage *entry;
333 {
334   /* used to parse attributes */
335   char **v=NULL;
336   char **p=NULL;
337   char *var;
338   char *val;
339   char op;
340
341   if (!entry) return;
342
343   v=ldap_get_values(ld,entry,"sudoOption");
344
345   /* walk through options */
346   for (p=v; p && *p;p++){
347
348     if (ldap_conf.debug>1) printf("ldap sudoOption: '%s'\n",*p);
349     var=estrdup(*p);
350     /* check for = char */
351     val=strchr(var,'=');
352
353     /* check for equals sign past first char */
354     if (val>var){
355       *val++='\0'; /* split on = and truncate var */
356       op=*(val-2); /* peek for += or -= cases */
357       if (op == '+' || op == '-') {
358         *(val-2)='\0'; /* found, remove extra char */
359         /* case var+=val or var-=val */
360         set_default(var,val,(int)op);
361       } else {
362         /* case var=val */
363         set_default(var,val,TRUE);
364       }
365     } else if (*var=='!'){
366       /* case !var Boolean False */
367       set_default(var+1,NULL,FALSE);
368     }  else {
369       /* case var Boolean True */
370       set_default(var,NULL,TRUE);
371     }
372     free(var);
373
374   }
375
376   if (v) ldap_value_free(v);
377
378 }
379
380 /*
381  * Concatenate strings, dynamically growing them as necessary.
382  * Strings can be arbitrarily long and are allocated/reallocated on
383  * the fly.  Make sure to free them when you are done.
384  *
385  * Usage:
386  *
387  * char *s=NULL;
388  * size_t sz;
389  *
390  * ncat(&s,&sz,"This ");
391  * ncat(&s,&sz,"is ");
392  * ncat(&s,&sz,"an ");
393  * ncat(&s,&sz,"arbitrarily ");
394  * ncat(&s,&sz,"long ");
395  * ncat(&s,&sz,"string!");
396  *
397  * printf("String Value='%s', but has %d bytes allocated\n",s,sz);
398  *
399  */
400 void
401 ncat(s,sz,src)
402   char **s;
403   size_t *sz;
404   char *src;
405 {
406   size_t nsz;
407
408   /* handle initial alloc */
409   if (*s == NULL){
410     *s=estrdup(src);
411     *sz=strlen(src)+1;
412     return;
413   }
414
415   /* handle realloc */
416   nsz= strlen(*s) + strlen(src) + 1;
417   if (*sz < nsz) *s=erealloc( (void *)*s , *sz=nsz*2);
418   strlcat(*s,src,*sz);
419 }
420
421
422 /*
423  * builds together a filter to check against ldap
424  */
425 char *
426 sudo_ldap_build_pass1()
427 {
428   struct group *grp;
429   gid_t *grplist=NULL;
430   int ngrps;
431   int i;
432
433   char *b=NULL;
434   size_t sz;
435
436   /* global OR */
437   ncat(&b,&sz,"(|");
438
439   /* build filter sudoUser=user_name */
440   ncat(&b,&sz,"(sudoUser=");
441   ncat(&b,&sz,user_name);
442   ncat(&b,&sz,")");
443
444   /* Append primary group */
445   grp=getgrgid(getgid());
446   if (grp!=NULL){
447     ncat(&b,&sz,"(sudoUser=%");
448     ncat(&b,&sz,grp->gr_name);
449     ncat(&b,&sz,")");
450   }
451
452   /* handle arbitrary number of groups */
453   if (0<(ngrps=getgroups(0,NULL))){
454     grplist=calloc(ngrps,sizeof(gid_t));
455     if (grplist!=NULL && (0<getgroups(ngrps,grplist)))
456       for(i=0;i<ngrps;i++){
457         if((grp=getgrgid(grplist[i]))!=NULL){
458           ncat(&b,&sz,"(sudoUser=%");
459           ncat(&b,&sz,grp->gr_name);
460           ncat(&b,&sz,")");
461         }
462       }
463   }
464
465
466   /* Add ALL to list */
467   ncat(&b,&sz,"(sudoUser=ALL)");
468
469   /* End of OR List */
470   ncat(&b,&sz,")");
471   return b ;
472 }
473
474 /*
475  * Map yes/true/on to 1, no/false/off to 0, else -1
476  */
477 int
478 _atobool(s)
479   char *s;
480 {
481   if (!strcasecmp(s,"yes") || !strcasecmp(s,"true")  || !strcasecmp(s,"on"))
482     return 1;
483   if (!strcasecmp(s,"no")  || !strcasecmp(s,"false") || !strcasecmp(s,"off"))
484     return 0;
485   return -1;
486 }
487
488 int
489 sudo_ldap_read_config()
490 {
491   FILE *f;
492   char buf[BUF_SIZ];
493   char *c;
494   char *keyword;
495   char *value;
496
497   ldap_conf.tls_checkpeer=-1; /* default */
498
499   f=fopen(_PATH_LDAP_CONF,"r");
500   if (!f) return 0;
501   while (f && fgets(buf,sizeof(buf)-1,f)){
502     c=buf;
503     if (*c == '#')  continue; /* ignore comment */
504     if (*c == '\n') continue; /* skip newline */
505     if (!*c)        continue; /* incomplete last line */
506
507     /* skip whitespace before keyword */
508     while (isspace(*c)) c++;
509     keyword=c;
510
511     /* properly terminate keyword string */
512     while (*c && !isspace(*c)) c++;
513     if (*c) {
514       *c='\0'; /* terminate keyword */
515       c++;
516     }
517
518     /* skip whitespace before value */
519     while (isspace(*c)) c++;
520     value=c;
521
522     /* trim whitespace after value */
523     while (*c) c++; /* wind to end */
524     while (--c > value && isspace(*c)) *c='\0';
525
526     /* The following macros make the code much more readable */
527
528 #define MATCH_S(x,y) if (!strcasecmp(keyword,x)) \
529     { if (y) free(y); y=estrdup(value); }
530 #define MATCH_I(x,y) if (!strcasecmp(keyword,x)) { y=atoi(value); }
531 #define MATCH_B(x,y) if (!strcasecmp(keyword,x)) { y=_atobool(value); }
532
533
534
535     /* parse values using a continues chain of
536      * if else if else if else if else ... */
537          MATCH_S("host",    ldap_conf.host)
538     else MATCH_I("port",    ldap_conf.port)
539     else MATCH_S("ssl",     ldap_conf.ssl)
540     else MATCH_B("tls_checkpeer",   ldap_conf.tls_checkpeer)
541     else MATCH_S("tls_cacertfile",  ldap_conf.tls_cacertfile)
542     else MATCH_S("tls_cacertdir",   ldap_conf.tls_cacertdir)
543     else MATCH_S("tls_randfile",    ldap_conf.tls_random_file)
544     else MATCH_S("tls_ciphers",     ldap_conf.tls_cipher_suite)
545     else MATCH_S("tls_cert",        ldap_conf.tls_certfile)
546     else MATCH_S("tls_key",         ldap_conf.tls_keyfile)
547     else MATCH_I("ldap_version", ldap_conf.version)
548     else MATCH_S("uri",     ldap_conf.uri)
549     else MATCH_S("binddn",  ldap_conf.binddn)
550     else MATCH_S("bindpw",  ldap_conf.bindpw)
551     else MATCH_S("sudoers_base",    ldap_conf.base)
552     else MATCH_I("sudoers_debug",   ldap_conf.debug)
553     else {
554
555     /* The keyword was unrecognized.  Since this config file is shared
556      * by multiple programs, it is appropriate to silently ignore options this
557      * program does not understand
558      */
559     }
560
561   } /* parse next line */
562
563   if (f) fclose(f);
564
565   /* defaults */
566   if (!ldap_conf.version) ldap_conf.version=3;
567   if (!ldap_conf.port) ldap_conf.port=389;
568   if (!ldap_conf.host) ldap_conf.host=estrdup("localhost");
569
570
571   if (ldap_conf.debug>1) {
572     printf("LDAP Config Summary\n");
573     printf("===================\n");
574 #ifdef HAVE_LDAP_INITIALIZE
575     if (ldap_conf.uri){
576       printf("uri          %s\n", ldap_conf.uri);
577     } else
578 #endif
579     {
580     printf("host         %s\n", ldap_conf.host ?
581                  ldap_conf.host   : "(NONE)");
582     printf("port         %d\n", ldap_conf.port);
583     }
584     printf("ldap_version %d\n", ldap_conf.version);
585
586     printf("sudoers_base %s\n", ldap_conf.base ?
587                  ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
588     printf("binddn       %s\n", ldap_conf.binddn ?
589                  ldap_conf.binddn : "(anonymous)");
590     printf("bindpw       %s\n", ldap_conf.bindpw ?
591                  ldap_conf.bindpw : "(anonymous)");
592 #ifdef HAVE_LDAP_START_TLS_S
593     printf("ssl          %s\n", ldap_conf.ssl ?
594                  ldap_conf.ssl    : "(no)");
595 #endif
596     printf("===================\n");
597   }
598
599   /* if no base is defined, ignore LDAP */
600   if (!ldap_conf.base) return 0;
601   /* All is good */
602   return 1;
603 }
604
605 /*
606   like perl's join(sep,@ARGS)
607 */
608 char *
609 _ldap_join_values(sep,v)
610   char *sep;
611   char **v;
612 {
613   char **p=NULL;
614   char *b=NULL;
615   size_t sz=0;
616
617   /* paste values together */
618   for (p=v; p && *p;p++){
619     if (p!=v && sep!=NULL) ncat(&b,&sz,sep); /* append seperator */
620     ncat(&b,&sz,*p); /* append value */
621   }
622
623   /* sanity check */
624   if (b[0]=='\0'){
625     /* something went wrong, put something here */
626     ncat(&b,&sz,"(empty list)"); /* append value */
627   }
628
629   /* all done */
630   return b;
631 }
632
633 char * sudo_ldap_cm_list=NULL;
634 size_t sudo_ldap_cm_list_size;
635
636 #define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
637 /*
638  * Walks through search result and returns true if we have a
639  * command match
640  */
641 int
642 sudo_ldap_add_match(ld,entry)
643   LDAP *ld;
644   LDAPMessage *entry;
645 {
646   char **v=NULL;
647   char *dn;
648   char **edn;
649
650   /* if we are not collecting matches, then don't print them */
651   if (printmatches != TRUE) return 1;
652
653   /* collect the dn, only show the rdn */
654   dn=ldap_get_dn(ld,entry);
655   edn=dn ? ldap_explode_dn(dn,1) : NULL;
656   SAVE_LIST("\nLDAP Role: ");
657   SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
658   SAVE_LIST("\n");
659   if (dn)  ldap_memfree(dn);
660   if (edn) ldap_value_free(edn);
661
662   /* get the Runas Values from the entry */
663   v=ldap_get_values(ld,entry,"sudoRunAs");
664   if (v && *v){
665     SAVE_LIST("  RunAs: (");
666     SAVE_LIST(_ldap_join_values(", ",v));
667     SAVE_LIST(")\n");
668   }
669   if (v) ldap_value_free(v);
670
671   /* get the Command Values from the entry */
672   v=ldap_get_values(ld,entry,"sudoCommand");
673   if (v && *v){
674     SAVE_LIST("  Commands:\n    ");
675     SAVE_LIST(_ldap_join_values("\n    ",v));
676     SAVE_LIST("\n");
677   } else {
678     SAVE_LIST("  Commands: NONE\n");
679   }
680   if (v) ldap_value_free(v);
681
682   return 0; /* Don't stop at the first match */
683 }
684 #undef SAVE_LIST
685
686 void
687 sudo_ldap_list_matches()
688 {
689   if (sudo_ldap_cm_list!=NULL) printf("%s",sudo_ldap_cm_list);
690 }
691
692 /*
693  * like sudoers_lookup() - only LDAP style
694  *
695  */
696
697 int
698 sudo_ldap_check(pwflag)
699 int pwflag;
700 {
701
702   LDAP *ld=NULL;
703
704   /* Used for searches */
705   LDAPMessage *result=NULL;
706   LDAPMessage *entry=NULL;
707   /* used to parse attributes */
708   char *filt;
709   /* temp/final return values */
710   int rc=0;
711   int ret=0;
712   int pass=0;
713   /* flags */
714   int ldap_user_matches=0;
715   int ldap_host_matches=0;
716
717   if (!sudo_ldap_read_config())  return VALIDATE_ERROR;
718
719   /* macro to set option, error on failure plus consistent debugging */
720 #define SET_OPT(opt,optname,val) \
721   if (ldap_conf.val!=NULL) { \
722     if (ldap_conf.debug>1) fprintf(stderr, \
723            "ldap_set_option(LDAP_OPT_%s,\"%s\")\n",optname,ldap_conf.val); \
724     rc=ldap_set_option(ld,opt,ldap_conf.val); \
725     if(rc != LDAP_OPT_SUCCESS){ \
726       fprintf(stderr,"ldap_set_option(LDAP_OPT_%s,\"%s\")=%d: %s\n", \
727            optname, ldap_conf.val, rc, ldap_err2string(rc)); \
728       return VALIDATE_ERROR ; \
729     } \
730   } \
731
732   /* like above, but assumes val is in int */
733 #define SET_OPTI(opt,optname,val) \
734     if (ldap_conf.debug>1) fprintf(stderr, \
735            "ldap_set_option(LDAP_OPT_%s,0x%02x)\n",optname,ldap_conf.val); \
736     rc=ldap_set_option(ld,opt,&ldap_conf.val); \
737     if(rc != LDAP_OPT_SUCCESS){ \
738       fprintf(stderr,"ldap_set_option(LDAP_OPT_%s,0x%02x)=%d: %s\n", \
739            optname, ldap_conf.val, rc, ldap_err2string(rc)); \
740       return VALIDATE_ERROR ; \
741     } \
742
743   /* attempt to setup ssl options */
744 #ifdef    LDAP_OPT_X_TLS_CACERTFILE
745   SET_OPT(LDAP_OPT_X_TLS_CACERTFILE,   "X_TLS_CACERTFILE",   tls_cacertfile);
746 #endif /* LDAP_OPT_X_TLS_CACERTFILE */
747
748 #ifdef    LDAP_OPT_X_TLS_CACERTDIR
749   SET_OPT(LDAP_OPT_X_TLS_CACERTDIR,    "X_TLS_CACERTDIR",    tls_cacertdir);
750 #endif /* LDAP_OPT_X_TLS_CACERTDIR */
751
752 #ifdef    LDAP_OPT_X_TLS_CERTFILE
753   SET_OPT(LDAP_OPT_X_TLS_CERTFILE,     "X_TLS_CERTFILE",     tls_certfile);
754 #endif /* LDAP_OPT_X_TLS_CERTFILE */
755
756 #ifdef    LDAP_OPT_X_TLS_KEYFILE
757   SET_OPT(LDAP_OPT_X_TLS_KEYFILE,      "X_TLS_KEYFILE",      tls_keyfile);
758 #endif /* LDAP_OPT_X_TLS_KEYFILE */
759
760 #ifdef    LDAP_OPT_X_TLS_CIPHER_SUITE
761   SET_OPT(LDAP_OPT_X_TLS_CIPHER_SUITE, "X_TLS_CIPHER_SUITE", tls_cipher_suite);
762 #endif /* LDAP_OPT_X_TLS_CIPHER_SUITE */
763
764 #ifdef    LDAP_OPT_X_TLS_RANDOM_FILE
765   SET_OPT(LDAP_OPT_X_TLS_RANDOM_FILE,  "X_TLS_RANDOM_FILE",  tls_random_file);
766 #endif /* LDAP_OPT_X_TLS_RANDOM_FILE */
767
768 #ifdef    LDAP_OPT_X_TLS_REQUIRE_CERT
769   /* check the server certificate? */
770   if (ldap_conf.tls_checkpeer!=-1){
771    SET_OPTI(LDAP_OPT_X_TLS_REQUIRE_CERT,"X_TLS_REQUIRE_CERT",tls_checkpeer);
772   }
773 #endif /* LDAP_OPT_X_TLS_REQUIRE_CERT */
774
775   /* attempt connect */
776 #ifdef HAVE_LDAP_INITIALIZE
777   if (ldap_conf.uri) {
778
779     if (ldap_conf.debug>1) fprintf(stderr,
780            "ldap_initialize(ld,%s)\n",ldap_conf.uri);
781
782     rc=ldap_initialize(&ld,ldap_conf.uri);
783     if(rc){
784       fprintf(stderr, "ldap_initialize()=%d : %s\n",
785            rc,ldap_err2string(rc));
786       return VALIDATE_ERROR;
787     }
788   } else
789 #endif /* HAVE_LDAP_INITIALIZE */
790   if (ldap_conf.host) {
791
792     if (ldap_conf.debug>1) fprintf(stderr,
793            "ldap_init(%s,%d)\n",ldap_conf.host,ldap_conf.port);
794
795     ld=ldap_init(ldap_conf.host,ldap_conf.port);
796     if (!ld) {
797       fprintf(stderr, "ldap_init(): errno=%d : %s\n",
798                  errno, strerror(errno));
799       return VALIDATE_ERROR;
800     }
801   }
802
803 #ifdef LDAP_OPT_PROTOCOL_VERSION
804
805   /* Set the LDAP Protocol version */
806   SET_OPTI(LDAP_OPT_PROTOCOL_VERSION,"PROTOCOL_VERSION", version);
807
808 #endif /* LDAP_OPT_PROTOCOL_VERSION */
809
810 #ifdef HAVE_LDAP_START_TLS_S
811   /* Turn on TLS */
812   if (ldap_conf.ssl && !strcasecmp(ldap_conf.ssl, "start_tls")){
813     rc = ldap_start_tls_s(ld, NULL, NULL);
814     if (rc != LDAP_SUCCESS) {
815       fprintf(stderr, "ldap_start_tls_s(): %d: %s\n", rc, ldap_err2string(rc));
816       ldap_unbind(ld);
817       return VALIDATE_ERROR;
818     }
819
820     if (ldap_conf.debug) printf("ldap_start_tls_s() ok\n");
821   }
822 #endif /* HAVE_LDAP_START_TLS_S */
823
824   /* Actually connect */
825
826   rc=ldap_simple_bind_s(ld,ldap_conf.binddn,ldap_conf.bindpw);
827   if(rc){
828     fprintf(stderr,"ldap_simple_bind_s()=%d : %s\n",
829            rc, ldap_err2string(rc));
830     return VALIDATE_ERROR ;
831   }
832
833   if (ldap_conf.debug) printf("ldap_bind() ok\n");
834
835
836   /* Parse Default Options */
837
838   rc=ldap_search_s(ld,ldap_conf.base,LDAP_SCOPE_ONELEVEL,
839              "cn=defaults",NULL,0,&result);
840   if (!rc && (entry=ldap_first_entry(ld,result))){
841     if (ldap_conf.debug) printf("found:%s\n",ldap_get_dn(ld,entry));
842     sudo_ldap_parse_options(ld,entry);
843   } else {
844     if (ldap_conf.debug) printf("no default options found!\n");
845   }
846
847   if (result) ldap_msgfree(result);
848   result=NULL;
849
850   /*
851    * Okay - time to search for anything that matches this user
852    * Lets limit it to only two queries of the LDAP server
853    *
854    * The first pass will look by the username, groups, and
855    * the keyword ALL.  We will then inspect the results that
856    * came back from the query.  We don't need to inspect the
857    * sudoUser in this pass since the LDAP server already scanned
858    * it for us.
859    *
860    * The second pass will return all the entries that contain
861    * user netgroups.  Then we take the netgroups returned and
862    * try to match them against the username.
863    *
864    */
865
866   for(pass=1;!ret && pass<=2;pass++){
867
868     if (pass==1) {
869       /* Want the entries that match our usernames or groups */
870       filt=sudo_ldap_build_pass1();
871     } else { /* pass=2 */
872       /* Want the entries that have user netgroups in them. */
873       filt=strdup("sudoUser=+*");
874     }
875     if (ldap_conf.debug) printf("ldap search '%s'\n",filt);
876     rc=ldap_search_s(ld,ldap_conf.base,LDAP_SCOPE_ONELEVEL,
877                filt,NULL,0,&result);
878     if (rc) {
879       if (ldap_conf.debug) printf("nothing found for '%s'\n",filt);
880     }
881     if (filt) free (filt);
882     /* parse each entry returned from this most recent search */
883     for(
884         entry=rc ? NULL : ldap_first_entry(ld,result);
885         entry!=NULL;
886         entry=ldap_next_entry(ld,entry))
887     {
888       if (ldap_conf.debug) printf("found:%s\n",ldap_get_dn(ld,entry));
889       if (
890           /* first verify user netgroup matches - only if in pass 2 */
891           (pass!=2 || sudo_ldap_check_user_netgroup(ld,entry)) &&
892        /* remember that user matched */
893        (ldap_user_matches=-1) &&
894           /* verify host match */
895           sudo_ldap_check_host(ld,entry) &&
896        /* remember that host matched */
897        (ldap_host_matches=-1) &&
898           /* add matches for listing later */
899           sudo_ldap_add_match(ld,entry) &&
900           /* verify command match */
901           sudo_ldap_check_command(ld,entry) &&
902           /* verify runas match */
903           sudo_ldap_check_runas(ld,entry)
904       )
905       {
906         /* We have a match! */
907         if(ldap_conf.debug) printf("Perfect Matched!\n");
908         /* pick up any options */
909         sudo_ldap_parse_options(ld,entry);
910         /* make sure we dont reenter loop */
911         ret=VALIDATE_OK;
912         /* break from inside for loop */
913         break;
914       }
915
916     }
917     if (result) ldap_msgfree(result);
918     result=NULL;
919
920   }
921
922   /* shut down connection */
923   if (ld) ldap_unbind_s(ld);
924
925
926   if (ldap_conf.debug) printf("user_matches=%d\n",ldap_user_matches);
927   if (ldap_conf.debug) printf("host_matches=%d\n",ldap_host_matches);
928
929   /* Check for special case for -v, -k, -l options */
930   if (pwflag && ldap_user_matches && ldap_host_matches){
931     /*
932      * Handle verifypw & listpw
933      *
934      * To be extra paranoid, since we haven't read any NOPASSWD options
935      * in /etc/sudoers yet, but we have to make the decission now, lets
936      * assume the worst and prefer to prompt for password unless the setting
937      * is "never". (example verifypw=never or listpw=never)
938      *
939      */
940     if (pwflag<0) { /* -k */
941       ret=VALIDATE_OK; SET(ret,FLAG_NOPASS);
942     } else if (sudo_defs_table[pwflag].sd_un.tuple == never){ /* see note above */
943       ret=VALIDATE_OK; SET(ret,FLAG_NOPASS);
944     } else {
945       ret=VALIDATE_OK; /* extra paranoid */
946     }
947   }
948
949   if (ISSET(ret,VALIDATE_OK)) {
950     /* We have a match.  Should we check the password? */
951     /* Note: This could be the global or a rule specific option */
952     if (!def_authenticate) SET(ret,FLAG_NOPASS);
953     /* Same logic with noexec */
954     if (def_noexec)        SET(ret,FLAG_NOEXEC);
955   } else {
956     /* we do not have a match */
957     ret=VALIDATE_NOT_OK;
958     if (!ldap_user_matches) SET(ret,FLAG_NO_USER);
959     else if (!ldap_host_matches) SET(ret,FLAG_NO_HOST);
960   }
961
962   if (ldap_conf.debug) printf("sudo_ldap_check(%d)=0x%02x\n",pwflag,ret);
963
964   return ret ;
965 }