1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <errno.h> 29 #include <deflt.h> 30 #include <locale.h> 31 #include <sys/types.h> 32 #include <sys/param.h> 33 #include <sys/stat.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <ctype.h> 37 #include <pwd.h> 38 #include <grp.h> 39 #include <string.h> 40 #include <exec_attr.h> 41 #include <user_attr.h> 42 #include <auth_attr.h> 43 #include <prof_attr.h> 44 #include <errno.h> 45 #include <priv.h> 46 47 #include <bsm/adt.h> 48 #include <bsm/adt_event.h> 49 50 #ifndef TEXT_DOMAIN /* Should be defined by cc -D */ 51 #define TEXT_DOMAIN "SYS_TEST" 52 #endif 53 54 extern int cannot_audit(int); 55 56 static char *pathsearch(char *); 57 static int getrealpath(const char *, char *); 58 static int checkattrs(char *, int, char *[]); 59 static void sanitize_environ(); 60 static uid_t get_uid(char *); 61 static gid_t get_gid(char *); 62 static priv_set_t *get_privset(const char *); 63 static priv_set_t *get_granted_privs(uid_t); 64 static void get_default_privs(const char *, priv_set_t *); 65 static void get_profile_privs(char *, char **, int *, priv_set_t *); 66 67 static int isnumber(char *); 68 static void usage(void); 69 70 extern char **environ; 71 72 #define PROFLIST_SEP "," 73 74 int 75 main(int argc, char *argv[]) 76 { 77 char *cmd; 78 char **cmdargs; 79 char cmd_realpath[MAXPATHLEN]; 80 int c; 81 char *pset = NULL; 82 83 (void) setlocale(LC_ALL, ""); 84 (void) textdomain(TEXT_DOMAIN); 85 86 while ((c = getopt(argc, argv, "P:")) != EOF) { 87 switch (c) { 88 case 'P': 89 if (pset == NULL) { 90 pset = optarg; 91 break; 92 } 93 /* FALLTHROUGH */ 94 default: 95 usage(); 96 } 97 } 98 argc -= optind; 99 argv += optind; 100 101 if (argc < 1) 102 usage(); 103 104 cmd = argv[0]; 105 cmdargs = &argv[0]; 106 107 if (pset != NULL) { 108 uid_t uid = getuid(); 109 priv_set_t *wanted = get_privset(pset); 110 priv_set_t *granted; 111 112 adt_session_data_t *ah; /* audit session handle */ 113 adt_event_data_t *event; /* event to be generated */ 114 char cwd[MAXPATHLEN]; 115 116 granted = get_granted_privs(uid); 117 118 /* Audit use */ 119 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) { 120 perror("pfexec: adt_start_session"); 121 exit(EXIT_FAILURE); 122 } 123 if ((event = adt_alloc_event(ah, ADT_prof_cmd)) == NULL) { 124 perror("pfexec: adt_alloc_event"); 125 exit(EXIT_FAILURE); 126 } 127 if ((event->adt_prof_cmd.cwdpath = 128 getcwd(cwd, sizeof (cwd))) == NULL) { 129 (void) fprintf(stderr, 130 gettext("pfexec: can't add cwd path\n")); 131 exit(EXIT_FAILURE); 132 } 133 134 event->adt_prof_cmd.cmdpath = cmd; 135 event->adt_prof_cmd.argc = argc - 1; 136 event->adt_prof_cmd.argv = &argv[1]; 137 event->adt_prof_cmd.envp = environ; 138 139 if (granted != NULL) { 140 priv_intersect(granted, wanted); 141 event->adt_prof_cmd.inherit_set = wanted; 142 if (adt_put_event(event, ADT_SUCCESS, 143 ADT_SUCCESS) != 0) { 144 perror("pfexec: adt_put_event"); 145 exit(EXIT_FAILURE); 146 } 147 if (setppriv(PRIV_ON, PRIV_INHERITABLE, wanted) != 0) { 148 (void) fprintf(stderr, 149 gettext("setppriv(): %s\n"), 150 strerror(errno)); 151 exit(EXIT_FAILURE); 152 } 153 /* Trick exec into thinking we're not suid */ 154 (void) setppriv(PRIV_ON, PRIV_PERMITTED, wanted); 155 priv_freeset(event->adt_prof_cmd.inherit_set); 156 } else { 157 if (adt_put_event(event, ADT_SUCCESS, 158 ADT_SUCCESS) != 0) { 159 perror("pfexec: adt_put_event"); 160 exit(EXIT_FAILURE); 161 } 162 } 163 adt_free_event(event); 164 (void) adt_end_session(ah); 165 (void) setreuid(uid, uid); 166 (void) execvp(cmd, cmdargs); 167 (void) fprintf(stderr, 168 gettext("pfexec: can't execute %s: %s\n"), 169 cmd, strerror(errno)); 170 exit(EXIT_FAILURE); 171 } 172 173 if ((cmd = pathsearch(cmd)) == NULL) 174 exit(EXIT_FAILURE); 175 176 if (getrealpath(cmd, cmd_realpath) == 0) 177 exit(EXIT_FAILURE); 178 179 if (checkattrs(cmd_realpath, argc, argv) == 0) 180 exit(EXIT_FAILURE); 181 182 (void) execv(cmd, cmdargs); 183 /* 184 * We'd be here only if execv fails. 185 */ 186 (void) fprintf(stderr, gettext("pfexec: can't execute %s: %s\n"), 187 cmd, strerror(errno)); 188 exit(EXIT_FAILURE); 189 /* LINTED */ 190 } 191 192 193 /* 194 * gets realpath for cmd. 195 * return 1 on success, 0 on failure. 196 */ 197 static int 198 getrealpath(const char *cmd, char *cmd_realpath) 199 { 200 if (realpath(cmd, cmd_realpath) == NULL) { 201 (void) fprintf(stderr, 202 gettext("pfexec: can't get real path of ``%s''\n"), cmd); 203 return (0); 204 } 205 return (1); 206 } 207 208 /* 209 * gets execution attributed for cmd, sets uids/gids, checks environ. 210 * returns 1 on success, 0 on failure. 211 */ 212 static int 213 checkattrs(char *cmd_realpath, int argc, char *argv[]) 214 { 215 char *value; 216 uid_t uid, euid; 217 gid_t gid = (gid_t)-1; 218 gid_t egid = (gid_t)-1; 219 struct passwd *pwent; 220 execattr_t *exec; 221 priv_set_t *lset = NULL; 222 priv_set_t *iset = NULL; 223 224 adt_session_data_t *ah; /* audit session handle */ 225 adt_event_data_t *event; /* event to be generated */ 226 char cwd[MAXPATHLEN]; 227 228 uid = euid = getuid(); 229 if ((pwent = getpwuid(uid)) == NULL) { 230 (void) fprintf(stderr, "%d: ", (int)uid); 231 (void) fprintf(stderr, gettext("can't get passwd entry\n")); 232 return (0); 233 } 234 /* Set up to audit use */ 235 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) { 236 perror("pfexec: adt_start_session"); 237 return (0); 238 } 239 if ((event = adt_alloc_event(ah, ADT_prof_cmd)) == NULL) { 240 perror("pfexec: adt_alloc_event"); 241 return (0); 242 } 243 if ((event->adt_prof_cmd.cwdpath = getcwd(cwd, sizeof (cwd))) == NULL) { 244 (void) fprintf(stderr, gettext("pfexec: can't add cwd path\n")); 245 return (0); 246 } 247 /* 248 * Get the exec attrs: uid, gid, euid and egid 249 */ 250 if ((exec = getexecuser(pwent->pw_name, 251 KV_COMMAND, (char *)cmd_realpath, GET_ONE)) == NULL) { 252 (void) fprintf(stderr, "%s: ", cmd_realpath); 253 (void) fprintf(stderr, 254 gettext("can't get execution attributes\n")); 255 return (0); 256 } 257 if ((value = kva_match(exec->attr, EXECATTR_UID_KW)) != NULL) { 258 euid = uid = get_uid(value); 259 event->adt_prof_cmd.proc_euid = uid; 260 event->adt_prof_cmd.proc_ruid = uid; 261 } 262 if ((value = kva_match(exec->attr, EXECATTR_GID_KW)) != NULL) { 263 egid = gid = get_gid(value); 264 event->adt_prof_cmd.proc_egid = gid; 265 event->adt_prof_cmd.proc_rgid = gid; 266 } 267 if ((value = kva_match(exec->attr, EXECATTR_EUID_KW)) != NULL) { 268 event->adt_prof_cmd.proc_euid = euid = get_uid(value); 269 } 270 if ((value = kva_match(exec->attr, EXECATTR_EGID_KW)) != NULL) { 271 event->adt_prof_cmd.proc_egid = egid = get_gid(value); 272 } 273 if ((value = kva_match(exec->attr, EXECATTR_LPRIV_KW)) != NULL) { 274 lset = get_privset(value); 275 event->adt_prof_cmd.limit_set = lset; 276 } 277 if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) != NULL) { 278 iset = get_privset(value); 279 event->adt_prof_cmd.inherit_set = iset; 280 } 281 if (euid == uid || iset != NULL) { 282 sanitize_environ(); 283 } 284 285 /* Finish audit info */ 286 event->adt_prof_cmd.cmdpath = cmd_realpath; 287 event->adt_prof_cmd.argc = argc - 1; 288 event->adt_prof_cmd.argv = &argv[1]; 289 event->adt_prof_cmd.envp = environ; 290 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { 291 perror("pfexec: adt_put_event"); 292 return (0); 293 } 294 adt_free_event(event); 295 (void) adt_end_session(ah); 296 297 set_attrs: 298 /* 299 * Set gids/uids and privileges. 300 * 301 */ 302 if ((gid != (gid_t)-1) || (egid != (gid_t)-1)) { 303 if ((setregid(gid, egid) == -1)) { 304 (void) fprintf(stderr, "%s: ", cmd_realpath); 305 (void) fprintf(stderr, gettext("can't set gid\n")); 306 return (0); 307 } 308 } 309 if (lset != NULL && setppriv(PRIV_SET, PRIV_LIMIT, lset) != 0 || 310 iset != NULL && setppriv(PRIV_ON, PRIV_INHERITABLE, iset) != 0) { 311 (void) fprintf(stderr, gettext("%s: can't set privileges\n"), 312 cmd_realpath); 313 return (0); 314 } 315 if (setreuid(uid, euid) == -1) { 316 (void) fprintf(stderr, "%s: ", cmd_realpath); 317 (void) fprintf(stderr, gettext("can't set uid\n")); 318 return (0); 319 } 320 if (iset != NULL && getppriv(PRIV_INHERITABLE, iset) == 0) 321 (void) setppriv(PRIV_SET, PRIV_PERMITTED, iset); 322 323 free_execattr(exec); 324 325 return (1); 326 } 327 328 329 /* 330 * cleans up environ. code from su.c 331 */ 332 static void 333 sanitize_environ() 334 { 335 char **pp = environ; 336 char **qq, *p; 337 338 while ((p = *pp) != NULL) { 339 if (*p == 'L' && p[1] == 'D' && p[2] == '_') { 340 for (qq = pp; (*qq = qq[1]) != NULL; qq++) { 341 ; 342 } 343 } else { 344 pp++; 345 } 346 } 347 } 348 349 350 static uid_t 351 get_uid(char *value) 352 { 353 struct passwd *passwd_ent; 354 355 if ((passwd_ent = getpwnam(value)) != NULL) 356 return (passwd_ent->pw_uid); 357 358 if (isnumber(value)) 359 return (atoi(value)); 360 361 (void) fprintf(stderr, "pfexec: %s: ", value); 362 (void) fprintf(stderr, gettext("can't get user entry\n")); 363 exit(EXIT_FAILURE); 364 /*NOTREACHED*/ 365 } 366 367 368 static uid_t 369 get_gid(char *value) 370 { 371 struct group *group_ent; 372 373 if ((group_ent = getgrnam(value)) != NULL) 374 return (group_ent->gr_gid); 375 376 if (isnumber(value)) 377 return (atoi(value)); 378 379 (void) fprintf(stderr, "pfexec: %s: ", value); 380 (void) fprintf(stderr, gettext("can't get group entry\n")); 381 exit(EXIT_FAILURE); 382 /*NOTREACHED*/ 383 } 384 385 386 static int 387 isnumber(char *s) 388 { 389 int c; 390 391 if (*s == '\0') 392 return (0); 393 394 while ((c = *s++) != '\0') { 395 if (!isdigit(c)) { 396 return (0); 397 } 398 } 399 400 return (1); 401 } 402 403 static priv_set_t * 404 get_privset(const char *s) 405 { 406 priv_set_t *res; 407 408 if ((res = priv_str_to_set(s, ",", NULL)) == NULL) { 409 (void) fprintf(stderr, "%s: bad privilege set\n", s); 410 exit(EXIT_FAILURE); 411 } 412 return (res); 413 } 414 415 static void 416 usage(void) 417 { 418 (void) fprintf(stderr, gettext("pfexec [-P privset] cmd [arg ..]\n")); 419 exit(EXIT_FAILURE); 420 } 421 422 423 /* 424 * This routine exists on failure and returns NULL if no granted privileges 425 * are set. 426 */ 427 static priv_set_t * 428 get_granted_privs(uid_t uid) 429 { 430 struct passwd *pwent; 431 userattr_t *ua; 432 char *profs; 433 priv_set_t *res; 434 char *profArray[MAXPROFS]; 435 int profcnt = 0; 436 437 res = priv_allocset(); 438 if (res == NULL) { 439 perror("priv_allocset"); 440 exit(EXIT_FAILURE); 441 } 442 443 priv_emptyset(res); 444 445 if ((pwent = getpwuid(uid)) == NULL) { 446 (void) fprintf(stderr, "%d: ", (int)uid); 447 (void) fprintf(stderr, gettext("can't get passwd entry\n")); 448 exit(EXIT_FAILURE); 449 } 450 451 ua = getusernam(pwent->pw_name); 452 453 if (ua != NULL && ua->attr != NULL && 454 (profs = kva_match(ua->attr, USERATTR_PROFILES_KW)) != NULL) { 455 get_profile_privs(profs, profArray, &profcnt, res); 456 free_proflist(profArray, profcnt); 457 } 458 459 get_default_privs(pwent->pw_name, res); 460 461 if (ua != NULL) 462 free_userattr(ua); 463 464 return (res); 465 } 466 467 static void 468 get_default_privs(const char *user, priv_set_t *pset) 469 { 470 char *profs = NULL; 471 char *profArray[MAXPROFS]; 472 int profcnt = 0; 473 474 if (_get_user_defs(user, NULL, &profs) == 0) { 475 /* get privileges from default profiles */ 476 if (profs != NULL) { 477 get_profile_privs(profs, profArray, &profcnt, pset); 478 free_proflist(profArray, profcnt); 479 _free_user_defs(NULL, profs); 480 } 481 } 482 } 483 484 static void 485 get_profile_privs(char *profiles, char **profArray, int *profcnt, 486 priv_set_t *pset) 487 { 488 489 char *prof; 490 char *lasts; 491 profattr_t *pa; 492 char *privs; 493 int i; 494 495 for (prof = strtok_r(profiles, PROFLIST_SEP, &lasts); 496 prof != NULL; 497 prof = strtok_r(NULL, PROFLIST_SEP, &lasts)) 498 getproflist(prof, profArray, profcnt); 499 500 /* get the privileges from list of profiles */ 501 for (i = 0; i < *profcnt; i++) { 502 503 if ((pa = getprofnam(profArray[i])) == NULL) { 504 /* 505 * this should never happen. 506 * unless the database has an undefined profile 507 */ 508 continue; 509 } 510 511 /* get privs from this profile */ 512 privs = kva_match(pa->attr, PROFATTR_PRIVS_KW); 513 if (privs != NULL) { 514 priv_set_t *tmp = priv_str_to_set(privs, ",", NULL); 515 if (tmp != NULL) { 516 priv_union(tmp, pset); 517 priv_freeset(tmp); 518 } 519 } 520 521 free_profattr(pa); 522 } 523 } 524 525 /* 526 * True if someone (user, group, other) can execute this file. 527 */ 528 #define S_ISEXEC(mode) (((mode)&(S_IXUSR|S_IXGRP|S_IXOTH)) != 0) 529 530 /* 531 * This function can return either the first argument or dynamically 532 * allocated memory. Reuse with care. 533 */ 534 static char * 535 pathsearch(char *cmd) 536 { 537 char *path, *dir, *result; 538 char buf[MAXPATHLEN]; 539 struct stat stbuf; 540 541 /* 542 * Implement shell like PATH searching; if the pathname contains 543 * one or more slashes, don't search the path, even if the '/' 544 * isn't the first character. (E.g., ./command or dir/command) 545 * No path equals to a search in ".", just like the shell. 546 */ 547 if (strchr(cmd, '/') != NULL) 548 return (cmd); 549 550 path = getenv("PATH"); 551 if (path == NULL) 552 return (cmd); 553 554 /* 555 * We need to copy $PATH because our sub processes may need it. 556 */ 557 path = strdup(path); 558 if (path == NULL) { 559 perror("pfexec: strdup $PATH"); 560 exit(EXIT_FAILURE); 561 } 562 563 result = NULL; 564 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) { 565 if (snprintf(buf, sizeof (buf), "%s/%s", dir, cmd) >= 566 sizeof (buf)) { 567 continue; 568 } 569 if (stat(buf, &stbuf) < 0) 570 continue; 571 /* 572 * Shells typically call access() with E_OK flag 573 * to determine if the effective uid can execute 574 * the file. We don't know what the eventual euid 575 * will be; it is determined by the exec_attr 576 * attributes which depend on the full pathname of 577 * the command. Therefore, we match the first regular 578 * file we find that is executable by someone. 579 */ 580 if (S_ISREG(stbuf.st_mode) && S_ISEXEC(stbuf.st_mode)) { 581 result = strdup(buf); 582 break; 583 } 584 } 585 free(path); 586 if (result == NULL) 587 (void) fprintf(stderr, gettext("%s: Command not found\n"), cmd); 588 return (result); 589 } 590