1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 30*7c478bd9Sstevel@tonic-gate #include <sys/task.h> 31*7c478bd9Sstevel@tonic-gate 32*7c478bd9Sstevel@tonic-gate #include <alloca.h> 33*7c478bd9Sstevel@tonic-gate #include <libproc.h> 34*7c478bd9Sstevel@tonic-gate #include <libintl.h> 35*7c478bd9Sstevel@tonic-gate #include <libgen.h> 36*7c478bd9Sstevel@tonic-gate #include <limits.h> 37*7c478bd9Sstevel@tonic-gate #include <project.h> 38*7c478bd9Sstevel@tonic-gate #include <pwd.h> 39*7c478bd9Sstevel@tonic-gate #include <secdb.h> 40*7c478bd9Sstevel@tonic-gate #include <stdio.h> 41*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 42*7c478bd9Sstevel@tonic-gate #include <string.h> 43*7c478bd9Sstevel@tonic-gate #include <sys/varargs.h> 44*7c478bd9Sstevel@tonic-gate #include <unistd.h> 45*7c478bd9Sstevel@tonic-gate #include <errno.h> 46*7c478bd9Sstevel@tonic-gate #include <signal.h> 47*7c478bd9Sstevel@tonic-gate #include <priv_utils.h> 48*7c478bd9Sstevel@tonic-gate 49*7c478bd9Sstevel@tonic-gate #include "utils.h" 50*7c478bd9Sstevel@tonic-gate 51*7c478bd9Sstevel@tonic-gate #define OPTIONS_STRING "Fc:lp:v" 52*7c478bd9Sstevel@tonic-gate #define NENV 8 53*7c478bd9Sstevel@tonic-gate #define ENVSIZE 255 54*7c478bd9Sstevel@tonic-gate #define PATH "PATH=/usr/bin" 55*7c478bd9Sstevel@tonic-gate #define SUPATH "PATH=/usr/sbin:/usr/bin" 56*7c478bd9Sstevel@tonic-gate #define SHELL "/usr/bin/sh" 57*7c478bd9Sstevel@tonic-gate #define SHELL2 "/sbin/sh" 58*7c478bd9Sstevel@tonic-gate #define TIMEZONEFILE "/etc/default/init" 59*7c478bd9Sstevel@tonic-gate #define LOGINFILE "/etc/default/login" 60*7c478bd9Sstevel@tonic-gate #define GLOBAL_ERR_SZ 1024 61*7c478bd9Sstevel@tonic-gate #define GRAB_RETRY_MAX 100 62*7c478bd9Sstevel@tonic-gate 63*7c478bd9Sstevel@tonic-gate static const char *pname; 64*7c478bd9Sstevel@tonic-gate extern char **environ; 65*7c478bd9Sstevel@tonic-gate static char *supath = SUPATH; 66*7c478bd9Sstevel@tonic-gate static char *path = PATH; 67*7c478bd9Sstevel@tonic-gate static char global_error[GLOBAL_ERR_SZ]; 68*7c478bd9Sstevel@tonic-gate static int verbose = 0; 69*7c478bd9Sstevel@tonic-gate 70*7c478bd9Sstevel@tonic-gate static priv_set_t *nset; 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate /* Private definitions for libproject */ 73*7c478bd9Sstevel@tonic-gate extern projid_t setproject_proc(const char *, const char *, int, pid_t, 74*7c478bd9Sstevel@tonic-gate struct ps_prochandle *, struct project *); 75*7c478bd9Sstevel@tonic-gate extern priv_set_t *setproject_initpriv(void); 76*7c478bd9Sstevel@tonic-gate 77*7c478bd9Sstevel@tonic-gate static void usage(void); 78*7c478bd9Sstevel@tonic-gate 79*7c478bd9Sstevel@tonic-gate static void preserve_error(const char *format, ...); 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate static int update_running_proc(int, char *, char *); 82*7c478bd9Sstevel@tonic-gate static int set_ids(struct ps_prochandle *, struct project *, 83*7c478bd9Sstevel@tonic-gate struct passwd *); 84*7c478bd9Sstevel@tonic-gate static struct passwd *match_user(uid_t, char *, int); 85*7c478bd9Sstevel@tonic-gate static void setproject_err(char *, char *, int, struct project *); 86*7c478bd9Sstevel@tonic-gate 87*7c478bd9Sstevel@tonic-gate static void 88*7c478bd9Sstevel@tonic-gate usage(void) 89*7c478bd9Sstevel@tonic-gate { 90*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("usage: \n\t%s [-v] [-p project] " 91*7c478bd9Sstevel@tonic-gate "[-c pid | [-Fl] [command [args ...]]]\n"), pname); 92*7c478bd9Sstevel@tonic-gate exit(2); 93*7c478bd9Sstevel@tonic-gate } 94*7c478bd9Sstevel@tonic-gate 95*7c478bd9Sstevel@tonic-gate int 96*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 97*7c478bd9Sstevel@tonic-gate { 98*7c478bd9Sstevel@tonic-gate int c; 99*7c478bd9Sstevel@tonic-gate struct passwd *pw; 100*7c478bd9Sstevel@tonic-gate char *projname = NULL; 101*7c478bd9Sstevel@tonic-gate uid_t uid; 102*7c478bd9Sstevel@tonic-gate int login_flag = 0; 103*7c478bd9Sstevel@tonic-gate int finalize_flag = TASK_NORMAL; 104*7c478bd9Sstevel@tonic-gate int newproj_flag = 0; 105*7c478bd9Sstevel@tonic-gate taskid_t taskid; 106*7c478bd9Sstevel@tonic-gate char *shell; 107*7c478bd9Sstevel@tonic-gate char *env[NENV]; 108*7c478bd9Sstevel@tonic-gate char **targs; 109*7c478bd9Sstevel@tonic-gate char *filename, *procname = NULL; 110*7c478bd9Sstevel@tonic-gate int error; 111*7c478bd9Sstevel@tonic-gate 112*7c478bd9Sstevel@tonic-gate nset = setproject_initpriv(); 113*7c478bd9Sstevel@tonic-gate if (nset == NULL) 114*7c478bd9Sstevel@tonic-gate die(gettext("privilege initialization failed\n")); 115*7c478bd9Sstevel@tonic-gate 116*7c478bd9Sstevel@tonic-gate pname = getpname(argv[0]); 117*7c478bd9Sstevel@tonic-gate 118*7c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, OPTIONS_STRING)) != EOF) { 119*7c478bd9Sstevel@tonic-gate switch (c) { 120*7c478bd9Sstevel@tonic-gate case 'v': 121*7c478bd9Sstevel@tonic-gate verbose = 1; 122*7c478bd9Sstevel@tonic-gate break; 123*7c478bd9Sstevel@tonic-gate case 'p': 124*7c478bd9Sstevel@tonic-gate newproj_flag = 1; 125*7c478bd9Sstevel@tonic-gate projname = optarg; 126*7c478bd9Sstevel@tonic-gate break; 127*7c478bd9Sstevel@tonic-gate case 'F': 128*7c478bd9Sstevel@tonic-gate finalize_flag = TASK_FINAL; 129*7c478bd9Sstevel@tonic-gate break; 130*7c478bd9Sstevel@tonic-gate case 'l': 131*7c478bd9Sstevel@tonic-gate login_flag++; 132*7c478bd9Sstevel@tonic-gate break; 133*7c478bd9Sstevel@tonic-gate case 'c': 134*7c478bd9Sstevel@tonic-gate procname = optarg; 135*7c478bd9Sstevel@tonic-gate break; 136*7c478bd9Sstevel@tonic-gate case '?': 137*7c478bd9Sstevel@tonic-gate default: 138*7c478bd9Sstevel@tonic-gate usage(); 139*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 140*7c478bd9Sstevel@tonic-gate } 141*7c478bd9Sstevel@tonic-gate } 142*7c478bd9Sstevel@tonic-gate 143*7c478bd9Sstevel@tonic-gate /* -c option is invalid with -F, -l, or a specified command */ 144*7c478bd9Sstevel@tonic-gate if ((procname != NULL) && 145*7c478bd9Sstevel@tonic-gate (finalize_flag == TASK_FINAL || login_flag || optind < argc)) 146*7c478bd9Sstevel@tonic-gate usage(); 147*7c478bd9Sstevel@tonic-gate 148*7c478bd9Sstevel@tonic-gate if (procname != NULL) { 149*7c478bd9Sstevel@tonic-gate /* Change project/task of an existing process */ 150*7c478bd9Sstevel@tonic-gate return (update_running_proc(newproj_flag, procname, projname)); 151*7c478bd9Sstevel@tonic-gate } 152*7c478bd9Sstevel@tonic-gate 153*7c478bd9Sstevel@tonic-gate /* 154*7c478bd9Sstevel@tonic-gate * Get user data, so that we can confirm project membership as 155*7c478bd9Sstevel@tonic-gate * well as construct an appropriate login environment. 156*7c478bd9Sstevel@tonic-gate */ 157*7c478bd9Sstevel@tonic-gate uid = getuid(); 158*7c478bd9Sstevel@tonic-gate if ((pw = match_user(uid, projname, 1)) == NULL) { 159*7c478bd9Sstevel@tonic-gate die("%s\n", global_error); 160*7c478bd9Sstevel@tonic-gate } 161*7c478bd9Sstevel@tonic-gate 162*7c478bd9Sstevel@tonic-gate /* 163*7c478bd9Sstevel@tonic-gate * If no projname was specified, we're just creating a new task 164*7c478bd9Sstevel@tonic-gate * under the current project, so we can just set the new taskid. 165*7c478bd9Sstevel@tonic-gate * If our project is changing, we need to update any attendant 166*7c478bd9Sstevel@tonic-gate * pool/rctl bindings, so let setproject() do the dirty work. 167*7c478bd9Sstevel@tonic-gate */ 168*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 169*7c478bd9Sstevel@tonic-gate if (projname == NULL) { 170*7c478bd9Sstevel@tonic-gate if (settaskid(getprojid(), finalize_flag) == -1) 171*7c478bd9Sstevel@tonic-gate if (errno == EAGAIN) 172*7c478bd9Sstevel@tonic-gate die(gettext("resource control limit has been " 173*7c478bd9Sstevel@tonic-gate "reached")); 174*7c478bd9Sstevel@tonic-gate else 175*7c478bd9Sstevel@tonic-gate die(gettext("settaskid failed")); 176*7c478bd9Sstevel@tonic-gate } else { 177*7c478bd9Sstevel@tonic-gate if ((error = setproject(projname, 178*7c478bd9Sstevel@tonic-gate pw->pw_name, finalize_flag)) != 0) { 179*7c478bd9Sstevel@tonic-gate setproject_err(pw->pw_name, projname, error, NULL); 180*7c478bd9Sstevel@tonic-gate if (error < 0) 181*7c478bd9Sstevel@tonic-gate die("%s\n", global_error); 182*7c478bd9Sstevel@tonic-gate else 183*7c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 184*7c478bd9Sstevel@tonic-gate } 185*7c478bd9Sstevel@tonic-gate } 186*7c478bd9Sstevel@tonic-gate __priv_relinquish(); 187*7c478bd9Sstevel@tonic-gate 188*7c478bd9Sstevel@tonic-gate taskid = gettaskid(); 189*7c478bd9Sstevel@tonic-gate 190*7c478bd9Sstevel@tonic-gate if (verbose) 191*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d\n", (int)taskid); 192*7c478bd9Sstevel@tonic-gate 193*7c478bd9Sstevel@tonic-gate /* 194*7c478bd9Sstevel@tonic-gate * Validate user's shell from passwd database. 195*7c478bd9Sstevel@tonic-gate */ 196*7c478bd9Sstevel@tonic-gate if (strcmp(pw->pw_shell, "") == 0) { 197*7c478bd9Sstevel@tonic-gate if (access(SHELL, X_OK) == 0) 198*7c478bd9Sstevel@tonic-gate pw->pw_shell = SHELL; 199*7c478bd9Sstevel@tonic-gate else 200*7c478bd9Sstevel@tonic-gate pw->pw_shell = SHELL2; 201*7c478bd9Sstevel@tonic-gate } 202*7c478bd9Sstevel@tonic-gate 203*7c478bd9Sstevel@tonic-gate if (login_flag) { 204*7c478bd9Sstevel@tonic-gate /* 205*7c478bd9Sstevel@tonic-gate * Since we've been invoked as a "simulated login", set up the 206*7c478bd9Sstevel@tonic-gate * environment. 207*7c478bd9Sstevel@tonic-gate */ 208*7c478bd9Sstevel@tonic-gate char *cur_tz = getenv("TZ"); 209*7c478bd9Sstevel@tonic-gate char *cur_term = getenv("TERM"); 210*7c478bd9Sstevel@tonic-gate 211*7c478bd9Sstevel@tonic-gate char **envnext; 212*7c478bd9Sstevel@tonic-gate 213*7c478bd9Sstevel@tonic-gate size_t len_home = strlen(pw->pw_dir) + strlen("HOME=") + 1; 214*7c478bd9Sstevel@tonic-gate size_t len_logname = strlen(pw->pw_name) + strlen("LOGNAME=") + 215*7c478bd9Sstevel@tonic-gate 1; 216*7c478bd9Sstevel@tonic-gate size_t len_shell = strlen(pw->pw_shell) + strlen("SHELL=") + 1; 217*7c478bd9Sstevel@tonic-gate size_t len_mail = strlen(pw->pw_name) + 218*7c478bd9Sstevel@tonic-gate strlen("MAIL=/var/mail/") + 1; 219*7c478bd9Sstevel@tonic-gate size_t len_tz; 220*7c478bd9Sstevel@tonic-gate size_t len_term; 221*7c478bd9Sstevel@tonic-gate 222*7c478bd9Sstevel@tonic-gate char *env_home = safe_malloc(len_home); 223*7c478bd9Sstevel@tonic-gate char *env_logname = safe_malloc(len_logname); 224*7c478bd9Sstevel@tonic-gate char *env_shell = safe_malloc(len_shell); 225*7c478bd9Sstevel@tonic-gate char *env_mail = safe_malloc(len_mail); 226*7c478bd9Sstevel@tonic-gate char *env_tz; 227*7c478bd9Sstevel@tonic-gate char *env_term; 228*7c478bd9Sstevel@tonic-gate 229*7c478bd9Sstevel@tonic-gate (void) snprintf(env_home, len_home, "HOME=%s", pw->pw_dir); 230*7c478bd9Sstevel@tonic-gate (void) snprintf(env_logname, len_logname, "LOGNAME=%s", 231*7c478bd9Sstevel@tonic-gate pw->pw_name); 232*7c478bd9Sstevel@tonic-gate (void) snprintf(env_shell, len_shell, "SHELL=%s", pw->pw_shell); 233*7c478bd9Sstevel@tonic-gate (void) snprintf(env_mail, len_mail, "MAIL=/var/mail/%s", 234*7c478bd9Sstevel@tonic-gate pw->pw_name); 235*7c478bd9Sstevel@tonic-gate 236*7c478bd9Sstevel@tonic-gate env[0] = env_home; 237*7c478bd9Sstevel@tonic-gate env[1] = env_logname; 238*7c478bd9Sstevel@tonic-gate env[2] = (pw->pw_uid == 0 ? supath : path); 239*7c478bd9Sstevel@tonic-gate env[3] = env_shell; 240*7c478bd9Sstevel@tonic-gate env[4] = env_mail; 241*7c478bd9Sstevel@tonic-gate env[5] = NULL; 242*7c478bd9Sstevel@tonic-gate env[6] = NULL; 243*7c478bd9Sstevel@tonic-gate env[7] = NULL; 244*7c478bd9Sstevel@tonic-gate 245*7c478bd9Sstevel@tonic-gate envnext = (char **)&env[5]; 246*7c478bd9Sstevel@tonic-gate 247*7c478bd9Sstevel@tonic-gate /* 248*7c478bd9Sstevel@tonic-gate * It's possible that TERM wasn't defined in the outer 249*7c478bd9Sstevel@tonic-gate * environment. 250*7c478bd9Sstevel@tonic-gate */ 251*7c478bd9Sstevel@tonic-gate if (cur_term != NULL) { 252*7c478bd9Sstevel@tonic-gate len_term = strlen(cur_term) + strlen("TERM=") + 1; 253*7c478bd9Sstevel@tonic-gate env_term = safe_malloc(len_term); 254*7c478bd9Sstevel@tonic-gate 255*7c478bd9Sstevel@tonic-gate (void) snprintf(env_term, len_term, "TERM=%s", 256*7c478bd9Sstevel@tonic-gate cur_term); 257*7c478bd9Sstevel@tonic-gate *envnext = env_term; 258*7c478bd9Sstevel@tonic-gate envnext++; 259*7c478bd9Sstevel@tonic-gate } 260*7c478bd9Sstevel@tonic-gate 261*7c478bd9Sstevel@tonic-gate /* 262*7c478bd9Sstevel@tonic-gate * It is also possible that TZ wasn't defined in the outer 263*7c478bd9Sstevel@tonic-gate * environment. In that case, we must attempt to open the file 264*7c478bd9Sstevel@tonic-gate * defining the default timezone and select the appropriate 265*7c478bd9Sstevel@tonic-gate * entry. If there is no default timezone there, try 266*7c478bd9Sstevel@tonic-gate * TIMEZONE in /etc/default/login, duplicating the algorithm 267*7c478bd9Sstevel@tonic-gate * that login uses. 268*7c478bd9Sstevel@tonic-gate */ 269*7c478bd9Sstevel@tonic-gate if (cur_tz != NULL) { 270*7c478bd9Sstevel@tonic-gate len_tz = strlen(cur_tz) + strlen("TZ=") + 1; 271*7c478bd9Sstevel@tonic-gate env_tz = safe_malloc(len_tz); 272*7c478bd9Sstevel@tonic-gate 273*7c478bd9Sstevel@tonic-gate (void) snprintf(env_tz, len_tz, "TZ=%s", cur_tz); 274*7c478bd9Sstevel@tonic-gate *envnext = env_tz; 275*7c478bd9Sstevel@tonic-gate } else { 276*7c478bd9Sstevel@tonic-gate if ((env_tz = getdefault(TIMEZONEFILE, "TZ=", 277*7c478bd9Sstevel@tonic-gate "TZ=")) != NULL) 278*7c478bd9Sstevel@tonic-gate *envnext = env_tz; 279*7c478bd9Sstevel@tonic-gate else { 280*7c478bd9Sstevel@tonic-gate env_tz = getdefault(LOGINFILE, "TIMEZONE=", 281*7c478bd9Sstevel@tonic-gate "TZ="); 282*7c478bd9Sstevel@tonic-gate *envnext = env_tz; 283*7c478bd9Sstevel@tonic-gate } 284*7c478bd9Sstevel@tonic-gate } 285*7c478bd9Sstevel@tonic-gate 286*7c478bd9Sstevel@tonic-gate environ = (char **)&env[0]; 287*7c478bd9Sstevel@tonic-gate 288*7c478bd9Sstevel@tonic-gate /* 289*7c478bd9Sstevel@tonic-gate * Prefix the shell string with a hyphen, indicating a login 290*7c478bd9Sstevel@tonic-gate * shell. 291*7c478bd9Sstevel@tonic-gate */ 292*7c478bd9Sstevel@tonic-gate shell = safe_malloc(PATH_MAX); 293*7c478bd9Sstevel@tonic-gate (void) snprintf(shell, PATH_MAX, "-%s", basename(pw->pw_shell)); 294*7c478bd9Sstevel@tonic-gate } else { 295*7c478bd9Sstevel@tonic-gate shell = basename(pw->pw_shell); 296*7c478bd9Sstevel@tonic-gate } 297*7c478bd9Sstevel@tonic-gate 298*7c478bd9Sstevel@tonic-gate /* 299*7c478bd9Sstevel@tonic-gate * If there are no arguments, we launch the user's shell; otherwise, the 300*7c478bd9Sstevel@tonic-gate * remaining commands are assumed to form a valid command invocation 301*7c478bd9Sstevel@tonic-gate * that we can exec. 302*7c478bd9Sstevel@tonic-gate */ 303*7c478bd9Sstevel@tonic-gate if (optind >= argc) { 304*7c478bd9Sstevel@tonic-gate targs = alloca(2 * sizeof (char *)); 305*7c478bd9Sstevel@tonic-gate filename = pw->pw_shell; 306*7c478bd9Sstevel@tonic-gate targs[0] = shell; 307*7c478bd9Sstevel@tonic-gate targs[1] = NULL; 308*7c478bd9Sstevel@tonic-gate } else { 309*7c478bd9Sstevel@tonic-gate targs = &argv[optind]; 310*7c478bd9Sstevel@tonic-gate filename = targs[0]; 311*7c478bd9Sstevel@tonic-gate } 312*7c478bd9Sstevel@tonic-gate 313*7c478bd9Sstevel@tonic-gate if (execvp(filename, targs) == -1) 314*7c478bd9Sstevel@tonic-gate die(gettext("exec of %s failed"), targs[0]); 315*7c478bd9Sstevel@tonic-gate 316*7c478bd9Sstevel@tonic-gate /* 317*7c478bd9Sstevel@tonic-gate * We should never get here. 318*7c478bd9Sstevel@tonic-gate */ 319*7c478bd9Sstevel@tonic-gate return (1); 320*7c478bd9Sstevel@tonic-gate } 321*7c478bd9Sstevel@tonic-gate 322*7c478bd9Sstevel@tonic-gate static int 323*7c478bd9Sstevel@tonic-gate update_running_proc(int newproj_flag, char *procname, char *projname) 324*7c478bd9Sstevel@tonic-gate { 325*7c478bd9Sstevel@tonic-gate struct ps_prochandle *p; 326*7c478bd9Sstevel@tonic-gate prcred_t original_prcred, current_prcred; 327*7c478bd9Sstevel@tonic-gate projid_t prprojid; 328*7c478bd9Sstevel@tonic-gate taskid_t taskid; 329*7c478bd9Sstevel@tonic-gate int error = 0, gret; 330*7c478bd9Sstevel@tonic-gate struct project project; 331*7c478bd9Sstevel@tonic-gate char prbuf[PROJECT_BUFSZ]; 332*7c478bd9Sstevel@tonic-gate struct passwd *passwd_entry; 333*7c478bd9Sstevel@tonic-gate int grab_retry_count = 0; 334*7c478bd9Sstevel@tonic-gate 335*7c478bd9Sstevel@tonic-gate /* 336*7c478bd9Sstevel@tonic-gate * Catch signals from terminal. There isn't much sense in 337*7c478bd9Sstevel@tonic-gate * doing anything but ignoring them since we don't do anything 338*7c478bd9Sstevel@tonic-gate * after the point we'd be capable of handling them again. 339*7c478bd9Sstevel@tonic-gate */ 340*7c478bd9Sstevel@tonic-gate (void) sigignore(SIGHUP); 341*7c478bd9Sstevel@tonic-gate (void) sigignore(SIGINT); 342*7c478bd9Sstevel@tonic-gate (void) sigignore(SIGQUIT); 343*7c478bd9Sstevel@tonic-gate (void) sigignore(SIGTERM); 344*7c478bd9Sstevel@tonic-gate 345*7c478bd9Sstevel@tonic-gate /* flush stdout before grabbing the proc to avoid deadlock */ 346*7c478bd9Sstevel@tonic-gate (void) fflush(stdout); 347*7c478bd9Sstevel@tonic-gate 348*7c478bd9Sstevel@tonic-gate /* 349*7c478bd9Sstevel@tonic-gate * We need to grab the process, which will force it to stop execution 350*7c478bd9Sstevel@tonic-gate * until the grab is released, in order to aquire some information about 351*7c478bd9Sstevel@tonic-gate * it, such as its current project (which is achieved via an injected 352*7c478bd9Sstevel@tonic-gate * system call and therefore needs an agent) and its credentials. We 353*7c478bd9Sstevel@tonic-gate * will then need to release it again because it may be a process that 354*7c478bd9Sstevel@tonic-gate * we rely on for later calls, for example nscd. 355*7c478bd9Sstevel@tonic-gate */ 356*7c478bd9Sstevel@tonic-gate if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) { 357*7c478bd9Sstevel@tonic-gate warn(gettext("failed to grab for process %s: %s\n"), 358*7c478bd9Sstevel@tonic-gate procname, Pgrab_error(gret)); 359*7c478bd9Sstevel@tonic-gate return (1); 360*7c478bd9Sstevel@tonic-gate } 361*7c478bd9Sstevel@tonic-gate if (Pcreate_agent(p) != 0) { 362*7c478bd9Sstevel@tonic-gate Prelease(p, 0); 363*7c478bd9Sstevel@tonic-gate warn(gettext("cannot control process %s\n"), procname); 364*7c478bd9Sstevel@tonic-gate return (1); 365*7c478bd9Sstevel@tonic-gate } 366*7c478bd9Sstevel@tonic-gate 367*7c478bd9Sstevel@tonic-gate /* 368*7c478bd9Sstevel@tonic-gate * The victim process is now held. Do not call any functions 369*7c478bd9Sstevel@tonic-gate * which generate stdout/stderr until the process has been 370*7c478bd9Sstevel@tonic-gate * released. 371*7c478bd9Sstevel@tonic-gate */ 372*7c478bd9Sstevel@tonic-gate 373*7c478bd9Sstevel@tonic-gate /* 374*7c478bd9Sstevel@tonic-gate * The target process will soon be restarted (in case it is in newtask's 375*7c478bd9Sstevel@tonic-gate * execution path) and then stopped again. We need to ensure that our cached 376*7c478bd9Sstevel@tonic-gate * data doesn't change while the process runs so return here if the target 377*7c478bd9Sstevel@tonic-gate * process changes its user id in between our stop operations, so that we can 378*7c478bd9Sstevel@tonic-gate * try again. 379*7c478bd9Sstevel@tonic-gate */ 380*7c478bd9Sstevel@tonic-gate pgrab_retry: 381*7c478bd9Sstevel@tonic-gate 382*7c478bd9Sstevel@tonic-gate /* Cache required information about the process. */ 383*7c478bd9Sstevel@tonic-gate if (Pcred(p, &original_prcred, 0) != 0) { 384*7c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot get process credentials %s\n"), 385*7c478bd9Sstevel@tonic-gate procname); 386*7c478bd9Sstevel@tonic-gate error = 1; 387*7c478bd9Sstevel@tonic-gate } 388*7c478bd9Sstevel@tonic-gate if ((prprojid = pr_getprojid(p)) == -1) { 389*7c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot get process project id %s\n"), 390*7c478bd9Sstevel@tonic-gate procname); 391*7c478bd9Sstevel@tonic-gate error = 1; 392*7c478bd9Sstevel@tonic-gate } 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate /* 395*7c478bd9Sstevel@tonic-gate * We now have all the required information, so release the target 396*7c478bd9Sstevel@tonic-gate * process and perform our sanity checks. The process needs to be 397*7c478bd9Sstevel@tonic-gate * running at this point because it may be in the execution path of the 398*7c478bd9Sstevel@tonic-gate * calls made below. 399*7c478bd9Sstevel@tonic-gate */ 400*7c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 401*7c478bd9Sstevel@tonic-gate Prelease(p, 0); 402*7c478bd9Sstevel@tonic-gate 403*7c478bd9Sstevel@tonic-gate /* if our data acquisition failed, then we can't continue. */ 404*7c478bd9Sstevel@tonic-gate if (error) { 405*7c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 406*7c478bd9Sstevel@tonic-gate return (1); 407*7c478bd9Sstevel@tonic-gate } 408*7c478bd9Sstevel@tonic-gate 409*7c478bd9Sstevel@tonic-gate if (newproj_flag == 0) { 410*7c478bd9Sstevel@tonic-gate /* 411*7c478bd9Sstevel@tonic-gate * Just changing the task, so set projname to the current 412*7c478bd9Sstevel@tonic-gate * project of the running process. 413*7c478bd9Sstevel@tonic-gate */ 414*7c478bd9Sstevel@tonic-gate if (getprojbyid(prprojid, &project, &prbuf, 415*7c478bd9Sstevel@tonic-gate PROJECT_BUFSZ) == NULL) { 416*7c478bd9Sstevel@tonic-gate warn(gettext("unable to get project name " 417*7c478bd9Sstevel@tonic-gate "for projid %d"), prprojid); 418*7c478bd9Sstevel@tonic-gate return (1); 419*7c478bd9Sstevel@tonic-gate } 420*7c478bd9Sstevel@tonic-gate projname = project.pj_name; 421*7c478bd9Sstevel@tonic-gate } else { 422*7c478bd9Sstevel@tonic-gate /* 423*7c478bd9Sstevel@tonic-gate * cache info for the project which user passed in via the 424*7c478bd9Sstevel@tonic-gate * command line 425*7c478bd9Sstevel@tonic-gate */ 426*7c478bd9Sstevel@tonic-gate if (getprojbyname(projname, &project, &prbuf, 427*7c478bd9Sstevel@tonic-gate PROJECT_BUFSZ) == NULL) { 428*7c478bd9Sstevel@tonic-gate warn(gettext("unknown project \"%s\"\n"), projname); 429*7c478bd9Sstevel@tonic-gate return (1); 430*7c478bd9Sstevel@tonic-gate } 431*7c478bd9Sstevel@tonic-gate } 432*7c478bd9Sstevel@tonic-gate 433*7c478bd9Sstevel@tonic-gate /* 434*7c478bd9Sstevel@tonic-gate * Use our cached information to verify that the owner of the running 435*7c478bd9Sstevel@tonic-gate * process is a member of proj 436*7c478bd9Sstevel@tonic-gate */ 437*7c478bd9Sstevel@tonic-gate if ((passwd_entry = match_user(original_prcred.pr_ruid, 438*7c478bd9Sstevel@tonic-gate projname, 0)) == NULL) { 439*7c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 440*7c478bd9Sstevel@tonic-gate return (1); 441*7c478bd9Sstevel@tonic-gate } 442*7c478bd9Sstevel@tonic-gate 443*7c478bd9Sstevel@tonic-gate /* 444*7c478bd9Sstevel@tonic-gate * We can now safely stop the process again in order to change the 445*7c478bd9Sstevel@tonic-gate * project and taskid as required. 446*7c478bd9Sstevel@tonic-gate */ 447*7c478bd9Sstevel@tonic-gate if ((p = proc_arg_grab(procname, PR_ARG_PIDS, 0, &gret)) == NULL) { 448*7c478bd9Sstevel@tonic-gate warn(gettext("failed to grab for process %s: %s\n"), 449*7c478bd9Sstevel@tonic-gate procname, Pgrab_error(gret)); 450*7c478bd9Sstevel@tonic-gate return (1); 451*7c478bd9Sstevel@tonic-gate } 452*7c478bd9Sstevel@tonic-gate if (Pcreate_agent(p) != 0) { 453*7c478bd9Sstevel@tonic-gate Prelease(p, 0); 454*7c478bd9Sstevel@tonic-gate warn(gettext("cannot control process %s\n"), procname); 455*7c478bd9Sstevel@tonic-gate return (1); 456*7c478bd9Sstevel@tonic-gate } 457*7c478bd9Sstevel@tonic-gate 458*7c478bd9Sstevel@tonic-gate /* 459*7c478bd9Sstevel@tonic-gate * Now that the target process is stopped, check the validity of our 460*7c478bd9Sstevel@tonic-gate * cached info. If we aren't superuser then match_user() will have 461*7c478bd9Sstevel@tonic-gate * checked to make sure that the owner of the process is in the relevant 462*7c478bd9Sstevel@tonic-gate * project. If our ruid has changed, then match_user()'s conclusion may 463*7c478bd9Sstevel@tonic-gate * be invalid. 464*7c478bd9Sstevel@tonic-gate */ 465*7c478bd9Sstevel@tonic-gate if (getuid() != 0) { 466*7c478bd9Sstevel@tonic-gate if (Pcred(p, ¤t_prcred, 0) != 0) { 467*7c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 468*7c478bd9Sstevel@tonic-gate Prelease(p, 0); 469*7c478bd9Sstevel@tonic-gate warn(gettext("can't get process credentials %s\n"), 470*7c478bd9Sstevel@tonic-gate procname); 471*7c478bd9Sstevel@tonic-gate return (1); 472*7c478bd9Sstevel@tonic-gate } 473*7c478bd9Sstevel@tonic-gate 474*7c478bd9Sstevel@tonic-gate if (original_prcred.pr_ruid != current_prcred.pr_ruid) { 475*7c478bd9Sstevel@tonic-gate if (grab_retry_count++ < GRAB_RETRY_MAX) 476*7c478bd9Sstevel@tonic-gate goto pgrab_retry; 477*7c478bd9Sstevel@tonic-gate 478*7c478bd9Sstevel@tonic-gate warn(gettext("process consistently changed its " 479*7c478bd9Sstevel@tonic-gate "user id %s\n"), procname); 480*7c478bd9Sstevel@tonic-gate return (1); 481*7c478bd9Sstevel@tonic-gate } 482*7c478bd9Sstevel@tonic-gate } 483*7c478bd9Sstevel@tonic-gate 484*7c478bd9Sstevel@tonic-gate error = set_ids(p, &project, passwd_entry); 485*7c478bd9Sstevel@tonic-gate 486*7c478bd9Sstevel@tonic-gate if (verbose) 487*7c478bd9Sstevel@tonic-gate taskid = pr_gettaskid(p); 488*7c478bd9Sstevel@tonic-gate 489*7c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 490*7c478bd9Sstevel@tonic-gate Prelease(p, 0); 491*7c478bd9Sstevel@tonic-gate 492*7c478bd9Sstevel@tonic-gate if (error) { 493*7c478bd9Sstevel@tonic-gate /* 494*7c478bd9Sstevel@tonic-gate * error is serious enough to stop, only if negative. 495*7c478bd9Sstevel@tonic-gate * Otherwise, it simply indicates one of the resource 496*7c478bd9Sstevel@tonic-gate * control assignments failed, which is worth warning 497*7c478bd9Sstevel@tonic-gate * about. 498*7c478bd9Sstevel@tonic-gate */ 499*7c478bd9Sstevel@tonic-gate warn("%s\n", global_error); 500*7c478bd9Sstevel@tonic-gate if (error < 0) 501*7c478bd9Sstevel@tonic-gate return (1); 502*7c478bd9Sstevel@tonic-gate } 503*7c478bd9Sstevel@tonic-gate 504*7c478bd9Sstevel@tonic-gate if (verbose) 505*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d\n", (int)taskid); 506*7c478bd9Sstevel@tonic-gate 507*7c478bd9Sstevel@tonic-gate return (0); 508*7c478bd9Sstevel@tonic-gate } 509*7c478bd9Sstevel@tonic-gate 510*7c478bd9Sstevel@tonic-gate static int 511*7c478bd9Sstevel@tonic-gate set_ids(struct ps_prochandle *p, struct project *project, 512*7c478bd9Sstevel@tonic-gate struct passwd *passwd_entry) 513*7c478bd9Sstevel@tonic-gate { 514*7c478bd9Sstevel@tonic-gate int be_su = 0; 515*7c478bd9Sstevel@tonic-gate prcred_t old_prcred; 516*7c478bd9Sstevel@tonic-gate int error; 517*7c478bd9Sstevel@tonic-gate prpriv_t *old_prpriv, *new_prpriv; 518*7c478bd9Sstevel@tonic-gate size_t prsz = sizeof (prpriv_t); 519*7c478bd9Sstevel@tonic-gate priv_set_t *eset, *pset; 520*7c478bd9Sstevel@tonic-gate int ind; 521*7c478bd9Sstevel@tonic-gate 522*7c478bd9Sstevel@tonic-gate if (Pcred(p, &old_prcred, 0) != 0) { 523*7c478bd9Sstevel@tonic-gate preserve_error(gettext("can't get process credentials")); 524*7c478bd9Sstevel@tonic-gate return (1); 525*7c478bd9Sstevel@tonic-gate } 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate old_prpriv = proc_get_priv(Pstatus(p)->pr_pid); 528*7c478bd9Sstevel@tonic-gate if (old_prpriv == NULL) { 529*7c478bd9Sstevel@tonic-gate preserve_error(gettext("can't get process privileges")); 530*7c478bd9Sstevel@tonic-gate return (1); 531*7c478bd9Sstevel@tonic-gate } 532*7c478bd9Sstevel@tonic-gate 533*7c478bd9Sstevel@tonic-gate prsz = PRIV_PRPRIV_SIZE(old_prpriv); 534*7c478bd9Sstevel@tonic-gate 535*7c478bd9Sstevel@tonic-gate new_prpriv = malloc(prsz); 536*7c478bd9Sstevel@tonic-gate if (new_prpriv == NULL) { 537*7c478bd9Sstevel@tonic-gate preserve_error(gettext("can't allocate memory")); 538*7c478bd9Sstevel@tonic-gate free(old_prpriv); 539*7c478bd9Sstevel@tonic-gate return (1); 540*7c478bd9Sstevel@tonic-gate } 541*7c478bd9Sstevel@tonic-gate 542*7c478bd9Sstevel@tonic-gate (void) memcpy(new_prpriv, old_prpriv, prsz); 543*7c478bd9Sstevel@tonic-gate 544*7c478bd9Sstevel@tonic-gate /* 545*7c478bd9Sstevel@tonic-gate * If the process already has the proc_taskid privilege, 546*7c478bd9Sstevel@tonic-gate * we don't need to elevate its privileges; if it doesn't, 547*7c478bd9Sstevel@tonic-gate * we try to do it here. 548*7c478bd9Sstevel@tonic-gate * As we do not wish to leave a window in which the process runs 549*7c478bd9Sstevel@tonic-gate * with elevated privileges, we make sure that the process dies 550*7c478bd9Sstevel@tonic-gate * when we go away unexpectedly. 551*7c478bd9Sstevel@tonic-gate */ 552*7c478bd9Sstevel@tonic-gate 553*7c478bd9Sstevel@tonic-gate ind = priv_getsetbyname(PRIV_EFFECTIVE); 554*7c478bd9Sstevel@tonic-gate eset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind]; 555*7c478bd9Sstevel@tonic-gate ind = priv_getsetbyname(PRIV_PERMITTED); 556*7c478bd9Sstevel@tonic-gate pset = (priv_set_t *)&new_prpriv->pr_sets[new_prpriv->pr_setsize * ind]; 557*7c478bd9Sstevel@tonic-gate 558*7c478bd9Sstevel@tonic-gate if (!priv_issubset(nset, eset)) { 559*7c478bd9Sstevel@tonic-gate be_su = 1; 560*7c478bd9Sstevel@tonic-gate priv_union(nset, eset); 561*7c478bd9Sstevel@tonic-gate priv_union(nset, pset); 562*7c478bd9Sstevel@tonic-gate if (Psetflags(p, PR_KLC) != 0) { 563*7c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot set process " 564*7c478bd9Sstevel@tonic-gate "privileges")); 565*7c478bd9Sstevel@tonic-gate (void) Punsetflags(p, PR_KLC); 566*7c478bd9Sstevel@tonic-gate free(new_prpriv); 567*7c478bd9Sstevel@tonic-gate free(old_prpriv); 568*7c478bd9Sstevel@tonic-gate return (1); 569*7c478bd9Sstevel@tonic-gate } 570*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 571*7c478bd9Sstevel@tonic-gate if (Psetpriv(p, new_prpriv) != 0) { 572*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 573*7c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot set process " 574*7c478bd9Sstevel@tonic-gate "privileges")); 575*7c478bd9Sstevel@tonic-gate (void) Punsetflags(p, PR_KLC); 576*7c478bd9Sstevel@tonic-gate free(new_prpriv); 577*7c478bd9Sstevel@tonic-gate free(old_prpriv); 578*7c478bd9Sstevel@tonic-gate return (1); 579*7c478bd9Sstevel@tonic-gate } 580*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 581*7c478bd9Sstevel@tonic-gate } 582*7c478bd9Sstevel@tonic-gate 583*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 584*7c478bd9Sstevel@tonic-gate if ((error = setproject_proc(project->pj_name, 585*7c478bd9Sstevel@tonic-gate passwd_entry->pw_name, 0, Pstatus(p)->pr_pid, p, project)) != 0) { 586*7c478bd9Sstevel@tonic-gate /* global_error is set by setproject_err */ 587*7c478bd9Sstevel@tonic-gate setproject_err(passwd_entry->pw_name, project->pj_name, 588*7c478bd9Sstevel@tonic-gate error, project); 589*7c478bd9Sstevel@tonic-gate } 590*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 591*7c478bd9Sstevel@tonic-gate 592*7c478bd9Sstevel@tonic-gate /* relinquish added privileges */ 593*7c478bd9Sstevel@tonic-gate if (be_su) { 594*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_ON); 595*7c478bd9Sstevel@tonic-gate if (Psetpriv(p, old_prpriv) != 0) { 596*7c478bd9Sstevel@tonic-gate /* 597*7c478bd9Sstevel@tonic-gate * We shouldn't ever be in a state where we can't 598*7c478bd9Sstevel@tonic-gate * set the process back to its old creds, but we 599*7c478bd9Sstevel@tonic-gate * don't want to take the chance of leaving a 600*7c478bd9Sstevel@tonic-gate * non-privileged process with enhanced creds. So, 601*7c478bd9Sstevel@tonic-gate * release the process from libproc control, knowing 602*7c478bd9Sstevel@tonic-gate * that it will be killed. 603*7c478bd9Sstevel@tonic-gate */ 604*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 605*7c478bd9Sstevel@tonic-gate Pdestroy_agent(p); 606*7c478bd9Sstevel@tonic-gate die(gettext("cannot relinquish superuser credentials " 607*7c478bd9Sstevel@tonic-gate "for pid %d. The process was killed."), 608*7c478bd9Sstevel@tonic-gate Pstatus(p)->pr_pid); 609*7c478bd9Sstevel@tonic-gate } 610*7c478bd9Sstevel@tonic-gate (void) __priv_bracket(PRIV_OFF); 611*7c478bd9Sstevel@tonic-gate if (Punsetflags(p, PR_KLC) != 0) 612*7c478bd9Sstevel@tonic-gate preserve_error(gettext("error relinquishing " 613*7c478bd9Sstevel@tonic-gate "credentials. Process %d will be killed."), 614*7c478bd9Sstevel@tonic-gate Pstatus(p)->pr_pid); 615*7c478bd9Sstevel@tonic-gate } 616*7c478bd9Sstevel@tonic-gate free(new_prpriv); 617*7c478bd9Sstevel@tonic-gate free(old_prpriv); 618*7c478bd9Sstevel@tonic-gate 619*7c478bd9Sstevel@tonic-gate return (error); 620*7c478bd9Sstevel@tonic-gate } 621*7c478bd9Sstevel@tonic-gate 622*7c478bd9Sstevel@tonic-gate /* 623*7c478bd9Sstevel@tonic-gate * preserve_error() should be called rather than warn() by any 624*7c478bd9Sstevel@tonic-gate * function that is called while the victim process is being 625*7c478bd9Sstevel@tonic-gate * held by Pgrab. 626*7c478bd9Sstevel@tonic-gate * 627*7c478bd9Sstevel@tonic-gate * It saves a single error message to be printed until after 628*7c478bd9Sstevel@tonic-gate * the process has been released. Since multiple errors are not 629*7c478bd9Sstevel@tonic-gate * stored, any error should be considered critical. 630*7c478bd9Sstevel@tonic-gate */ 631*7c478bd9Sstevel@tonic-gate void 632*7c478bd9Sstevel@tonic-gate preserve_error(const char *format, ...) 633*7c478bd9Sstevel@tonic-gate { 634*7c478bd9Sstevel@tonic-gate va_list alist; 635*7c478bd9Sstevel@tonic-gate 636*7c478bd9Sstevel@tonic-gate va_start(alist, format); 637*7c478bd9Sstevel@tonic-gate 638*7c478bd9Sstevel@tonic-gate /* 639*7c478bd9Sstevel@tonic-gate * GLOBAL_ERR_SZ is pretty big. If the error is longer 640*7c478bd9Sstevel@tonic-gate * than that, just truncate it, rather than chance missing 641*7c478bd9Sstevel@tonic-gate * the error altogether. 642*7c478bd9Sstevel@tonic-gate */ 643*7c478bd9Sstevel@tonic-gate (void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist); 644*7c478bd9Sstevel@tonic-gate 645*7c478bd9Sstevel@tonic-gate va_end(alist); 646*7c478bd9Sstevel@tonic-gate 647*7c478bd9Sstevel@tonic-gate } 648*7c478bd9Sstevel@tonic-gate 649*7c478bd9Sstevel@tonic-gate /* 650*7c478bd9Sstevel@tonic-gate * Given the input arguments, return the passwd structure that matches best. 651*7c478bd9Sstevel@tonic-gate * Also, since we use getpwnam() and friends, subsequent calls to this 652*7c478bd9Sstevel@tonic-gate * function will re-use the memory previously returned. 653*7c478bd9Sstevel@tonic-gate */ 654*7c478bd9Sstevel@tonic-gate static struct passwd * 655*7c478bd9Sstevel@tonic-gate match_user(uid_t uid, char *projname, int is_my_uid) 656*7c478bd9Sstevel@tonic-gate { 657*7c478bd9Sstevel@tonic-gate char prbuf[PROJECT_BUFSZ], username[LOGNAME_MAX+1]; 658*7c478bd9Sstevel@tonic-gate struct project prj; 659*7c478bd9Sstevel@tonic-gate char *tmp_name; 660*7c478bd9Sstevel@tonic-gate struct passwd *pw = NULL; 661*7c478bd9Sstevel@tonic-gate 662*7c478bd9Sstevel@tonic-gate /* 663*7c478bd9Sstevel@tonic-gate * In order to allow users with the same UID but distinguishable 664*7c478bd9Sstevel@tonic-gate * user names to be in different projects we play a guessing 665*7c478bd9Sstevel@tonic-gate * game of which username is most appropriate. If we're checking 666*7c478bd9Sstevel@tonic-gate * for the uid of the calling process, the login name is a 667*7c478bd9Sstevel@tonic-gate * good starting point. 668*7c478bd9Sstevel@tonic-gate */ 669*7c478bd9Sstevel@tonic-gate if (is_my_uid) { 670*7c478bd9Sstevel@tonic-gate if ((tmp_name = getlogin()) == NULL || 671*7c478bd9Sstevel@tonic-gate (pw = getpwnam(tmp_name)) == NULL || (pw->pw_uid != uid) || 672*7c478bd9Sstevel@tonic-gate (pw->pw_name == NULL)) 673*7c478bd9Sstevel@tonic-gate pw = NULL; 674*7c478bd9Sstevel@tonic-gate } 675*7c478bd9Sstevel@tonic-gate 676*7c478bd9Sstevel@tonic-gate /* 677*7c478bd9Sstevel@tonic-gate * If the login name doesn't work, we try the first match for 678*7c478bd9Sstevel@tonic-gate * the current uid in the password file. 679*7c478bd9Sstevel@tonic-gate */ 680*7c478bd9Sstevel@tonic-gate if (pw == NULL) { 681*7c478bd9Sstevel@tonic-gate if (((pw = getpwuid(uid)) == NULL) || pw->pw_name == NULL) { 682*7c478bd9Sstevel@tonic-gate preserve_error(gettext("cannot find username " 683*7c478bd9Sstevel@tonic-gate "for uid %d"), uid); 684*7c478bd9Sstevel@tonic-gate return (NULL); 685*7c478bd9Sstevel@tonic-gate } 686*7c478bd9Sstevel@tonic-gate } 687*7c478bd9Sstevel@tonic-gate 688*7c478bd9Sstevel@tonic-gate /* 689*7c478bd9Sstevel@tonic-gate * If projname wasn't supplied, we've done our best, so just return 690*7c478bd9Sstevel@tonic-gate * what we've got now. Alternatively, if newtask's invoker has 691*7c478bd9Sstevel@tonic-gate * superuser privileges, return the pw structure we've got now, with 692*7c478bd9Sstevel@tonic-gate * no further checking from inproj(). Superuser should be able to 693*7c478bd9Sstevel@tonic-gate * join any project, and the subsequent call to setproject() will 694*7c478bd9Sstevel@tonic-gate * allow this. 695*7c478bd9Sstevel@tonic-gate */ 696*7c478bd9Sstevel@tonic-gate if (projname == NULL || getuid() == (uid_t)0) 697*7c478bd9Sstevel@tonic-gate return (pw); 698*7c478bd9Sstevel@tonic-gate 699*7c478bd9Sstevel@tonic-gate (void) strcpy(username, pw->pw_name); 700*7c478bd9Sstevel@tonic-gate 701*7c478bd9Sstevel@tonic-gate if (inproj(username, projname, prbuf, PROJECT_BUFSZ) == 0) { 702*7c478bd9Sstevel@tonic-gate char **u; 703*7c478bd9Sstevel@tonic-gate tmp_name = NULL; 704*7c478bd9Sstevel@tonic-gate 705*7c478bd9Sstevel@tonic-gate /* 706*7c478bd9Sstevel@tonic-gate * If the previous guesses didn't work, walk through all 707*7c478bd9Sstevel@tonic-gate * project members and test for UID-equivalence. 708*7c478bd9Sstevel@tonic-gate */ 709*7c478bd9Sstevel@tonic-gate 710*7c478bd9Sstevel@tonic-gate if (getprojbyname(projname, &prj, prbuf, 711*7c478bd9Sstevel@tonic-gate PROJECT_BUFSZ) == NULL) { 712*7c478bd9Sstevel@tonic-gate preserve_error(gettext("unknown project \"%s\""), 713*7c478bd9Sstevel@tonic-gate projname); 714*7c478bd9Sstevel@tonic-gate return (NULL); 715*7c478bd9Sstevel@tonic-gate } 716*7c478bd9Sstevel@tonic-gate 717*7c478bd9Sstevel@tonic-gate for (u = prj.pj_users; *u; u++) { 718*7c478bd9Sstevel@tonic-gate if ((pw = getpwnam(*u)) == NULL) 719*7c478bd9Sstevel@tonic-gate continue; 720*7c478bd9Sstevel@tonic-gate 721*7c478bd9Sstevel@tonic-gate if (pw->pw_uid == uid) { 722*7c478bd9Sstevel@tonic-gate tmp_name = pw->pw_name; 723*7c478bd9Sstevel@tonic-gate break; 724*7c478bd9Sstevel@tonic-gate } 725*7c478bd9Sstevel@tonic-gate } 726*7c478bd9Sstevel@tonic-gate 727*7c478bd9Sstevel@tonic-gate if (tmp_name == NULL) { 728*7c478bd9Sstevel@tonic-gate preserve_error(gettext("user \"%s\" is not a member of " 729*7c478bd9Sstevel@tonic-gate "project \"%s\""), username, projname); 730*7c478bd9Sstevel@tonic-gate return (NULL); 731*7c478bd9Sstevel@tonic-gate } 732*7c478bd9Sstevel@tonic-gate } 733*7c478bd9Sstevel@tonic-gate 734*7c478bd9Sstevel@tonic-gate return (pw); 735*7c478bd9Sstevel@tonic-gate } 736*7c478bd9Sstevel@tonic-gate 737*7c478bd9Sstevel@tonic-gate void 738*7c478bd9Sstevel@tonic-gate setproject_err(char *username, char *projname, int error, struct project *proj) 739*7c478bd9Sstevel@tonic-gate { 740*7c478bd9Sstevel@tonic-gate kva_t *kv_array = NULL; 741*7c478bd9Sstevel@tonic-gate char prbuf[PROJECT_BUFSZ]; 742*7c478bd9Sstevel@tonic-gate struct project local_proj; 743*7c478bd9Sstevel@tonic-gate 744*7c478bd9Sstevel@tonic-gate switch (error) { 745*7c478bd9Sstevel@tonic-gate case SETPROJ_ERR_TASK: 746*7c478bd9Sstevel@tonic-gate if (errno == EAGAIN) 747*7c478bd9Sstevel@tonic-gate preserve_error(gettext("resource control limit has " 748*7c478bd9Sstevel@tonic-gate "been reached")); 749*7c478bd9Sstevel@tonic-gate else if (errno == ESRCH) 750*7c478bd9Sstevel@tonic-gate preserve_error(gettext("user \"%s\" is not a member of " 751*7c478bd9Sstevel@tonic-gate "project \"%s\""), username, projname); 752*7c478bd9Sstevel@tonic-gate else if (errno == EACCES) 753*7c478bd9Sstevel@tonic-gate preserve_error(gettext("the invoking task is final")); 754*7c478bd9Sstevel@tonic-gate else 755*7c478bd9Sstevel@tonic-gate preserve_error( 756*7c478bd9Sstevel@tonic-gate gettext("could not join project \"%s\""), 757*7c478bd9Sstevel@tonic-gate projname); 758*7c478bd9Sstevel@tonic-gate break; 759*7c478bd9Sstevel@tonic-gate case SETPROJ_ERR_POOL: 760*7c478bd9Sstevel@tonic-gate if (errno == EACCES) 761*7c478bd9Sstevel@tonic-gate preserve_error(gettext("no resource pool accepting " 762*7c478bd9Sstevel@tonic-gate "default bindings exists for project \"%s\""), 763*7c478bd9Sstevel@tonic-gate projname); 764*7c478bd9Sstevel@tonic-gate else if (errno == ESRCH) 765*7c478bd9Sstevel@tonic-gate preserve_error(gettext("specified resource pool does " 766*7c478bd9Sstevel@tonic-gate "not exist for project \"%s\""), projname); 767*7c478bd9Sstevel@tonic-gate else 768*7c478bd9Sstevel@tonic-gate preserve_error(gettext("could not bind to default " 769*7c478bd9Sstevel@tonic-gate "resource pool for project \"%s\""), projname); 770*7c478bd9Sstevel@tonic-gate break; 771*7c478bd9Sstevel@tonic-gate default: 772*7c478bd9Sstevel@tonic-gate if (error <= 0) { 773*7c478bd9Sstevel@tonic-gate preserve_error(gettext("setproject failed for " 774*7c478bd9Sstevel@tonic-gate "project \"%s\""), projname); 775*7c478bd9Sstevel@tonic-gate return; 776*7c478bd9Sstevel@tonic-gate } 777*7c478bd9Sstevel@tonic-gate /* 778*7c478bd9Sstevel@tonic-gate * If we have a stopped target process it may be in 779*7c478bd9Sstevel@tonic-gate * getprojbyname()'s execution path which would make it unsafe 780*7c478bd9Sstevel@tonic-gate * to access the project table, so only do that if the caller 781*7c478bd9Sstevel@tonic-gate * hasn't provided a cached version of the project structure. 782*7c478bd9Sstevel@tonic-gate */ 783*7c478bd9Sstevel@tonic-gate if (proj == NULL) 784*7c478bd9Sstevel@tonic-gate proj = getprojbyname(projname, &local_proj, prbuf, 785*7c478bd9Sstevel@tonic-gate PROJECT_BUFSZ); 786*7c478bd9Sstevel@tonic-gate 787*7c478bd9Sstevel@tonic-gate if (proj == NULL || (kv_array = _str2kva(proj->pj_attr, 788*7c478bd9Sstevel@tonic-gate KV_ASSIGN, KV_DELIMITER)) == NULL || 789*7c478bd9Sstevel@tonic-gate kv_array->length < error) { 790*7c478bd9Sstevel@tonic-gate preserve_error(gettext("warning, resource control " 791*7c478bd9Sstevel@tonic-gate "assignment failed for project \"%s\" " 792*7c478bd9Sstevel@tonic-gate "attribute %d"), 793*7c478bd9Sstevel@tonic-gate projname, error); 794*7c478bd9Sstevel@tonic-gate if (kv_array) 795*7c478bd9Sstevel@tonic-gate _kva_free(kv_array); 796*7c478bd9Sstevel@tonic-gate return; 797*7c478bd9Sstevel@tonic-gate } 798*7c478bd9Sstevel@tonic-gate preserve_error(gettext("warning, %s resource control " 799*7c478bd9Sstevel@tonic-gate "assignment failed for project \"%s\""), 800*7c478bd9Sstevel@tonic-gate kv_array->data[error - 1].key, projname); 801*7c478bd9Sstevel@tonic-gate _kva_free(kv_array); 802*7c478bd9Sstevel@tonic-gate } 803*7c478bd9Sstevel@tonic-gate } 804