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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <errno.h> 30 #include <deflt.h> 31 #include <locale.h> 32 #include <sys/types.h> 33 #include <sys/param.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(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 perror(cmd); 168 exit(EXIT_FAILURE); 169 } 170 171 if ((cmd = pathsearch(cmd)) == NULL) 172 exit(EXIT_FAILURE); 173 174 if (getrealpath(cmd, cmd_realpath) == 0) 175 exit(EXIT_FAILURE); 176 177 if (checkattrs(cmd_realpath, argc, argv) == 0) 178 exit(EXIT_FAILURE); 179 180 (void) execv(cmd, cmdargs); 181 /* 182 * We'd be here only if execv fails. 183 */ 184 perror("pfexec"); 185 exit(EXIT_FAILURE); 186 /* LINTED */ 187 } 188 189 190 /* 191 * gets realpath for cmd. 192 * return 1 on success, 0 on failure. 193 */ 194 static int 195 getrealpath(const char *cmd, char *cmd_realpath) 196 { 197 if (realpath(cmd, cmd_realpath) == NULL) { 198 (void) fprintf(stderr, 199 gettext("pfexec: can't get real path of ``%s''\n"), cmd); 200 return (0); 201 } 202 return (1); 203 } 204 205 /* 206 * gets execution attributed for cmd, sets uids/gids, checks environ. 207 * returns 1 on success, 0 on failure. 208 */ 209 static int 210 checkattrs(char *cmd_realpath, int argc, char *argv[]) 211 { 212 char *value; 213 uid_t uid, euid; 214 gid_t gid = -1; 215 gid_t egid = -1; 216 struct passwd *pwent; 217 execattr_t *exec; 218 priv_set_t *lset = NULL; 219 priv_set_t *iset = NULL; 220 221 adt_session_data_t *ah; /* audit session handle */ 222 adt_event_data_t *event; /* event to be generated */ 223 char cwd[MAXPATHLEN]; 224 225 uid = euid = getuid(); 226 if ((pwent = getpwuid(uid)) == NULL) { 227 (void) fprintf(stderr, "%d: ", (int)uid); 228 (void) fprintf(stderr, gettext("can't get passwd entry\n")); 229 return (0); 230 } 231 /* Set up to audit use */ 232 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) { 233 perror("pfexec: adt_start_session"); 234 return (0); 235 } 236 if ((event = adt_alloc_event(ah, ADT_prof_cmd)) == NULL) { 237 perror("pfexec: adt_alloc_event"); 238 return (0); 239 } 240 if ((event->adt_prof_cmd.cwdpath = getcwd(cwd, sizeof (cwd))) == NULL) { 241 (void) fprintf(stderr, gettext("pfexec: can't add cwd path\n")); 242 return (0); 243 } 244 /* 245 * Get the exec attrs: uid, gid, euid and egid 246 */ 247 if ((exec = getexecuser(pwent->pw_name, 248 KV_COMMAND, (char *)cmd_realpath, GET_ONE)) == NULL) { 249 (void) fprintf(stderr, "%s: ", cmd_realpath); 250 (void) fprintf(stderr, 251 gettext("can't get execution attributes\n")); 252 return (0); 253 } 254 if ((value = kva_match(exec->attr, EXECATTR_UID_KW)) != NULL) { 255 euid = uid = get_uid(value); 256 event->adt_prof_cmd.proc_euid = uid; 257 event->adt_prof_cmd.proc_ruid = uid; 258 } 259 if ((value = kva_match(exec->attr, EXECATTR_GID_KW)) != NULL) { 260 egid = gid = get_gid(value); 261 event->adt_prof_cmd.proc_egid = gid; 262 event->adt_prof_cmd.proc_rgid = gid; 263 } 264 if ((value = kva_match(exec->attr, EXECATTR_EUID_KW)) != NULL) { 265 event->adt_prof_cmd.proc_euid = euid = get_uid(value); 266 } 267 if ((value = kva_match(exec->attr, EXECATTR_EGID_KW)) != NULL) { 268 event->adt_prof_cmd.proc_egid = egid = get_gid(value); 269 } 270 if ((value = kva_match(exec->attr, EXECATTR_LPRIV_KW)) != NULL) { 271 lset = get_privset(value); 272 event->adt_prof_cmd.limit_set = lset; 273 } 274 if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) != NULL) { 275 iset = get_privset(value); 276 event->adt_prof_cmd.inherit_set = iset; 277 } 278 if (euid == uid || iset != NULL) { 279 sanitize_environ(); 280 } 281 282 /* Finish audit info */ 283 event->adt_prof_cmd.cmdpath = cmd_realpath; 284 event->adt_prof_cmd.argc = argc - 1; 285 event->adt_prof_cmd.argv = &argv[1]; 286 event->adt_prof_cmd.envp = environ; 287 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { 288 perror("pfexec: adt_put_event"); 289 return (0); 290 } 291 adt_free_event(event); 292 (void) adt_end_session(ah); 293 294 set_attrs: 295 /* 296 * Set gids/uids and privileges. 297 * 298 */ 299 if ((gid != -1) || (egid != -1)) { 300 if ((setregid(gid, egid) == -1)) { 301 (void) fprintf(stderr, "%s: ", cmd_realpath); 302 (void) fprintf(stderr, gettext("can't set gid\n")); 303 return (0); 304 } 305 } 306 if (lset != NULL && setppriv(PRIV_SET, PRIV_LIMIT, lset) != 0 || 307 iset != NULL && setppriv(PRIV_ON, PRIV_INHERITABLE, iset) != 0) { 308 (void) fprintf(stderr, gettext("%s: can't set privileges\n"), 309 cmd_realpath); 310 return (0); 311 } 312 if (setreuid(uid, euid) == -1) { 313 (void) fprintf(stderr, "%s: ", cmd_realpath); 314 (void) fprintf(stderr, gettext("can't set uid\n")); 315 return (0); 316 } 317 if (iset != NULL && getppriv(PRIV_INHERITABLE, iset) == 0) 318 (void) setppriv(PRIV_SET, PRIV_PERMITTED, iset); 319 320 free_execattr(exec); 321 322 return (1); 323 } 324 325 326 /* 327 * cleans up environ. code from su.c 328 */ 329 static void 330 sanitize_environ() 331 { 332 char **pp = environ; 333 char **qq, *p; 334 335 while ((p = *pp) != NULL) { 336 if (*p == 'L' && p[1] == 'D' && p[2] == '_') { 337 for (qq = pp; (*qq = qq[1]) != NULL; qq++) { 338 ; 339 } 340 } else { 341 pp++; 342 } 343 } 344 } 345 346 347 static uid_t 348 get_uid(char *value) 349 { 350 struct passwd *passwd_ent; 351 352 if ((passwd_ent = getpwnam(value)) != NULL) 353 return (passwd_ent->pw_uid); 354 355 if (isnumber(value)) 356 return (atoi(value)); 357 358 (void) fprintf(stderr, "pfexec: %s: ", value); 359 (void) fprintf(stderr, gettext("can't get user entry\n")); 360 exit(EXIT_FAILURE); 361 /*NOTREACHED*/ 362 } 363 364 365 static uid_t 366 get_gid(char *value) 367 { 368 struct group *group_ent; 369 370 if ((group_ent = getgrnam(value)) != NULL) 371 return (group_ent->gr_gid); 372 373 if (isnumber(value)) 374 return (atoi(value)); 375 376 (void) fprintf(stderr, "pfexec: %s: ", value); 377 (void) fprintf(stderr, gettext("can't get group entry\n")); 378 exit(EXIT_FAILURE); 379 /*NOTREACHED*/ 380 } 381 382 383 static int 384 isnumber(char *s) 385 { 386 int c; 387 388 if (*s == '\0') 389 return (0); 390 391 while ((c = *s++) != '\0') { 392 if (!isdigit(c)) { 393 return (0); 394 } 395 } 396 397 return (1); 398 } 399 400 static priv_set_t * 401 get_privset(const char *s) 402 { 403 priv_set_t *res; 404 405 if ((res = priv_str_to_set(s, ",", NULL)) == NULL) { 406 (void) fprintf(stderr, "%s: bad privilege set\n", s); 407 exit(EXIT_FAILURE); 408 } 409 return (res); 410 } 411 412 static void 413 usage(void) 414 { 415 (void) fprintf(stderr, gettext("pfexec [-P privset] cmd [arg ..]\n")); 416 exit(EXIT_FAILURE); 417 } 418 419 420 /* 421 * This routine exists on failure and returns NULL if no granted privileges 422 * are set. 423 */ 424 static priv_set_t * 425 get_granted_privs(uid_t uid) 426 { 427 struct passwd *pwent; 428 userattr_t *ua; 429 char *profs; 430 priv_set_t *res; 431 char *profArray[MAXPROFS]; 432 int profcnt = 0; 433 434 res = priv_allocset(); 435 if (res == NULL) { 436 perror("priv_allocset"); 437 exit(EXIT_FAILURE); 438 } 439 440 priv_emptyset(res); 441 442 if ((pwent = getpwuid(uid)) == NULL) { 443 (void) fprintf(stderr, "%d: ", (int)uid); 444 (void) fprintf(stderr, gettext("can't get passwd entry\n")); 445 exit(EXIT_FAILURE); 446 } 447 448 ua = getusernam(pwent->pw_name); 449 450 if (ua != NULL && ua->attr != NULL && 451 (profs = kva_match(ua->attr, USERATTR_PROFILES_KW)) != NULL) { 452 get_profile_privs(profs, profArray, &profcnt, res); 453 free_proflist(profArray, profcnt); 454 } 455 456 get_default_privs(res); 457 458 if (ua != NULL) 459 free_userattr(ua); 460 461 return (res); 462 } 463 464 static void 465 get_default_privs(priv_set_t *pset) 466 { 467 char *profs = NULL; 468 char *profArray[MAXPROFS]; 469 int profcnt = 0; 470 471 if (defopen(AUTH_POLICY) == 0) { 472 /* get privileges from default profiles */ 473 profs = defread(DEF_PROF); 474 if (profs != NULL) { 475 get_profile_privs(profs, profArray, &profcnt, pset); 476 free_proflist(profArray, profcnt); 477 } 478 } 479 (void) defopen(NULL); 480 } 481 482 static void 483 get_profile_privs(char *profiles, char **profArray, int *profcnt, 484 priv_set_t *pset) 485 { 486 487 char *prof; 488 char *lasts; 489 profattr_t *pa; 490 char *privs; 491 int i; 492 493 for (prof = strtok_r(profiles, PROFLIST_SEP, &lasts); 494 prof != NULL; 495 prof = strtok_r(NULL, PROFLIST_SEP, &lasts)) 496 getproflist(prof, profArray, profcnt); 497 498 /* get the privileges from list of profiles */ 499 for (i = 0; i < *profcnt; i++) { 500 501 if ((pa = getprofnam(profArray[i])) == NULL) { 502 /* 503 * this should never happen. 504 * unless the database has an undefined profile 505 */ 506 continue; 507 } 508 509 /* get privs from this profile */ 510 privs = kva_match(pa->attr, PROFATTR_PRIVS_KW); 511 if (privs != NULL) { 512 priv_set_t *tmp = priv_str_to_set(privs, ",", NULL); 513 if (tmp != NULL) { 514 priv_union(tmp, pset); 515 priv_freeset(tmp); 516 } 517 } 518 519 free_profattr(pa); 520 } 521 } 522 523 /* 524 * This function can return either the first argument or dynamically 525 * allocated memory. Reuse with care. 526 */ 527 static char * 528 pathsearch(char *cmd) 529 { 530 char *path, *dir; 531 char buf[MAXPATHLEN]; 532 533 /* 534 * Implement shell like PATH searching; if the pathname contains 535 * one or more slashes, don't search the path, even if the '/' 536 * isn't the first character. (E.g., ./command or dir/command) 537 * No path equals to a search in ".", just like the shell. 538 */ 539 if (strchr(cmd, '/') != NULL) 540 return (cmd); 541 542 path = getenv("PATH"); 543 if (path == NULL) 544 return (cmd); 545 546 /* 547 * We need to copy $PATH because our sub processes may need it. 548 */ 549 path = strdup(path); 550 if (path == NULL) { 551 perror("pfexec: strdup $PATH"); 552 exit(EXIT_FAILURE); 553 } 554 555 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) { 556 if (snprintf(buf, sizeof (buf), "%s/%s", dir, cmd) >= 557 sizeof (buf)) { 558 continue; 559 } 560 if (access(buf, X_OK) == 0) { 561 free(path); 562 return (strdup(buf)); 563 } 564 } 565 free(path); 566 (void) fprintf(stderr, gettext("%s: Command not found\n"), cmd); 567 return (NULL); 568 } 569