Annotation of src/usr.bin/sudo/testsudoers.c, Revision 1.8
1.1 millert 1: /*
1.7 millert 2: * Copyright (c) 1996, 1998-2003 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: * All rights reserved.
4: *
1.6 millert 5: * This code is derived from software contributed by Chris Jepeway.
1.1 millert 6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: *
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: *
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: *
18: * 3. The name of the author may not be used to endorse or promote products
19: * derived from this software without specific prior written permission.
20: *
21: * 4. Products derived from this software may not be called "Sudo" nor
22: * may "Sudo" appear in their names without specific prior written
23: * permission from the author.
24: *
25: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
28: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35: */
36:
37: #include "config.h"
38:
1.5 millert 39: #include <sys/param.h>
40: #include <sys/types.h>
41: #include <sys/stat.h>
42: #include <sys/socket.h>
1.1 millert 43: #include <stdio.h>
44: #ifdef STDC_HEADERS
45: # include <stdlib.h>
1.5 millert 46: # include <stddef.h>
47: #else
48: # ifdef HAVE_STDLIB_H
49: # include <stdlib.h>
50: # endif
1.1 millert 51: #endif /* STDC_HEADERS */
1.5 millert 52: #ifdef HAVE_STRING_H
53: # include <string.h>
54: #else
55: # ifdef HAVE_STRINGS_H
56: # include <strings.h>
57: # endif
58: #endif /* HAVE_STRING_H */
1.1 millert 59: #ifdef HAVE_UNISTD_H
60: # include <unistd.h>
61: #endif /* HAVE_UNISTD_H */
1.2 millert 62: #ifdef HAVE_FNMATCH
1.1 millert 63: # include <fnmatch.h>
64: #endif /* HAVE_FNMATCH_H */
65: #ifdef HAVE_NETGROUP_H
66: # include <netgroup.h>
67: #endif /* HAVE_NETGROUP_H */
68: #include <ctype.h>
69: #include <pwd.h>
70: #include <grp.h>
71: #include <netinet/in.h>
72: #include <arpa/inet.h>
73: #include <netdb.h>
74: #include <dirent.h>
75:
76: #include "sudo.h"
77: #include "parse.h"
78: #include "interfaces.h"
79:
80: #ifndef HAVE_FNMATCH
81: # include "emul/fnmatch.h"
82: #endif /* HAVE_FNMATCH */
83:
84: #ifndef lint
1.8 ! millert 85: static const char rcsid[] = "$Sudo: testsudoers.c,v 1.80 2003/04/01 15:02:49 millert Exp $";
1.1 millert 86: #endif /* lint */
87:
1.5 millert 88:
89: /*
90: * Prototypes
91: */
92: void init_parser __P((void));
93: void dumpaliases __P((void));
1.7 millert 94: void set_perms_dummy __P((int));
1.5 millert 95:
1.1 millert 96: /*
97: * Globals
98: */
99: char **Argv, **NewArgv;
100: int Argc, NewArgc;
101: int parse_error = FALSE;
102: int num_interfaces;
103: struct interface *interfaces;
104: struct sudo_user sudo_user;
1.7 millert 105: void (*set_perms) __P((int)) = set_perms_dummy;
1.1 millert 106: extern int clearaliases;
107: extern int pedantic;
108:
109: /*
110: * Returns TRUE if "s" has shell meta characters in it,
111: * else returns FALSE.
112: */
113: int
114: has_meta(s)
115: char *s;
116: {
1.4 millert 117: char *t;
1.1 millert 118:
119: for (t = s; *t; t++) {
120: if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
121: return(TRUE);
122: }
123: return(FALSE);
124: }
125:
126: /*
127: * Returns TRUE if cmnd matches, in the sudo sense,
128: * the pathname in path; otherwise, return FALSE
129: */
130: int
131: command_matches(cmnd, cmnd_args, path, sudoers_args)
132: char *cmnd;
133: char *cmnd_args;
134: char *path;
135: char *sudoers_args;
136: {
137: int clen, plen;
138: char *args;
139:
140: if (cmnd == NULL)
141: return(FALSE);
142:
143: if ((args = strchr(path, ' ')))
144: *args++ = '\0';
145:
146: if (has_meta(path)) {
147: if (fnmatch(path, cmnd, FNM_PATHNAME))
148: return(FALSE);
149: if (!sudoers_args)
150: return(TRUE);
151: else if (!cmnd_args && sudoers_args && !strcmp("\"\"", sudoers_args))
152: return(TRUE);
153: else if (sudoers_args)
154: return((fnmatch(sudoers_args, cmnd_args ? cmnd_args : "", 0) == 0));
155: else
156: return(FALSE);
157: } else {
158: plen = strlen(path);
159: if (path[plen - 1] != '/') {
160: if (strcmp(cmnd, path))
161: return(FALSE);
162: if (!sudoers_args)
163: return(TRUE);
164: else if (!cmnd_args && sudoers_args && !strcmp("\"\"", sudoers_args))
165: return(TRUE);
166: else if (sudoers_args)
167: return((fnmatch(sudoers_args, cmnd_args ? cmnd_args : "", 0) == 0));
168: else
169: return(FALSE);
170: }
171:
172: clen = strlen(cmnd);
173: if (clen < plen + 1)
174: /* path cannot be the parent dir of cmnd */
175: return(FALSE);
176:
177: if (strchr(cmnd + plen + 1, '/') != NULL)
178: /* path could only be an anscestor of cmnd -- */
179: /* ignoring, of course, things like // & /./ */
180: return(FALSE);
181:
182: /* see whether path is the prefix of cmnd */
183: return((strncmp(cmnd, path, plen) == 0));
184: }
185: }
186:
187: int
188: addr_matches(n)
189: char *n;
190: {
191: int i;
192: char *m;
193: struct in_addr addr, mask;
194:
195: /* If there's an explicit netmask, use it. */
196: if ((m = strchr(n, '/'))) {
197: *m++ = '\0';
198: addr.s_addr = inet_addr(n);
199: if (strchr(m, '.'))
200: mask.s_addr = inet_addr(m);
1.5 millert 201: else {
202: i = 32 - atoi(m);
203: mask.s_addr = 0xffffffff;
204: mask.s_addr >>= i;
205: mask.s_addr <<= i;
206: mask.s_addr = htonl(mask.s_addr);
207: }
1.1 millert 208: *(m - 1) = '/';
209:
210: for (i = 0; i < num_interfaces; i++)
211: if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr)
212: return(TRUE);
213: } else {
214: addr.s_addr = inet_addr(n);
215:
216: for (i = 0; i < num_interfaces; i++)
217: if (interfaces[i].addr.s_addr == addr.s_addr ||
218: (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr)
219: == addr.s_addr)
220: return(TRUE);
221: }
222:
223: return(FALSE);
1.4 millert 224: }
225:
226: int
227: hostname_matches(shost, lhost, pattern)
228: char *shost;
229: char *lhost;
230: char *pattern;
231: {
232: if (has_meta(pattern)) {
233: if (strchr(pattern, '.'))
234: return(fnmatch(pattern, lhost, FNM_CASEFOLD));
235: else
236: return(fnmatch(pattern, shost, FNM_CASEFOLD));
237: } else {
238: if (strchr(pattern, '.'))
239: return(strcasecmp(lhost, pattern));
240: else
241: return(strcasecmp(shost, pattern));
242: }
1.1 millert 243: }
244:
245: int
246: usergr_matches(group, user)
247: char *group;
248: char *user;
249: {
250: struct group *grp;
251: char **cur;
252:
253: /* Make sure we have a valid usergroup, sudo style. */
254: if (*group++ != '%')
255: return(FALSE);
256:
257: if ((grp = getgrnam(group)) == NULL)
258: return(FALSE);
259:
260: /*
261: * Check against user's real gid as well as group's user list
262: */
263: if (getgid() == grp->gr_gid)
264: return(TRUE);
265:
266: for (cur=grp->gr_mem; *cur; cur++) {
267: if (strcmp(*cur, user) == 0)
268: return(TRUE);
269: }
270:
271: return(FALSE);
272: }
273:
274: int
1.3 millert 275: netgr_matches(netgr, host, shost, user)
1.1 millert 276: char *netgr;
277: char *host;
1.3 millert 278: char *shost;
1.1 millert 279: char *user;
280: {
281: #ifdef HAVE_GETDOMAINNAME
282: static char *domain = (char *) -1;
283: #else
284: static char *domain = NULL;
285: #endif /* HAVE_GETDOMAINNAME */
286:
287: /* Make sure we have a valid netgroup, sudo style. */
288: if (*netgr++ != '+')
289: return(FALSE);
290:
291: #ifdef HAVE_GETDOMAINNAME
292: /* Get the domain name (if any). */
293: if (domain == (char *) -1) {
294: domain = (char *) emalloc(MAXHOSTNAMELEN);
295:
296: if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') {
297: free(domain);
298: domain = NULL;
299: }
300: }
301: #endif /* HAVE_GETDOMAINNAME */
302:
303: #ifdef HAVE_INNETGR
1.3 millert 304: if (innetgr(netgr, host, user, domain))
305: return(TRUE);
306: else if (host != shost && innetgr(netgr, shost, user, domain))
307: return(TRUE);
308: #endif /* HAVE_INNETGR */
309:
1.1 millert 310: return(FALSE);
311: }
312:
313: void
1.7 millert 314: set_perms_dummy(i)
315: int i;
1.2 millert 316: {
317: return;
318: }
319:
320: void
321: set_fqdn()
1.5 millert 322: {
323: return;
324: }
325:
326: void
327: init_envtables()
1.1 millert 328: {
329: return;
330: }
331:
332: int
333: main(argc, argv)
334: int argc;
335: char **argv;
336: {
337: struct passwd pw;
338: char *p;
339: #ifdef YYDEBUG
340: extern int yydebug;
341: yydebug = 1;
342: #endif
343:
344: Argv = argv;
345: Argc = argc;
346:
347: if (Argc >= 6 && strcmp(Argv[1], "-u") == 0) {
348: user_runas = &Argv[2];
349: pw.pw_name = Argv[3];
350: user_host = Argv[4];
351: user_cmnd = Argv[5];
352:
353: NewArgv = &Argv[5];
354: NewArgc = Argc - 5;
355: } else if (Argc >= 4) {
356: pw.pw_name = Argv[1];
357: user_host = Argv[2];
358: user_cmnd = Argv[3];
359:
360: NewArgv = &Argv[3];
361: NewArgc = Argc - 3;
362: } else {
363: (void) fprintf(stderr,
364: "usage: %s [-u user] <user> <host> <command> [args]\n", Argv[0]);
365: exit(1);
366: }
367:
368: sudo_user.pw = &pw; /* user_name needs to be defined */
369:
370: if ((p = strchr(user_host, '.'))) {
371: *p = '\0';
372: user_shost = estrdup(user_host);
373: *p = '.';
374: } else {
375: user_shost = user_host;
376: }
377:
378: /* Fill in cmnd_args from NewArgv. */
379: if (NewArgc > 1) {
380: char *to, **from;
1.7 millert 381: size_t size, n;
1.1 millert 382:
1.7 millert 383: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
384: strlen(NewArgv[NewArgc-1]) + 1;
1.1 millert 385: user_args = (char *) emalloc(size);
1.7 millert 386: for (to = user_args, from = NewArgv + 1; *from; from++) {
387: n = strlcpy(to, *from, size - (to - user_args));
1.8 ! millert 388: if (n >= size - (to - user_args)) {
1.7 millert 389: (void) fprintf(stderr,
390: "%s: internal error, init_vars() overflow\n", Argv[0]);
391: exit(1);
392: }
393: to += n;
1.1 millert 394: *to++ = ' ';
395: }
1.7 millert 396: *--to = '\0';
1.1 millert 397: }
398:
399: /* Initialize default values. */
400: init_defaults();
401:
402: /* Warn about aliases that are used before being defined. */
403: pedantic = TRUE;
404:
405: /* Need to keep aliases around for dumpaliases(). */
406: clearaliases = FALSE;
407:
408: /* Load ip addr/mask for each interface. */
409: load_interfaces();
410:
411: /* Allocate space for data structures in the parser. */
412: init_parser();
413:
414: if (yyparse() || parse_error) {
415: (void) printf("doesn't parse.\n");
416: } else {
417: (void) printf("parses OK.\n\n");
418: if (top == 0)
419: (void) printf("User %s not found\n", pw.pw_name);
420: else while (top) {
421: (void) printf("[%d]\n", top-1);
422: (void) printf("user_match : %d\n", user_matches);
423: (void) printf("host_match : %d\n", host_matches);
424: (void) printf("cmnd_match : %d\n", cmnd_matches);
425: (void) printf("no_passwd : %d\n", no_passwd);
426: (void) printf("runas_match: %d\n", runas_matches);
427: (void) printf("runas : %s\n", *user_runas);
428: top--;
429: }
430: }
431:
432: /* Dump aliases. */
433: (void) printf("Matching Aliases --\n");
434: dumpaliases();
435:
436: exit(0);
437: }