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
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.
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]
23 * Copyright (c) 2013 Gary Mills
24 * Copyright 2015, Joyent, Inc.
26 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
30 #include <sys/types.h>
44 #include <sys/varargs.h>
48 #include <priv_utils.h>
52 #define OPTIONS_STRING "Fc:lp:v"
55 #define PATH "PATH=/usr/bin"
56 #define SUPATH "PATH=/usr/sbin:/usr/bin"
57 #define SHELL "/usr/bin/sh"
58 #define SHELL2 "/sbin/sh"
59 #define TIMEZONEFILE "/etc/default/init"
60 #define LOGINFILE "/etc/default/login"
61 #define GLOBAL_ERR_SZ 1024
62 #define GRAB_RETRY_MAX 100
64 static const char *pname
;
65 extern char **environ
;
66 static char *supath
= SUPATH
;
67 static char *path
= PATH
;
68 static char global_error
[GLOBAL_ERR_SZ
];
69 static int verbose
= 0;
71 static priv_set_t
*nset
;
73 /* Private definitions for libproject */
74 extern projid_t
setproject_proc(const char *, const char *, int, pid_t
,
75 struct ps_prochandle
*, struct project
*);
76 extern priv_set_t
*setproject_initpriv(void);
78 static void usage(void);
80 static void preserve_error(const char *format
, ...);
82 static int update_running_proc(int, char *, char *);
83 static int set_ids(struct ps_prochandle
*, struct project
*,
85 static struct passwd
*match_user(uid_t
, char *, int);
86 static void setproject_err(char *, char *, int, struct project
*);
91 (void) fprintf(stderr
, gettext("usage: \n\t%s [-v] [-p project] "
92 "[-c pid | [-Fl] [command [args ...]]]\n"), pname
);
97 main(int argc
, char *argv
[])
101 char *projname
= NULL
;
104 int finalize_flag
= TASK_NORMAL
;
105 int newproj_flag
= 0;
110 char *filename
, *procname
= NULL
;
113 nset
= setproject_initpriv();
115 die(gettext("privilege initialization failed\n"));
117 pname
= getpname(argv
[0]);
119 while ((c
= getopt(argc
, argv
, OPTIONS_STRING
)) != EOF
) {
129 finalize_flag
= TASK_FINAL
;
144 /* -c option is invalid with -F, -l, or a specified command */
145 if ((procname
!= NULL
) &&
146 (finalize_flag
== TASK_FINAL
|| login_flag
|| optind
< argc
))
149 if (procname
!= NULL
) {
150 /* Change project/task of an existing process */
151 return (update_running_proc(newproj_flag
, procname
, projname
));
155 * Get user data, so that we can confirm project membership as
156 * well as construct an appropriate login environment.
159 if ((pw
= match_user(uid
, projname
, 1)) == NULL
) {
160 die("%s\n", global_error
);
164 * If no projname was specified, we're just creating a new task
165 * under the current project, so we can just set the new taskid.
166 * If our project is changing, we need to update any attendant
167 * pool/rctl bindings, so let setproject() do the dirty work.
169 (void) __priv_bracket(PRIV_ON
);
170 if (projname
== NULL
) {
171 if (settaskid(getprojid(), finalize_flag
) == -1)
173 die(gettext("resource control limit has been "
176 die(gettext("settaskid failed"));
178 if ((error
= setproject(projname
,
179 pw
->pw_name
, finalize_flag
)) != 0) {
180 setproject_err(pw
->pw_name
, projname
, error
, NULL
);
182 die("%s\n", global_error
);
184 warn("%s\n", global_error
);
189 taskid
= gettaskid();
192 (void) fprintf(stderr
, "%d\n", (int)taskid
);
195 * Validate user's shell from passwd database.
197 if (strcmp(pw
->pw_shell
, "") == 0) {
198 if (access(SHELL
, X_OK
) == 0)
199 pw
->pw_shell
= SHELL
;
201 pw
->pw_shell
= SHELL2
;
206 * Since we've been invoked as a "simulated login", set up the
209 char *cur_tz
= getenv("TZ");
210 char *cur_term
= getenv("TERM");
214 size_t len_home
= strlen(pw
->pw_dir
) + strlen("HOME=") + 1;
215 size_t len_logname
= strlen(pw
->pw_name
) + strlen("LOGNAME=") +
217 size_t len_shell
= strlen(pw
->pw_shell
) + strlen("SHELL=") + 1;
218 size_t len_mail
= strlen(pw
->pw_name
) +
219 strlen("MAIL=/var/mail/") + 1;
223 char *env_home
= safe_malloc(len_home
);
224 char *env_logname
= safe_malloc(len_logname
);
225 char *env_shell
= safe_malloc(len_shell
);
226 char *env_mail
= safe_malloc(len_mail
);
230 (void) snprintf(env_home
, len_home
, "HOME=%s", pw
->pw_dir
);
231 (void) snprintf(env_logname
, len_logname
, "LOGNAME=%s",
233 (void) snprintf(env_shell
, len_shell
, "SHELL=%s", pw
->pw_shell
);
234 (void) snprintf(env_mail
, len_mail
, "MAIL=/var/mail/%s",
238 env
[1] = env_logname
;
239 env
[2] = (pw
->pw_uid
== 0 ? supath
: path
);
246 envnext
= (char **)&env
[5];
249 * It's possible that TERM wasn't defined in the outer
252 if (cur_term
!= NULL
) {
253 len_term
= strlen(cur_term
) + strlen("TERM=") + 1;
254 env_term
= safe_malloc(len_term
);
256 (void) snprintf(env_term
, len_term
, "TERM=%s",
263 * It is also possible that TZ wasn't defined in the outer
264 * environment. In that case, we must attempt to open the file
265 * defining the default timezone and select the appropriate
266 * entry. If there is no default timezone there, try
267 * TIMEZONE in /etc/default/login, duplicating the algorithm
270 if (cur_tz
!= NULL
) {
271 len_tz
= strlen(cur_tz
) + strlen("TZ=") + 1;
272 env_tz
= safe_malloc(len_tz
);
274 (void) snprintf(env_tz
, len_tz
, "TZ=%s", cur_tz
);
277 if ((env_tz
= getdefault(TIMEZONEFILE
, "TZ=",
281 env_tz
= getdefault(LOGINFILE
, "TIMEZONE=",
287 environ
= (char **)&env
[0];
290 * Prefix the shell string with a hyphen, indicating a login
293 shell
= safe_malloc(PATH_MAX
);
294 (void) snprintf(shell
, PATH_MAX
, "-%s", basename(pw
->pw_shell
));
296 shell
= basename(pw
->pw_shell
);
300 * If there are no arguments, we launch the user's shell; otherwise, the
301 * remaining commands are assumed to form a valid command invocation
304 if (optind
>= argc
) {
305 targs
= alloca(2 * sizeof (char *));
306 filename
= pw
->pw_shell
;
310 targs
= &argv
[optind
];
314 if (execvp(filename
, targs
) == -1)
315 die(gettext("exec of %s failed"), targs
[0]);
318 * We should never get here.
324 update_running_proc(int newproj_flag
, char *procname
, char *projname
)
326 struct ps_prochandle
*p
;
327 prcred_t original_prcred
, current_prcred
;
331 struct project project
;
332 char prbuf
[PROJECT_BUFSZ
];
333 struct passwd
*passwd_entry
;
334 int grab_retry_count
= 0;
337 * Catch signals from terminal. There isn't much sense in
338 * doing anything but ignoring them since we don't do anything
339 * after the point we'd be capable of handling them again.
341 (void) sigignore(SIGHUP
);
342 (void) sigignore(SIGINT
);
343 (void) sigignore(SIGQUIT
);
344 (void) sigignore(SIGTERM
);
346 /* flush stdout before grabbing the proc to avoid deadlock */
347 (void) fflush(stdout
);
350 * We need to grab the process, which will force it to stop execution
351 * until the grab is released, in order to aquire some information about
352 * it, such as its current project (which is achieved via an injected
353 * system call and therefore needs an agent) and its credentials. We
354 * will then need to release it again because it may be a process that
355 * we rely on for later calls, for example nscd.
357 if ((p
= proc_arg_grab(procname
, PR_ARG_PIDS
, 0, &gret
)) == NULL
) {
358 warn(gettext("failed to grab for process %s: %s\n"),
359 procname
, Pgrab_error(gret
));
362 if (Pcreate_agent(p
) != 0) {
364 warn(gettext("cannot control process %s\n"), procname
);
369 * The victim process is now held. Do not call any functions
370 * which generate stdout/stderr until the process has been
375 * The target process will soon be restarted (in case it is in newtask's
376 * execution path) and then stopped again. We need to ensure that our cached
377 * data doesn't change while the process runs so return here if the target
378 * process changes its user id in between our stop operations, so that we can
383 /* Cache required information about the process. */
384 if (Pcred(p
, &original_prcred
, 0) != 0) {
385 preserve_error(gettext("cannot get process credentials %s\n"),
389 if ((prprojid
= pr_getprojid(p
)) == -1) {
390 preserve_error(gettext("cannot get process project id %s\n"),
396 * We now have all the required information, so release the target
397 * process and perform our sanity checks. The process needs to be
398 * running at this point because it may be in the execution path of the
404 /* if our data acquisition failed, then we can't continue. */
406 warn("%s\n", global_error
);
410 if (newproj_flag
== 0) {
412 * Just changing the task, so set projname to the current
413 * project of the running process.
415 if (getprojbyid(prprojid
, &project
, &prbuf
,
416 PROJECT_BUFSZ
) == NULL
) {
417 warn(gettext("unable to get project name "
418 "for projid %d"), prprojid
);
421 projname
= project
.pj_name
;
424 * cache info for the project which user passed in via the
427 if (getprojbyname(projname
, &project
, &prbuf
,
428 PROJECT_BUFSZ
) == NULL
) {
429 warn(gettext("unknown project \"%s\"\n"), projname
);
435 * Use our cached information to verify that the owner of the running
436 * process is a member of proj
438 if ((passwd_entry
= match_user(original_prcred
.pr_ruid
,
439 projname
, 0)) == NULL
) {
440 warn("%s\n", global_error
);
445 * We can now safely stop the process again in order to change the
446 * project and taskid as required.
448 if ((p
= proc_arg_grab(procname
, PR_ARG_PIDS
, 0, &gret
)) == NULL
) {
449 warn(gettext("failed to grab for process %s: %s\n"),
450 procname
, Pgrab_error(gret
));
453 if (Pcreate_agent(p
) != 0) {
455 warn(gettext("cannot control process %s\n"), procname
);
460 * Now that the target process is stopped, check the validity of our
461 * cached info. If we aren't superuser then match_user() will have
462 * checked to make sure that the owner of the process is in the relevant
463 * project. If our ruid has changed, then match_user()'s conclusion may
467 if (Pcred(p
, ¤t_prcred
, 0) != 0) {
470 warn(gettext("can't get process credentials %s\n"),
475 if (original_prcred
.pr_ruid
!= current_prcred
.pr_ruid
) {
476 if (grab_retry_count
++ < GRAB_RETRY_MAX
)
479 warn(gettext("process consistently changed its "
480 "user id %s\n"), procname
);
485 error
= set_ids(p
, &project
, passwd_entry
);
488 taskid
= pr_gettaskid(p
);
495 * error is serious enough to stop, only if negative.
496 * Otherwise, it simply indicates one of the resource
497 * control assignments failed, which is worth warning
500 warn("%s\n", global_error
);
506 (void) fprintf(stderr
, "%d\n", (int)taskid
);
512 set_ids(struct ps_prochandle
*p
, struct project
*project
,
513 struct passwd
*passwd_entry
)
518 prpriv_t
*old_prpriv
, *new_prpriv
;
519 size_t prsz
= sizeof (prpriv_t
);
520 priv_set_t
*eset
, *pset
;
523 if (Pcred(p
, &old_prcred
, 0) != 0) {
524 preserve_error(gettext("can't get process credentials"));
528 old_prpriv
= proc_get_priv(Pstatus(p
)->pr_pid
);
529 if (old_prpriv
== NULL
) {
530 preserve_error(gettext("can't get process privileges"));
534 prsz
= PRIV_PRPRIV_SIZE(old_prpriv
);
536 new_prpriv
= malloc(prsz
);
537 if (new_prpriv
== NULL
) {
538 preserve_error(gettext("can't allocate memory"));
539 proc_free_priv(old_prpriv
);
543 (void) memcpy(new_prpriv
, old_prpriv
, prsz
);
546 * If the process already has the proc_taskid privilege,
547 * we don't need to elevate its privileges; if it doesn't,
548 * we try to do it here.
549 * As we do not wish to leave a window in which the process runs
550 * with elevated privileges, we make sure that the process dies
551 * when we go away unexpectedly.
554 ind
= priv_getsetbyname(PRIV_EFFECTIVE
);
555 eset
= (priv_set_t
*)&new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
* ind
];
556 ind
= priv_getsetbyname(PRIV_PERMITTED
);
557 pset
= (priv_set_t
*)&new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
* ind
];
559 if (!priv_issubset(nset
, eset
)) {
561 priv_union(nset
, eset
);
562 priv_union(nset
, pset
);
563 if (Psetflags(p
, PR_KLC
) != 0) {
564 preserve_error(gettext("cannot set process "
566 (void) Punsetflags(p
, PR_KLC
);
568 proc_free_priv(old_prpriv
);
571 (void) __priv_bracket(PRIV_ON
);
572 if (Psetpriv(p
, new_prpriv
) != 0) {
573 (void) __priv_bracket(PRIV_OFF
);
574 preserve_error(gettext("cannot set process "
576 (void) Punsetflags(p
, PR_KLC
);
578 proc_free_priv(old_prpriv
);
581 (void) __priv_bracket(PRIV_OFF
);
584 (void) __priv_bracket(PRIV_ON
);
585 if ((error
= setproject_proc(project
->pj_name
,
586 passwd_entry
->pw_name
, 0, Pstatus(p
)->pr_pid
, p
, project
)) != 0) {
587 /* global_error is set by setproject_err */
588 setproject_err(passwd_entry
->pw_name
, project
->pj_name
,
591 (void) __priv_bracket(PRIV_OFF
);
593 /* relinquish added privileges */
595 (void) __priv_bracket(PRIV_ON
);
596 if (Psetpriv(p
, old_prpriv
) != 0) {
598 * We shouldn't ever be in a state where we can't
599 * set the process back to its old creds, but we
600 * don't want to take the chance of leaving a
601 * non-privileged process with enhanced creds. So,
602 * release the process from libproc control, knowing
603 * that it will be killed.
605 (void) __priv_bracket(PRIV_OFF
);
607 die(gettext("cannot relinquish superuser credentials "
608 "for pid %d. The process was killed."),
611 (void) __priv_bracket(PRIV_OFF
);
612 if (Punsetflags(p
, PR_KLC
) != 0)
613 preserve_error(gettext("error relinquishing "
614 "credentials. Process %d will be killed."),
618 proc_free_priv(old_prpriv
);
624 * preserve_error() should be called rather than warn() by any
625 * function that is called while the victim process is being
628 * It saves a single error message to be printed until after
629 * the process has been released. Since multiple errors are not
630 * stored, any error should be considered critical.
633 preserve_error(const char *format
, ...)
637 va_start(alist
, format
);
640 * GLOBAL_ERR_SZ is pretty big. If the error is longer
641 * than that, just truncate it, rather than chance missing
642 * the error altogether.
644 (void) vsnprintf(global_error
, GLOBAL_ERR_SZ
-1, format
, alist
);
651 * Given the input arguments, return the passwd structure that matches best.
652 * Also, since we use getpwnam() and friends, subsequent calls to this
653 * function will re-use the memory previously returned.
655 static struct passwd
*
656 match_user(uid_t uid
, char *projname
, int is_my_uid
)
658 char prbuf
[PROJECT_BUFSZ
], username
[LOGNAME_MAX
+1];
661 struct passwd
*pw
= NULL
;
664 * In order to allow users with the same UID but distinguishable
665 * user names to be in different projects we play a guessing
666 * game of which username is most appropriate. If we're checking
667 * for the uid of the calling process, the login name is a
668 * good starting point.
671 if ((tmp_name
= getlogin()) == NULL
||
672 (pw
= getpwnam(tmp_name
)) == NULL
|| (pw
->pw_uid
!= uid
) ||
673 (pw
->pw_name
== NULL
))
678 * If the login name doesn't work, we try the first match for
679 * the current uid in the password file.
682 if (((pw
= getpwuid(uid
)) == NULL
) || pw
->pw_name
== NULL
) {
683 preserve_error(gettext("cannot find username "
690 * If projname wasn't supplied, we've done our best, so just return
691 * what we've got now. Alternatively, if newtask's invoker has
692 * superuser privileges, return the pw structure we've got now, with
693 * no further checking from inproj(). Superuser should be able to
694 * join any project, and the subsequent call to setproject() will
697 if (projname
== NULL
|| getuid() == (uid_t
)0)
700 (void) strlcpy(username
, pw
->pw_name
, sizeof (username
));
702 if (inproj(username
, projname
, prbuf
, PROJECT_BUFSZ
) == 0) {
707 * If the previous guesses didn't work, walk through all
708 * project members and test for UID-equivalence.
711 if (getprojbyname(projname
, &prj
, prbuf
,
712 PROJECT_BUFSZ
) == NULL
) {
713 preserve_error(gettext("unknown project \"%s\""),
718 for (u
= prj
.pj_users
; *u
; u
++) {
719 if ((pw
= getpwnam(*u
)) == NULL
)
722 if (pw
->pw_uid
== uid
) {
723 tmp_name
= pw
->pw_name
;
728 if (tmp_name
== NULL
) {
729 preserve_error(gettext("user \"%s\" is not a member of "
730 "project \"%s\""), username
, projname
);
739 setproject_err(char *username
, char *projname
, int error
, struct project
*proj
)
741 kva_t
*kv_array
= NULL
;
742 char prbuf
[PROJECT_BUFSZ
];
743 struct project local_proj
;
746 case SETPROJ_ERR_TASK
:
748 preserve_error(gettext("resource control limit has "
750 else if (errno
== ESRCH
)
751 preserve_error(gettext("user \"%s\" is not a member of "
752 "project \"%s\""), username
, projname
);
753 else if (errno
== EACCES
)
754 preserve_error(gettext("the invoking task is final"));
757 gettext("could not join project \"%s\""),
760 case SETPROJ_ERR_POOL
:
762 preserve_error(gettext("no resource pool accepting "
763 "default bindings exists for project \"%s\""),
765 else if (errno
== ESRCH
)
766 preserve_error(gettext("specified resource pool does "
767 "not exist for project \"%s\""), projname
);
769 preserve_error(gettext("could not bind to default "
770 "resource pool for project \"%s\""), projname
);
774 preserve_error(gettext("setproject failed for "
775 "project \"%s\""), projname
);
779 * If we have a stopped target process it may be in
780 * getprojbyname()'s execution path which would make it unsafe
781 * to access the project table, so only do that if the caller
782 * hasn't provided a cached version of the project structure.
785 proj
= getprojbyname(projname
, &local_proj
, prbuf
,
788 if (proj
== NULL
|| (kv_array
= _str2kva(proj
->pj_attr
,
789 KV_ASSIGN
, KV_DELIMITER
)) == NULL
||
790 kv_array
->length
< error
) {
791 preserve_error(gettext("warning, resource control "
792 "assignment failed for project \"%s\" "
799 preserve_error(gettext("warning, %s resource control "
800 "assignment failed for project \"%s\""),
801 kv_array
->data
[error
- 1].key
, projname
);