Annotation of src/usr.bin/sudo/testsudoers.c, Revision 1.13
1.1 millert 1: /*
1.12 millert 2: * Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: *
1.11 millert 4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
1.1 millert 7: *
1.11 millert 8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 millert 15: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
16: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.10 millert 17: *
18: * Sponsored in part by the Defense Advanced Research Projects
19: * Agency (DARPA) and Air Force Research Laboratory, Air Force
20: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.1 millert 21: */
22:
1.9 millert 23: #define _SUDO_MAIN
24:
1.12 millert 25: #include <config.h>
1.1 millert 26:
1.5 millert 27: #include <sys/param.h>
28: #include <sys/types.h>
29: #include <sys/stat.h>
30: #include <sys/socket.h>
1.1 millert 31: #include <stdio.h>
32: #ifdef STDC_HEADERS
33: # include <stdlib.h>
1.5 millert 34: # include <stddef.h>
35: #else
36: # ifdef HAVE_STDLIB_H
37: # include <stdlib.h>
38: # endif
1.1 millert 39: #endif /* STDC_HEADERS */
1.5 millert 40: #ifdef HAVE_STRING_H
41: # include <string.h>
42: #else
43: # ifdef HAVE_STRINGS_H
44: # include <strings.h>
45: # endif
46: #endif /* HAVE_STRING_H */
1.1 millert 47: #ifdef HAVE_UNISTD_H
48: # include <unistd.h>
49: #endif /* HAVE_UNISTD_H */
1.2 millert 50: #ifdef HAVE_FNMATCH
1.1 millert 51: # include <fnmatch.h>
1.9 millert 52: #endif /* HAVE_FNMATCH */
1.1 millert 53: #ifdef HAVE_NETGROUP_H
54: # include <netgroup.h>
55: #endif /* HAVE_NETGROUP_H */
1.9 millert 56: #ifdef HAVE_ERR_H
57: # include <err.h>
58: #else
59: # include "emul/err.h"
60: #endif /* HAVE_ERR_H */
1.1 millert 61: #include <ctype.h>
62: #include <pwd.h>
63: #include <grp.h>
64: #include <netinet/in.h>
65: #include <arpa/inet.h>
66: #include <netdb.h>
67: #include <dirent.h>
68:
69: #include "sudo.h"
70: #include "parse.h"
71: #include "interfaces.h"
72:
73: #ifndef HAVE_FNMATCH
74: # include "emul/fnmatch.h"
75: #endif /* HAVE_FNMATCH */
76:
77: #ifndef lint
1.13 ! millert 78: __unused static const char rcsid[] = "$Sudo: testsudoers.c,v 1.88.2.5 2007/08/25 02:45:09 millert Exp $";
1.1 millert 79: #endif /* lint */
80:
1.5 millert 81:
82: /*
83: * Prototypes
84: */
85: void init_parser __P((void));
86: void dumpaliases __P((void));
87:
1.1 millert 88: /*
89: * Globals
90: */
1.9 millert 91: int Argc, NewArgc;
1.1 millert 92: char **Argv, **NewArgv;
93: int parse_error = FALSE;
94: int num_interfaces;
95: struct interface *interfaces;
96: struct sudo_user sudo_user;
97: extern int clearaliases;
98: extern int pedantic;
99:
100: /*
101: * Returns TRUE if "s" has shell meta characters in it,
102: * else returns FALSE.
103: */
104: int
105: has_meta(s)
106: char *s;
107: {
1.4 millert 108: char *t;
1.11 millert 109:
1.1 millert 110: for (t = s; *t; t++) {
111: if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
112: return(TRUE);
113: }
114: return(FALSE);
115: }
116:
117: /*
1.11 millert 118: * Returns TRUE if user_cmnd matches, in the sudo sense,
1.1 millert 119: * the pathname in path; otherwise, return FALSE
120: */
121: int
1.11 millert 122: command_matches(path, sudoers_args)
1.1 millert 123: char *path;
124: char *sudoers_args;
125: {
126: int clen, plen;
127: char *args;
128:
1.11 millert 129: if (user_cmnd == NULL)
1.1 millert 130: return(FALSE);
131:
1.11 millert 132: if ((args = strchr(path, ' ')))
1.1 millert 133: *args++ = '\0';
134:
135: if (has_meta(path)) {
1.11 millert 136: if (fnmatch(path, user_cmnd, FNM_PATHNAME))
1.1 millert 137: return(FALSE);
138: if (!sudoers_args)
139: return(TRUE);
1.11 millert 140: else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
1.1 millert 141: return(TRUE);
142: else if (sudoers_args)
1.11 millert 143: return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
1.1 millert 144: else
145: return(FALSE);
146: } else {
147: plen = strlen(path);
148: if (path[plen - 1] != '/') {
1.11 millert 149: if (strcmp(user_cmnd, path))
1.1 millert 150: return(FALSE);
151: if (!sudoers_args)
152: return(TRUE);
1.11 millert 153: else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
1.1 millert 154: return(TRUE);
155: else if (sudoers_args)
1.11 millert 156: return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
1.1 millert 157: else
158: return(FALSE);
159: }
160:
1.11 millert 161: clen = strlen(user_cmnd);
1.1 millert 162: if (clen < plen + 1)
1.11 millert 163: /* path cannot be the parent dir of user_cmnd */
1.1 millert 164: return(FALSE);
165:
1.11 millert 166: if (strchr(user_cmnd + plen + 1, '/') != NULL)
167: /* path could only be an anscestor of user_cmnd -- */
1.1 millert 168: /* ignoring, of course, things like // & /./ */
169: return(FALSE);
170:
1.11 millert 171: /* see whether path is the prefix of user_cmnd */
172: return((strncmp(user_cmnd, path, plen) == 0));
1.1 millert 173: }
174: }
175:
1.13 ! millert 176: static int
! 177: addr_matches_if(n)
1.1 millert 178: char *n;
179: {
180: int i;
1.13 ! millert 181: struct in_addr addr;
! 182: struct interface *ifp;
! 183: #ifdef AF_INET6
! 184: struct in6_addr addr6;
! 185: int j;
! 186: #endif
! 187: int family;
! 188:
! 189: #ifdef AF_INET6
! 190: if (inet_pton(AF_INET6, n, &addr6) > 0) {
! 191: family = AF_INET6;
! 192: } else
! 193: #endif
! 194: {
! 195: family = AF_INET;
! 196: addr.s_addr = inet_addr(n);
! 197: }
! 198:
! 199: for (i = 0; i < num_interfaces; i++) {
! 200: ifp = &interfaces[i];
! 201: if (ifp->family != family)
! 202: continue;
! 203: switch(family) {
! 204: case AF_INET:
! 205: if (ifp->addr.ip4.s_addr == addr.s_addr ||
! 206: (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
! 207: == addr.s_addr)
! 208: return(TRUE);
! 209: break;
! 210: #ifdef AF_INET6
! 211: case AF_INET6:
! 212: if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,
! 213: sizeof(addr6.s6_addr)) == 0)
! 214: return(TRUE);
! 215: for (j = 0; j < sizeof(addr6.s6_addr); j++) {
! 216: if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])
! 217: break;
! 218: }
! 219: if (j == sizeof(addr6.s6_addr))
! 220: return(TRUE);
! 221: #endif /* AF_INET6 */
! 222: }
! 223: }
! 224:
! 225: return(FALSE);
! 226: }
! 227:
! 228: static int
! 229: addr_matches_if_netmask(n, m)
! 230: char *n;
1.1 millert 231: char *m;
1.13 ! millert 232: {
! 233: int i;
1.1 millert 234: struct in_addr addr, mask;
1.13 ! millert 235: struct interface *ifp;
! 236: #ifdef AF_INET6
! 237: struct in6_addr addr6, mask6;
! 238: int j;
! 239: #endif
! 240: int family;
! 241:
! 242: #ifdef AF_INET6
! 243: if (inet_pton(AF_INET6, n, &addr6) > 0)
! 244: family = AF_INET6;
! 245: else
! 246: #endif
! 247: {
! 248: family = AF_INET;
! 249: addr.s_addr = inet_addr(n);
! 250: }
1.1 millert 251:
1.13 ! millert 252: if (family == AF_INET) {
1.1 millert 253: if (strchr(m, '.'))
254: mask.s_addr = inet_addr(m);
1.5 millert 255: else {
256: i = 32 - atoi(m);
257: mask.s_addr = 0xffffffff;
258: mask.s_addr >>= i;
259: mask.s_addr <<= i;
260: mask.s_addr = htonl(mask.s_addr);
261: }
1.13 ! millert 262: }
! 263: #ifdef AF_INET6
! 264: else {
! 265: if (inet_pton(AF_INET6, m, &mask6) <= 0) {
! 266: j = atoi(m);
! 267: for (i = 0; i < 16; i++) {
! 268: if (j < i * 8)
! 269: mask6.s6_addr[i] = 0;
! 270: else if (i * 8 + 8 <= j)
! 271: mask6.s6_addr[i] = 0xff;
! 272: else
! 273: mask6.s6_addr[i] = 0xff00 >> (j - i * 8);
! 274: }
! 275: }
! 276: }
! 277: #endif /* AF_INET6 */
1.1 millert 278:
1.13 ! millert 279: for (i = 0; i < num_interfaces; i++) {
! 280: ifp = &interfaces[i];
! 281: if (ifp->family != family)
! 282: continue;
! 283: switch(family) {
! 284: case AF_INET:
! 285: if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
! 286: return(TRUE);
! 287: #ifdef AF_INET6
! 288: case AF_INET6:
! 289: for (j = 0; j < sizeof(addr6.s6_addr); j++) {
! 290: if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])
! 291: break;
! 292: }
! 293: if (j == sizeof(addr6.s6_addr))
! 294: return(TRUE);
! 295: #endif /* AF_INET6 */
! 296: }
1.1 millert 297: }
298:
299: return(FALSE);
1.13 ! millert 300: }
! 301:
! 302: /*
! 303: * Returns TRUE if "n" is one of our ip addresses or if
! 304: * "n" is a network that we are on, else returns FALSE.
! 305: */
! 306: int
! 307: addr_matches(n)
! 308: char *n;
! 309: {
! 310: char *m;
! 311: int retval;
! 312:
! 313: /* If there's an explicit netmask, use it. */
! 314: if ((m = strchr(n, '/'))) {
! 315: *m++ = '\0';
! 316: retval = addr_matches_if_netmask(n, m);
! 317: *(m - 1) = '/';
! 318: } else
! 319: retval = addr_matches_if(n);
! 320:
! 321: return(retval);
1.4 millert 322: }
323:
324: int
325: hostname_matches(shost, lhost, pattern)
326: char *shost;
327: char *lhost;
328: char *pattern;
329: {
1.11 millert 330: if (has_meta(pattern)) {
331: if (strchr(pattern, '.'))
1.4 millert 332: return(fnmatch(pattern, lhost, FNM_CASEFOLD));
333: else
334: return(fnmatch(pattern, shost, FNM_CASEFOLD));
335: } else {
336: if (strchr(pattern, '.'))
337: return(strcasecmp(lhost, pattern));
338: else
339: return(strcasecmp(shost, pattern));
340: }
1.1 millert 341: }
342:
343: int
1.11 millert 344: userpw_matches(sudoers_user, user, pw)
345: char *sudoers_user;
346: char *user;
347: struct passwd *pw;
348: {
349: if (pw != NULL && *sudoers_user == '#') {
350: uid_t uid = atoi(sudoers_user + 1);
351: if (uid == pw->pw_uid)
352: return(1);
353: }
354: return(strcmp(sudoers_user, user) == 0);
355: }
356:
357: int
358: usergr_matches(group, user, pw)
1.1 millert 359: char *group;
360: char *user;
1.11 millert 361: struct passwd *pw;
1.1 millert 362: {
363: struct group *grp;
364: char **cur;
365:
366: /* Make sure we have a valid usergroup, sudo style. */
367: if (*group++ != '%')
368: return(FALSE);
369:
1.11 millert 370: if ((grp = getgrnam(group)) == NULL)
1.1 millert 371: return(FALSE);
372:
373: /*
374: * Check against user's real gid as well as group's user list
375: */
376: if (getgid() == grp->gr_gid)
377: return(TRUE);
378:
379: for (cur=grp->gr_mem; *cur; cur++) {
380: if (strcmp(*cur, user) == 0)
381: return(TRUE);
382: }
383:
384: return(FALSE);
385: }
386:
387: int
1.3 millert 388: netgr_matches(netgr, host, shost, user)
1.1 millert 389: char *netgr;
390: char *host;
1.3 millert 391: char *shost;
1.1 millert 392: char *user;
393: {
394: #ifdef HAVE_GETDOMAINNAME
395: static char *domain = (char *) -1;
396: #else
397: static char *domain = NULL;
398: #endif /* HAVE_GETDOMAINNAME */
399:
400: /* Make sure we have a valid netgroup, sudo style. */
401: if (*netgr++ != '+')
402: return(FALSE);
403:
404: #ifdef HAVE_GETDOMAINNAME
405: /* Get the domain name (if any). */
406: if (domain == (char *) -1) {
407: domain = (char *) emalloc(MAXHOSTNAMELEN);
408:
409: if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') {
1.12 millert 410: efree(domain);
1.1 millert 411: domain = NULL;
412: }
413: }
414: #endif /* HAVE_GETDOMAINNAME */
415:
416: #ifdef HAVE_INNETGR
1.3 millert 417: if (innetgr(netgr, host, user, domain))
418: return(TRUE);
419: else if (host != shost && innetgr(netgr, shost, user, domain))
420: return(TRUE);
421: #endif /* HAVE_INNETGR */
422:
1.1 millert 423: return(FALSE);
424: }
425:
426: void
1.12 millert 427: set_perms(i)
1.7 millert 428: int i;
1.2 millert 429: {
430: return;
431: }
432:
433: void
434: set_fqdn()
1.5 millert 435: {
436: return;
437: }
438:
1.11 millert 439: int
440: set_runaspw(user)
441: char *user;
442: {
443: return(TRUE);
444: }
445:
1.5 millert 446: void
447: init_envtables()
1.1 millert 448: {
449: return;
450: }
451:
452: int
453: main(argc, argv)
454: int argc;
455: char **argv;
456: {
457: struct passwd pw;
458: char *p;
459: #ifdef YYDEBUG
460: extern int yydebug;
461: yydebug = 1;
462: #endif
463:
464: Argv = argv;
465: Argc = argc;
466:
467: if (Argc >= 6 && strcmp(Argv[1], "-u") == 0) {
468: user_runas = &Argv[2];
469: pw.pw_name = Argv[3];
470: user_host = Argv[4];
471: user_cmnd = Argv[5];
472:
473: NewArgv = &Argv[5];
474: NewArgc = Argc - 5;
475: } else if (Argc >= 4) {
476: pw.pw_name = Argv[1];
477: user_host = Argv[2];
478: user_cmnd = Argv[3];
479:
480: NewArgv = &Argv[3];
481: NewArgc = Argc - 3;
482: } else {
483: (void) fprintf(stderr,
1.9 millert 484: "usage: sudo [-u user] <user> <host> <command> [args]\n");
1.1 millert 485: exit(1);
486: }
487:
488: sudo_user.pw = &pw; /* user_name needs to be defined */
489:
490: if ((p = strchr(user_host, '.'))) {
491: *p = '\0';
492: user_shost = estrdup(user_host);
493: *p = '.';
494: } else {
495: user_shost = user_host;
496: }
497:
1.11 millert 498: /* Fill in user_args from NewArgv. */
1.1 millert 499: if (NewArgc > 1) {
500: char *to, **from;
1.7 millert 501: size_t size, n;
1.1 millert 502:
1.7 millert 503: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
504: strlen(NewArgv[NewArgc-1]) + 1;
1.1 millert 505: user_args = (char *) emalloc(size);
1.7 millert 506: for (to = user_args, from = NewArgv + 1; *from; from++) {
507: n = strlcpy(to, *from, size - (to - user_args));
1.9 millert 508: if (n >= size - (to - user_args))
509: errx(1, "internal error, init_vars() overflow");
1.7 millert 510: to += n;
1.1 millert 511: *to++ = ' ';
512: }
1.7 millert 513: *--to = '\0';
1.1 millert 514: }
515:
516: /* Initialize default values. */
517: init_defaults();
518:
519: /* Warn about aliases that are used before being defined. */
520: pedantic = TRUE;
521:
522: /* Need to keep aliases around for dumpaliases(). */
523: clearaliases = FALSE;
524:
525: /* Load ip addr/mask for each interface. */
526: load_interfaces();
527:
528: /* Allocate space for data structures in the parser. */
529: init_parser();
530:
531: if (yyparse() || parse_error) {
532: (void) printf("doesn't parse.\n");
533: } else {
534: (void) printf("parses OK.\n\n");
535: if (top == 0)
536: (void) printf("User %s not found\n", pw.pw_name);
537: else while (top) {
538: (void) printf("[%d]\n", top-1);
539: (void) printf("user_match : %d\n", user_matches);
540: (void) printf("host_match : %d\n", host_matches);
541: (void) printf("cmnd_match : %d\n", cmnd_matches);
542: (void) printf("no_passwd : %d\n", no_passwd);
543: (void) printf("runas_match: %d\n", runas_matches);
544: (void) printf("runas : %s\n", *user_runas);
545: top--;
546: }
547: }
548:
549: /* Dump aliases. */
550: (void) printf("Matching Aliases --\n");
551: dumpaliases();
552:
553: exit(0);
554: }