Imported Upstream version 1.8.7
[debian/sudo] / plugins / sudoers / match.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2013
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23
24 #include <config.h>
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #ifdef STDC_HEADERS
30 # include <stdlib.h>
31 # include <stddef.h>
32 #else
33 # ifdef HAVE_STDLIB_H
34 #  include <stdlib.h>
35 # endif
36 #endif /* STDC_HEADERS */
37 #ifdef HAVE_STRING_H
38 # include <string.h>
39 #endif /* HAVE_STRING_H */
40 #ifdef HAVE_STRINGS_H
41 # include <strings.h>
42 #endif /* HAVE_STRINGS_H */
43 #if defined(HAVE_STDINT_H)
44 # include <stdint.h>
45 #elif defined(HAVE_INTTYPES_H)
46 # include <inttypes.h>
47 #endif
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif /* HAVE_UNISTD_H */
51 #ifdef HAVE_FNMATCH
52 # include <fnmatch.h>
53 #else
54 # include "compat/fnmatch.h"
55 #endif /* HAVE_FNMATCH */
56 #ifndef SUDOERS_NAME_MATCH
57 # ifdef HAVE_GLOB
58 #  include <glob.h>
59 # else
60 #  include "compat/glob.h"
61 # endif /* HAVE_GLOB */
62 #endif /* SUDOERS_NAME_MATCH */
63 #ifdef HAVE_NETGROUP_H
64 # include <netgroup.h>
65 #else
66 # include <netdb.h>
67 #endif /* HAVE_NETGROUP_H */
68 #ifdef HAVE_DIRENT_H
69 # include <dirent.h>
70 # define NAMLEN(dirent) strlen((dirent)->d_name)
71 #else
72 # define dirent direct
73 # define NAMLEN(dirent) (dirent)->d_namlen
74 # ifdef HAVE_SYS_NDIR_H
75 #  include <sys/ndir.h>
76 # endif
77 # ifdef HAVE_SYS_DIR_H
78 #  include <sys/dir.h>
79 # endif
80 # ifdef HAVE_NDIR_H
81 #  include <ndir.h>
82 # endif
83 #endif
84 #include <ctype.h>
85 #include <pwd.h>
86 #include <grp.h>
87 #include <errno.h>
88
89 #include "sudoers.h"
90 #include "parse.h"
91 #include "sha2.h"
92 #include <gram.h>
93
94 static struct member_list empty;
95
96 static bool command_matches_dir(char *, size_t);
97 #ifndef SUDOERS_NAME_MATCH
98 static bool command_matches_glob(char *, char *);
99 #endif
100 static bool command_matches_fnmatch(char *, char *);
101 static bool command_matches_normal(char *, char *, struct sudo_digest *);
102
103 /*
104  * Returns true if string 's' contains meta characters.
105  */
106 #define has_meta(s)     (strpbrk(s, "\\?*[]") != NULL)
107
108 /*
109  * Check for user described by pw in a list of members.
110  * Returns ALLOW, DENY or UNSPEC.
111  */
112 int
113 userlist_matches(struct passwd *pw, struct member_list *list)
114 {
115     struct member *m;
116     struct alias *a;
117     int rval, matched = UNSPEC;
118     debug_decl(userlist_matches, SUDO_DEBUG_MATCH)
119
120     tq_foreach_rev(list, m) {
121         switch (m->type) {
122             case ALL:
123                 matched = !m->negated;
124                 break;
125             case NETGROUP:
126                 if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
127                     matched = !m->negated;
128                 break;
129             case USERGROUP:
130                 if (usergr_matches(m->name, pw->pw_name, pw))
131                     matched = !m->negated;
132                 break;
133             case ALIAS:
134                 if ((a = alias_get(m->name, USERALIAS)) != NULL) {
135                     rval = userlist_matches(pw, &a->members);
136                     if (rval != UNSPEC)
137                         matched = m->negated ? !rval : rval;
138                     alias_put(a);
139                     break;
140                 }
141                 /* FALLTHROUGH */
142             case WORD:
143                 if (userpw_matches(m->name, pw->pw_name, pw))
144                     matched = !m->negated;
145                 break;
146         }
147         if (matched != UNSPEC)
148             break;
149     }
150     debug_return_bool(matched);
151 }
152
153 /*
154  * Check for user described by pw in a list of members.
155  * If both lists are empty compare against def_runas_default.
156  * Returns ALLOW, DENY or UNSPEC.
157  */
158 int
159 runaslist_matches(struct member_list *user_list,
160     struct member_list *group_list, struct member **matching_user,
161     struct member **matching_group)
162 {
163     struct member *m;
164     struct alias *a;
165     int rval;
166     int user_matched = UNSPEC;
167     int group_matched = UNSPEC;
168     debug_decl(runaslist_matches, SUDO_DEBUG_MATCH)
169
170     if (runas_pw != NULL) {
171         /* If no runas user or runas group listed in sudoers, use default. */
172         if (tq_empty(user_list) && tq_empty(group_list))
173             debug_return_int(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
174
175         tq_foreach_rev(user_list, m) {
176             switch (m->type) {
177                 case ALL:
178                     user_matched = !m->negated;
179                     break;
180                 case NETGROUP:
181                     if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
182                         user_matched = !m->negated;
183                     break;
184                 case USERGROUP:
185                     if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
186                         user_matched = !m->negated;
187                     break;
188                 case ALIAS:
189                     if ((a = alias_get(m->name, RUNASALIAS)) != NULL) {
190                         rval = runaslist_matches(&a->members, &empty,
191                             matching_user, NULL);
192                         if (rval != UNSPEC)
193                             user_matched = m->negated ? !rval : rval;
194                         alias_put(a);
195                         break;
196                     }
197                     /* FALLTHROUGH */
198                 case WORD:
199                     if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
200                         user_matched = !m->negated;
201                     break;
202                 case MYSELF:
203                     if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) ||
204                         strcmp(user_name, runas_pw->pw_name) == 0)
205                         user_matched = !m->negated;
206                     break;
207             }
208             if (user_matched != UNSPEC) {
209                 if (matching_user != NULL && m->type != ALIAS)
210                     *matching_user = m;
211                 break;
212             }
213         }
214     }
215
216     if (runas_gr != NULL) {
217         if (user_matched == UNSPEC) {
218             if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0)
219                 user_matched = ALLOW;   /* only changing group */
220         }
221         tq_foreach_rev(group_list, m) {
222             switch (m->type) {
223                 case ALL:
224                     group_matched = !m->negated;
225                     break;
226                 case ALIAS:
227                     if ((a = alias_get(m->name, RUNASALIAS)) != NULL) {
228                         rval = runaslist_matches(&empty, &a->members,
229                             NULL, matching_group);
230                         if (rval != UNSPEC)
231                             group_matched = m->negated ? !rval : rval;
232                         alias_put(a);
233                         break;
234                     }
235                     /* FALLTHROUGH */
236                 case WORD:
237                     if (group_matches(m->name, runas_gr))
238                         group_matched = !m->negated;
239                     break;
240             }
241             if (group_matched != UNSPEC) {
242                 if (matching_group != NULL && m->type != ALIAS)
243                     *matching_group = m;
244                 break;
245             }
246         }
247         if (group_matched == UNSPEC) {
248             if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid)
249                 group_matched = ALLOW;  /* runas group matches passwd db */
250         }
251     }
252
253     if (user_matched == DENY || group_matched == DENY)
254         debug_return_int(DENY);
255     if (user_matched == group_matched || runas_gr == NULL)
256         debug_return_int(user_matched);
257     debug_return_int(UNSPEC);
258 }
259
260 /*
261  * Check for host and shost in a list of members.
262  * Returns ALLOW, DENY or UNSPEC.
263  */
264 int
265 hostlist_matches(struct member_list *list)
266 {
267     struct member *m;
268     struct alias *a;
269     int rval, matched = UNSPEC;
270     debug_decl(hostlist_matches, SUDO_DEBUG_MATCH)
271
272     tq_foreach_rev(list, m) {
273         switch (m->type) {
274             case ALL:
275                 matched = !m->negated;
276                 break;
277             case NETGROUP:
278                 if (netgr_matches(m->name, user_host, user_shost, NULL))
279                     matched = !m->negated;
280                 break;
281             case NTWKADDR:
282                 if (addr_matches(m->name))
283                     matched = !m->negated;
284                 break;
285             case ALIAS:
286                 if ((a = alias_get(m->name, HOSTALIAS)) != NULL) {
287                     rval = hostlist_matches(&a->members);
288                     if (rval != UNSPEC)
289                         matched = m->negated ? !rval : rval;
290                     alias_put(a);
291                     break;
292                 }
293                 /* FALLTHROUGH */
294             case WORD:
295                 if (hostname_matches(user_shost, user_host, m->name))
296                     matched = !m->negated;
297                 break;
298         }
299         if (matched != UNSPEC)
300             break;
301     }
302     debug_return_bool(matched);
303 }
304
305 /*
306  * Check for cmnd and args in a list of members.
307  * Returns ALLOW, DENY or UNSPEC.
308  */
309 int
310 cmndlist_matches(struct member_list *list)
311 {
312     struct member *m;
313     int matched = UNSPEC;
314     debug_decl(cmndlist_matches, SUDO_DEBUG_MATCH)
315
316     tq_foreach_rev(list, m) {
317         matched = cmnd_matches(m);
318         if (matched != UNSPEC)
319             break;
320     }
321     debug_return_bool(matched);
322 }
323
324 /*
325  * Check cmnd and args.
326  * Returns ALLOW, DENY or UNSPEC.
327  */
328 int
329 cmnd_matches(struct member *m)
330 {
331     struct alias *a;
332     struct sudo_command *c;
333     int rval, matched = UNSPEC;
334     debug_decl(cmnd_matches, SUDO_DEBUG_MATCH)
335
336     switch (m->type) {
337         case ALL:
338             matched = !m->negated;
339             break;
340         case ALIAS:
341             if ((a = alias_get(m->name, CMNDALIAS)) != NULL) {
342                 rval = cmndlist_matches(&a->members);
343                 if (rval != UNSPEC)
344                     matched = m->negated ? !rval : rval;
345                 alias_put(a);
346             }
347             break;
348         case COMMAND:
349             c = (struct sudo_command *)m->name;
350             if (command_matches(c->cmnd, c->args, c->digest))
351                 matched = !m->negated;
352             break;
353     }
354     debug_return_bool(matched);
355 }
356
357 static bool
358 command_args_match(char *sudoers_cmnd, char *sudoers_args)
359 {
360     int flags = 0;
361     debug_decl(command_args_match, SUDO_DEBUG_MATCH)
362
363     /*
364      * If no args specified in sudoers, any user args are allowed.
365      * If the empty string is specified in sudoers, no user args are allowed.
366      */
367     if (!sudoers_args ||
368         (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)))
369         debug_return_bool(true);
370     /*
371      * If args are specified in sudoers, they must match the user args.
372      * If running as sudoedit, all args are assumed to be paths.
373      */
374     if (sudoers_args) {
375         /* For sudoedit, all args are assumed to be pathnames. */
376         if (strcmp(sudoers_cmnd, "sudoedit") == 0)
377             flags = FNM_PATHNAME;
378         if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
379             debug_return_bool(true);
380     }
381     debug_return_bool(false);
382 }
383
384 /*
385  * If path doesn't end in /, return true iff cmnd & path name the same inode;
386  * otherwise, return true if user_cmnd names one of the inodes in path.
387  */
388 bool
389 command_matches(char *sudoers_cmnd, char *sudoers_args, struct sudo_digest *digest)
390 {
391     debug_decl(command_matches, SUDO_DEBUG_MATCH)
392
393     /* Check for pseudo-commands */
394     if (sudoers_cmnd[0] != '/') {
395         /*
396          * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
397          *  a) there are no args in sudoers OR
398          *  b) there are no args on command line and none req by sudoers OR
399          *  c) there are args in sudoers and on command line and they match
400          */
401         if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
402             strcmp(user_cmnd, "sudoedit") != 0)
403             debug_return_bool(false);
404         if (command_args_match(sudoers_cmnd, sudoers_args)) {
405             efree(safe_cmnd);
406             safe_cmnd = estrdup(sudoers_cmnd);
407             debug_return_bool(true);
408         } else
409             debug_return_bool(false);
410     }
411
412     if (has_meta(sudoers_cmnd)) {
413         /*
414          * If sudoers_cmnd has meta characters in it, we need to
415          * use glob(3) and/or fnmatch(3) to do the matching.
416          */
417 #ifdef SUDOERS_NAME_MATCH
418         debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
419 #else
420         if (def_fast_glob)
421             debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
422         debug_return_bool(command_matches_glob(sudoers_cmnd, sudoers_args));
423 #endif
424     }
425     debug_return_bool(command_matches_normal(sudoers_cmnd, sudoers_args, digest));
426 }
427
428 static bool
429 command_matches_fnmatch(char *sudoers_cmnd, char *sudoers_args)
430 {
431     debug_decl(command_matches_fnmatch, SUDO_DEBUG_MATCH)
432
433     /*
434      * Return true if fnmatch(3) succeeds AND
435      *  a) there are no args in sudoers OR
436      *  b) there are no args on command line and none required by sudoers OR
437      *  c) there are args in sudoers and on command line and they match
438      * else return false.
439      */
440     if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
441         debug_return_bool(false);
442     if (command_args_match(sudoers_cmnd, sudoers_args)) {
443         if (safe_cmnd)
444             free(safe_cmnd);
445         safe_cmnd = estrdup(user_cmnd);
446         debug_return_bool(true);
447     }
448     debug_return_bool(false);
449 }
450
451 #ifndef SUDOERS_NAME_MATCH
452 static bool
453 command_matches_glob(char *sudoers_cmnd, char *sudoers_args)
454 {
455     struct stat sudoers_stat;
456     size_t dlen;
457     char **ap, *base, *cp;
458     glob_t gl;
459     debug_decl(command_matches_glob, SUDO_DEBUG_MATCH)
460
461     /*
462      * First check to see if we can avoid the call to glob(3).
463      * Short circuit if there are no meta chars in the command itself
464      * and user_base and basename(sudoers_cmnd) don't match.
465      */
466     dlen = strlen(sudoers_cmnd);
467     if (sudoers_cmnd[dlen - 1] != '/') {
468         if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
469             base++;
470             if (!has_meta(base) && strcmp(user_base, base) != 0)
471                 debug_return_bool(false);
472         }
473     }
474     /*
475      * Return true if we find a match in the glob(3) results AND
476      *  a) there are no args in sudoers OR
477      *  b) there are no args on command line and none required by sudoers OR
478      *  c) there are args in sudoers and on command line and they match
479      * else return false.
480      */
481     if (glob(sudoers_cmnd, GLOB_NOSORT, NULL, &gl) != 0 || gl.gl_pathc == 0) {
482         globfree(&gl);
483         debug_return_bool(false);
484     }
485     /* For each glob match, compare basename, st_dev and st_ino. */
486     for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
487         /* If it ends in '/' it is a directory spec. */
488         dlen = strlen(cp);
489         if (cp[dlen - 1] == '/') {
490             if (command_matches_dir(cp, dlen))
491                 debug_return_bool(true);
492             continue;
493         }
494
495         /* Only proceed if user_base and basename(cp) match */
496         if ((base = strrchr(cp, '/')) != NULL)
497             base++;
498         else
499             base = cp;
500         if (strcmp(user_base, base) != 0 ||
501             stat(cp, &sudoers_stat) == -1)
502             continue;
503         if (user_stat == NULL ||
504             (user_stat->st_dev == sudoers_stat.st_dev &&
505             user_stat->st_ino == sudoers_stat.st_ino)) {
506             efree(safe_cmnd);
507             safe_cmnd = estrdup(cp);
508             break;
509         }
510     }
511     globfree(&gl);
512     if (cp == NULL)
513         debug_return_bool(false);
514
515     if (command_args_match(sudoers_cmnd, sudoers_args)) {
516         efree(safe_cmnd);
517         safe_cmnd = estrdup(user_cmnd);
518         debug_return_bool(true);
519     }
520     debug_return_bool(false);
521 }
522 #endif /* SUDOERS_NAME_MATCH */
523
524 #ifdef SUDOERS_NAME_MATCH
525 static bool
526 command_matches_normal(char *sudoers_cmnd, char *sudoers_args, struct sudo_digest *digest)
527 {
528     size_t dlen;
529     debug_decl(command_matches_normal, SUDO_DEBUG_MATCH)
530
531     dlen = strlen(sudoers_cmnd);
532
533     /* If it ends in '/' it is a directory spec. */
534     if (sudoers_cmnd[dlen - 1] == '/')
535         debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
536
537     if (strcmp(user_cmnd, sudoers_cmnd) == 0) {
538         if (command_args_match(sudoers_cmnd, sudoers_args)) {
539             efree(safe_cmnd);
540             safe_cmnd = estrdup(sudoers_cmnd);
541             debug_return_bool(true);
542         }
543     }
544     debug_return_bool(false);
545 }
546 #else /* !SUDOERS_NAME_MATCH */
547
548 static struct digest_function {
549     const char *digest_name;
550     const unsigned int digest_len;
551     void (*init)(SHA2_CTX *);
552     void (*update)(SHA2_CTX *, const unsigned char *, size_t);
553     void (*final)(unsigned char *, SHA2_CTX *);
554 } digest_functions[] = {
555     {
556         "SHA224",
557         SHA224_DIGEST_LENGTH,
558         SHA224Init,
559         SHA224Update,
560         SHA224Final
561     }, {
562         "SHA256",
563         SHA256_DIGEST_LENGTH,
564         SHA256Init,
565         SHA256Update,
566         SHA256Final
567     }, {
568         "SHA384",
569         SHA384_DIGEST_LENGTH,
570         SHA384Init,
571         SHA384Update,
572         SHA384Final
573     }, {
574         "SHA512",
575         SHA512_DIGEST_LENGTH,
576         SHA512Init,
577         SHA512Update,
578         SHA512Final
579     }, {
580         NULL
581     }
582 };
583
584 static bool
585 digest_matches(char *file, struct sudo_digest *sd)
586 {
587     unsigned char file_digest[SHA512_DIGEST_LENGTH];
588     unsigned char sudoers_digest[SHA512_DIGEST_LENGTH];
589     unsigned char buf[32 * 1024];
590     struct digest_function *func = NULL;
591     size_t nread;
592     SHA2_CTX ctx;
593     FILE *fp;
594     unsigned int i;
595     debug_decl(digest_matches, SUDO_DEBUG_MATCH)
596
597     for (i = 0; digest_functions[i].digest_name != NULL; i++) {
598         if (sd->digest_type == i) {
599             func = &digest_functions[i];
600             break;
601         }
602     }
603     if (func == NULL) {
604         warningx(_("unsupported digest type %d for %s"), sd->digest_type, file);
605         debug_return_bool(false);
606     }
607     if (strlen(sd->digest_str) == func->digest_len * 2) {
608         /* Convert the command digest from ascii hex to binary. */
609         for (i = 0; i < func->digest_len; i++) {
610             if (!isxdigit((unsigned char)sd->digest_str[i + i]) ||
611                 !isxdigit((unsigned char)sd->digest_str[i + i + 1])) {
612                 goto bad_format;
613             }
614             sudoers_digest[i] = hexchar(&sd->digest_str[i + i]);
615         }
616     } else {
617         size_t len = base64_decode(sd->digest_str, sudoers_digest,
618             sizeof(sudoers_digest));
619         if (len != func->digest_len)
620             goto bad_format;
621     }
622
623     if ((fp = fopen(file, "r")) == NULL) {
624         sudo_debug_printf(SUDO_DEBUG_INFO, "unable to open %s: %s",
625             file, strerror(errno));
626         debug_return_bool(false);
627     }
628
629     func->init(&ctx);
630     while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) {
631         func->update(&ctx, buf, nread);
632     }
633     if (ferror(fp)) {
634         warningx(_("%s: read error"), file);
635         fclose(fp);
636         debug_return_bool(false);
637     }
638     fclose(fp);
639     func->final(file_digest, &ctx);
640
641     debug_return_bool(memcmp(file_digest, sudoers_digest, func->digest_len) == 0);
642 bad_format:
643     warningx(_("digest for %s (%s) is not in %s form"), file,
644         sd->digest_str, func->digest_name);
645     debug_return_bool(false);
646 }
647
648 static bool
649 command_matches_normal(char *sudoers_cmnd, char *sudoers_args, struct sudo_digest *digest)
650 {
651     struct stat sudoers_stat;
652     char *base;
653     size_t dlen;
654     debug_decl(command_matches_normal, SUDO_DEBUG_MATCH)
655
656     /* If it ends in '/' it is a directory spec. */
657     dlen = strlen(sudoers_cmnd);
658     if (sudoers_cmnd[dlen - 1] == '/')
659         debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
660
661     /* Only proceed if user_base and basename(sudoers_cmnd) match */
662     if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
663         base = sudoers_cmnd;
664     else
665         base++;
666     if (strcmp(user_base, base) != 0 ||
667         stat(sudoers_cmnd, &sudoers_stat) == -1)
668         debug_return_bool(false);
669
670     /*
671      * Return true if inode/device matches AND
672      *  a) there are no args in sudoers OR
673      *  b) there are no args on command line and none req by sudoers OR
674      *  c) there are args in sudoers and on command line and they match
675      *  d) there is a digest and it matches
676      */
677     if (user_stat != NULL &&
678         (user_stat->st_dev != sudoers_stat.st_dev ||
679         user_stat->st_ino != sudoers_stat.st_ino))
680         debug_return_bool(false);
681     if (!command_args_match(sudoers_cmnd, sudoers_args))
682         debug_return_bool(false);
683     if (digest != NULL && !digest_matches(sudoers_cmnd, digest)) {
684         /* XXX - log functions not available but we should log very loudly */
685         debug_return_bool(false);
686     }
687     efree(safe_cmnd);
688     safe_cmnd = estrdup(sudoers_cmnd);
689     debug_return_bool(true);
690 }
691 #endif /* SUDOERS_NAME_MATCH */
692
693 #ifdef SUDOERS_NAME_MATCH
694 /*
695  * Return true if user_cmnd begins with sudoers_dir, else false.
696  * Note that sudoers_dir include the trailing '/'
697  */
698 static bool
699 command_matches_dir(char *sudoers_dir, size_t dlen)
700 {
701     debug_decl(command_matches_dir, SUDO_DEBUG_MATCH)
702     debug_return_bool(strncmp(user_cmnd, sudoers_dir, dlen) == 0);
703 }
704 #else /* !SUDOERS_NAME_MATCH */
705 /*
706  * Return true if user_cmnd names one of the inodes in dir, else false.
707  */
708 static bool
709 command_matches_dir(char *sudoers_dir, size_t dlen)
710 {
711     struct stat sudoers_stat;
712     struct dirent *dent;
713     char buf[PATH_MAX];
714     DIR *dirp;
715     debug_decl(command_matches_dir, SUDO_DEBUG_MATCH)
716
717     /*
718      * Grot through directory entries, looking for user_base.
719      */
720     dirp = opendir(sudoers_dir);
721     if (dirp == NULL)
722         debug_return_bool(false);
723
724     if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
725         closedir(dirp);
726         debug_return_bool(false);
727     }
728     while ((dent = readdir(dirp)) != NULL) {
729         /* ignore paths > PATH_MAX (XXX - log) */
730         buf[dlen] = '\0';
731         if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
732             continue;
733
734         /* only stat if basenames are the same */
735         if (strcmp(user_base, dent->d_name) != 0 ||
736             stat(buf, &sudoers_stat) == -1)
737             continue;
738         if (user_stat == NULL ||
739             (user_stat->st_dev == sudoers_stat.st_dev &&
740             user_stat->st_ino == sudoers_stat.st_ino)) {
741             efree(safe_cmnd);
742             safe_cmnd = estrdup(buf);
743             break;
744         }
745     }
746
747     closedir(dirp);
748     debug_return_bool(dent != NULL);
749 }
750 #endif /* SUDOERS_NAME_MATCH */
751
752 /*
753  * Returns true if the hostname matches the pattern, else false
754  */
755 bool
756 hostname_matches(char *shost, char *lhost, char *pattern)
757 {
758     debug_decl(hostname_matches, SUDO_DEBUG_MATCH)
759
760     if (has_meta(pattern)) {
761         if (strchr(pattern, '.'))
762             debug_return_bool(!fnmatch(pattern, lhost, FNM_CASEFOLD));
763         else
764             debug_return_bool(!fnmatch(pattern, shost, FNM_CASEFOLD));
765     } else {
766         if (strchr(pattern, '.'))
767             debug_return_bool(!strcasecmp(lhost, pattern));
768         else
769             debug_return_bool(!strcasecmp(shost, pattern));
770     }
771 }
772
773 /*
774  *  Returns true if the user/uid from sudoers matches the specified user/uid,
775  *  else returns false.
776  */
777 bool
778 userpw_matches(char *sudoers_user, char *user, struct passwd *pw)
779 {
780     debug_decl(userpw_matches, SUDO_DEBUG_MATCH)
781
782     if (pw != NULL && *sudoers_user == '#') {
783         uid_t uid = (uid_t) atoi(sudoers_user + 1);
784         if (uid == pw->pw_uid)
785             debug_return_bool(true);
786     }
787     debug_return_bool(strcmp(sudoers_user, user) == 0);
788 }
789
790 /*
791  *  Returns true if the group/gid from sudoers matches the specified group/gid,
792  *  else returns false.
793  */
794 bool
795 group_matches(char *sudoers_group, struct group *gr)
796 {
797     debug_decl(group_matches, SUDO_DEBUG_MATCH)
798
799     if (*sudoers_group == '#') {
800         gid_t gid = (gid_t) atoi(sudoers_group + 1);
801         if (gid == gr->gr_gid)
802             debug_return_bool(true);
803     }
804     debug_return_bool(strcmp(gr->gr_name, sudoers_group) == 0);
805 }
806
807 /*
808  *  Returns true if the given user belongs to the named group,
809  *  else returns false.
810  */
811 bool
812 usergr_matches(char *group, char *user, struct passwd *pw)
813 {
814     int matched = false;
815     struct passwd *pw0 = NULL;
816     debug_decl(usergr_matches, SUDO_DEBUG_MATCH)
817
818     /* make sure we have a valid usergroup, sudo style */
819     if (*group++ != '%')
820         goto done;
821
822     if (*group == ':' && def_group_plugin) {
823         matched = group_plugin_query(user, group + 1, pw);
824         goto done;
825     }
826
827     /* look up user's primary gid in the passwd file */
828     if (pw == NULL) {
829         if ((pw0 = sudo_getpwnam(user)) == NULL)
830             goto done;
831         pw = pw0;
832     }
833
834     if (user_in_group(pw, group)) {
835         matched = true;
836         goto done;
837     }
838
839     /* not a Unix group, could be an external group */
840     if (def_group_plugin && group_plugin_query(user, group, pw)) {
841         matched = true;
842         goto done;
843     }
844
845 done:
846     if (pw0 != NULL)
847         sudo_pw_delref(pw0);
848
849     debug_return_bool(matched);
850 }
851
852 #ifdef HAVE_INNETGR
853 /*
854  * Get NIS-style domain name and return a malloc()ed copy or NULL if none.
855  */
856 static char *
857 sudo_getdomainname(void)
858 {
859     char *domain = NULL;
860 #ifdef HAVE_GETDOMAINNAME
861     char *buf, *cp;
862
863     buf = emalloc(HOST_NAME_MAX + 1);
864     if (getdomainname(buf, HOST_NAME_MAX + 1) == 0 && *buf != '\0') {
865         domain = buf;
866         for (cp = buf; *cp != '\0'; cp++) {
867             /* Check for illegal characters, Linux may use "(none)". */
868             if (*cp == '(' || *cp == ')' || *cp == ',' || *cp == ' ') {
869                 domain = NULL;
870                 break;
871             }
872         }
873     }
874     if (domain == NULL)
875         efree(buf);
876 #endif /* HAVE_GETDOMAINNAME */
877     return domain;
878 }
879 #endif /* HAVE_INNETGR */
880
881 /*
882  * Returns true if "host" and "user" belong to the netgroup "netgr",
883  * else return false.  Either of "host", "shost" or "user" may be NULL
884  * in which case that argument is not checked...
885  *
886  * XXX - swap order of host & shost
887  */
888 bool
889 netgr_matches(char *netgr, char *lhost, char *shost, char *user)
890 {
891 #ifdef HAVE_INNETGR
892     static char *domain;
893     static int initialized;
894 #endif
895     debug_decl(netgr_matches, SUDO_DEBUG_MATCH)
896
897 #ifdef HAVE_INNETGR
898     /* make sure we have a valid netgroup, sudo style */
899     if (*netgr++ != '+')
900         debug_return_bool(false);
901
902     /* get the domain name (if any) */
903     if (!initialized) {
904         domain = sudo_getdomainname();
905         initialized = 1;
906     }
907
908     if (innetgr(netgr, lhost, user, domain))
909         debug_return_bool(true);
910     else if (lhost != shost && innetgr(netgr, shost, user, domain))
911         debug_return_bool(true);
912 #endif /* HAVE_INNETGR */
913
914     debug_return_bool(false);
915 }