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