1/*
2 * Copyright (c) 1996, 1998-2005, 2007-2011
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#endif /* HAVE_STRING_H */
42#ifdef HAVE_STRINGS_H
43# include <strings.h>
44#endif /* HAVE_STRINGS_H */
45#ifdef HAVE_UNISTD_H
46# include <unistd.h>
47#endif /* HAVE_UNISTD_H */
48#ifdef HAVE_FNMATCH
49# include <fnmatch.h>
50#endif /* HAVE_FNMATCH */
51#ifdef HAVE_GLOB
52# include <glob.h>
53#endif /* HAVE_GLOB */
54#ifdef HAVE_NETGROUP_H
55# include <netgroup.h>
56#endif /* HAVE_NETGROUP_H */
57#include <ctype.h>
58#include <pwd.h>
59#include <grp.h>
60#include <netinet/in.h>
61#include <arpa/inet.h>
62#include <netdb.h>
63#ifdef HAVE_DIRENT_H
64# include <dirent.h>
65# define NAMLEN(dirent) strlen((dirent)->d_name)
66#else
67# define dirent direct
68# define NAMLEN(dirent) (dirent)->d_namlen
69# ifdef HAVE_SYS_NDIR_H
70#  include <sys/ndir.h>
71# endif
72# ifdef HAVE_SYS_DIR_H
73#  include <sys/dir.h>
74# endif
75# ifdef HAVE_NDIR_H
76#  include <ndir.h>
77# endif
78#endif
79
80#include "sudo.h"
81#include "interfaces.h"
82#include "parse.h"
83#include <gram.h>
84
85#ifndef HAVE_FNMATCH
86# include "emul/fnmatch.h"
87#endif /* HAVE_FNMATCH */
88#ifndef HAVE_GLOB
89# include "emul/glob.h"
90#endif /* HAVE_GLOB */
91#ifdef USING_NONUNIX_GROUPS
92# include "nonunix.h"
93#endif /* USING_NONUNIX_GROUPS */
94
95#include <membership.h>
96
97static struct member_list empty;
98
99static int command_matches_dir __P((char *, size_t));
100static int command_matches_glob __P((char *, char *));
101static int command_matches_fnmatch __P((char *, char *));
102static int command_matches_normal __P((char *, char *));
103
104/*
105 * Returns TRUE if string 's' contains meta characters.
106 */
107#define has_meta(s)	(strpbrk(s, "\\?*[]") != NULL)
108
109/*
110 * Check for user described by pw in a list of members.
111 * Returns ALLOW, DENY or UNSPEC.
112 */
113static int
114_userlist_matches(pw, list)
115    struct passwd *pw;
116    struct member_list *list;
117{
118    struct member *m;
119    struct alias *a;
120    int rval, matched = UNSPEC;
121
122    tq_foreach_rev(list, m) {
123	switch (m->type) {
124	    case ALL:
125		matched = !m->negated;
126		break;
127	    case NETGROUP:
128		if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
129		    matched = !m->negated;
130		break;
131	    case USERGROUP:
132		if (usergr_matches(m->name, pw->pw_name, pw))
133		    matched = !m->negated;
134		break;
135	    case ALIAS:
136		if ((a = alias_find(m->name, USERALIAS)) != NULL) {
137		    rval = _userlist_matches(pw, &a->members);
138		    if (rval != UNSPEC)
139			matched = m->negated ? !rval : rval;
140		    break;
141		}
142		/* FALLTHROUGH */
143	    case WORD:
144		if (userpw_matches(m->name, pw->pw_name, pw))
145		    matched = !m->negated;
146		break;
147	}
148	if (matched != UNSPEC)
149	    break;
150    }
151    return matched;
152}
153
154int
155userlist_matches(pw, list)
156    struct passwd *pw;
157    struct member_list *list;
158{
159    alias_seqno++;
160    return _userlist_matches(pw, list);
161}
162
163/*
164 * Check for user described by pw in a list of members.
165 * If both lists are empty compare against def_runas_default.
166 * Returns ALLOW, DENY or UNSPEC.
167 */
168static int
169_runaslist_matches(user_list, group_list)
170    struct member_list *user_list;
171    struct member_list *group_list;
172{
173    struct member *m;
174    struct alias *a;
175    int rval;
176    int user_matched = UNSPEC;
177    int group_matched = UNSPEC;
178
179    if (runas_pw != NULL) {
180	/* If no runas user or runas group listed in sudoers, use default. */
181	if (tq_empty(user_list) && tq_empty(group_list))
182	    return userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw);
183
184	tq_foreach_rev(user_list, m) {
185	    switch (m->type) {
186		case ALL:
187		    user_matched = !m->negated;
188		    break;
189		case NETGROUP:
190		    if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
191			user_matched = !m->negated;
192		    break;
193		case USERGROUP:
194		    if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
195			user_matched = !m->negated;
196		    break;
197		case ALIAS:
198		    if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
199			rval = _runaslist_matches(&a->members, &empty);
200			if (rval != UNSPEC)
201			    user_matched = m->negated ? !rval : rval;
202			break;
203		    }
204		    /* FALLTHROUGH */
205		case WORD:
206		    if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
207			user_matched = !m->negated;
208		    break;
209	    }
210	    if (user_matched != UNSPEC)
211		break;
212	}
213    }
214
215    if (runas_gr != NULL) {
216	if (user_matched == UNSPEC) {
217	    if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0)
218		user_matched = ALLOW;	/* only changing group */
219	}
220	tq_foreach_rev(group_list, m) {
221	    switch (m->type) {
222		case ALL:
223		    group_matched = !m->negated;
224		    break;
225		case ALIAS:
226		    if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
227			rval = _runaslist_matches(&empty, &a->members);
228			if (rval != UNSPEC)
229			    group_matched = m->negated ? !rval : rval;
230			break;
231		    }
232		    /* FALLTHROUGH */
233		case WORD:
234		    if (group_matches(m->name, runas_gr))
235			group_matched = !m->negated;
236		    break;
237	    }
238	    if (group_matched != UNSPEC)
239		break;
240	}
241	if (group_matched == UNSPEC) {
242	    if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid)
243		group_matched = ALLOW;	/* runas group matches passwd db */
244	}
245    }
246
247    if (user_matched == DENY || group_matched == DENY)
248	return DENY;
249    if (user_matched == group_matched || runas_gr == NULL)
250	return user_matched;
251    return UNSPEC;
252}
253
254int
255runaslist_matches(user_list, group_list)
256    struct member_list *user_list;
257    struct member_list *group_list;
258{
259    alias_seqno++;
260    return _runaslist_matches(user_list ? user_list : &empty,
261	group_list ? group_list : &empty);
262}
263
264/*
265 * Check for host and shost in a list of members.
266 * Returns ALLOW, DENY or UNSPEC.
267 */
268static int
269_hostlist_matches(list)
270    struct member_list *list;
271{
272    struct member *m;
273    struct alias *a;
274    int rval, matched = UNSPEC;
275
276    tq_foreach_rev(list, m) {
277	switch (m->type) {
278	    case ALL:
279		matched = !m->negated;
280		break;
281	    case NETGROUP:
282		if (netgr_matches(m->name, user_host, user_shost, NULL))
283		    matched = !m->negated;
284		break;
285	    case NTWKADDR:
286		if (addr_matches(m->name))
287		    matched = !m->negated;
288		break;
289	    case ALIAS:
290		if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
291		    rval = _hostlist_matches(&a->members);
292		    if (rval != UNSPEC)
293			matched = m->negated ? !rval : rval;
294		    break;
295		}
296		/* FALLTHROUGH */
297	    case WORD:
298		if (hostname_matches(user_shost, user_host, m->name))
299		    matched = !m->negated;
300		break;
301	}
302	if (matched != UNSPEC)
303	    break;
304    }
305    return matched;
306}
307
308int
309hostlist_matches(list)
310    struct member_list *list;
311{
312    alias_seqno++;
313    return _hostlist_matches(list);
314}
315
316/*
317 * Check for cmnd and args in a list of members.
318 * Returns ALLOW, DENY or UNSPEC.
319 */
320static int
321_cmndlist_matches(list)
322    struct member_list *list;
323{
324    struct member *m;
325    int matched = UNSPEC;
326
327    tq_foreach_rev(list, m) {
328	matched = cmnd_matches(m);
329	if (matched != UNSPEC)
330	    break;
331    }
332    return matched;
333}
334
335int
336cmndlist_matches(list)
337    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 */
347int
348cmnd_matches(m)
349    struct member *m;
350{
351    struct alias *a;
352    struct sudo_command *c;
353    int rval, matched = UNSPEC;
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    return matched;
374}
375
376static int
377command_args_match(sudoers_cmnd, sudoers_args)
378    char *sudoers_cmnd;
379    char *sudoers_args;
380{
381    int flags = 0;
382
383    /*
384     * If no args specified in sudoers, any user args are allowed.
385     * If the empty string is specified in sudoers, no user args are allowed.
386     */
387    if (!sudoers_args ||
388	(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)))
389	return TRUE;
390    /*
391     * If args are specified in sudoers, they must match the user args.
392     * If running as sudoedit, all args are assumed to be paths.
393     */
394    if (sudoers_args) {
395	/* For sudoedit, all args are assumed to be pathnames. */
396	if (strcmp(sudoers_cmnd, "sudoedit") == 0)
397	    flags = FNM_PATHNAME;
398	if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
399	    return TRUE;
400    }
401    return FALSE;
402}
403
404/*
405 * If path doesn't end in /, return TRUE iff cmnd & path name the same inode;
406 * otherwise, return TRUE if user_cmnd names one of the inodes in path.
407 */
408int
409command_matches(sudoers_cmnd, sudoers_args)
410    char *sudoers_cmnd;
411    char *sudoers_args;
412{
413    /* Check for pseudo-commands */
414    if (sudoers_cmnd[0] != '/') {
415	/*
416	 * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
417	 *  a) there are no args in sudoers OR
418	 *  b) there are no args on command line and none req by sudoers OR
419	 *  c) there are args in sudoers and on command line and they match
420	 */
421	if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
422	    strcmp(user_cmnd, "sudoedit") != 0)
423	    return FALSE;
424	if (command_args_match(sudoers_cmnd, sudoers_args)) {
425	    efree(safe_cmnd);
426	    safe_cmnd = estrdup(sudoers_cmnd);
427	    return TRUE;
428	} else
429	    return FALSE;
430    }
431
432    if (has_meta(sudoers_cmnd)) {
433	/*
434	 * If sudoers_cmnd has meta characters in it, we need to
435	 * use glob(3) and/or fnmatch(3) to do the matching.
436	 */
437	if (def_fast_glob)
438	    return command_matches_fnmatch(sudoers_cmnd, sudoers_args);
439	return command_matches_glob(sudoers_cmnd, sudoers_args);
440    }
441    return command_matches_normal(sudoers_cmnd, sudoers_args);
442}
443
444static int
445command_matches_fnmatch(sudoers_cmnd, sudoers_args)
446    char *sudoers_cmnd;
447    char *sudoers_args;
448{
449    /*
450     * Return true if fnmatch(3) succeeds AND
451     *  a) there are no args in sudoers OR
452     *  b) there are no args on command line and none required by sudoers OR
453     *  c) there are args in sudoers and on command line and they match
454     * else return false.
455     */
456    if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
457	return FALSE;
458    if (command_args_match(sudoers_cmnd, sudoers_args)) {
459	if (safe_cmnd)
460	    free(safe_cmnd);
461	safe_cmnd = estrdup(user_cmnd);
462	return TRUE;
463    } else
464	return FALSE;
465}
466
467static int
468command_matches_glob(sudoers_cmnd, sudoers_args)
469    char *sudoers_cmnd;
470    char *sudoers_args;
471{
472    struct stat sudoers_stat;
473    size_t dlen;
474    char **ap, *base, *cp;
475    glob_t gl;
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		return 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	return 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		return 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	return FALSE;
530
531    if (command_args_match(sudoers_cmnd, sudoers_args)) {
532	efree(safe_cmnd);
533	safe_cmnd = estrdup(user_cmnd);
534	return TRUE;
535    }
536    return FALSE;
537}
538
539static int
540command_matches_normal(sudoers_cmnd, sudoers_args)
541    char *sudoers_cmnd;
542    char *sudoers_args;
543{
544    struct stat sudoers_stat;
545    char *base;
546    size_t dlen;
547
548    /* If it ends in '/' it is a directory spec. */
549    dlen = strlen(sudoers_cmnd);
550    if (sudoers_cmnd[dlen - 1] == '/')
551	return command_matches_dir(sudoers_cmnd, dlen);
552
553    /* Only proceed if user_base and basename(sudoers_cmnd) match */
554    if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
555	base = sudoers_cmnd;
556    else
557	base++;
558    if (strcmp(user_base, base) != 0 ||
559	stat(sudoers_cmnd, &sudoers_stat) == -1)
560	return FALSE;
561
562    /*
563     * Return true if inode/device matches AND
564     *  a) there are no args in sudoers OR
565     *  b) there are no args on command line and none req by sudoers OR
566     *  c) there are args in sudoers and on command line and they match
567     */
568    if (user_stat != NULL &&
569	(user_stat->st_dev != sudoers_stat.st_dev ||
570	user_stat->st_ino != sudoers_stat.st_ino))
571	return FALSE;
572    if (command_args_match(sudoers_cmnd, sudoers_args)) {
573	efree(safe_cmnd);
574	safe_cmnd = estrdup(sudoers_cmnd);
575	return TRUE;
576    }
577    return FALSE;
578}
579
580/*
581 * Return TRUE if user_cmnd names one of the inodes in dir, else FALSE.
582 */
583static int
584command_matches_dir(sudoers_dir, dlen)
585    char *sudoers_dir;
586    size_t dlen;
587{
588    struct stat sudoers_stat;
589    struct dirent *dent;
590    char buf[PATH_MAX];
591    DIR *dirp;
592
593    /*
594     * Grot through directory entries, looking for user_base.
595     */
596    dirp = opendir(sudoers_dir);
597    if (dirp == NULL)
598	return FALSE;
599
600    if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
601	closedir(dirp);
602	return FALSE;
603    }
604    while ((dent = readdir(dirp)) != NULL) {
605	/* ignore paths > PATH_MAX (XXX - log) */
606	buf[dlen] = '\0';
607	if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
608	    continue;
609
610	/* only stat if basenames are the same */
611	if (strcmp(user_base, dent->d_name) != 0 ||
612	    stat(buf, &sudoers_stat) == -1)
613	    continue;
614	if (user_stat == NULL ||
615	    (user_stat->st_dev == sudoers_stat.st_dev &&
616	    user_stat->st_ino == sudoers_stat.st_ino)) {
617	    efree(safe_cmnd);
618	    safe_cmnd = estrdup(buf);
619	    break;
620	}
621    }
622
623    closedir(dirp);
624    return dent != NULL;
625}
626
627static int
628addr_matches_if(n)
629    char *n;
630{
631    int i;
632    union sudo_in_addr_un addr;
633    struct interface *ifp;
634#ifdef HAVE_STRUCT_IN6_ADDR
635    int j;
636#endif
637    int family;
638
639#ifdef HAVE_STRUCT_IN6_ADDR
640    if (inet_pton(AF_INET6, n, &addr.ip6) > 0) {
641	family = AF_INET6;
642    } else
643#endif
644    {
645	family = AF_INET;
646	addr.ip4.s_addr = inet_addr(n);
647    }
648
649    for (i = 0; i < num_interfaces; i++) {
650	ifp = &interfaces[i];
651	if (ifp->family != family)
652	    continue;
653	switch (family) {
654	    case AF_INET:
655		if (ifp->addr.ip4.s_addr == addr.ip4.s_addr ||
656		    (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
657		    == addr.ip4.s_addr)
658		    return TRUE;
659		break;
660#ifdef HAVE_STRUCT_IN6_ADDR
661	    case AF_INET6:
662		if (memcmp(ifp->addr.ip6.s6_addr, addr.ip6.s6_addr,
663		    sizeof(addr.ip6.s6_addr)) == 0)
664		    return TRUE;
665		for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
666		    if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
667			break;
668		}
669		if (j == sizeof(addr.ip6.s6_addr))
670		    return TRUE;
671		break;
672#endif
673	}
674    }
675
676    return FALSE;
677}
678
679static int
680addr_matches_if_netmask(n, m)
681    char *n;
682    char *m;
683{
684    int i;
685    union sudo_in_addr_un addr, mask;
686    struct interface *ifp;
687#ifdef HAVE_STRUCT_IN6_ADDR
688    int j;
689#endif
690    int family;
691
692#ifdef HAVE_STRUCT_IN6_ADDR
693    if (inet_pton(AF_INET6, n, &addr.ip6) > 0)
694	family = AF_INET6;
695    else
696#endif
697    {
698	family = AF_INET;
699	addr.ip4.s_addr = inet_addr(n);
700    }
701
702    if (family == AF_INET) {
703	if (strchr(m, '.')) {
704	    mask.ip4.s_addr = inet_addr(m);
705	} else {
706	    i = atoi(m);
707	    if (i == 0)
708		mask.ip4.s_addr = 0;
709	    else if (i == 32)
710		mask.ip4.s_addr = 0xffffffff;
711	    else
712		mask.ip4.s_addr = 0xffffffff - (1 << (32 - i)) + 1;
713	    mask.ip4.s_addr = htonl(mask.ip4.s_addr);
714	}
715	addr.ip4.s_addr &= mask.ip4.s_addr;
716    }
717#ifdef HAVE_STRUCT_IN6_ADDR
718    else {
719	if (inet_pton(AF_INET6, m, &mask.ip6) <= 0) {
720	    j = atoi(m);
721	    for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) {
722		if (j < i * 8)
723		    mask.ip6.s6_addr[i] = 0;
724		else if (i * 8 + 8 <= j)
725		    mask.ip6.s6_addr[i] = 0xff;
726		else
727		    mask.ip6.s6_addr[i] = 0xff00 >> (j - i * 8);
728		addr.ip6.s6_addr[i] &= mask.ip6.s6_addr[i];
729	    }
730	}
731    }
732#endif /* HAVE_STRUCT_IN6_ADDR */
733
734    for (i = 0; i < num_interfaces; i++) {
735	ifp = &interfaces[i];
736	if (ifp->family != family)
737	    continue;
738	switch (family) {
739	    case AF_INET:
740		if ((ifp->addr.ip4.s_addr & mask.ip4.s_addr) == addr.ip4.s_addr)
741		    return TRUE;
742		break;
743#ifdef HAVE_STRUCT_IN6_ADDR
744	    case AF_INET6:
745		for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) {
746		    if ((ifp->addr.ip6.s6_addr[j] & mask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j])
747			break;
748		}
749		if (j == sizeof(addr.ip6.s6_addr))
750		    return TRUE;
751		break;
752#endif /* HAVE_STRUCT_IN6_ADDR */
753	}
754    }
755
756    return FALSE;
757}
758
759/*
760 * Returns TRUE if "n" is one of our ip addresses or if
761 * "n" is a network that we are on, else returns FALSE.
762 */
763int
764addr_matches(n)
765    char *n;
766{
767    char *m;
768    int retval;
769
770    /* If there's an explicit netmask, use it. */
771    if ((m = strchr(n, '/'))) {
772	*m++ = '\0';
773	retval = addr_matches_if_netmask(n, m);
774	*(m - 1) = '/';
775    } else
776	retval = addr_matches_if(n);
777
778    return retval;
779}
780
781/*
782 * Returns TRUE if the hostname matches the pattern, else FALSE
783 */
784int
785hostname_matches(shost, lhost, pattern)
786    char *shost;
787    char *lhost;
788    char *pattern;
789{
790    if (has_meta(pattern)) {
791	if (strchr(pattern, '.'))
792	    return !fnmatch(pattern, lhost, FNM_CASEFOLD);
793	else
794	    return !fnmatch(pattern, shost, FNM_CASEFOLD);
795    } else {
796	if (strchr(pattern, '.'))
797	    return !strcasecmp(lhost, pattern);
798	else
799	    return !strcasecmp(shost, pattern);
800    }
801}
802
803/*
804 *  Returns TRUE if the user/uid from sudoers matches the specified user/uid,
805 *  else returns FALSE.
806 */
807int
808userpw_matches(sudoers_user, user, pw)
809    char *sudoers_user;
810    char *user;
811    struct passwd *pw;
812{
813    if (pw != NULL && *sudoers_user == '#') {
814	uid_t uid = (uid_t) atoi(sudoers_user + 1);
815	if (uid == pw->pw_uid)
816	    return TRUE;
817    }
818    return strcmp(sudoers_user, user) == 0;
819}
820
821/*
822 *  Returns TRUE if the group/gid from sudoers matches the specified group/gid,
823 *  else returns FALSE.
824 */
825int
826group_matches(sudoers_group, gr)
827    char *sudoers_group;
828    struct group *gr;
829{
830    if (*sudoers_group == '#') {
831	gid_t gid = (gid_t) atoi(sudoers_group + 1);
832	if (gid == gr->gr_gid)
833	    return TRUE;
834    }
835    return strcmp(gr->gr_name, sudoers_group) == 0;
836}
837
838/*
839 *  Returns TRUE if the given user belongs to the named group,
840 *  else returns FALSE.
841 */
842int
843usergr_matches(group, user, pw)
844    char *group;
845    char *user;
846    struct passwd *pw;
847{
848    int matched = FALSE;
849    struct passwd *pw0 = NULL;
850
851    /* make sure we have a valid usergroup, sudo style */
852    if (*group++ != '%')
853	goto done;
854
855#ifdef USING_NONUNIX_GROUPS
856    if (*group == ':') {
857	matched = sudo_nonunix_groupcheck(++group, user, pw);
858	goto done;
859    }
860#endif /* USING_NONUNIX_GROUPS */
861
862    /* look up user's primary gid in the passwd file */
863    if (pw == NULL) {
864	if ((pw0 = sudo_getpwnam(user)) == NULL)
865	    goto done;
866	pw = pw0;
867    }
868
869    if (user_in_group(pw, group)) {
870	matched = TRUE;
871	goto done;
872    }
873
874#ifdef USING_NONUNIX_GROUPS
875    /* not a Unix group, could be an AD group */
876    if (sudo_nonunix_groupcheck_available() &&
877	sudo_nonunix_groupcheck(group, user, pw)) {
878	matched = TRUE;
879	goto done;
880    }
881#endif /* USING_NONUNIX_GROUPS */
882
883done:
884    if (pw0 != NULL)
885	pw_delref(pw0);
886
887    return matched;
888}
889
890/*
891 * Returns TRUE if "host" and "user" belong to the netgroup "netgr",
892 * else return FALSE.  Either of "host", "shost" or "user" may be NULL
893 * in which case that argument is not checked...
894 *
895 * XXX - swap order of host & shost
896 */
897int
898netgr_matches(netgr, lhost, shost, user)
899    char *netgr;
900    char *lhost;
901    char *shost;
902    char *user;
903{
904    static char *domain;
905#ifdef HAVE_GETDOMAINNAME
906    static int initialized;
907#endif
908
909    /* make sure we have a valid netgroup, sudo style */
910    if (*netgr++ != '+')
911	return FALSE;
912
913#ifdef HAVE_GETDOMAINNAME
914    /* get the domain name (if any) */
915    if (!initialized) {
916	domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
917	if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
918	    efree(domain);
919	    domain = NULL;
920	}
921	initialized = 1;
922    }
923#endif /* HAVE_GETDOMAINNAME */
924
925#ifdef HAVE_INNETGR
926    if (innetgr(netgr, lhost, user, domain))
927	return TRUE;
928    else if (lhost != shost && innetgr(netgr, shost, user, domain))
929	return TRUE;
930#endif /* HAVE_INNETGR */
931
932    return FALSE;
933}
934