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