summaryrefslogtreecommitdiffstats
path: root/shell_cmds/su
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /shell_cmds/su
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'shell_cmds/su')
-rw-r--r--shell_cmds/su/su.1213
-rw-r--r--shell_cmds/su/su.c737
-rw-r--r--shell_cmds/su/su.pam7
-rw-r--r--shell_cmds/su/su_entitlements.plist10
4 files changed, 967 insertions, 0 deletions
diff --git a/shell_cmds/su/su.1 b/shell_cmds/su/su.1
new file mode 100644
index 0000000..014a567
--- /dev/null
+++ b/shell_cmds/su/su.1
@@ -0,0 +1,213 @@
+.\" Copyright (c) 1988, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)su.1 8.2 (Berkeley) 4/18/94
+.\" $FreeBSD: src/usr.bin/su/su.1,v 1.40 2007/07/24 06:41:07 delphij Exp $
+.\"
+.Dd September 13, 2006
+.Dt SU 1
+.Os
+.Sh NAME
+.Nm su
+.Nd substitute user identity
+.Sh SYNOPSIS
+.Nm
+.Op Fl
+.Op Fl flm
+.Op Ar login Op Ar args
+.Sh DESCRIPTION
+The
+.Nm
+utility requests appropriate user credentials via PAM
+and switches to that user ID
+(the default user is the superuser).
+A shell is then executed.
+.Pp
+PAM is used to set the policy
+.Xr su 1
+will use.
+In particular, by default only users in the
+.Dq Li admin
+or
+.Dq Li wheel
+groups can switch to UID 0
+.Pq Dq Li root .
+This group requirement may be changed by modifying the
+.Dq Li pam_group
+section of
+.Pa /etc/pam.d/su .
+See
+.Xr pam_group 8
+for details on how to modify this setting.
+.Pp
+By default, the environment is unmodified with the exception of
+.Ev USER ,
+.Ev HOME ,
+and
+.Ev SHELL .
+.Ev HOME
+and
+.Ev SHELL
+are set to the target login's default values.
+.Ev USER
+is set to the target login, unless the target login has a user ID of 0,
+in which case it is unmodified.
+The invoked shell is the one belonging to the target login.
+This is the traditional behavior of
+.Nm .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f
+If the invoked shell is
+.Xr csh 1 ,
+this option prevents it from reading the
+.Dq Pa .cshrc
+file.
+.It Fl l
+Simulate a full login.
+The environment is discarded except for
+.Ev HOME ,
+.Ev SHELL ,
+.Ev PATH ,
+.Ev TERM ,
+and
+.Ev USER .
+.Ev HOME
+and
+.Ev SHELL
+are modified as above.
+.Ev USER
+is set to the target login.
+.Ev PATH
+is set to
+.Dq Pa /bin:/usr/bin .
+.Ev TERM
+is imported from your current environment.
+The invoked shell is the target login's, and
+.Nm
+will change directory to the target login's home directory.
+.It Fl
+(no letter) The same as
+.Fl l .
+.It Fl m
+Leave the environment unmodified.
+The invoked shell is your login shell, and no directory changes are made.
+As a security precaution, if the target user's shell is a non-standard
+shell (as defined by
+.Xr getusershell 3 )
+and the caller's real uid is
+non-zero,
+.Nm
+will fail.
+.El
+.Pp
+The
+.Fl l
+(or
+.Fl )
+and
+.Fl m
+options are mutually exclusive; the last one specified
+overrides any previous ones.
+.Pp
+If the optional
+.Ar args
+are provided on the command line, they are passed to the login shell of
+the target login.
+Note that all command line arguments before the target login name are
+processed by
+.Nm
+itself, everything after the target login name gets passed to the login
+shell.
+.Pp
+By default (unless the prompt is reset by a startup file) the super-user
+prompt is set to
+.Dq Sy \&#
+to remind one of its awesome power.
+.Sh ENVIRONMENT
+Environment variables used by
+.Nm :
+.Bl -tag -width HOME
+.It Ev HOME
+Default home directory of real user ID unless modified as
+specified above.
+.It Ev PATH
+Default search path of real user ID unless modified as specified above.
+.It Ev TERM
+Provides terminal type which may be retained for the substituted
+user ID.
+.It Ev USER
+The user ID is always the effective ID (the target user ID) after an
+.Nm
+unless the user ID is 0 (root).
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/pam.d/su" -compact
+.It Pa /etc/pam.d/su
+PAM configuration for
+.Nm .
+.El
+.Sh EXAMPLES
+.Bl -tag -width 5n -compact
+.It Li "su man -c catman"
+Runs the command
+.Li catman
+as user
+.Li man .
+You will be asked for man's password unless your real UID is 0.
+.It Li "su man -c 'catman /usr/share/man /usr/local/man'"
+Same as above, but the target command consists of more than a
+single word and hence is quoted for use with the
+.Fl c
+option being passed to the shell.
+(Most shells expect the argument to
+.Fl c
+to be a single word).
+.It Li "su -l foo"
+Simulate a login for user foo.
+.It Li "su - foo"
+Same as above.
+.It Li "su -"
+Simulate a login for root.
+.El
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr sh 1 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr environ 7 ,
+.Xr pam_group 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/shell_cmds/su/su.c b/shell_cmds/su/su.c
new file mode 100644
index 0000000..8ad7434
--- /dev/null
+++ b/shell_cmds/su/su.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/su/su.c,v 1.91 2009/12/13 03:14:06 delphij Exp $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#ifdef USE_BSM_AUDIT
+#include <bsm/libbsm.h>
+#include <bsm/audit_uevents.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#ifndef __APPLE__
+#include <login_cap.h>
+#endif /* !__APPLE__ */
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+
+#ifdef __APPLE__
+#include <bsm/audit_session.h>
+#endif /* __APPLE__ */
+
+#define PAM_END() do { \
+ int local_ret; \
+ if (pamh != NULL) { \
+ local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_setcred: %s", \
+ pam_strerror(pamh, local_ret)); \
+ if (asthem) { \
+ local_ret = pam_close_session(pamh, 0); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_close_session: %s",\
+ pam_strerror(pamh, local_ret)); \
+ } \
+ local_ret = pam_end(pamh, local_ret); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_end: %s", \
+ pam_strerror(pamh, local_ret)); \
+ } \
+} while (0)
+
+
+#define PAM_SET_ITEM(what, item) do { \
+ int local_ret; \
+ local_ret = pam_set_item(pamh, what, item); \
+ if (local_ret != PAM_SUCCESS) { \
+ syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
+ pam_strerror(pamh, local_ret)); \
+ errx(1, "pam_set_item(" #what "): %s", \
+ pam_strerror(pamh, local_ret)); \
+ /* NOTREACHED */ \
+ } \
+} while (0)
+
+enum tristate { UNSET, YES, NO };
+
+static pam_handle_t *pamh = NULL;
+static char **environ_pam;
+
+static char *ontty(void);
+static int chshell(const char *);
+static void usage(void) __dead2;
+static void export_pam_environment(void);
+static int ok_to_export(const char *);
+
+extern char **environ;
+
+int
+main(int argc, char *argv[])
+{
+ static char *cleanenv;
+ struct passwd *pwd;
+ struct pam_conv conv = { openpam_ttyconv, NULL };
+ enum tristate iscsh;
+#ifndef __APPLE__
+ login_cap_t *lc;
+#endif /* !__APPLE__ */
+ union {
+ const char **a;
+ char * const *b;
+ } np;
+ uid_t ruid;
+ pid_t child_pid, child_pgrp, pid;
+ int asme, ch, asthem, fastlogin, prio, i, retcode,
+ statusp, setmaclabel;
+#ifndef __APPLE__
+ u_int setwhat;
+#endif /* !__APPLE__ */
+ char *username, *class, shellbuf[MAXPATHLEN];
+ const char *p, *user, *shell, *mytty, **nargv;
+ const void *v;
+ struct sigaction sa, sa_int, sa_quit, sa_pipe;
+ int temp, fds[2];
+#ifdef USE_BSM_AUDIT
+ const char *aerr;
+ au_id_t auid;
+#endif
+#ifdef __APPLE__
+ /* 4043304 */
+ const char *avshell;
+ char avshellbuf[MAXPATHLEN];
+#endif /* __APPLE__ */
+
+ shell = class = cleanenv = NULL;
+ asme = asthem = fastlogin = statusp = 0;
+ user = "root";
+ iscsh = UNSET;
+ setmaclabel = 0;
+
+#ifdef __APPLE__
+ while ((ch = getopt(argc, argv, "-flm")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
+#endif /* __APPLE__ */
+ switch ((char)ch) {
+ case 'f':
+ fastlogin = 1;
+ break;
+ case '-':
+ case 'l':
+ asme = 0;
+ asthem = 1;
+ break;
+ case 'm':
+ asme = 1;
+ asthem = 0;
+ break;
+#ifndef __APPLE__
+ case 's':
+ setmaclabel = 1;
+ break;
+ case 'c':
+ class = optarg;
+ break;
+#endif /* !__APPLE__ */
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (optind < argc)
+ user = argv[optind++];
+
+ if (user == NULL)
+ usage();
+ /* NOTREACHED */
+
+ /*
+ * Try to provide more helpful debugging output if su(1) is running
+ * non-setuid, or was run from a file system not mounted setuid.
+ */
+ if (geteuid() != 0)
+ errx(1, "not running setuid");
+
+#ifdef USE_BSM_AUDIT
+ if (getauid(&auid) < 0 && errno != ENOSYS) {
+ syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
+ errx(1, "Permission denied");
+ }
+#endif
+ if (strlen(user) > MAXLOGNAME - 1) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid,
+ EPERM, 1, "username too long: '%s'", user))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "username too long");
+ }
+
+ nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
+ if (nargv == NULL)
+ errx(1, "malloc failure");
+
+ nargv[argc + 3] = NULL;
+ for (i = argc; i >= optind; i--)
+ nargv[i + 3] = argv[i];
+ np.a = &nargv[i + 3];
+
+ argv += optind;
+
+ errno = 0;
+ prio = getpriority(PRIO_PROCESS, 0);
+ if (errno)
+ prio = 0;
+
+ setpriority(PRIO_PROCESS, 0, -2);
+ openlog("su", LOG_CONS, LOG_AUTH);
+
+ /* get current login name, real uid and shell */
+ ruid = getuid();
+ username = getlogin();
+ pwd = getpwnam(username);
+ if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
+ pwd = getpwuid(ruid);
+ if (pwd == NULL) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "unable to determine invoking subject: '%s'", username))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "who are you?");
+ }
+
+ username = strdup(pwd->pw_name);
+ if (username == NULL)
+ err(1, "strdup failure");
+
+ if (asme) {
+ if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
+ /* must copy - pwd memory is recycled */
+ shell = strncpy(shellbuf, pwd->pw_shell,
+ sizeof(shellbuf));
+ shellbuf[sizeof(shellbuf) - 1] = '\0';
+ }
+ else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+ }
+
+ /* Do the whole PAM startup thing */
+ retcode = pam_start("su", user, &conv, &pamh);
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
+ errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
+ }
+
+ PAM_SET_ITEM(PAM_RUSER, username);
+
+ mytty = ttyname(STDERR_FILENO);
+ if (!mytty)
+ mytty = "tty";
+ PAM_SET_ITEM(PAM_TTY, mytty);
+
+ retcode = pam_authenticate(pamh, 0);
+ if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
+ username, user, mytty))
+ errx(1, "Permission denied");
+#endif
+ syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
+ username, user, mytty);
+ errx(1, "Sorry");
+ }
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
+ errx(1, "Permission denied");
+#endif
+ retcode = pam_get_item(pamh, PAM_USER, &v);
+ if (retcode == PAM_SUCCESS)
+ user = v;
+ else
+ syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
+ pam_strerror(pamh, retcode));
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "unknown subject: %s", user))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "unknown login: %s", user);
+ }
+
+ retcode = pam_acct_mgmt(pamh, 0);
+ if (retcode == PAM_NEW_AUTHTOK_REQD) {
+ retcode = pam_chauthtok(pamh,
+ PAM_CHANGE_EXPIRED_AUTHTOK);
+ if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ aerr = pam_strerror(pamh, retcode);
+ if (aerr == NULL)
+ aerr = "Unknown PAM error";
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "pam_chauthtok: %s", aerr))
+ errx(1, "Permission denied");
+#endif
+ syslog(LOG_ERR, "pam_chauthtok: %s",
+ pam_strerror(pamh, retcode));
+ errx(1, "Sorry");
+ }
+ }
+ if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
+ pam_strerror(pamh, retcode)))
+ errx(1, "Permission denied");
+#endif
+ syslog(LOG_ERR, "pam_acct_mgmt: %s",
+ pam_strerror(pamh, retcode));
+ errx(1, "Sorry");
+ }
+
+#ifndef __APPLE__
+ /* get target login information */
+ if (class == NULL)
+ lc = login_getpwclass(pwd);
+ else {
+ if (ruid != 0) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, EPERM, 1,
+ "only root may use -c"))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "only root may use -c");
+ }
+ lc = login_getclass(class);
+ if (lc == NULL)
+ errx(1, "unknown class: %s", class);
+ }
+#endif /* !__APPLE__ */
+
+ /* if asme and non-standard target shell, must be root */
+ if (asme) {
+ if (ruid != 0 && !chshell(pwd->pw_shell))
+ errx(1, "permission denied (shell)");
+ }
+ else if (pwd->pw_shell && *pwd->pw_shell) {
+#ifdef __APPLE__
+ /* 3825554 */
+ shell = strncpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
+ shellbuf[sizeof(shellbuf) - 1] = '\0';
+#else
+ shell = pwd->pw_shell;
+#endif /* __APPLE__ */
+ iscsh = UNSET;
+ }
+ else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+
+ /* if we're forking a csh, we want to slightly muck the args */
+ if (iscsh == UNSET) {
+ p = strrchr(shell, '/');
+ if (p)
+ ++p;
+ else
+ p = shell;
+ iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
+ }
+ setpriority(PRIO_PROCESS, 0, prio);
+
+ /*
+ * PAM modules might add supplementary groups in pam_setcred(), so
+ * initialize them first.
+ */
+#ifdef __APPLE__
+ if (initgroups(user, pwd->pw_gid))
+ err(1, "initgroups");
+#else
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
+ err(1, "setusercontext");
+#endif /* __APPLE__ */
+
+#ifdef __APPLE__
+ /* 8530846 */
+ if (asthem) {
+ auditinfo_addr_t auinfo = {
+ .ai_termid = { .at_type = AU_IPv4 },
+ .ai_asid = AU_ASSIGN_ASID,
+ .ai_auid = getuid(),
+ .ai_flags = 0,
+ };
+ if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
+ char session[16];
+ snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
+ setenv("SECURITYSESSIONID", session, 1);
+ } else {
+ errx(1, "failed to create session.");
+ }
+ }
+#endif /* __APPLE__ */
+
+ retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_setcred: %s",
+ pam_strerror(pamh, retcode));
+ errx(1, "failed to establish credentials.");
+ }
+ if (asthem) {
+ retcode = pam_open_session(pamh, 0);
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_open_session: %s",
+ pam_strerror(pamh, retcode));
+ errx(1, "failed to open session.");
+ }
+ }
+
+ /*
+ * We must fork() before setuid() because we need to call
+ * pam_setcred(pamh, PAM_DELETE_CRED) as root.
+ */
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGINT, &sa, &sa_int);
+ sigaction(SIGQUIT, &sa, &sa_quit);
+ sigaction(SIGPIPE, &sa, &sa_pipe);
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTSTP, &sa, NULL);
+ statusp = 1;
+ if (pipe(fds) == -1) {
+ PAM_END();
+ err(1, "pipe");
+ }
+ child_pid = fork();
+ switch (child_pid) {
+ default:
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGTTOU, &sa, NULL);
+ close(fds[0]);
+ setpgid(child_pid, child_pid);
+ if (tcgetpgrp(STDERR_FILENO) == getpgrp())
+ tcsetpgrp(STDERR_FILENO, child_pid);
+ close(fds[1]);
+ sigaction(SIGPIPE, &sa_pipe, NULL);
+ while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
+ if (WIFSTOPPED(statusp)) {
+ child_pgrp = getpgid(child_pid);
+ if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
+ tcsetpgrp(STDERR_FILENO, getpgrp());
+ kill(getpid(), SIGSTOP);
+ if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
+ child_pgrp = getpgid(child_pid);
+ tcsetpgrp(STDERR_FILENO, child_pgrp);
+ }
+ kill(child_pid, SIGCONT);
+ statusp = 1;
+ continue;
+ }
+ break;
+ }
+ tcsetpgrp(STDERR_FILENO, getpgrp());
+ if (pid == -1)
+ err(1, "waitpid");
+ PAM_END();
+ exit(WEXITSTATUS(statusp));
+ case -1:
+ PAM_END();
+ err(1, "fork");
+ case 0:
+ close(fds[1]);
+ read(fds[0], &temp, 1);
+ close(fds[0]);
+ sigaction(SIGPIPE, &sa_pipe, NULL);
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+
+#ifdef __APPLE__
+ if (setgid(pwd->pw_gid))
+ err(1, "setgid");
+ /* Call initgroups(2) after setgid(2) to re-establish memberd */
+ if (initgroups(user, pwd->pw_gid))
+ err(1, "initgroups");
+ if (setuid(pwd->pw_uid))
+ err(1, "setuid");
+#else
+ /*
+ * Set all user context except for: Environmental variables
+ * Umask Login records (wtmp, etc) Path
+ */
+ setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
+ LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
+ LOGIN_SETMAC);
+ /*
+ * If -s is present, also set the MAC label.
+ */
+ if (setmaclabel)
+ setwhat |= LOGIN_SETMAC;
+ /*
+ * Don't touch resource/priority settings if -m has been used
+ * or -l and -c hasn't, and we're not su'ing to root.
+ */
+ if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
+ setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
+ if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
+ err(1, "setusercontext");
+#endif /* __APPLE__ */
+
+ if (!asme) {
+ if (asthem) {
+ p = getenv("TERM");
+ environ = &cleanenv;
+ }
+
+ if (asthem || pwd->pw_uid)
+ setenv("USER", pwd->pw_name, 1);
+ setenv("HOME", pwd->pw_dir, 1);
+ setenv("SHELL", shell, 1);
+#ifdef __APPLE__
+ unsetenv("TMPDIR");
+#endif /* __APPLE__ */
+
+ if (asthem) {
+ /*
+ * Add any environmental variables that the
+ * PAM modules may have set.
+ */
+ environ_pam = pam_getenvlist(pamh);
+ if (environ_pam)
+ export_pam_environment();
+
+#ifdef __APPLE__
+ /* 5276965: As documented, set $PATH. */
+ setenv("PATH", "/bin:/usr/bin", 1);
+#else
+ /* set the su'd user's environment & umask */
+ setusercontext(lc, pwd, pwd->pw_uid,
+ LOGIN_SETPATH | LOGIN_SETUMASK |
+ LOGIN_SETENV);
+#endif /* __APPLE__ */
+ if (p)
+ setenv("TERM", p, 1);
+
+ p = pam_getenv(pamh, "HOME");
+ if (chdir(p ? p : pwd->pw_dir) < 0)
+ errx(1, "no directory");
+ }
+ }
+#ifndef __APPLE__
+ login_close(lc);
+#endif /* !__APPLE__ */
+
+ if (iscsh == YES) {
+ if (fastlogin)
+ *np.a-- = "-f";
+ if (asme)
+ *np.a-- = "-m";
+ }
+#ifdef __APPLE__
+ /* 4043304 */
+ if ((p = strrchr(shell, '/')) != NULL)
+ avshell = p + 1;
+ else
+ avshell = shell;
+
+ if (asthem) {
+ avshellbuf[0] = '-';
+ strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
+ avshell = avshellbuf;
+ }
+
+ /* csh *no longer* strips the first character... */
+ *np.a = avshell;
+#else
+ /* csh strips the first character... */
+ *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
+#endif /* __APPLE__ */
+
+ if (ruid != 0)
+ syslog(LOG_NOTICE, "%s to %s%s", username, user,
+ ontty());
+
+ execv(shell, np.b);
+ err(1, "%s", shell);
+ }
+}
+
+static void
+export_pam_environment(void)
+{
+ char **pp;
+ char *p;
+
+ for (pp = environ_pam; *pp != NULL; pp++) {
+ if (ok_to_export(*pp)) {
+ p = strchr(*pp, '=');
+ *p = '\0';
+ setenv(*pp, p + 1, 1);
+ }
+ free(*pp);
+ }
+}
+
+/*
+ * Sanity checks on PAM environmental variables:
+ * - Make sure there is an '=' in the string.
+ * - Make sure the string doesn't run on too long.
+ * - Do not export certain variables. This list was taken from the
+ * Solaris pam_putenv(3) man page.
+ * Note that if the user is chrooted, PAM may have a better idea than we
+ * do of where her home directory is.
+ */
+static int
+ok_to_export(const char *s)
+{
+ static const char *noexport[] = {
+ "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
+ "IFS", "PATH", NULL
+ };
+ const char **pp;
+ size_t n;
+
+ if (strlen(s) > 1024 || strchr(s, '=') == NULL)
+ return 0;
+ if (strncmp(s, "LD_", 3) == 0)
+ return 0;
+ for (pp = noexport; *pp != NULL; pp++) {
+ n = strlen(*pp);
+ if (s[n] == '=' && strncmp(s, *pp, n) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+usage(void)
+{
+
+#ifdef __APPLE__
+ fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n");
+#else
+ fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
+#endif /* __APPLE__ */
+ exit(1);
+ /* NOTREACHED */
+}
+
+static int
+chshell(const char *sh)
+{
+ int r;
+ char *cp;
+
+ r = 0;
+ setusershell();
+ while ((cp = getusershell()) != NULL && !r)
+ r = (strcmp(cp, sh) == 0);
+ endusershell();
+ return r;
+}
+
+static char *
+ontty(void)
+{
+ char *p;
+ static char buf[MAXPATHLEN + 4];
+
+ buf[0] = 0;
+ p = ttyname(STDERR_FILENO);
+ if (p)
+ snprintf(buf, sizeof(buf), " on %s", p);
+ return buf;
+}
diff --git a/shell_cmds/su/su.pam b/shell_cmds/su/su.pam
new file mode 100644
index 0000000..fd8a789
--- /dev/null
+++ b/shell_cmds/su/su.pam
@@ -0,0 +1,7 @@
+# su: auth account session
+auth sufficient pam_rootok.so
+auth required pam_opendirectory.so
+account required pam_group.so no_warn group=admin,wheel ruser root_only fail_safe
+account required pam_opendirectory.so no_check_shell
+password required pam_opendirectory.so
+session required pam_launchd.so
diff --git a/shell_cmds/su/su_entitlements.plist b/shell_cmds/su/su_entitlements.plist
new file mode 100644
index 0000000..a96cb44
--- /dev/null
+++ b/shell_cmds/su/su_entitlements.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.keystore.console</key>
+ <true/>
+ <key>com.apple.private.security.clear-library-validation</key>
+ <true/>
+</dict>
+</plist>