]> git.gag.com Git - debian/sudo/blob - plugins/sudoers/match.c
Imported Upstream version 1.8.4p4
[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_EXTENDED_GLOB
51 # include <glob.h>
52 #endif /* HAVE_EXTENDED_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_EXTENDED_GLOB
85 # include "compat/glob.h"
86 #endif /* HAVE_EXTENDED_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, struct member_list *group_list)
158 {
159     struct member *m;
160     struct alias *a;
161     int rval;
162     int user_matched = UNSPEC;
163     int group_matched = UNSPEC;
164     debug_decl(_runaslist_matches, SUDO_DEBUG_MATCH)
165
166     if (runas_pw != NULL) {
167         /* If no runas user or runas group listed in sudoers, use default. */
168         if (tq_empty(user_list) && tq_empty(group_list))
169             debug_return_int(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
170
171         tq_foreach_rev(user_list, m) {
172             switch (m->type) {
173                 case ALL:
174                     user_matched = !m->negated;
175                     break;
176                 case NETGROUP:
177                     if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
178                         user_matched = !m->negated;
179                     break;
180                 case USERGROUP:
181                     if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
182                         user_matched = !m->negated;
183                     break;
184                 case ALIAS:
185                     if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
186                         rval = _runaslist_matches(&a->members, &empty);
187                         if (rval != UNSPEC)
188                             user_matched = m->negated ? !rval : rval;
189                         break;
190                     }
191                     /* FALLTHROUGH */
192                 case WORD:
193                     if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
194                         user_matched = !m->negated;
195                     break;
196             }
197             if (user_matched != UNSPEC)
198                 break;
199         }
200     }
201
202     if (runas_gr != NULL) {
203         if (user_matched == UNSPEC) {
204             if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0)
205                 user_matched = ALLOW;   /* only changing group */
206         }
207         tq_foreach_rev(group_list, m) {
208             switch (m->type) {
209                 case ALL:
210                     group_matched = !m->negated;
211                     break;
212                 case ALIAS:
213                     if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
214                         rval = _runaslist_matches(&empty, &a->members);
215                         if (rval != UNSPEC)
216                             group_matched = m->negated ? !rval : rval;
217                         break;
218                     }
219                     /* FALLTHROUGH */
220                 case WORD:
221                     if (group_matches(m->name, runas_gr))
222                         group_matched = !m->negated;
223                     break;
224             }
225             if (group_matched != UNSPEC)
226                 break;
227         }
228         if (group_matched == UNSPEC) {
229             if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid)
230                 group_matched = ALLOW;  /* runas group matches passwd db */
231         }
232     }
233
234     if (user_matched == DENY || group_matched == DENY)
235         debug_return_int(DENY);
236     if (user_matched == group_matched || runas_gr == NULL)
237         debug_return_int(user_matched);
238     debug_return_int(UNSPEC);
239 }
240
241 int
242 runaslist_matches(struct member_list *user_list, struct member_list *group_list)
243 {
244     alias_seqno++;
245     return _runaslist_matches(user_list ? user_list : &empty,
246         group_list ? group_list : &empty);
247 }
248
249 /*
250  * Check for host and shost in a list of members.
251  * Returns ALLOW, DENY or UNSPEC.
252  */
253 static int
254 _hostlist_matches(struct member_list *list)
255 {
256     struct member *m;
257     struct alias *a;
258     int rval, matched = UNSPEC;
259     debug_decl(_hostlist_matches, SUDO_DEBUG_MATCH)
260
261     tq_foreach_rev(list, m) {
262         switch (m->type) {
263             case ALL:
264                 matched = !m->negated;
265                 break;
266             case NETGROUP:
267                 if (netgr_matches(m->name, user_host, user_shost, NULL))
268                     matched = !m->negated;
269                 break;
270             case NTWKADDR:
271                 if (addr_matches(m->name))
272                     matched = !m->negated;
273                 break;
274             case ALIAS:
275                 if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
276                     rval = _hostlist_matches(&a->members);
277                     if (rval != UNSPEC)
278                         matched = m->negated ? !rval : rval;
279                     break;
280                 }
281                 /* FALLTHROUGH */
282             case WORD:
283                 if (hostname_matches(user_shost, user_host, m->name))
284                     matched = !m->negated;
285                 break;
286         }
287         if (matched != UNSPEC)
288             break;
289     }
290     debug_return_bool(matched);
291 }
292
293 int
294 hostlist_matches(struct member_list *list)
295 {
296     alias_seqno++;
297     return _hostlist_matches(list);
298 }
299
300 /*
301  * Check for cmnd and args in a list of members.
302  * Returns ALLOW, DENY or UNSPEC.
303  */
304 static int
305 _cmndlist_matches(struct member_list *list)
306 {
307     struct member *m;
308     int matched = UNSPEC;
309     debug_decl(_cmndlist_matches, SUDO_DEBUG_MATCH)
310
311     tq_foreach_rev(list, m) {
312         matched = cmnd_matches(m);
313         if (matched != UNSPEC)
314             break;
315     }
316     debug_return_bool(matched);
317 }
318
319 int
320 cmndlist_matches(struct member_list *list)
321 {
322     alias_seqno++;
323     return _cmndlist_matches(list);
324 }
325
326 /*
327  * Check cmnd and args.
328  * Returns ALLOW, DENY or UNSPEC.
329  */
330 int
331 cmnd_matches(struct member *m)
332 {
333     struct alias *a;
334     struct sudo_command *c;
335     int rval, matched = UNSPEC;
336     debug_decl(cmnd_matches, SUDO_DEBUG_MATCH)
337
338     switch (m->type) {
339         case ALL:
340             matched = !m->negated;
341             break;
342         case ALIAS:
343             alias_seqno++;
344             if ((a = alias_find(m->name, CMNDALIAS)) != NULL) {
345                 rval = _cmndlist_matches(&a->members);
346                 if (rval != UNSPEC)
347                     matched = m->negated ? !rval : rval;
348             }
349             break;
350         case COMMAND:
351             c = (struct sudo_command *)m->name;
352             if (command_matches(c->cmnd, c->args))
353                 matched = !m->negated;
354             break;
355     }
356     debug_return_bool(matched);
357 }
358
359 static bool
360 command_args_match(sudoers_cmnd, sudoers_args)
361     char *sudoers_cmnd;
362     char *sudoers_args;
363 {
364     int flags = 0;
365     debug_decl(command_args_match, SUDO_DEBUG_MATCH)
366
367     /*
368      * If no args specified in sudoers, any user args are allowed.
369      * If the empty string is specified in sudoers, no user args are allowed.
370      */
371     if (!sudoers_args ||
372         (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)))
373         debug_return_bool(true);
374     /*
375      * If args are specified in sudoers, they must match the user args.
376      * If running as sudoedit, all args are assumed to be paths.
377      */
378     if (sudoers_args) {
379         /* For sudoedit, all args are assumed to be pathnames. */
380         if (strcmp(sudoers_cmnd, "sudoedit") == 0)
381             flags = FNM_PATHNAME;
382         if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
383             debug_return_bool(true);
384     }
385     debug_return_bool(false);
386 }
387
388 /*
389  * If path doesn't end in /, return true iff cmnd & path name the same inode;
390  * otherwise, return true if user_cmnd names one of the inodes in path.
391  */
392 bool
393 command_matches(char *sudoers_cmnd, char *sudoers_args)
394 {
395     debug_decl(command_matches, SUDO_DEBUG_MATCH)
396
397     /* Check for pseudo-commands */
398     if (sudoers_cmnd[0] != '/') {
399         /*
400          * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
401          *  a) there are no args in sudoers OR
402          *  b) there are no args on command line and none req by sudoers OR
403          *  c) there are args in sudoers and on command line and they match
404          */
405         if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
406             strcmp(user_cmnd, "sudoedit") != 0)
407             debug_return_bool(false);
408         if (command_args_match(sudoers_cmnd, sudoers_args)) {
409             efree(safe_cmnd);
410             safe_cmnd = estrdup(sudoers_cmnd);
411             debug_return_bool(true);
412         } else
413             debug_return_bool(false);
414     }
415
416     if (has_meta(sudoers_cmnd)) {
417         /*
418          * If sudoers_cmnd has meta characters in it, we need to
419          * use glob(3) and/or fnmatch(3) to do the matching.
420          */
421         if (def_fast_glob)
422             debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
423         debug_return_bool(command_matches_glob(sudoers_cmnd, sudoers_args));
424     }
425     debug_return_bool(command_matches_normal(sudoers_cmnd, sudoers_args));
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 static bool
452 command_matches_glob(char *sudoers_cmnd, char *sudoers_args)
453 {
454     struct stat sudoers_stat;
455     size_t dlen;
456     char **ap, *base, *cp;
457     glob_t gl;
458     debug_decl(command_matches_glob, SUDO_DEBUG_MATCH)
459
460     /*
461      * First check to see if we can avoid the call to glob(3).
462      * Short circuit if there are no meta chars in the command itself
463      * and user_base and basename(sudoers_cmnd) don't match.
464      */
465     dlen = strlen(sudoers_cmnd);
466     if (sudoers_cmnd[dlen - 1] != '/') {
467         if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
468             base++;
469             if (!has_meta(base) && strcmp(user_base, base) != 0)
470                 debug_return_bool(false);
471         }
472     }
473     /*
474      * Return true if we find a match in the glob(3) results AND
475      *  a) there are no args in sudoers OR
476      *  b) there are no args on command line and none required by sudoers OR
477      *  c) there are args in sudoers and on command line and they match
478      * else return false.
479      */
480 #define GLOB_FLAGS      (GLOB_NOSORT | GLOB_BRACE | GLOB_TILDE)
481     if (glob(sudoers_cmnd, GLOB_FLAGS, 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
523 static bool
524 command_matches_normal(char *sudoers_cmnd, char *sudoers_args)
525 {
526     struct stat sudoers_stat;
527     char *base;
528     size_t dlen;
529     debug_decl(command_matches_normal, SUDO_DEBUG_MATCH)
530
531     /* If it ends in '/' it is a directory spec. */
532     dlen = strlen(sudoers_cmnd);
533     if (sudoers_cmnd[dlen - 1] == '/')
534         debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
535
536     /* Only proceed if user_base and basename(sudoers_cmnd) match */
537     if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
538         base = sudoers_cmnd;
539     else
540         base++;
541     if (strcmp(user_base, base) != 0 ||
542         stat(sudoers_cmnd, &sudoers_stat) == -1)
543         debug_return_bool(false);
544
545     /*
546      * Return true if inode/device matches AND
547      *  a) there are no args in sudoers OR
548      *  b) there are no args on command line and none req by sudoers OR
549      *  c) there are args in sudoers and on command line and they match
550      */
551     if (user_stat != NULL &&
552         (user_stat->st_dev != sudoers_stat.st_dev ||
553         user_stat->st_ino != sudoers_stat.st_ino))
554         debug_return_bool(false);
555     if (command_args_match(sudoers_cmnd, sudoers_args)) {
556         efree(safe_cmnd);
557         safe_cmnd = estrdup(sudoers_cmnd);
558         debug_return_bool(true);
559     }
560     debug_return_bool(false);
561 }
562
563 /*
564  * Return true if user_cmnd names one of the inodes in dir, else false.
565  */
566 static bool
567 command_matches_dir(char *sudoers_dir, size_t dlen)
568 {
569     struct stat sudoers_stat;
570     struct dirent *dent;
571     char buf[PATH_MAX];
572     DIR *dirp;
573     debug_decl(command_matches_dir, SUDO_DEBUG_MATCH)
574
575     /*
576      * Grot through directory entries, looking for user_base.
577      */
578     dirp = opendir(sudoers_dir);
579     if (dirp == NULL)
580         debug_return_bool(false);
581
582     if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
583         closedir(dirp);
584         debug_return_bool(false);
585     }
586     while ((dent = readdir(dirp)) != NULL) {
587         /* ignore paths > PATH_MAX (XXX - log) */
588         buf[dlen] = '\0';
589         if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
590             continue;
591
592         /* only stat if basenames are the same */
593         if (strcmp(user_base, dent->d_name) != 0 ||
594             stat(buf, &sudoers_stat) == -1)
595             continue;
596         if (user_stat == NULL ||
597             (user_stat->st_dev == sudoers_stat.st_dev &&
598             user_stat->st_ino == sudoers_stat.st_ino)) {
599             efree(safe_cmnd);
600             safe_cmnd = estrdup(buf);
601             break;
602         }
603     }
604
605     closedir(dirp);
606     debug_return_bool(dent != NULL);
607 }
608
609 /*
610  * Returns true if the hostname matches the pattern, else false
611  */
612 bool
613 hostname_matches(char *shost, char *lhost, char *pattern)
614 {
615     debug_decl(hostname_matches, SUDO_DEBUG_MATCH)
616
617     if (has_meta(pattern)) {
618         if (strchr(pattern, '.'))
619             debug_return_bool(!fnmatch(pattern, lhost, FNM_CASEFOLD));
620         else
621             debug_return_bool(!fnmatch(pattern, shost, FNM_CASEFOLD));
622     } else {
623         if (strchr(pattern, '.'))
624             debug_return_bool(!strcasecmp(lhost, pattern));
625         else
626             debug_return_bool(!strcasecmp(shost, pattern));
627     }
628 }
629
630 /*
631  *  Returns true if the user/uid from sudoers matches the specified user/uid,
632  *  else returns false.
633  */
634 bool
635 userpw_matches(char *sudoers_user, char *user, struct passwd *pw)
636 {
637     debug_decl(userpw_matches, SUDO_DEBUG_MATCH)
638
639     if (pw != NULL && *sudoers_user == '#') {
640         uid_t uid = (uid_t) atoi(sudoers_user + 1);
641         if (uid == pw->pw_uid)
642             debug_return_bool(true);
643     }
644     debug_return_bool(strcmp(sudoers_user, user) == 0);
645 }
646
647 /*
648  *  Returns true if the group/gid from sudoers matches the specified group/gid,
649  *  else returns false.
650  */
651 bool
652 group_matches(char *sudoers_group, struct group *gr)
653 {
654     debug_decl(group_matches, SUDO_DEBUG_MATCH)
655
656     if (*sudoers_group == '#') {
657         gid_t gid = (gid_t) atoi(sudoers_group + 1);
658         if (gid == gr->gr_gid)
659             debug_return_bool(true);
660     }
661     debug_return_bool(strcmp(gr->gr_name, sudoers_group) == 0);
662 }
663
664 /*
665  *  Returns true if the given user belongs to the named group,
666  *  else returns false.
667  */
668 bool
669 usergr_matches(char *group, char *user, struct passwd *pw)
670 {
671     int matched = false;
672     struct passwd *pw0 = NULL;
673     debug_decl(usergr_matches, SUDO_DEBUG_MATCH)
674
675     /* make sure we have a valid usergroup, sudo style */
676     if (*group++ != '%')
677         goto done;
678
679     if (*group == ':' && def_group_plugin) {
680         matched = group_plugin_query(user, group + 1, pw);
681         goto done;
682     }
683
684     /* look up user's primary gid in the passwd file */
685     if (pw == NULL) {
686         if ((pw0 = sudo_getpwnam(user)) == NULL)
687             goto done;
688         pw = pw0;
689     }
690
691     if (user_in_group(pw, group)) {
692         matched = true;
693         goto done;
694     }
695
696     /* not a Unix group, could be an external group */
697     if (def_group_plugin && group_plugin_query(user, group, pw)) {
698         matched = true;
699         goto done;
700     }
701
702 done:
703     if (pw0 != NULL)
704         pw_delref(pw0);
705
706     debug_return_bool(matched);
707 }
708
709 /*
710  * Returns true if "host" and "user" belong to the netgroup "netgr",
711  * else return false.  Either of "host", "shost" or "user" may be NULL
712  * in which case that argument is not checked...
713  *
714  * XXX - swap order of host & shost
715  */
716 bool
717 netgr_matches(char *netgr, char *lhost, char *shost, char *user)
718 {
719     static char *domain;
720 #ifdef HAVE_GETDOMAINNAME
721     static int initialized;
722 #endif
723     debug_decl(netgr_matches, SUDO_DEBUG_MATCH)
724
725     /* make sure we have a valid netgroup, sudo style */
726     if (*netgr++ != '+')
727         debug_return_bool(false);
728
729 #ifdef HAVE_GETDOMAINNAME
730     /* get the domain name (if any) */
731     if (!initialized) {
732         domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
733         if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
734             efree(domain);
735             domain = NULL;
736         }
737         initialized = 1;
738     }
739 #endif /* HAVE_GETDOMAINNAME */
740
741 #ifdef HAVE_INNETGR
742     if (innetgr(netgr, lhost, user, domain))
743         debug_return_bool(true);
744     else if (lhost != shost && innetgr(netgr, shost, user, domain))
745         debug_return_bool(true);
746 #endif /* HAVE_INNETGR */
747
748     debug_return_bool(false);
749 }