17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 23*0a1278f2SGary Mills * Copyright (c) 2013 Gary Mills 24*0a1278f2SGary Mills * 257c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 267c478bd9Sstevel@tonic-gate * Use is subject to license terms. 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/task.h> 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include <alloca.h> 337c478bd9Sstevel@tonic-gate #include <libproc.h> 347c478bd9Sstevel@tonic-gate #include <libintl.h> 357c478bd9Sstevel@tonic-gate #include <libgen.h> 367c478bd9Sstevel@tonic-gate #include <limits.h> 377c478bd9Sstevel@tonic-gate #include <project.h> 387c478bd9Sstevel@tonic-gate #include <pwd.h> 397c478bd9Sstevel@tonic-gate #include <secdb.h> 407c478bd9Sstevel@tonic-gate #include <stdio.h> 417c478bd9Sstevel@tonic-gate #include <stdlib.h> 427c478bd9Sstevel@tonic-gate #include <string.h> 437c478bd9Sstevel@tonic-gate #include <sys/varargs.h> 447c478bd9Sstevel@tonic-gate #include <unistd.h> 457c478bd9Sstevel@tonic-gate #include <errno.h> 467c478bd9Sstevel@tonic-gate #include <signal.h> 477c478bd9Sstevel@tonic-gate #include <priv_utils.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include "utils.h" 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #define OPTIONS_STRING "Fc:lp:v" 527c478bd9Sstevel@tonic-gate #define NENV 8 537c478bd9Sstevel@tonic-gate #define ENVSIZE 255 547c478bd9Sstevel@tonic-gate #define PATH "PATH=/usr/bin" 557c478bd9Sstevel@tonic-gate #define SUPATH "PATH=/usr/sbin:/usr/bin" 567c478bd9Sstevel@tonic-gate #define SHELL "/usr/bin/sh" 577c478bd9Sstevel@tonic-gate #define SHELL2 "/sbin/sh" 587c478bd9Sstevel@tonic-gate #define TIMEZONEFILE "/etc/default/init" 597c478bd9Sstevel@tonic-gate #define LOGINFILE "/etc/default/login" 607c478bd9Sstevel@tonic-gate #define GLOBAL_ERR_SZ 1024 617c478bd9Sstevel@tonic-gate #define GRAB_RETRY_MAX 100 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate static const char *pname; 647c478bd9Sstevel@tonic-gate extern char **environ; 657c478bd9Sstevel@tonic-gate static char *supath = SUPATH; 667c478bd9Sstevel@tonic-gate static char *path = PATH; 677c478bd9Sstevel@tonic-gate static char global_error[GLOBAL_ERR_SZ]; 687c478bd9Sstevel@tonic-gate static int verbose = 0; 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate static priv_set_t *nset; 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate /* Private definitions for libproject */ 737c478bd9Sstevel@tonic-gate extern projid_t setproject_proc(const char *, const char *, int, pid_t, 747c478bd9Sstevel@tonic-gate struct ps_prochandle *, struct project *); 757c478bd9Sstevel@tonic-gate extern priv_set_t *setproject_initpriv(void); 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate static void usage(void); 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate static void preserve_error(const char *format, ...); 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate static int update_running_proc(int, char *, char *); 827c478bd9Sstevel@tonic-gate static int set_ids(struct ps_prochandle *, struct project *, 837c478bd9Sstevel@tonic-gate struct passwd *); 847c478bd9Sstevel@tonic-gate static struct passwd *match_user(uid_t, char *, int); 857c478bd9Sstevel@tonic-gate static void setproject_err(char *, char *, int, struct project *); 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate static void 887c478bd9Sstevel@tonic-gate usage(void) 897c478bd9Sstevel@tonic-gate { 907c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("usage: \n\t%s [-v] [-p project] " 917c478bd9Sstevel@tonic-gate "[-c pid | [-Fl] [command [args ...]]]\n"), pname); 927c478bd9Sstevel@tonic-gate exit(2); 937c478bd9Sstevel@tonic-gate } 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate int 967c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 977c478bd9Sstevel@tonic-gate { 987c478bd9Sstevel@tonic-gate int c; 997c478bd9Sstevel@tonic-gate struct passwd *pw; 1007c478bd9Sstevel@tonic-gate char *projname = NULL; 1017c478bd9Sstevel@tonic-gate uid_t uid; 1027c478bd9Sstevel@tonic-gate int login_flag = 0; 1037c478bd9Sstevel@tonic-gate int finalize_flag = TASK_NORMAL; 1047c478bd9Sstevel@tonic-gate int newproj_flag = 0; 1057c478bd9Sstevel@tonic-gate taskid_t taskid; 1067c478bd9Sstevel@tonic-gate char *shell; 1077c478bd9Sstevel@tonic-gate char *env[NENV]; 1087c478bd9Sstevel@tonic-gate char **targs; 1097c478bd9Sstevel@tonic-gate char *filename, *procname = NULL; 1107c478bd9Sstevel@tonic-gate int error; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate nset = setproject_initpriv(); 1137c478bd9Sstevel@tonic-gate if (nset == NULL) 1147c478bd9Sstevel@tonic-gate die(gettext("privilege initialization failed\n")); 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate pname = getpname(argv[0]); 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, OPTIONS_STRING)) != EOF) { 1197c478bd9Sstevel@tonic-gate switch (c) { 1207c478bd9Sstevel@tonic-gate case 'v': 1217c478bd9Sstevel@tonic-gate verbose = 1; 1227c478bd9Sstevel@tonic-gate break; 1237c478bd9Sstevel@tonic-gate case 'p': 1247c478bd9Sstevel@tonic-gate newproj_flag = 1; 1257c478bd9Sstevel@tonic-gate projname = optarg; 1267c478bd9Sstevel@tonic-gate break; 1277c478bd9Sstevel@tonic-gate case 'F': 1287c478bd9Sstevel@tonic-gate finalize_flag = TASK_FINAL; 1297c478bd9Sstevel@tonic-gate break; 1307c478bd9Sstevel@tonic-gate case 'l': 1317c478bd9Sstevel@tonic-gate login_flag++; 1327c478bd9Sstevel@tonic-gate break; 1337c478bd9Sstevel@tonic-gate case 'c': 1347c478bd9Sstevel@tonic-gate procname = optarg; 1357c478bd9Sstevel@tonic-gate break; 1367c478bd9Sstevel@tonic-gate case '?': 1377c478bd9Sstevel@tonic-gate default: 1387c478bd9Sstevel@tonic-gate usage(); 1397c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 1407c478bd9Sstevel@tonic-gate } 1417c478bd9Sstevel@tonic-gate } 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate /* -c option is invalid with -F, -l, or a specified command */ 1447c478bd9Sstevel@tonic-gate if ((procname != NULL) && 1457c478bd9Sstevel@tonic-gate (finalize_flag == TASK_FINAL || login_flag || optind < argc)) 1467c478bd9Sstevel@tonic-gate usage(); 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate if (procname != NULL) { 1497c478bd9Sstevel@tonic-gate /* Change project/task of an existing process */ 1507c478bd9Sstevel@tonic-gate return (update_running_proc(newproj_flag, procname, projname)); 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate /* 1547c478bd9Sstevel@tonic-gate * Get user data, so that we can confirm project membership as 1557c478bd9Sstevel@tonic-gate * well as construct an appropriate login environment. 1567c478bd9Sstevel@tonic-gate */ 1577c478bd9Sstevel@tonic-gate uid = getuid(); 1587c478bd9Sstevel@tonic-gate if ((pw = match_user(uid, projname, 1)) == NULL) { 1597c478bd9Sstevel@tonic-gate die("%s\n", global_error); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate /* 1637c478bd9Sstevel@tonic-gate * If no projname was specified, we're just creating a new task 1647c478bd9Sstevel@tonic-gate * under the current project, so we can just set the new taskid. 1657c478bd9Sstevel@tonic-gate * If our project is changing, we need to update any attendant 1667c478bd9Sstevel@tonic-gate * pool/rctl bindings, so let setproject() do the dirty work. 1677c478bd9Sstevel@tonic-gate */ 1687c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 1697c478bd9Sstevel@tonic-gate if (projname == NULL) { 1707c478bd9Sstevel@tonic-gate if (settaskid(getprojid(), finalize_flag) == -1) 1717c478bd9Sstevel@tonic-gate if (errno == EAGAIN) 1727c478bd9Sstevel@tonic-gate die(gettext("resource control limit has been " 1737c478bd9Sstevel@tonic-gate "reached")); 1747c478bd9Sstevel@tonic-gate else 1757c478bd9Sstevel@tonic-gate die(gettext("settaskid failed")); 1767c478bd9Sstevel@tonic-gate } else { 1777c478bd9Sstevel@tonic-gate if ((error = setproject(projname, 1787c478bd9Sstevel@tonic-gate pw->pw_name, finalize_flag)) != 0) { 1797c478bd9Sstevel@tonic-gate setproject_err(pw->pw_name, projname, error, NULL); 1807c478bd9Sstevel@tonic-gate if (error < 0) 1817c478bd9Sstevel@tonic-gate die("%s\n", global_error); 1827c478bd9Sstevel@tonic-gate else 1837c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate } 1867c478bd9Sstevel@tonic-gate __priv_relinquish(); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate taskid = gettaskid(); 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate if (verbose) 1917c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d\n", (int)taskid); 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate /* 1947c478bd9Sstevel@tonic-gate * Validate user's shell from passwd database. 1957c478bd9Sstevel@tonic-gate */ 1967c478bd9Sstevel@tonic-gate if (strcmp(pw->pw_shell, "") == 0) { 1977c478bd9Sstevel@tonic-gate if (access(SHELL, X_OK) == 0) 1987c478bd9Sstevel@tonic-gate pw->pw_shell = SHELL; 1997c478bd9Sstevel@tonic-gate else 2007c478bd9Sstevel@tonic-gate pw->pw_shell = SHELL2; 2017c478bd9Sstevel@tonic-gate } 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate if (login_flag) { 2047c478bd9Sstevel@tonic-gate /* 2057c478bd9Sstevel@tonic-gate * Since we've been invoked as a "simulated login", set up the 2067c478bd9Sstevel@tonic-gate * environment. 2077c478bd9Sstevel@tonic-gate */ 2087c478bd9Sstevel@tonic-gate char *cur_tz = getenv("TZ"); 2097c478bd9Sstevel@tonic-gate char *cur_term = getenv("TERM"); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate char **envnext; 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate size_t len_home = strlen(pw->pw_dir) + strlen("HOME=") + 1; 2147c478bd9Sstevel@tonic-gate size_t len_logname = strlen(pw->pw_name) + strlen("LOGNAME=") + 2157c478bd9Sstevel@tonic-gate 1; 2167c478bd9Sstevel@tonic-gate size_t len_shell = strlen(pw->pw_shell) + strlen("SHELL=") + 1; 2177c478bd9Sstevel@tonic-gate size_t len_mail = strlen(pw->pw_name) + 2187c478bd9Sstevel@tonic-gate strlen("MAIL=/var/mail/") + 1; 2197c478bd9Sstevel@tonic-gate size_t len_tz; 2207c478bd9Sstevel@tonic-gate size_t len_term; 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate char *env_home = safe_malloc(len_home); 2237c478bd9Sstevel@tonic-gate char *env_logname = safe_malloc(len_logname); 2247c478bd9Sstevel@tonic-gate char *env_shell = safe_malloc(len_shell); 2257c478bd9Sstevel@tonic-gate char *env_mail = safe_malloc(len_mail); 2267c478bd9Sstevel@tonic-gate char *env_tz; 2277c478bd9Sstevel@tonic-gate char *env_term; 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate (void) snprintf(env_home, len_home, "HOME=%s", pw->pw_dir); 2307c478bd9Sstevel@tonic-gate (void) snprintf(env_logname, len_logname, "LOGNAME=%s", 2317c478bd9Sstevel@tonic-gate pw->pw_name); 2327c478bd9Sstevel@tonic-gate (void) snprintf(env_shell, len_shell, "SHELL=%s", pw->pw_shell); 2337c478bd9Sstevel@tonic-gate (void) snprintf(env_mail, len_mail, "MAIL=/var/mail/%s", 2347c478bd9Sstevel@tonic-gate pw->pw_name); 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate env[0] = env_home; 2377c478bd9Sstevel@tonic-gate env[1] = env_logname; 2387c478bd9Sstevel@tonic-gate env[2] = (pw->pw_uid == 0 ? supath : path); 2397c478bd9Sstevel@tonic-gate env[3] = env_shell; 2407c478bd9Sstevel@tonic-gate env[4] = env_mail; 2417c478bd9Sstevel@tonic-gate env[5] = NULL; 2427c478bd9Sstevel@tonic-gate env[6] = NULL; 2437c478bd9Sstevel@tonic-gate env[7] = NULL; 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate envnext = (char **)&env[5]; 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate /* 2487c478bd9Sstevel@tonic-gate * It's possible that TERM wasn't defined in the outer 2497c478bd9Sstevel@tonic-gate * environment. 2507c478bd9Sstevel@tonic-gate */ 2517c478bd9Sstevel@tonic-gate if (cur_term != NULL) { 2527c478bd9Sstevel@tonic-gate len_term = strlen(cur_term) + strlen("TERM=") + 1; 2537c478bd9Sstevel@tonic-gate env_term = safe_malloc(len_term); 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate (void) snprintf(env_term, len_term, "TERM=%s", 2567c478bd9Sstevel@tonic-gate cur_term); 2577c478bd9Sstevel@tonic-gate *envnext = env_term; 2587c478bd9Sstevel@tonic-gate envnext++; 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate /* 2627c478bd9Sstevel@tonic-gate * It is also possible that TZ wasn't defined in the outer 2637c478bd9Sstevel@tonic-gate * environment. In that case, we must attempt to open the file 2647c478bd9Sstevel@tonic-gate * defining the default timezone and select the appropriate 2657c478bd9Sstevel@tonic-gate * entry. If there is no default timezone there, try 2667c478bd9Sstevel@tonic-gate * TIMEZONE in /etc/default/login, duplicating the algorithm 2677c478bd9Sstevel@tonic-gate * that login uses. 2687c478bd9Sstevel@tonic-gate */ 2697c478bd9Sstevel@tonic-gate if (cur_tz != NULL) { 2707c478bd9Sstevel@tonic-gate len_tz = strlen(cur_tz) + strlen("TZ=") + 1; 2717c478bd9Sstevel@tonic-gate env_tz = safe_malloc(len_tz); 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate (void) snprintf(env_tz, len_tz, "TZ=%s", cur_tz); 2747c478bd9Sstevel@tonic-gate *envnext = env_tz; 2757c478bd9Sstevel@tonic-gate } else { 2767c478bd9Sstevel@tonic-gate if ((env_tz = getdefault(TIMEZONEFILE, "TZ=", 2777c478bd9Sstevel@tonic-gate "TZ=")) != NULL) 2787c478bd9Sstevel@tonic-gate *envnext = env_tz; 2797c478bd9Sstevel@tonic-gate else { 2807c478bd9Sstevel@tonic-gate env_tz = getdefault(LOGINFILE, "TIMEZONE=", 2817c478bd9Sstevel@tonic-gate "TZ="); 2827c478bd9Sstevel@tonic-gate *envnext = env_tz; 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate } 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate environ = (char **)&env[0]; 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate /* 2897c478bd9Sstevel@tonic-gate * Prefix the shell string with a hyphen, indicating a login 2907c478bd9Sstevel@tonic-gate * shell. 2917c478bd9Sstevel@tonic-gate */ 2927c478bd9Sstevel@tonic-gate shell = safe_malloc(PATH_MAX); 2937c478bd9Sstevel@tonic-gate (void) snprintf(shell, PATH_MAX, "-%s", basename(pw->pw_shell)); 2947c478bd9Sstevel@tonic-gate } else { 2957c478bd9Sstevel@tonic-gate shell = basename(pw->pw_shell); 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate /* 2997c478bd9Sstevel@tonic-gate * If there are no arguments, we launch the user's shell; otherwise, the 3007c478bd9Sstevel@tonic-gate * remaining commands are assumed to form a valid command invocation 3017c478bd9Sstevel@tonic-gate * that we can exec. 3027c478bd9Sstevel@tonic-gate */ 3037c478bd9Sstevel@tonic-gate if (optind >= argc) { 3047c478bd9Sstevel@tonic-gate targs = alloca(2 * sizeof (char *)); 3057c478bd9Sstevel@tonic-gate filename = pw->pw_shell; 3067c478bd9Sstevel@tonic-gate targs[0] = shell; 3077c478bd9Sstevel@tonic-gate targs[1] = NULL; 3087c478bd9Sstevel@tonic-gate } else { 3097c478bd9Sstevel@tonic-gate targs = &argv[optind]; 3107c478bd9Sstevel@tonic-gate filename = targs[0]; 3117c478bd9Sstevel@tonic-gate } 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate if (execvp(filename, targs) == -1) 3147c478bd9Sstevel@tonic-gate die(gettext("exec of %s failed"), targs[0]); 3157c478bd9Sstevel@tonic-gate 3167c478bd9Sstevel@tonic-gate /* 3177c478bd9Sstevel@tonic-gate * We should never get here. 3187c478bd9Sstevel@tonic-gate */ 3197c478bd9Sstevel@tonic-gate return (1); 3207c478bd9Sstevel@tonic-gate } 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate static int 3237c478bd9Sstevel@tonic-gate update_running_proc(int newproj_flag, char *procname, char *projname) 3247c478bd9Sstevel@tonic-gate { 3257c478bd9Sstevel@tonic-gate struct ps_prochandle *p; 3267c478bd9Sstevel@tonic-gate prcred_t original_prcred, current_prcred; 3277c478bd9Sstevel@tonic-gate projid_t prprojid; 3287c478bd9Sstevel@tonic-gate taskid_t taskid; 3297c478bd9Sstevel@tonic-gate int error = 0, gret; 3307c478bd9Sstevel@tonic-gate struct project project; 3317c478bd9Sstevel@tonic-gate char prbuf[PROJECT_BUFSZ]; 3327c478bd9Sstevel@tonic-gate struct passwd *passwd_entry; 3337c478bd9Sstevel@tonic-gate int grab_retry_count = 0; 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate /* 3367c478bd9Sstevel@tonic-gate * Catch signals from terminal. There isn't much sense in 3377c478bd9Sstevel@tonic-gate * doing anything but ignoring them since we don't do anything 3387c478bd9Sstevel@tonic-gate * after the point we'd be capable of handling them again. 3397c478bd9Sstevel@tonic-gate */ 3407c478bd9Sstevel@tonic-gate (void) sigignore(SIGHUP); 3417c478bd9Sstevel@tonic-gate (void) sigignore(SIGINT); 3427c478bd9Sstevel@tonic-gate (void) sigignore(SIGQUIT); 3437c478bd9Sstevel@tonic-gate (void) sigignore(SIGTERM); 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* flush stdout before grabbing the proc to avoid deadlock */ 3467c478bd9Sstevel@tonic-gate (void) fflush(stdout); 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate /* 3497c478bd9Sstevel@tonic-gate * We need to grab the process, which will force it to stop execution 3507c478bd9Sstevel@tonic-gate * until the grab is released, in order to aquire some information about 3517c478bd9Sstevel@tonic-gate * it, such as its current project (which is achieved via an injected 3527c478bd9Sstevel@tonic-gate * system call and therefore needs an agent) and its credentials. We 3537c478bd9Sstevel@tonic-gate * will then need to release it again because it may be a process that 3547c478bd9Sstevel@tonic-gate * we rely on for later calls, for example nscd. 3557c478bd9Sstevel@tonic-gate */ 3567c478bd9Sstevel@tonic-gate if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) { 3577c478bd9Sstevel@tonic-gate warn(gettext("failed to grab for process %s: %s\n"), 3587c478bd9Sstevel@tonic-gate procname, Pgrab_error(gret)); 3597c478bd9Sstevel@tonic-gate return (1); 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate if (Pcreate_agent(p) != 0) { 3627c478bd9Sstevel@tonic-gate Prelease(p, 0); 3637c478bd9Sstevel@tonic-gate warn(gettext("cannot control process %s\n"), procname); 3647c478bd9Sstevel@tonic-gate return (1); 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * The victim process is now held. Do not call any functions 3697c478bd9Sstevel@tonic-gate * which generate stdout/stderr until the process has been 3707c478bd9Sstevel@tonic-gate * released. 3717c478bd9Sstevel@tonic-gate */ 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate /* 3747c478bd9Sstevel@tonic-gate * The target process will soon be restarted (in case it is in newtask's 3757c478bd9Sstevel@tonic-gate * execution path) and then stopped again. We need to ensure that our cached 3767c478bd9Sstevel@tonic-gate * data doesn't change while the process runs so return here if the target 3777c478bd9Sstevel@tonic-gate * process changes its user id in between our stop operations, so that we can 3787c478bd9Sstevel@tonic-gate * try again. 3797c478bd9Sstevel@tonic-gate */ 3807c478bd9Sstevel@tonic-gate pgrab_retry: 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate /* Cache required information about the process. */ 3837c478bd9Sstevel@tonic-gate if (Pcred(p, &original_prcred, 0) != 0) { 3847c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot get process credentials %s\n"), 3857c478bd9Sstevel@tonic-gate procname); 3867c478bd9Sstevel@tonic-gate error = 1; 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate if ((prprojid = pr_getprojid(p)) == -1) { 3897c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot get process project id %s\n"), 3907c478bd9Sstevel@tonic-gate procname); 3917c478bd9Sstevel@tonic-gate error = 1; 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate /* 3957c478bd9Sstevel@tonic-gate * We now have all the required information, so release the target 3967c478bd9Sstevel@tonic-gate * process and perform our sanity checks. The process needs to be 3977c478bd9Sstevel@tonic-gate * running at this point because it may be in the execution path of the 3987c478bd9Sstevel@tonic-gate * calls made below. 3997c478bd9Sstevel@tonic-gate */ 4007c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 4017c478bd9Sstevel@tonic-gate Prelease(p, 0); 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate /* if our data acquisition failed, then we can't continue. */ 4047c478bd9Sstevel@tonic-gate if (error) { 4057c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 4067c478bd9Sstevel@tonic-gate return (1); 4077c478bd9Sstevel@tonic-gate } 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate if (newproj_flag == 0) { 4107c478bd9Sstevel@tonic-gate /* 4117c478bd9Sstevel@tonic-gate * Just changing the task, so set projname to the current 4127c478bd9Sstevel@tonic-gate * project of the running process. 4137c478bd9Sstevel@tonic-gate */ 4147c478bd9Sstevel@tonic-gate if (getprojbyid(prprojid, &project, &prbuf, 4157c478bd9Sstevel@tonic-gate PROJECT_BUFSZ) == NULL) { 4167c478bd9Sstevel@tonic-gate warn(gettext("unable to get project name " 4177c478bd9Sstevel@tonic-gate "for projid %d"), prprojid); 4187c478bd9Sstevel@tonic-gate return (1); 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate projname = project.pj_name; 4217c478bd9Sstevel@tonic-gate } else { 4227c478bd9Sstevel@tonic-gate /* 4237c478bd9Sstevel@tonic-gate * cache info for the project which user passed in via the 4247c478bd9Sstevel@tonic-gate * command line 4257c478bd9Sstevel@tonic-gate */ 4267c478bd9Sstevel@tonic-gate if (getprojbyname(projname, &project, &prbuf, 4277c478bd9Sstevel@tonic-gate PROJECT_BUFSZ) == NULL) { 4287c478bd9Sstevel@tonic-gate warn(gettext("unknown project \"%s\"\n"), projname); 4297c478bd9Sstevel@tonic-gate return (1); 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate } 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate /* 4347c478bd9Sstevel@tonic-gate * Use our cached information to verify that the owner of the running 4357c478bd9Sstevel@tonic-gate * process is a member of proj 4367c478bd9Sstevel@tonic-gate */ 4377c478bd9Sstevel@tonic-gate if ((passwd_entry = match_user(original_prcred.pr_ruid, 4387c478bd9Sstevel@tonic-gate projname, 0)) == NULL) { 4397c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 4407c478bd9Sstevel@tonic-gate return (1); 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate /* 4447c478bd9Sstevel@tonic-gate * We can now safely stop the process again in order to change the 4457c478bd9Sstevel@tonic-gate * project and taskid as required. 4467c478bd9Sstevel@tonic-gate */ 4477c478bd9Sstevel@tonic-gate if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) { 4487c478bd9Sstevel@tonic-gate warn(gettext("failed to grab for process %s: %s\n"), 4497c478bd9Sstevel@tonic-gate procname, Pgrab_error(gret)); 4507c478bd9Sstevel@tonic-gate return (1); 4517c478bd9Sstevel@tonic-gate } 4527c478bd9Sstevel@tonic-gate if (Pcreate_agent(p) != 0) { 4537c478bd9Sstevel@tonic-gate Prelease(p, 0); 4547c478bd9Sstevel@tonic-gate warn(gettext("cannot control process %s\n"), procname); 4557c478bd9Sstevel@tonic-gate return (1); 4567c478bd9Sstevel@tonic-gate } 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate /* 4597c478bd9Sstevel@tonic-gate * Now that the target process is stopped, check the validity of our 4607c478bd9Sstevel@tonic-gate * cached info. If we aren't superuser then match_user() will have 4617c478bd9Sstevel@tonic-gate * checked to make sure that the owner of the process is in the relevant 4627c478bd9Sstevel@tonic-gate * project. If our ruid has changed, then match_user()'s conclusion may 4637c478bd9Sstevel@tonic-gate * be invalid. 4647c478bd9Sstevel@tonic-gate */ 4657c478bd9Sstevel@tonic-gate if (getuid() != 0) { 4667c478bd9Sstevel@tonic-gate if (Pcred(p, ¤t_prcred, 0) != 0) { 4677c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 4687c478bd9Sstevel@tonic-gate Prelease(p, 0); 4697c478bd9Sstevel@tonic-gate warn(gettext("can't get process credentials %s\n"), 4707c478bd9Sstevel@tonic-gate procname); 4717c478bd9Sstevel@tonic-gate return (1); 4727c478bd9Sstevel@tonic-gate } 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate if (original_prcred.pr_ruid != current_prcred.pr_ruid) { 4757c478bd9Sstevel@tonic-gate if (grab_retry_count++ < GRAB_RETRY_MAX) 4767c478bd9Sstevel@tonic-gate goto pgrab_retry; 4777c478bd9Sstevel@tonic-gate 4787c478bd9Sstevel@tonic-gate warn(gettext("process consistently changed its " 4797c478bd9Sstevel@tonic-gate "user id %s\n"), procname); 4807c478bd9Sstevel@tonic-gate return (1); 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate error = set_ids(p, &project, passwd_entry); 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate if (verbose) 4877c478bd9Sstevel@tonic-gate taskid = pr_gettaskid(p); 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 4907c478bd9Sstevel@tonic-gate Prelease(p, 0); 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate if (error) { 4937c478bd9Sstevel@tonic-gate /* 4947c478bd9Sstevel@tonic-gate * error is serious enough to stop, only if negative. 4957c478bd9Sstevel@tonic-gate * Otherwise, it simply indicates one of the resource 4967c478bd9Sstevel@tonic-gate * control assignments failed, which is worth warning 4977c478bd9Sstevel@tonic-gate * about. 4987c478bd9Sstevel@tonic-gate */ 4997c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 5007c478bd9Sstevel@tonic-gate if (error < 0) 5017c478bd9Sstevel@tonic-gate return (1); 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate if (verbose) 5057c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d\n", (int)taskid); 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate return (0); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate static int 5117c478bd9Sstevel@tonic-gate set_ids(struct ps_prochandle *p, struct project *project, 5127c478bd9Sstevel@tonic-gate struct passwd *passwd_entry) 5137c478bd9Sstevel@tonic-gate { 5147c478bd9Sstevel@tonic-gate int be_su = 0; 5157c478bd9Sstevel@tonic-gate prcred_t old_prcred; 5167c478bd9Sstevel@tonic-gate int error; 5177c478bd9Sstevel@tonic-gate prpriv_t *old_prpriv, *new_prpriv; 5187c478bd9Sstevel@tonic-gate size_t prsz = sizeof (prpriv_t); 5197c478bd9Sstevel@tonic-gate priv_set_t *eset, *pset; 5207c478bd9Sstevel@tonic-gate int ind; 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate if (Pcred(p, &old_prcred, 0) != 0) { 5237c478bd9Sstevel@tonic-gate preserve_error(gettext("can't get process credentials")); 5247c478bd9Sstevel@tonic-gate return (1); 5257c478bd9Sstevel@tonic-gate } 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate old_prpriv = proc_get_priv(Pstatus(p)->pr_pid); 5287c478bd9Sstevel@tonic-gate if (old_prpriv == NULL) { 5297c478bd9Sstevel@tonic-gate preserve_error(gettext("can't get process privileges")); 5307c478bd9Sstevel@tonic-gate return (1); 5317c478bd9Sstevel@tonic-gate } 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate prsz = PRIV_PRPRIV_SIZE(old_prpriv); 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate new_prpriv = malloc(prsz); 5367c478bd9Sstevel@tonic-gate if (new_prpriv == NULL) { 5377c478bd9Sstevel@tonic-gate preserve_error(gettext("can't allocate memory")); 5387c478bd9Sstevel@tonic-gate free(old_prpriv); 5397c478bd9Sstevel@tonic-gate return (1); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate (void) memcpy(new_prpriv, old_prpriv, prsz); 5437c478bd9Sstevel@tonic-gate 5447c478bd9Sstevel@tonic-gate /* 5457c478bd9Sstevel@tonic-gate * If the process already has the proc_taskid privilege, 5467c478bd9Sstevel@tonic-gate * we don't need to elevate its privileges; if it doesn't, 5477c478bd9Sstevel@tonic-gate * we try to do it here. 5487c478bd9Sstevel@tonic-gate * As we do not wish to leave a window in which the process runs 5497c478bd9Sstevel@tonic-gate * with elevated privileges, we make sure that the process dies 5507c478bd9Sstevel@tonic-gate * when we go away unexpectedly. 5517c478bd9Sstevel@tonic-gate */ 5527c478bd9Sstevel@tonic-gate 5537c478bd9Sstevel@tonic-gate ind = priv_getsetbyname(PRIV_EFFECTIVE); 5547c478bd9Sstevel@tonic-gate eset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind]; 5557c478bd9Sstevel@tonic-gate ind = priv_getsetbyname(PRIV_PERMITTED); 5567c478bd9Sstevel@tonic-gate pset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind]; 5577c478bd9Sstevel@tonic-gate 5587c478bd9Sstevel@tonic-gate if (!priv_issubset(nset, eset)) { 5597c478bd9Sstevel@tonic-gate be_su = 1; 5607c478bd9Sstevel@tonic-gate priv_union(nset, eset); 5617c478bd9Sstevel@tonic-gate priv_union(nset, pset); 5627c478bd9Sstevel@tonic-gate if (Psetflags(p, PR_KLC) != 0) { 5637c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot set process " 5647c478bd9Sstevel@tonic-gate "privileges")); 5657c478bd9Sstevel@tonic-gate (void) Punsetflags(p, PR_KLC); 5667c478bd9Sstevel@tonic-gate free(new_prpriv); 5677c478bd9Sstevel@tonic-gate free(old_prpriv); 5687c478bd9Sstevel@tonic-gate return (1); 5697c478bd9Sstevel@tonic-gate } 5707c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 5717c478bd9Sstevel@tonic-gate if (Psetpriv(p, new_prpriv) != 0) { 5727c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 5737c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot set process " 5747c478bd9Sstevel@tonic-gate "privileges")); 5757c478bd9Sstevel@tonic-gate (void) Punsetflags(p, PR_KLC); 5767c478bd9Sstevel@tonic-gate free(new_prpriv); 5777c478bd9Sstevel@tonic-gate free(old_prpriv); 5787c478bd9Sstevel@tonic-gate return (1); 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 5847c478bd9Sstevel@tonic-gate if ((error = setproject_proc(project->pj_name, 5857c478bd9Sstevel@tonic-gate passwd_entry->pw_name, 0, Pstatus(p)->pr_pid, p, project)) != 0) { 5867c478bd9Sstevel@tonic-gate /* global_error is set by setproject_err */ 5877c478bd9Sstevel@tonic-gate setproject_err(passwd_entry->pw_name, project->pj_name, 5887c478bd9Sstevel@tonic-gate error, project); 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate /* relinquish added privileges */ 5937c478bd9Sstevel@tonic-gate if (be_su) { 5947c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 5957c478bd9Sstevel@tonic-gate if (Psetpriv(p, old_prpriv) != 0) { 5967c478bd9Sstevel@tonic-gate /* 5977c478bd9Sstevel@tonic-gate * We shouldn't ever be in a state where we can't 5987c478bd9Sstevel@tonic-gate * set the process back to its old creds, but we 5997c478bd9Sstevel@tonic-gate * don't want to take the chance of leaving a 6007c478bd9Sstevel@tonic-gate * non-privileged process with enhanced creds. So, 6017c478bd9Sstevel@tonic-gate * release the process from libproc control, knowing 6027c478bd9Sstevel@tonic-gate * that it will be killed. 6037c478bd9Sstevel@tonic-gate */ 6047c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 6057c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 6067c478bd9Sstevel@tonic-gate die(gettext("cannot relinquish superuser credentials " 6077c478bd9Sstevel@tonic-gate "for pid %d. The process was killed."), 6087c478bd9Sstevel@tonic-gate Pstatus(p)->pr_pid); 6097c478bd9Sstevel@tonic-gate } 6107c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 6117c478bd9Sstevel@tonic-gate if (Punsetflags(p, PR_KLC) != 0) 6127c478bd9Sstevel@tonic-gate preserve_error(gettext("error relinquishing " 6137c478bd9Sstevel@tonic-gate "credentials. Process %d will be killed."), 6147c478bd9Sstevel@tonic-gate Pstatus(p)->pr_pid); 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate free(new_prpriv); 6177c478bd9Sstevel@tonic-gate free(old_prpriv); 6187c478bd9Sstevel@tonic-gate 6197c478bd9Sstevel@tonic-gate return (error); 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate /* 6237c478bd9Sstevel@tonic-gate * preserve_error() should be called rather than warn() by any 6247c478bd9Sstevel@tonic-gate * function that is called while the victim process is being 6257c478bd9Sstevel@tonic-gate * held by Pgrab. 6267c478bd9Sstevel@tonic-gate * 6277c478bd9Sstevel@tonic-gate * It saves a single error message to be printed until after 6287c478bd9Sstevel@tonic-gate * the process has been released. Since multiple errors are not 6297c478bd9Sstevel@tonic-gate * stored, any error should be considered critical. 6307c478bd9Sstevel@tonic-gate */ 6317c478bd9Sstevel@tonic-gate void 6327c478bd9Sstevel@tonic-gate preserve_error(const char *format, ...) 6337c478bd9Sstevel@tonic-gate { 6347c478bd9Sstevel@tonic-gate va_list alist; 6357c478bd9Sstevel@tonic-gate 6367c478bd9Sstevel@tonic-gate va_start(alist, format); 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate /* 6397c478bd9Sstevel@tonic-gate * GLOBAL_ERR_SZ is pretty big. If the error is longer 6407c478bd9Sstevel@tonic-gate * than that, just truncate it, rather than chance missing 6417c478bd9Sstevel@tonic-gate * the error altogether. 6427c478bd9Sstevel@tonic-gate */ 6437c478bd9Sstevel@tonic-gate (void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist); 6447c478bd9Sstevel@tonic-gate 6457c478bd9Sstevel@tonic-gate va_end(alist); 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate } 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate /* 6507c478bd9Sstevel@tonic-gate * Given the input arguments, return the passwd structure that matches best. 6517c478bd9Sstevel@tonic-gate * Also, since we use getpwnam() and friends, subsequent calls to this 6527c478bd9Sstevel@tonic-gate * function will re-use the memory previously returned. 6537c478bd9Sstevel@tonic-gate */ 6547c478bd9Sstevel@tonic-gate static struct passwd * 6557c478bd9Sstevel@tonic-gate match_user(uid_t uid, char *projname, int is_my_uid) 6567c478bd9Sstevel@tonic-gate { 6577c478bd9Sstevel@tonic-gate char prbuf[PROJECT_BUFSZ], username[LOGNAME_MAX+1]; 6587c478bd9Sstevel@tonic-gate struct project prj; 6597c478bd9Sstevel@tonic-gate char *tmp_name; 6607c478bd9Sstevel@tonic-gate struct passwd *pw = NULL; 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate /* 6637c478bd9Sstevel@tonic-gate * In order to allow users with the same UID but distinguishable 6647c478bd9Sstevel@tonic-gate * user names to be in different projects we play a guessing 6657c478bd9Sstevel@tonic-gate * game of which username is most appropriate. If we're checking 6667c478bd9Sstevel@tonic-gate * for the uid of the calling process, the login name is a 6677c478bd9Sstevel@tonic-gate * good starting point. 6687c478bd9Sstevel@tonic-gate */ 6697c478bd9Sstevel@tonic-gate if (is_my_uid) { 6707c478bd9Sstevel@tonic-gate if ((tmp_name = getlogin()) == NULL || 6717c478bd9Sstevel@tonic-gate (pw = getpwnam(tmp_name)) == NULL || (pw->pw_uid != uid) || 6727c478bd9Sstevel@tonic-gate (pw->pw_name == NULL)) 6737c478bd9Sstevel@tonic-gate pw = NULL; 6747c478bd9Sstevel@tonic-gate } 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate /* 6777c478bd9Sstevel@tonic-gate * If the login name doesn't work, we try the first match for 6787c478bd9Sstevel@tonic-gate * the current uid in the password file. 6797c478bd9Sstevel@tonic-gate */ 6807c478bd9Sstevel@tonic-gate if (pw == NULL) { 6817c478bd9Sstevel@tonic-gate if (((pw = getpwuid(uid)) == NULL) || pw->pw_name == NULL) { 6827c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot find username " 6837c478bd9Sstevel@tonic-gate "for uid %d"), uid); 6847c478bd9Sstevel@tonic-gate return (NULL); 6857c478bd9Sstevel@tonic-gate } 6867c478bd9Sstevel@tonic-gate } 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate /* 6897c478bd9Sstevel@tonic-gate * If projname wasn't supplied, we've done our best, so just return 6907c478bd9Sstevel@tonic-gate * what we've got now. Alternatively, if newtask's invoker has 6917c478bd9Sstevel@tonic-gate * superuser privileges, return the pw structure we've got now, with 6927c478bd9Sstevel@tonic-gate * no further checking from inproj(). Superuser should be able to 6937c478bd9Sstevel@tonic-gate * join any project, and the subsequent call to setproject() will 6947c478bd9Sstevel@tonic-gate * allow this. 6957c478bd9Sstevel@tonic-gate */ 6967c478bd9Sstevel@tonic-gate if (projname == NULL || getuid() == (uid_t)0) 6977c478bd9Sstevel@tonic-gate return (pw); 6987c478bd9Sstevel@tonic-gate 699*0a1278f2SGary Mills (void) strlcpy(username, pw->pw_name, sizeof (username)); 7007c478bd9Sstevel@tonic-gate 7017c478bd9Sstevel@tonic-gate if (inproj(username, projname, prbuf, PROJECT_BUFSZ) == 0) { 7027c478bd9Sstevel@tonic-gate char **u; 7037c478bd9Sstevel@tonic-gate tmp_name = NULL; 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate /* 7067c478bd9Sstevel@tonic-gate * If the previous guesses didn't work, walk through all 7077c478bd9Sstevel@tonic-gate * project members and test for UID-equivalence. 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate if (getprojbyname(projname, &prj, prbuf, 7117c478bd9Sstevel@tonic-gate PROJECT_BUFSZ) == NULL) { 7127c478bd9Sstevel@tonic-gate preserve_error(gettext("unknown project \"%s\""), 7137c478bd9Sstevel@tonic-gate projname); 7147c478bd9Sstevel@tonic-gate return (NULL); 7157c478bd9Sstevel@tonic-gate } 7167c478bd9Sstevel@tonic-gate 7177c478bd9Sstevel@tonic-gate for (u = prj.pj_users; *u; u++) { 7187c478bd9Sstevel@tonic-gate if ((pw = getpwnam(*u)) == NULL) 7197c478bd9Sstevel@tonic-gate continue; 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate if (pw->pw_uid == uid) { 7227c478bd9Sstevel@tonic-gate tmp_name = pw->pw_name; 7237c478bd9Sstevel@tonic-gate break; 7247c478bd9Sstevel@tonic-gate } 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate 7277c478bd9Sstevel@tonic-gate if (tmp_name == NULL) { 7287c478bd9Sstevel@tonic-gate preserve_error(gettext("user \"%s\" is not a member of " 7297c478bd9Sstevel@tonic-gate "project \"%s\""), username, projname); 7307c478bd9Sstevel@tonic-gate return (NULL); 7317c478bd9Sstevel@tonic-gate } 7327c478bd9Sstevel@tonic-gate } 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate return (pw); 7357c478bd9Sstevel@tonic-gate } 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate void 7387c478bd9Sstevel@tonic-gate setproject_err(char *username, char *projname, int error, struct project *proj) 7397c478bd9Sstevel@tonic-gate { 7407c478bd9Sstevel@tonic-gate kva_t *kv_array = NULL; 7417c478bd9Sstevel@tonic-gate char prbuf[PROJECT_BUFSZ]; 7427c478bd9Sstevel@tonic-gate struct project local_proj; 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate switch (error) { 7457c478bd9Sstevel@tonic-gate case SETPROJ_ERR_TASK: 7467c478bd9Sstevel@tonic-gate if (errno == EAGAIN) 7477c478bd9Sstevel@tonic-gate preserve_error(gettext("resource control limit has " 7487c478bd9Sstevel@tonic-gate "been reached")); 7497c478bd9Sstevel@tonic-gate else if (errno == ESRCH) 7507c478bd9Sstevel@tonic-gate preserve_error(gettext("user \"%s\" is not a member of " 7517c478bd9Sstevel@tonic-gate "project \"%s\""), username, projname); 7527c478bd9Sstevel@tonic-gate else if (errno == EACCES) 7537c478bd9Sstevel@tonic-gate preserve_error(gettext("the invoking task is final")); 7547c478bd9Sstevel@tonic-gate else 7557c478bd9Sstevel@tonic-gate preserve_error( 7567c478bd9Sstevel@tonic-gate gettext("could not join project \"%s\""), 7577c478bd9Sstevel@tonic-gate projname); 7587c478bd9Sstevel@tonic-gate break; 7597c478bd9Sstevel@tonic-gate case SETPROJ_ERR_POOL: 7607c478bd9Sstevel@tonic-gate if (errno == EACCES) 7617c478bd9Sstevel@tonic-gate preserve_error(gettext("no resource pool accepting " 7627c478bd9Sstevel@tonic-gate "default bindings exists for project \"%s\""), 7637c478bd9Sstevel@tonic-gate projname); 7647c478bd9Sstevel@tonic-gate else if (errno == ESRCH) 7657c478bd9Sstevel@tonic-gate preserve_error(gettext("specified resource pool does " 7667c478bd9Sstevel@tonic-gate "not exist for project \"%s\""), projname); 7677c478bd9Sstevel@tonic-gate else 7687c478bd9Sstevel@tonic-gate preserve_error(gettext("could not bind to default " 7697c478bd9Sstevel@tonic-gate "resource pool for project \"%s\""), projname); 7707c478bd9Sstevel@tonic-gate break; 7717c478bd9Sstevel@tonic-gate default: 7727c478bd9Sstevel@tonic-gate if (error <= 0) { 7737c478bd9Sstevel@tonic-gate preserve_error(gettext("setproject failed for " 7747c478bd9Sstevel@tonic-gate "project \"%s\""), projname); 7757c478bd9Sstevel@tonic-gate return; 7767c478bd9Sstevel@tonic-gate } 7777c478bd9Sstevel@tonic-gate /* 7787c478bd9Sstevel@tonic-gate * If we have a stopped target process it may be in 7797c478bd9Sstevel@tonic-gate * getprojbyname()'s execution path which would make it unsafe 7807c478bd9Sstevel@tonic-gate * to access the project table, so only do that if the caller 7817c478bd9Sstevel@tonic-gate * hasn't provided a cached version of the project structure. 7827c478bd9Sstevel@tonic-gate */ 7837c478bd9Sstevel@tonic-gate if (proj == NULL) 7847c478bd9Sstevel@tonic-gate proj = getprojbyname(projname, &local_proj, prbuf, 7857c478bd9Sstevel@tonic-gate PROJECT_BUFSZ); 7867c478bd9Sstevel@tonic-gate 7877c478bd9Sstevel@tonic-gate if (proj == NULL || (kv_array = _str2kva(proj->pj_attr, 7887c478bd9Sstevel@tonic-gate KV_ASSIGN, KV_DELIMITER)) == NULL || 7897c478bd9Sstevel@tonic-gate kv_array->length < error) { 7907c478bd9Sstevel@tonic-gate preserve_error(gettext("warning, resource control " 7917c478bd9Sstevel@tonic-gate "assignment failed for project \"%s\" " 7927c478bd9Sstevel@tonic-gate "attribute %d"), 7937c478bd9Sstevel@tonic-gate projname, error); 7947c478bd9Sstevel@tonic-gate if (kv_array) 7957c478bd9Sstevel@tonic-gate _kva_free(kv_array); 7967c478bd9Sstevel@tonic-gate return; 7977c478bd9Sstevel@tonic-gate } 7987c478bd9Sstevel@tonic-gate preserve_error(gettext("warning, %s resource control " 7997c478bd9Sstevel@tonic-gate "assignment failed for project \"%s\""), 8007c478bd9Sstevel@tonic-gate kv_array->data[error - 1].key, projname); 8017c478bd9Sstevel@tonic-gate _kva_free(kv_array); 8027c478bd9Sstevel@tonic-gate } 8037c478bd9Sstevel@tonic-gate } 804