Imported Upstream version 1.8.6p8
[debian/sudo] / plugins / sudoers / match.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2012
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/param.h>
28 #include <sys/stat.h>
29 #include <stdio.h>
30 #ifdef STDC_HEADERS
31 # include <stdlib.h>
32 # include <stddef.h>
33 #else
34 # ifdef HAVE_STDLIB_H
35 #  include <stdlib.h>
36 # endif
37 #endif /* STDC_HEADERS */
38 #ifdef HAVE_STRING_H
39 # include <string.h>
40 #endif /* HAVE_STRING_H */
41 #ifdef HAVE_STRINGS_H
42 # include <strings.h>
43 #endif /* HAVE_STRINGS_H */
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif /* HAVE_UNISTD_H */
47 #ifdef HAVE_FNMATCH
48 # include <fnmatch.h>
49 #endif /* HAVE_FNMATCH */
50 #ifdef HAVE_GLOB
51 # include <glob.h>
52 #endif /* HAVE_GLOB */
53 #ifdef HAVE_NETGROUP_H
54 # include <netgroup.h>
55 #endif /* HAVE_NETGROUP_H */
56 #include <ctype.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <netdb.h>
60 #ifdef HAVE_DIRENT_H
61 # include <dirent.h>
62 # define NAMLEN(dirent) strlen((dirent)->d_name)
63 #else
64 # define dirent direct
65 # define NAMLEN(dirent) (dirent)->d_namlen
66 # ifdef HAVE_SYS_NDIR_H
67 #  include <sys/ndir.h>
68 # endif
69 # ifdef HAVE_SYS_DIR_H
70 #  include <sys/dir.h>
71 # endif
72 # ifdef HAVE_NDIR_H
73 #  include <ndir.h>
74 # endif
75 #endif
76
77 #include "sudoers.h"
78 #include "parse.h"
79 #include <gram.h>
80
81 #ifndef HAVE_FNMATCH
82 # include "compat/fnmatch.h"
83 #endif /* HAVE_FNMATCH */
84 #ifndef HAVE_GLOB
85 # include "compat/glob.h"
86 #endif /* HAVE_GLOB */
87
88 static struct member_list empty;
89
90 static bool command_matches_dir(char *, size_t);
91 static bool command_matches_glob(char *, char *);
92 static bool command_matches_fnmatch(char *, char *);
93 static bool command_matches_normal(char *, char *);
94
95 /*
96  * Returns true if string 's' contains meta characters.
97  */
98 #define has_meta(s)     (strpbrk(s, "\\?*[]") != NULL)
99
100 /*
101  * Check for user described by pw in a list of members.
102  * Returns ALLOW, DENY or UNSPEC.
103  */
104 static int
105 _userlist_matches(struct passwd *pw, struct member_list *list)
106 {
107     struct member *m;
108     struct alias *a;
109     int rval, matched = UNSPEC;
110     debug_decl(_userlist_matches, SUDO_DEBUG_MATCH)
111
112     tq_foreach_rev(list, m) {
113         switch (m->type) {
114             case ALL:
115                 matched = !m->negated;
116                 break;
117             case NETGROUP:
118                 if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
119                     matched = !m->negated;
120                 break;
121             case USERGROUP:
122                 if (usergr_matches(m->name, pw->pw_name, pw))
123                     matched = !m->negated;
124                 break;
125             case ALIAS:
126                 if ((a = alias_find(m->name, USERALIAS)) != NULL) {
127                     rval = _userlist_matches(pw, &a->members);
128                     if (rval != UNSPEC)
129                         matched = m->negated ? !rval : rval;
130                     break;
131                 }
132                 /* FALLTHROUGH */
133             case WORD:
134                 if (userpw_matches(m->name, pw->pw_name, pw))
135                     matched = !m->negated;
136                 break;
137         }
138         if (matched != UNSPEC)
139             break;
140     }
141     debug_return_bool(matched);
142 }
143
144 int
145 userlist_matches(struct passwd *pw, struct member_list *list)
146 {
147     alias_seqno++;
148     return _userlist_matches(pw, list);
149 }
150
151 /*
152  * Check for user described by pw in a list of members.
153  * If both lists are empty compare against def_runas_default.
154  * Returns ALLOW, DENY or UNSPEC.
155  */
156 static int
157 _runaslist_matches(struct member_list *user_list,
158     struct member_list *group_list, struct member **matching_user,
159     struct member **matching_group)
160 {
161     struct member *m;
162     struct alias *a;
163     int rval;
164     int user_matched = UNSPEC;
165     int group_matched = UNSPEC;
166     debug_decl(_runaslist_matches, SUDO_DEBUG_MATCH)
167
168     if (runas_pw != NULL) {
169         /* If no runas user or runas group listed in sudoers, use default. */
170         if (tq_empty(user_list) && tq_empty(group_list))
171             debug_return_int(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
172
173         tq_foreach_rev(user_list, m) {
174             switch (m->type) {
175                 case ALL:
176                     user_matched = !m->negated;
177                     break;
178                 case NETGROUP:
179                     if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
180                         user_matched = !m->negated;
181                     break;
182                 case USERGROUP:
183                     if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
184                         user_matched = !m->negated;
185                     break;
186                 case ALIAS:
187                     if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
188                         rval = _runaslist_matches(&a->members, &empty,
189                             matching_user, NULL);
190                         if (rval != UNSPEC)
191                             user_matched = m->negated ? !rval : rval;
192                         break;
193                     }
194                     /* FALLTHROUGH */
195                 case WORD:
196                     if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
197                         user_matched = !m->negated;
198                     break;
199                 case MYSELF:
200                     if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) ||
201                         strcmp(user_name, runas_pw->pw_name) == 0)
202                         user_matched = !m->negated;
203                     break;
204             }
205             if (user_matched != UNSPEC) {
206                 if (matching_user != NULL && m->type != ALIAS)
207                     *matching_user = m;
208                 break;
209             }
210         }
211     }
212
213     if (runas_gr != NULL) {
214         if (user_matched == UNSPEC) {
215             if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0)
216                 user_matched = ALLOW;   /* only changing group */
217         }
218         tq_foreach_rev(group_list, m) {
219             switch (m->type) {
220                 case ALL:
221                     group_matched = !m->negated;
222                     break;
223                 case ALIAS:
224                     if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
225                         rval = _runaslist_matches(&empty, &a->members,
226                             NULL, matching_group);
227                         if (rval != UNSPEC)
228                             group_matched = m->negated ? !rval : rval;
229                         break;
230                     }
231                     /* FALLTHROUGH */
232                 case WORD:
233                     if (group_matches(m->name, runas_gr))
234                         group_matched = !m->negated;
235                     break;
236             }
237             if (group_matched != UNSPEC) {
238                 if (matching_group != NULL && m->type != ALIAS)
239                     *matching_group = m;
240                 break;
241             }
242         }
243         if (group_matched == UNSPEC) {
244             if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid)
245                 group_matched = ALLOW;  /* runas group matches passwd db */
246         }
247     }
248
249     if (user_matched == DENY || group_matched == DENY)
250         debug_return_int(DENY);
251     if (user_matched == group_matched || runas_gr == NULL)
252         debug_return_int(user_matched);
253     debug_return_int(UNSPEC);
254 }
255
256 int
257 runaslist_matches(struct member_list *user_list,
258     struct member_list *group_list, struct member **matching_user,
259     struct member **matching_group)
260 {
261     alias_seqno++;
262     return _runaslist_matches(user_list ? user_list : &empty,
263         group_list ? group_list : &empty, matching_user, matching_group);
264 }
265
266 /*
267  * Check for host and shost in a list of members.
268  * Returns ALLOW, DENY or UNSPEC.
269  */
270 static int
271 _hostlist_matches(struct member_list *list)
272 {
273     struct member *m;
274     struct alias *a;
275     int rval, matched = UNSPEC;
276     debug_decl(_hostlist_matches, SUDO_DEBUG_MATCH)
277
278     tq_foreach_rev(list, m) {
279         switch (m->type) {
280             case ALL:
281                 matched = !m->negated;
282                 break;
283             case NETGROUP:
284                 if (netgr_matches(m->name, user_host, user_shost, NULL))
285                     matched = !m->negated;
286                 break;
287             case NTWKADDR:
288                 if (addr_matches(m->name))
289                     matched = !m->negated;
290                 break;
291             case ALIAS:
292                 if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
293                     rval = _hostlist_matches(&a->members);
294                     if (rval != UNSPEC)
295                         matched = m->negated ? !rval : rval;
296                     break;
297                 }
298                 /* FALLTHROUGH */
299             case WORD:
300                 if (hostname_matches(user_shost, user_host, m->name))
301                     matched = !m->negated;
302                 break;
303         }
304         if (matched != UNSPEC)
305             break;
306     }
307     debug_return_bool(matched);
308 }
309
310 int
311 hostlist_matches(struct member_list *list)
312 {
313     alias_seqno++;
314     return _hostlist_matches(list);
315 }
316
317 /*
318  * Check for cmnd and args in a list of members.
319  * Returns ALLOW, DENY or UNSPEC.
320  */
321 static int
322 _cmndlist_matches(struct member_list *list)
323 {
324     struct member *m;
325     int matched = UNSPEC;
326     debug_decl(_cmndlist_matches, SUDO_DEBUG_MATCH)
327
328     tq_foreach_rev(list, m) {
329         matched = cmnd_matches(m);
330         if (matched != UNSPEC)
331             break;
332     }
333     debug_return_bool(matched);
334 }
335
336 int
337 cmndlist_matches(struct member_list *list)
338 {
339     alias_seqno++;
340     return _cmndlist_matches(list);
341 }
342
343 /*
344  * Check cmnd and args.
345  * Returns ALLOW, DENY or UNSPEC.
346  */
347 int
348 cmnd_matches(struct member *m)
349 {
350     struct alias *a;
351     struct sudo_command *c;
352     int rval, matched = UNSPEC;
353     debug_decl(cmnd_matches, SUDO_DEBUG_MATCH)
354
355     switch (m->type) {
356         case ALL:
357             matched = !m->negated;
358             break;
359         case ALIAS:
360             alias_seqno++;
361             if ((a = alias_find(m->name, CMNDALIAS)) != NULL) {
362                 rval = _cmndlist_matches(&a->members);
363                 if (rval != UNSPEC)
364                     matched = m->negated ? !rval : rval;
365             }
366             break;
367         case COMMAND:
368             c = (struct sudo_command *)m->name;
369             if (command_matches(c->cmnd, c->args))
370                 matched = !m->negated;
371             break;
372     }
373     debug_return_bool(matched);
374 }
375
376 static bool
377 command_args_match(sudoers_cmnd, sudoers_args)
378     char *sudoers_cmnd;
379     char *sudoers_args;
380 {
381     int flags = 0;
382     debug_decl(command_args_match, SUDO_DEBUG_MATCH)
383
384     /*
385      * If no args specified in sudoers, any user args are allowed.
386      * If the empty string is specified in sudoers, no user args are allowed.
387      */
388     if (!sudoers_args ||
389         (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)))
390         debug_return_bool(true);
391     /*
392      * If args are specified in sudoers, they must match the user args.
393      * If running as sudoedit, all args are assumed to be paths.
394      */
395     if (sudoers_args) {
396         /* For sudoedit, all args are assumed to be pathnames. */
397         if (strcmp(sudoers_cmnd, "sudoedit") == 0)
398             flags = FNM_PATHNAME;
399         if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
400             debug_return_bool(true);
401     }
402     debug_return_bool(false);
403 }
404
405 /*
406  * If path doesn't end in /, return true iff cmnd & path name the same inode;
407  * otherwise, return true if user_cmnd names one of the inodes in path.
408  */
409 bool
410 command_matches(char *sudoers_cmnd, char *sudoers_args)
411 {
412     debug_decl(command_matches, SUDO_DEBUG_MATCH)
413
414     /* Check for pseudo-commands */
415     if (sudoers_cmnd[0] != '/') {
416         /*
417          * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
418          *  a) there are no args in sudoers OR
419          *  b) there are no args on command line and none req by sudoers OR
420          *  c) there are args in sudoers and on command line and they match
421          */
422         if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
423             strcmp(user_cmnd, "sudoedit") != 0)
424             debug_return_bool(false);
425         if (command_args_match(sudoers_cmnd, sudoers_args)) {
426             efree(safe_cmnd);
427             safe_cmnd = estrdup(sudoers_cmnd);
428             debug_return_bool(true);
429         } else
430             debug_return_bool(false);
431     }
432
433     if (has_meta(sudoers_cmnd)) {
434         /*
435          * If sudoers_cmnd has meta characters in it, we need to
436          * use glob(3) and/or fnmatch(3) to do the matching.
437          */
438         if (def_fast_glob)
439             debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
440         debug_return_bool(command_matches_glob(sudoers_cmnd, sudoers_args));
441     }
442     debug_return_bool(command_matches_normal(sudoers_cmnd, sudoers_args));
443 }
444
445 static bool
446 command_matches_fnmatch(char *sudoers_cmnd, char *sudoers_args)
447 {
448     debug_decl(command_matches_fnmatch, SUDO_DEBUG_MATCH)
449
450     /*
451      * Return true if fnmatch(3) succeeds AND
452      *  a) there are no args in sudoers OR
453      *  b) there are no args on command line and none required by sudoers OR
454      *  c) there are args in sudoers and on command line and they match
455      * else return false.
456      */
457     if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
458         debug_return_bool(false);
459     if (command_args_match(sudoers_cmnd, sudoers_args)) {
460         if (safe_cmnd)
461             free(safe_cmnd);
462         safe_cmnd = estrdup(user_cmnd);
463         debug_return_bool(true);
464     }
465     debug_return_bool(false);
466 }
467
468 static bool
469 command_matches_glob(char *sudoers_cmnd, char *sudoers_args)
470 {
471     struct stat sudoers_stat;
472     size_t dlen;
473     char **ap, *base, *cp;
474     glob_t gl;
475     debug_decl(command_matches_glob, SUDO_DEBUG_MATCH)
476
477     /*
478      * First check to see if we can avoid the call to glob(3).
479      * Short circuit if there are no meta chars in the command itself
480      * and user_base and basename(sudoers_cmnd) don't match.
481      */
482     dlen = strlen(sudoers_cmnd);
483     if (sudoers_cmnd[dlen - 1] != '/') {
484         if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
485             base++;
486             if (!has_meta(base) && strcmp(user_base, base) != 0)
487                 debug_return_bool(false);
488         }
489     }
490     /*
491      * Return true if we find a match in the glob(3) results AND
492      *  a) there are no args in sudoers OR
493      *  b) there are no args on command line and none required by sudoers OR
494      *  c) there are args in sudoers and on command line and they match
495      * else return false.
496      */
497     if (glob(sudoers_cmnd, GLOB_NOSORT, NULL, &gl) != 0 || gl.gl_pathc == 0) {
498         globfree(&gl);
499         debug_return_bool(false);
500     }
501     /* For each glob match, compare basename, st_dev and st_ino. */
502     for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
503         /* If it ends in '/' it is a directory spec. */
504         dlen = strlen(cp);
505         if (cp[dlen - 1] == '/') {
506             if (command_matches_dir(cp, dlen))
507                 debug_return_bool(true);
508             continue;
509         }
510
511         /* Only proceed if user_base and basename(cp) match */
512         if ((base = strrchr(cp, '/')) != NULL)
513             base++;
514         else
515             base = cp;
516         if (strcmp(user_base, base) != 0 ||
517             stat(cp, &sudoers_stat) == -1)
518             continue;
519         if (user_stat == NULL ||
520             (user_stat->st_dev == sudoers_stat.st_dev &&
521             user_stat->st_ino == sudoers_stat.st_ino)) {
522             efree(safe_cmnd);
523             safe_cmnd = estrdup(cp);
524             break;
525         }
526     }
527     globfree(&gl);
528     if (cp == NULL)
529         debug_return_bool(false);
530
531     if (command_args_match(sudoers_cmnd, sudoers_args)) {
532         efree(safe_cmnd);
533         safe_cmnd = estrdup(user_cmnd);
534         debug_return_bool(true);
535     }
536     debug_return_bool(false);
537 }
538
539 static bool
540 command_matches_normal(char *sudoers_cmnd, char *sudoers_args)
541 {
542     struct stat sudoers_stat;
543     char *base;
544     size_t dlen;
545     debug_decl(command_matches_normal, SUDO_DEBUG_MATCH)
546
547     /* If it ends in '/' it is a directory spec. */
548     dlen = strlen(sudoers_cmnd);
549     if (sudoers_cmnd[dlen - 1] == '/')
550         debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
551
552     /* Only proceed if user_base and basename(sudoers_cmnd) match */
553     if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
554         base = sudoers_cmnd;
555     else
556         base++;
557     if (strcmp(user_base, base) != 0 ||
558         stat(sudoers_cmnd, &sudoers_stat) == -1)
559         debug_return_bool(false);
560
561     /*
562      * Return true if inode/device matches AND
563      *  a) there are no args in sudoers OR
564      *  b) there are no args on command line and none req by sudoers OR
565      *  c) there are args in sudoers and on command line and they match
566      */
567     if (user_stat != NULL &&
568         (user_stat->st_dev != sudoers_stat.st_dev ||
569         user_stat->st_ino != sudoers_stat.st_ino))
570         debug_return_bool(false);
571     if (command_args_match(sudoers_cmnd, sudoers_args)) {
572         efree(safe_cmnd);
573         safe_cmnd = estrdup(sudoers_cmnd);
574         debug_return_bool(true);
575     }
576     debug_return_bool(false);
577 }
578
579 /*
580  * Return true if user_cmnd names one of the inodes in dir, else false.
581  */
582 static bool
583 command_matches_dir(char *sudoers_dir, size_t dlen)
584 {
585     struct stat sudoers_stat;
586     struct dirent *dent;
587     char buf[PATH_MAX];
588     DIR *dirp;
589     debug_decl(command_matches_dir, SUDO_DEBUG_MATCH)
590
591     /*
592      * Grot through directory entries, looking for user_base.
593      */
594     dirp = opendir(sudoers_dir);
595     if (dirp == NULL)
596         debug_return_bool(false);
597
598     if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
599         closedir(dirp);
600         debug_return_bool(false);
601     }
602     while ((dent = readdir(dirp)) != NULL) {
603         /* ignore paths > PATH_MAX (XXX - log) */
604         buf[dlen] = '\0';
605         if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
606             continue;
607
608         /* only stat if basenames are the same */
609         if (strcmp(user_base, dent->d_name) != 0 ||
610             stat(buf, &sudoers_stat) == -1)
611             continue;
612         if (user_stat == NULL ||
613             (user_stat->st_dev == sudoers_stat.st_dev &&
614             user_stat->st_ino == sudoers_stat.st_ino)) {
615             efree(safe_cmnd);
616             safe_cmnd = estrdup(buf);
617             break;
618         }
619     }
620
621     closedir(dirp);
622     debug_return_bool(dent != NULL);
623 }
624
625 /*
626  * Returns true if the hostname matches the pattern, else false
627  */
628 bool
629 hostname_matches(char *shost, char *lhost, char *pattern)
630 {
631     debug_decl(hostname_matches, SUDO_DEBUG_MATCH)
632
633     if (has_meta(pattern)) {
634         if (strchr(pattern, '.'))
635             debug_return_bool(!fnmatch(pattern, lhost, FNM_CASEFOLD));
636         else
637             debug_return_bool(!fnmatch(pattern, shost, FNM_CASEFOLD));
638     } else {
639         if (strchr(pattern, '.'))
640             debug_return_bool(!strcasecmp(lhost, pattern));
641         else
642             debug_return_bool(!strcasecmp(shost, pattern));
643     }
644 }
645
646 /*
647  *  Returns true if the user/uid from sudoers matches the specified user/uid,
648  *  else returns false.
649  */
650 bool
651 userpw_matches(char *sudoers_user, char *user, struct passwd *pw)
652 {
653     debug_decl(userpw_matches, SUDO_DEBUG_MATCH)
654
655     if (pw != NULL && *sudoers_user == '#') {
656         uid_t uid = (uid_t) atoi(sudoers_user + 1);
657         if (uid == pw->pw_uid)
658             debug_return_bool(true);
659     }
660     debug_return_bool(strcmp(sudoers_user, user) == 0);
661 }
662
663 /*
664  *  Returns true if the group/gid from sudoers matches the specified group/gid,
665  *  else returns false.
666  */
667 bool
668 group_matches(char *sudoers_group, struct group *gr)
669 {
670     debug_decl(group_matches, SUDO_DEBUG_MATCH)
671
672     if (*sudoers_group == '#') {
673         gid_t gid = (gid_t) atoi(sudoers_group + 1);
674         if (gid == gr->gr_gid)
675             debug_return_bool(true);
676     }
677     debug_return_bool(strcmp(gr->gr_name, sudoers_group) == 0);
678 }
679
680 /*
681  *  Returns true if the given user belongs to the named group,
682  *  else returns false.
683  */
684 bool
685 usergr_matches(char *group, char *user, struct passwd *pw)
686 {
687     int matched = false;
688     struct passwd *pw0 = NULL;
689     debug_decl(usergr_matches, SUDO_DEBUG_MATCH)
690
691     /* make sure we have a valid usergroup, sudo style */
692     if (*group++ != '%')
693         goto done;
694
695     if (*group == ':' && def_group_plugin) {
696         matched = group_plugin_query(user, group + 1, pw);
697         goto done;
698     }
699
700     /* look up user's primary gid in the passwd file */
701     if (pw == NULL) {
702         if ((pw0 = sudo_getpwnam(user)) == NULL)
703             goto done;
704         pw = pw0;
705     }
706
707     if (user_in_group(pw, group)) {
708         matched = true;
709         goto done;
710     }
711
712     /* not a Unix group, could be an external group */
713     if (def_group_plugin && group_plugin_query(user, group, pw)) {
714         matched = true;
715         goto done;
716     }
717
718 done:
719     if (pw0 != NULL)
720         sudo_pw_delref(pw0);
721
722     debug_return_bool(matched);
723 }
724
725 /*
726  * Returns true if "host" and "user" belong to the netgroup "netgr",
727  * else return false.  Either of "host", "shost" or "user" may be NULL
728  * in which case that argument is not checked...
729  *
730  * XXX - swap order of host & shost
731  */
732 bool
733 netgr_matches(char *netgr, char *lhost, char *shost, char *user)
734 {
735     static char *domain;
736 #ifdef HAVE_GETDOMAINNAME
737     static int initialized;
738 #endif
739     debug_decl(netgr_matches, SUDO_DEBUG_MATCH)
740
741     /* make sure we have a valid netgroup, sudo style */
742     if (*netgr++ != '+')
743         debug_return_bool(false);
744
745 #ifdef HAVE_GETDOMAINNAME
746     /* get the domain name (if any) */
747     if (!initialized) {
748         domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
749         if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
750             efree(domain);
751             domain = NULL;
752         }
753         initialized = 1;
754     }
755 #endif /* HAVE_GETDOMAINNAME */
756
757 #ifdef HAVE_INNETGR
758     if (innetgr(netgr, lhost, user, domain))
759         debug_return_bool(true);
760     else if (lhost != shost && innetgr(netgr, shost, user, domain))
761         debug_return_bool(true);
762 #endif /* HAVE_INNETGR */
763
764     debug_return_bool(false);
765 }