1/* 2 * Copyright (c) 1996, 1998-2005, 2007-2010 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#define _SUDO_MAIN 25 26#include <config.h> 27 28#include <sys/param.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <sys/socket.h> 32#include <stdio.h> 33#ifdef STDC_HEADERS 34# include <stdlib.h> 35# include <stddef.h> 36#else 37# ifdef HAVE_STDLIB_H 38# include <stdlib.h> 39# endif 40#endif /* STDC_HEADERS */ 41#ifdef HAVE_STRING_H 42# include <string.h> 43#endif /* HAVE_STRING_H */ 44#ifdef HAVE_STRINGS_H 45# include <strings.h> 46#endif /* HAVE_STRINGS_H */ 47#ifdef HAVE_UNISTD_H 48# include <unistd.h> 49#endif /* HAVE_UNISTD_H */ 50#ifdef HAVE_FNMATCH 51# include <fnmatch.h> 52#endif /* HAVE_FNMATCH */ 53#ifdef HAVE_NETGROUP_H 54# include <netgroup.h> 55#endif /* HAVE_NETGROUP_H */ 56#include <ctype.h> 57#include <pwd.h> 58#include <grp.h> 59#include <netinet/in.h> 60#include <arpa/inet.h> 61#include <netdb.h> 62 63#include "tsgetgrpw.h" 64#include "sudo.h" 65#include "interfaces.h" 66#include "parse.h" 67#include <gram.h> 68 69#ifndef HAVE_FNMATCH 70# include "emul/fnmatch.h" 71#endif /* HAVE_FNMATCH */ 72 73/* 74 * Globals 75 */ 76int Argc, NewArgc; 77char **Argv, **NewArgv; 78int num_interfaces; 79struct interface *interfaces; 80struct sudo_user sudo_user; 81struct passwd *list_pw; 82extern int parse_error; 83 84/* For getopt(3) */ 85extern char *optarg; 86extern int optind; 87 88#if defined(SUDO_DEVEL) && defined(__OpenBSD__) 89extern char *malloc_options; 90#endif 91#ifdef YYDEBUG 92extern int yydebug; 93#endif 94 95int print_alias __P((void *, void *)); 96void dump_sudoers __P((void)); 97void print_defaults __P((void)); 98void print_privilege __P((struct privilege *)); 99void print_userspecs __P((void)); 100void usage __P((void)) __attribute__((__noreturn__)); 101void set_runasgr __P((char *)); 102void set_runaspw __P((char *)); 103 104int 105main(argc, argv) 106 int argc; 107 char **argv; 108{ 109 struct cmndspec *cs; 110 struct privilege *priv; 111 struct userspec *us; 112 char *p, *grfile, *pwfile, *runas_group, *runas_user; 113 char hbuf[MAXHOSTNAMELEN + 1]; 114 int match, host_match, runas_match, cmnd_match; 115 int ch, dflag; 116 117#if defined(SUDO_DEVEL) && defined(__OpenBSD__) 118 malloc_options = "AFGJPR"; 119#endif 120#ifdef YYDEBUG 121 yydebug = 1; 122#endif 123 124 Argv = argv; 125 Argc = argc; 126 127 dflag = 0; 128 grfile = pwfile = runas_group = runas_user = NULL; 129 while ((ch = getopt(argc, argv, "dg:P:h:p:u:")) != -1) { 130 switch (ch) { 131 case 'd': 132 dflag = 1; 133 break; 134 case 'h': 135 user_host = optarg; 136 break; 137 case 'g': 138 runas_group = optarg; 139 break; 140 case 'P': 141 grfile = optarg; 142 break; 143 case 'p': 144 pwfile = optarg; 145 break; 146 case 'u': 147 runas_user = optarg; 148 break; 149 default: 150 usage(); 151 break; 152 } 153 } 154 argc -= optind; 155 argv += optind; 156 NewArgc = argc; 157 NewArgv = argv; 158 159 /* Set group/passwd file and init the cache. */ 160 if (grfile) 161 setgrfile(grfile); 162 if (pwfile) 163 setpwfile(pwfile); 164 sudo_setpwent(); 165 sudo_setgrent(); 166 167 if (argc < 2) { 168 if (!dflag) 169 usage(); 170 if ((sudo_user.pw = sudo_getpwnam("root")) == NULL) 171 errorx(1, "no passwd entry for root!"); 172 user_cmnd = user_base = "true"; 173 } else { 174 if ((sudo_user.pw = sudo_getpwnam(*argv)) == NULL) 175 errorx(1, "no passwd entry for %s!", *argv); 176 user_cmnd = *++argv; 177 if ((p = strrchr(user_cmnd, '/')) != NULL) 178 user_base = p + 1; 179 else 180 user_base = user_cmnd; 181 NewArgc -= 2; 182 } 183 184 if (user_host == NULL) { 185 if (gethostname(hbuf, sizeof(hbuf)) != 0) 186 error(1, "gethostname"); 187 hbuf[sizeof(hbuf) - 1] = '\0'; 188 user_host = hbuf; 189 } 190 if ((p = strchr(user_host, '.'))) { 191 *p = '\0'; 192 user_shost = estrdup(user_host); 193 *p = '.'; 194 } else { 195 user_shost = user_host; 196 } 197 198 /* Fill in user_args from NewArgv. */ 199 if (NewArgc > 0) { 200 char *to, **from; 201 size_t size, n; 202 203 for (size = 0, from = NewArgv + 1; *from; from++) 204 size += strlen(*from) + 1; 205 206 user_args = (char *) emalloc(size); 207 for (to = user_args, from = NewArgv + 1; *from; from++) { 208 n = strlcpy(to, *from, size - (to - user_args)); 209 if (n >= size - (to - user_args)) 210 errorx(1, "internal error, init_vars() overflow"); 211 to += n; 212 *to++ = ' '; 213 } 214 *--to = '\0'; 215 } 216 217 /* Initialize default values. */ 218 init_defaults(); 219 220 /* Load ip addr/mask for each interface. */ 221 load_interfaces(); 222 223 /* Allocate space for data structures in the parser. */ 224 init_parser("sudoers", 0); 225 226 if (yyparse() != 0 || parse_error) { 227 parse_error = TRUE; 228 (void) fputs("Does not parse", stdout); 229 } else { 230 (void) fputs("Parses OK", stdout); 231 } 232 233 if (!update_defaults(SETDEF_ALL)) 234 (void) fputs(" (problem with defaults entries)", stdout); 235 puts("."); 236 237 /* 238 * Set runas passwd/group entries based on command line or sudoers. 239 * Note that if runas_group was specified without runas_user we 240 * defer setting runas_pw so the match routines know to ignore it. 241 */ 242 if (runas_group != NULL) { 243 set_runasgr(runas_group); 244 if (runas_user != NULL) 245 set_runaspw(runas_user); 246 } else 247 set_runaspw(runas_user ? runas_user : def_runas_default); 248 249 if (dflag) { 250 (void) putchar('\n'); 251 dump_sudoers(); 252 if (argc < 2) 253 exit(parse_error ? 1 : 0); 254 } 255 256 /* This loop must match the one in sudo_file_lookup() */ 257 printf("\nEntries for user %s:\n", user_name); 258 match = UNSPEC; 259 tq_foreach_rev(&userspecs, us) { 260 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW) 261 continue; 262 tq_foreach_rev(&us->privileges, priv) { 263 putchar('\n'); 264 print_privilege(priv); /* XXX */ 265 putchar('\n'); 266 host_match = hostlist_matches(&priv->hostlist); 267 if (host_match == ALLOW) { 268 puts("\thost matched"); 269 tq_foreach_rev(&priv->cmndlist, cs) { 270 runas_match = runaslist_matches(&cs->runasuserlist, 271 &cs->runasgrouplist); 272 if (runas_match == ALLOW) { 273 puts("\trunas matched"); 274 cmnd_match = cmnd_matches(cs->cmnd); 275 if (cmnd_match != UNSPEC) 276 match = cmnd_match; 277 printf("\tcmnd %s\n", match == ALLOW ? "allowed" : 278 match == DENY ? "denied" : "unmatched"); 279 } 280 } 281 } else 282 puts("\thost unmatched"); 283 } 284 } 285 printf("\nCommand %s\n", match == ALLOW ? "allowed" : 286 match == DENY ? "denied" : "unmatched"); 287 288 /* 289 * Exit codes: 290 * 0 - parsed OK and command matched. 291 * 1 - parse error 292 * 2 - command not matched 293 * 3 - command denied 294 */ 295 if (parse_error) 296 exit(1); 297 exit(match == ALLOW ? 0 : match + 3); 298} 299 300void 301set_runaspw(user) 302 char *user; 303{ 304 if (*user == '#') { 305 if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL) 306 runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0); 307 } else { 308 if ((runas_pw = sudo_getpwnam(user)) == NULL) 309 errorx(1, "unknown user: %s", user); 310 } 311} 312 313void 314set_runasgr(group) 315 char *group; 316{ 317 if (*group == '#') { 318 if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL) 319 runas_gr = sudo_fakegrnam(group); 320 } else { 321 if ((runas_gr = sudo_getgrnam(group)) == NULL) 322 errorx(1, "unknown group: %s", group); 323 } 324} 325 326void 327sudo_setspent() 328{ 329 return; 330} 331 332void 333sudo_endspent() 334{ 335 return; 336} 337 338char * 339sudo_getepw(pw) 340 const struct passwd *pw; 341{ 342 return pw->pw_passwd; 343} 344 345void 346set_fqdn() 347{ 348 return; 349} 350 351FILE * 352open_sudoers(path, isdir, keepopen) 353 const char *path; 354 int isdir; 355 int *keepopen; 356{ 357 return fopen(path, "r"); 358} 359 360void 361init_envtables() 362{ 363 return; 364} 365 366int 367set_perms(perm) 368 int perm; 369{ 370 return 1; 371} 372 373void 374cleanup(gotsignal) 375 int gotsignal; 376{ 377 if (!gotsignal) { 378 sudo_endpwent(); 379 sudo_endgrent(); 380 } 381} 382 383void 384print_member(m) 385 struct member *m; 386{ 387 struct sudo_command *c; 388 389 if (m->negated) 390 putchar('!'); 391 if (m->name == NULL) 392 fputs("ALL", stdout); 393 else if (m->type != COMMAND) 394 fputs(m->name, stdout); 395 else { 396 c = (struct sudo_command *) m->name; 397 printf("%s%s%s", c->cmnd, c->args ? " " : "", 398 c->args ? c->args : ""); 399 } 400} 401 402void 403print_defaults() 404{ 405 struct defaults *d; 406 struct member *m; 407 408 tq_foreach_fwd(&defaults, d) { 409 (void) fputs("Defaults", stdout); 410 switch (d->type) { 411 case DEFAULTS_HOST: 412 putchar('@'); 413 break; 414 case DEFAULTS_USER: 415 putchar(':'); 416 break; 417 case DEFAULTS_RUNAS: 418 putchar('>'); 419 break; 420 case DEFAULTS_CMND: 421 putchar('!'); 422 break; 423 } 424 tq_foreach_fwd(&d->binding, m) { 425 if (m != tq_first(&d->binding)) 426 putchar(','); 427 print_member(m); 428 } 429 printf("\t%s%s", d->op == FALSE ? "!" : "", d->var); 430 if (d->val != NULL) { 431 printf("%c%s", d->op == TRUE ? '=' : d->op, d->val); 432 } 433 putchar('\n'); 434 } 435} 436 437int 438print_alias(v1, v2) 439 void *v1, *v2; 440{ 441 struct alias *a = (struct alias *)v1; 442 struct member *m; 443 struct sudo_command *c; 444 445 switch (a->type) { 446 case HOSTALIAS: 447 (void) printf("Host_Alias\t%s = ", a->name); 448 break; 449 case CMNDALIAS: 450 (void) printf("Cmnd_Alias\t%s = ", a->name); 451 break; 452 case USERALIAS: 453 (void) printf("User_Alias\t%s = ", a->name); 454 break; 455 case RUNASALIAS: 456 (void) printf("Runas_Alias\t%s = ", a->name); 457 break; 458 } 459 tq_foreach_fwd(&a->members, m) { 460 if (m != tq_first(&a->members)) 461 fputs(", ", stdout); 462 if (m->type == COMMAND) { 463 c = (struct sudo_command *) m->name; 464 printf("%s%s%s", c->cmnd, c->args ? " " : "", 465 c->args ? c->args : ""); 466 } else if (m->type == ALL) { 467 fputs("ALL", stdout); 468 } else { 469 fputs(m->name, stdout); 470 } 471 } 472 putchar('\n'); 473 return 0; 474} 475 476void 477print_privilege(priv) 478 struct privilege *priv; 479{ 480 struct cmndspec *cs; 481 struct member *m; 482 struct privilege *p; 483 struct cmndtag tags; 484 485 for (p = priv; p != NULL; p = p->next) { 486 if (p != priv) 487 fputs(" : ", stdout); 488 tq_foreach_fwd(&p->hostlist, m) { 489 if (m != tq_first(&p->hostlist)) 490 fputs(", ", stdout); 491 print_member(m); 492 } 493 fputs(" = ", stdout); 494 tags.nopasswd = tags.noexec = UNSPEC; 495 tq_foreach_fwd(&p->cmndlist, cs) { 496 if (cs != tq_first(&p->cmndlist)) 497 fputs(", ", stdout); 498 if (!tq_empty(&cs->runasuserlist) || !tq_empty(&cs->runasgrouplist)) { 499 fputs("(", stdout); 500 if (!tq_empty(&cs->runasuserlist)) { 501 tq_foreach_fwd(&cs->runasuserlist, m) { 502 if (m != tq_first(&cs->runasuserlist)) 503 fputs(", ", stdout); 504 print_member(m); 505 } 506 } else if (tq_empty(&cs->runasgrouplist)) { 507 fputs(def_runas_default, stdout); 508 } else { 509 fputs(sudo_user.pw->pw_name, stdout); 510 } 511 if (!tq_empty(&cs->runasgrouplist)) { 512 fputs(" : ", stdout); 513 tq_foreach_fwd(&cs->runasgrouplist, m) { 514 if (m != tq_first(&cs->runasgrouplist)) 515 fputs(", ", stdout); 516 print_member(m); 517 } 518 } 519 fputs(") ", stdout); 520 } 521#ifdef HAVE_SELINUX 522 if (cs->role) 523 printf("ROLE=%s ", cs->role); 524 if (cs->type) 525 printf("TYPE=%s ", cs->type); 526#endif /* HAVE_SELINUX */ 527 if (cs->tags.nopasswd != UNSPEC && cs->tags.nopasswd != tags.nopasswd) 528 printf("%sPASSWD: ", cs->tags.nopasswd ? "NO" : ""); 529 if (cs->tags.noexec != UNSPEC && cs->tags.noexec != tags.noexec) 530 printf("%sEXEC: ", cs->tags.noexec ? "NO" : ""); 531 print_member(cs->cmnd); 532 memcpy(&tags, &cs->tags, sizeof(tags)); 533 } 534 } 535} 536 537void 538print_userspecs() 539{ 540 struct member *m; 541 struct userspec *us; 542 543 tq_foreach_fwd(&userspecs, us) { 544 tq_foreach_fwd(&us->users, m) { 545 if (m != tq_first(&us->users)) 546 fputs(", ", stdout); 547 print_member(m); 548 } 549 putchar('\t'); 550 print_privilege(us->privileges.first); /* XXX */ 551 putchar('\n'); 552 } 553} 554 555void 556dump_sudoers() 557{ 558 print_defaults(); 559 560 putchar('\n'); 561 alias_apply(print_alias, NULL); 562 563 putchar('\n'); 564 print_userspecs(); 565} 566 567void 568usage() 569{ 570 (void) fprintf(stderr, "usage: %s [-d] [-g group] [-h host] [-P grfile] [-p pwfile] [-u user] <user> <command> [args]\n", getprogname()); 571 exit(1); 572} 573