Annotation of src/usr.bin/sudo/testsudoers.c, Revision 1.4
1.1 millert 1: /*
1.3 millert 2: * Copyright (c) 1996, 1998-2000 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 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 */
1.2 millert 53: #ifdef HAVE_FNMATCH
1.1 millert 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
1.4 ! millert 80: static const char rcsid[] = "$Sudo: testsudoers.c,v 1.71 2000/03/23 04:38:22 millert Exp $";
1.1 millert 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: {
1.4 ! millert 109: char *t;
1.1 millert 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);
1.4 ! millert 211: }
! 212:
! 213: int
! 214: hostname_matches(shost, lhost, pattern)
! 215: char *shost;
! 216: char *lhost;
! 217: char *pattern;
! 218: {
! 219: if (has_meta(pattern)) {
! 220: if (strchr(pattern, '.'))
! 221: return(fnmatch(pattern, lhost, FNM_CASEFOLD));
! 222: else
! 223: return(fnmatch(pattern, shost, FNM_CASEFOLD));
! 224: } else {
! 225: if (strchr(pattern, '.'))
! 226: return(strcasecmp(lhost, pattern));
! 227: else
! 228: return(strcasecmp(shost, pattern));
! 229: }
1.1 millert 230: }
231:
232: int
233: usergr_matches(group, user)
234: char *group;
235: char *user;
236: {
237: struct group *grp;
238: char **cur;
239:
240: /* Make sure we have a valid usergroup, sudo style. */
241: if (*group++ != '%')
242: return(FALSE);
243:
244: if ((grp = getgrnam(group)) == NULL)
245: return(FALSE);
246:
247: /*
248: * Check against user's real gid as well as group's user list
249: */
250: if (getgid() == grp->gr_gid)
251: return(TRUE);
252:
253: for (cur=grp->gr_mem; *cur; cur++) {
254: if (strcmp(*cur, user) == 0)
255: return(TRUE);
256: }
257:
258: return(FALSE);
259: }
260:
261: int
1.3 millert 262: netgr_matches(netgr, host, shost, user)
1.1 millert 263: char *netgr;
264: char *host;
1.3 millert 265: char *shost;
1.1 millert 266: char *user;
267: {
268: #ifdef HAVE_GETDOMAINNAME
269: static char *domain = (char *) -1;
270: #else
271: static char *domain = NULL;
272: #endif /* HAVE_GETDOMAINNAME */
273:
274: /* Make sure we have a valid netgroup, sudo style. */
275: if (*netgr++ != '+')
276: return(FALSE);
277:
278: #ifdef HAVE_GETDOMAINNAME
279: /* Get the domain name (if any). */
280: if (domain == (char *) -1) {
281: domain = (char *) emalloc(MAXHOSTNAMELEN);
282:
283: if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') {
284: free(domain);
285: domain = NULL;
286: }
287: }
288: #endif /* HAVE_GETDOMAINNAME */
289:
290: #ifdef HAVE_INNETGR
1.3 millert 291: if (innetgr(netgr, host, user, domain))
292: return(TRUE);
293: else if (host != shost && innetgr(netgr, shost, user, domain))
294: return(TRUE);
295: #endif /* HAVE_INNETGR */
296:
1.1 millert 297: return(FALSE);
298: }
299:
300: void
301: set_perms(i, j)
302: int i, j;
1.2 millert 303: {
304: return;
305: }
306:
307: void
308: set_fqdn()
1.1 millert 309: {
310: return;
311: }
312:
313: int
314: main(argc, argv)
315: int argc;
316: char **argv;
317: {
318: struct passwd pw;
319: char *p;
320: #ifdef YYDEBUG
321: extern int yydebug;
322: yydebug = 1;
323: #endif
324:
325: Argv = argv;
326: Argc = argc;
327:
328: if (Argc >= 6 && strcmp(Argv[1], "-u") == 0) {
329: user_runas = &Argv[2];
330: pw.pw_name = Argv[3];
331: user_host = Argv[4];
332: user_cmnd = Argv[5];
333:
334: NewArgv = &Argv[5];
335: NewArgc = Argc - 5;
336: } else if (Argc >= 4) {
337: pw.pw_name = Argv[1];
338: user_host = Argv[2];
339: user_cmnd = Argv[3];
340:
341: NewArgv = &Argv[3];
342: NewArgc = Argc - 3;
343: } else {
344: (void) fprintf(stderr,
345: "usage: %s [-u user] <user> <host> <command> [args]\n", Argv[0]);
346: exit(1);
347: }
348:
349: sudo_user.pw = &pw; /* user_name needs to be defined */
350:
351: if ((p = strchr(user_host, '.'))) {
352: *p = '\0';
353: user_shost = estrdup(user_host);
354: *p = '.';
355: } else {
356: user_shost = user_host;
357: }
358:
359: /* Fill in cmnd_args from NewArgv. */
360: if (NewArgc > 1) {
361: size_t size;
362: char *to, **from;
363:
364: size = (size_t) NewArgv[NewArgc-1] + strlen(NewArgv[NewArgc-1]) -
365: (size_t) NewArgv[1] + 1;
366: user_args = (char *) emalloc(size);
367: for (to = user_args, from = &NewArgv[1]; *from; from++) {
368: *to++ = ' ';
369: (void) strcpy(to, *from);
370: to += strlen(*from);
371: }
372: }
373:
374: /* Initialize default values. */
375: init_defaults();
376:
377: /* Warn about aliases that are used before being defined. */
378: pedantic = TRUE;
379:
380: /* Need to keep aliases around for dumpaliases(). */
381: clearaliases = FALSE;
382:
383: /* Load ip addr/mask for each interface. */
384: load_interfaces();
385:
386: /* Allocate space for data structures in the parser. */
387: init_parser();
388:
389: if (yyparse() || parse_error) {
390: (void) printf("doesn't parse.\n");
391: } else {
392: (void) printf("parses OK.\n\n");
393: if (top == 0)
394: (void) printf("User %s not found\n", pw.pw_name);
395: else while (top) {
396: (void) printf("[%d]\n", top-1);
397: (void) printf("user_match : %d\n", user_matches);
398: (void) printf("host_match : %d\n", host_matches);
399: (void) printf("cmnd_match : %d\n", cmnd_matches);
400: (void) printf("no_passwd : %d\n", no_passwd);
401: (void) printf("runas_match: %d\n", runas_matches);
402: (void) printf("runas : %s\n", *user_runas);
403: top--;
404: }
405: }
406:
407: /* Dump aliases. */
408: (void) printf("Matching Aliases --\n");
409: dumpaliases();
410:
411: exit(0);
412: }