reboot remsync rev rget rlogin \
rotate rsh rshd sed service setup shar acksize \
sleep slip sort spell split srccrc \
- stty su sum svclog swifi sync synctree sysenv \
+ stty sum svclog swifi sync synctree sysenv \
syslogd tail tar tcpd tcpdp tcpstat tee telnet \
telnetd term termcap tget time touch tr \
truncate tsort tty udpstat umount uname unexpand \
+++ /dev/null
-PROG= su
-BINMODE= 4755
-MAN=
-LDADD+= -lcrypt
-
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* su - become super-user Author: Patrick van Kleef */
-
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <time.h>
-#if __minix_vmd
-#include <sys/syslog.h>
-#endif
-#include <minix/minlib.h>
-
-int main(int argc, char **argv);
-
-int main(argc, argv)
-int argc;
-char *argv[];
-{
- register char *name, *password;
- char *shell, sh0[100];
- char from_user[8+1], from_shell[100];
- register struct passwd *pwd;
- char USER[20], LOGNAME[25], HOME[100], SHELL[100];
- char *envv[20], **envp;
- int smallenv;
- char *p;
- int super;
- int loginshell;
- gid_t groups[NGROUPS_MAX];
- int ngroups;
- int g;
-
- smallenv = 0;
- loginshell = 0;
- if (argc > 1 && (strcmp(argv[1], "-") == 0 || strcmp(argv[1], "-e") == 0)) {
- if (argv[1][1] == 0)
- loginshell= 1; /* 'su -' reads .profile */
- argv[1] = argv[0];
- argv++;
- argc--;
- smallenv = 1; /* Use small environment. */
- }
- if (argc > 1) {
- if (argv[1][0] == '-') {
- fprintf(stderr,
- "Usage: su [-[e]] [user [shell-arguments ...]]\n");
- exit(1);
- }
- name = argv[1];
- argv[1] = argv[0];
- argv++;
- } else {
- name = "root";
- }
-
- if ((pwd = getpwuid(getuid())) == 0) {
- fprintf(stderr, "You do not exist\n");
- exit(1);
- }
- strncpy(from_user, pwd->pw_name, 8);
- from_user[8]= 0;
- strncpy(from_shell, pwd->pw_shell[0] == '\0' ? "/bin/sh" : pwd->pw_shell,
- sizeof(from_shell)-1);
- from_shell[sizeof(from_shell)-1]= 0;
-
- if ((pwd = getpwnam(name)) == 0) {
- fprintf(stderr, "Unknown id: %s\n", name);
- exit(1);
- }
- super = 0;
- if (getgid() == 0) super = 1;
- ngroups = getgroups(NGROUPS_MAX, groups);
- for (g = 0; g < ngroups; g++) if (groups[g] == 0) super = 1;
-
- if (!super && strcmp(pwd->pw_passwd, crypt("", pwd->pw_passwd)) != 0) {
-#if __minix_vmd
- openlog("su", 0, LOG_AUTH);
-#endif
- password = getpass("Password:");
- if (password == 0
- || strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0) {
- if (password != 0 && *password != 0) {
-#if __minix_vmd
- syslog(LOG_WARNING, "su %s failed for %s",
- name, from_user);
-#endif
- }
- fprintf(stderr, "Sorry\n");
- exit(2);
- }
-#if __minix_vmd
- syslog(LOG_NOTICE, "su %s succeeded for %s", name, from_user);
- closelog();
-#endif
- }
-
- initgroups(pwd->pw_name, pwd->pw_gid);
- setgid(pwd->pw_gid);
- setuid(pwd->pw_uid);
- if (loginshell) {
- shell = pwd->pw_shell[0] == '\0' ? "/bin/sh" : pwd->pw_shell;
- } else {
- if ((shell = getenv("SHELL")) == NULL) shell = from_shell;
- }
- if ((p= strrchr(shell, '/')) == 0) p= shell; else p++;
- sh0[0]= '-';
- strcpy(loginshell ? sh0+1 : sh0, p);
- argv[0]= sh0;
-
- if (smallenv) {
- envp = envv;
- *envp++ = "PATH=:/bin:/usr/bin",
- strcpy(USER, "USER=");
- strcpy(USER + 5, name);
- *envp++ = USER;
- strcpy(LOGNAME, "LOGNAME=");
- strcpy(LOGNAME + 8, name);
- *envp++ = LOGNAME;
- strcpy(SHELL, "SHELL=");
- strcpy(SHELL + 6, shell);
- *envp++ = SHELL;
- strcpy(HOME, "HOME=");
- strcpy(HOME + 5, pwd->pw_dir);
- *envp++ = HOME;
- if ((p = getenv("TERM")) != NULL) {
- *envp++ = p - 5;
- }
- if ((p = getenv("TERMCAP")) != NULL) {
- *envp++ = p - 8;
- }
- if ((p = getenv("TZ")) != NULL) {
- *envp++ = p - 3;
- }
- *envp = NULL;
- (void) chdir(pwd->pw_dir);
- execve(shell, argv, envv);
- perror(shell);
- } else {
- execv(shell, argv);
- perror(shell);
- }
- fprintf(stderr, "No shell\n");
- return(3);
-}
profile.1 ps.1 pwd.1 rcp.1 readall.1 recwave.1 \
ref.1 remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \
shar.1 acksize.1 sleep.1 sort.1 spell.1 \
- split.1 stty.1 su.1 sum.1 svc.1 \
+ split.1 stty.1 sum.1 svc.1 \
synctree.1 sysenv.1 sz.1 tail.1 tee.1 telnet.1 template.1 \
term.1 termcap.1 tget.1 time.1 tr.1 true.1 \
truncate.1 tsort.1 tty.1 umount.1 uname.1 unexpand.1 \
+++ /dev/null
-.TH SU 1
-.SH NAME
-su \- temporary become superuser or another user
-.SH SYNOPSIS
-.B su
-.RB [ \- [ e ]]
-.RI [ user
-.RI [ shell-arguments " ...]]"
-.SH DESCRIPTION
-.de SP
-.if t .sp 0.4
-.if n .sp
-..
-.B Su
-can be used to temporarily run a shell under the identity of the superuser
-or another user. Unless the caller is a member of the operator group, one
-is prompted for the password of the user-to-be. Calls that need a password
-are logged, whether they succeed or not. The default user is
-.BR root .
-Further arguments are handed to the shell. By default the shell started is
-the shell of the invoker, and the environment is passed on as is.
-.PP
-The activities of
-.B su
-are logged through
-.BR syslog (3)
-under Minix-vmd.
-.SH OPTIONS
-.TP
-.B \-
-Constructs a new environment consisting of the
-.BR PATH ,
-.BR USER ,
-.BR LOGNAME ,
-.BR HOME ,
-.BR SHELL ,
-.BR TERM ,
-.BR TERMCAP ,
-and
-.BR TZ
-variables. The environment is the same as on a normal login, except that
-.BR TERM ,
-.B TERMCAP
-and
-.B TZ
-are copied from the current environment if set. The current working
-directory is changed to the user home directory, the shell of the user-to-be
-is run, and it is started as a login shell, with the first character a minus
-sign.
-.TP
-.B \-e
-Like above, but the shell is started normally, not as a login shell.
-.SH "SEE ALSO"
-.BR sh (1),
-.BR login (1),
-.BR syslog (3).
-.SH AUTHOR
-Kees J. Bot <kjb@cs.vu.nl>
# Timestamp in UTC,minixpath,netbsdpath
# minixpath: path in Minix source tree (starting from /usr/src/)
# netbsdpath: path in BSD source tree (starting from src/)
+2012/02/10 16:16:12,usr.bin/su
2011/12/25 06:09:09,sys/arch/i386/stand
2012/02/10 16:16:12,share/zoneinfo
2012/02/10 16:16:12,share/misc
--- /dev/null
+# $NetBSD: Makefile,v 1.50 2011/04/24 21:42:06 elric Exp $
+# from: @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+.include <bsd.own.mk>
+
+USE_PAM=no
+USE_KERBEROS=no
+
+USE_FORT?= yes # setuid
+PROG= su
+BINOWN= root
+BINMODE=4555
+
+.PATH.c: ${.CURDIR}/../newgrp
+CPPFLAGS+=-I${.CURDIR}/../newgrp
+CPPFLAGS+=-DLOGIN_CAP
+CPPFLAGS+=-DALLOW_GROUP_CHANGE
+CPPFLAGS+=-DALLOW_EMPTY_USER
+CPPFLAGS+=-DGRUTIL_SETGROUPS_MAKESPACE
+
+.if ${USE_PAM} != "no"
+
+CPPFLAGS+=-DUSE_PAM
+# XXX: Need libcrypt here, because libcrypto defines it too.
+DPADD+= ${LIBPAM} ${LIBCRYPT} ${LIBUTIL} ${PAM_STATIC_DPADD}
+LDADD+= -lpam -lcrypt -lutil ${PAM_STATIC_LDADD}
+SRCS=su_pam.c grutil.c suutil.c
+
+.else # USE_PAM == no
+
+SRCS=su.c grutil.c suutil.c
+
+DPADD+= ${LIBCRYPT} ${LIBUTIL}
+LDADD+= -lcrypt -lutil
+
+# Uncomment the following line to change the group that may su root to "sugroup"
+#
+#CPPFLAGS+=-DSU_GROUP=\"sugroup\"
+
+# Uncomment the following line to make su
+# treat group wheel (SUGROUP) and/or ROOTAUTH as an indirect
+# list of groups.
+#CPPFLAGS+=-DSU_INDIRECT_GROUP
+
+.if (${USE_KERBEROS} != "no")
+.ifdef AFS
+DPADD+= ${LIBKAFS}
+LDADD+= -lkafs
+.endif
+
+CPPFLAGS+=-DKERBEROS5
+DPADD+= ${LIBKRB5} ${LIBASN1}
+LDADD+= -lkrb5 -lasn1
+
+DPADD+= ${LIBCRYPTO} ${LIBROKEN} ${LIBCOM_ERR}
+LDADD+= -lcrypto -lroken -lcom_err
+.endif
+
+.if (${USE_SKEY} != "no")
+CPPFLAGS+=-DSKEY
+DPADD+= ${LIBSKEY}
+LDADD+= -lskey
+.endif
+
+.ifdef SU_ROOTAUTH
+CPPFLAGS+=-DSU_ROOTAUTH=\"${SU_ROOTAUTH}\"
+.endif
+
+.endif # USE_PAM == no
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" 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. 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.
+.\"
+.\" from: @(#)su.1 8.2 (Berkeley) 4/18/94
+.\" $NetBSD: su.1,v 1.49 2009/05/18 09:37:44 wiz Exp $
+.\"
+.Dd October 27, 2007
+.Dt SU 1
+.Os
+.Sh NAME
+.Nm su
+.Nd substitute user identity
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfKlm
+.Op Fl c Ar login-class
+.Oo
+.Ar login Ns Op : Ns Ar group
+.Op Ar "shell arguments"
+.Oc
+.Nm
+.Op Fl dfKlm
+.Op Fl c Ar login-class
+.Oo
+.Ns : Ns Ar group
+.Op Ar "shell arguments"
+.Oc
+.Sh DESCRIPTION
+.Nm
+allows one user to become another user
+.Ar login
+without logging out and in as
+the new user.
+If a
+.Ar group
+is specified and
+.Ar login
+is a member of
+.Ar group ,
+then the group is changed to
+.Ar group
+rather than to
+.Ar login Ns 's
+primary group.
+If
+.Ar login
+is omitted and
+.Ar group
+is provided (form two above), then
+.Ar login
+is assumed to be the current username.
+.Pp
+When executed by a user, the
+.Ar login
+user's password is requested.
+When using Kerberos, the password for
+.Ar login
+(or for
+.Dq Ar login Ns .root ,
+if no login is provided) is requested, and
+.Nm
+switches to that user and group ID after obtaining a Kerberos ticket
+granting ticket.
+A shell is then executed, and any additional
+.Ar "shell arguments"
+after the login name are passed to the shell.
+.Nm
+will resort to the local password file to find the password for
+.Ar login
+if there is a Kerberos error.
+If
+.Nm
+is executed by root, no password is requested and a shell
+with the appropriate user ID is executed; no additional Kerberos tickets
+are obtained.
+.Pp
+Alternatively, if the user enters the password "s/key", authentication
+will use the S/Key one-time password system as described in
+.Xr skey 1 .
+S/Key is a Trademark of Bellcore.
+.Pp
+By default, the environment is unmodified with the exception of
+.Ev LOGNAME ,
+.Ev USER ,
+.Ev HOME ,
+.Ev SHELL ,
+and
+.Ev SU_FROM .
+.Ev HOME
+and
+.Ev SHELL
+are set to the target login's default values.
+.Ev LOGNAME
+and
+.Ev USER
+are set to the target login, unless the target login has a user ID of 0,
+in which case they are unmodified.
+.Ev SU_FROM
+is set to the caller's login.
+The invoked shell is the target login's.
+With the exception of
+.Ev SU_FROM
+this is the traditional behavior of
+.Nm .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Specify a login class.
+You may only override the default class if you're already root.
+See
+.Xr login.conf 5
+for details.
+.It Fl d
+Same as
+.Fl l ,
+but does not change the current directory.
+.It Fl f
+If the invoked shell is
+.Xr csh 1 ,
+this option prevents it from reading the
+.Dq Pa .cshrc
+file.
+If the invoked shell is
+.Xr sh 1 ,
+or
+.Xr ksh 1 ,
+this option unsets
+.Ev ENV ,
+thus preventing the shell from executing the startup file pointed to by
+this variable.
+.It Fl K
+Do not attempt to use Kerberos to authenticate the user.
+.It Fl l
+Simulate a full login.
+The environment is discarded except for
+.Ev HOME ,
+.Ev SHELL ,
+.Ev PATH ,
+.Ev TERM ,
+.Ev LOGNAME ,
+.Ev USER ,
+and
+.Ev SU_FROM .
+.Ev HOME ,
+.Ev SHELL ,
+and
+.Ev SU_FROM
+are modified as above.
+.Ev LOGNAME
+and
+.Ev USER
+are set to the target login.
+.Ev PATH
+is set to the path specified in the
+.Pa /etc/login.conf
+file (or to the default of
+.Dq Pa /usr/bin:/bin:/usr/pkg/bin:/usr/local/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
+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
+and
+.Fl m
+options are mutually exclusive; the last one specified
+overrides any previous ones.
+.Pp
+Only users in group
+.Dq wheel
+(normally gid 0),
+as listed in
+.Pa /etc/group ,
+can
+.Nm
+to
+.Dq root ,
+unless group wheel does not exist or has no members.
+(If you do not want anybody to be able to
+.Nm
+to
+.Dq root ,
+make
+.Dq root
+the only member of group
+.Dq wheel ,
+which is the default.)
+.Pp
+For sites with very large user populations, group
+.Dq wheel
+can contain the names of other groups that will be considered authorized
+to
+.Nm
+to
+.Dq root .
+.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 CUSTOMIZATION
+.Bl -tag -width ""
+.It Changing required group
+For the
+.Xr pam 8
+version of
+.Nm
+the name of the required group can be changed by setting
+.Ar gname
+in
+.Xr pam.conf 5 :
+.Bd -literal
+auth requisite pam_group.so no_warn group=gname root_only fail_safe
+.Ed
+.Pp
+For the non
+.Xr pam 8
+version of
+.Nm
+the same can be achieved by compiling with
+.Dv SU_GROUP
+set to the desired group name.
+.It Supplying own password
+.Nm
+can be configured so that users in a particular group can supply their
+own password to become
+.Dq root .
+For the
+.Xr pam 8
+version of
+.Nm
+this can be done by adding a line to
+.Xr pam.conf 5
+such as:
+.Bd -literal
+auth sufficient pam_group.so no_warn group=gname root_only authenticate
+.Ed
+.Pp
+where
+.Ar gname
+is the name of the desired group.
+For the non
+.Xr pam 8
+version of
+.Nm
+the same can be achieved by compiling with
+.Dv SU_ROOTAUTH
+set to the desired group name.
+.It Indirect groups
+This option is not available with the
+.Xr pam 8
+version of
+.Nm .
+For the non
+.Xr pam 8
+version of
+.Nm ,
+if
+.Dv SU_INDIRECT_GROUP
+is defined, the
+.Ar SU_GROUP
+and
+.Ar SU_ROOTAUTH
+groups are treated as indirect groups.
+The group members of those two groups are treated as groups themselves.
+.El
+.Sh EXIT STATUS
+.Nm
+returns the exit status of the executed subshell, or 1 if any error
+occurred while switching privileges.
+.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 LOGNAME
+The user ID is always the effective ID (the target user ID) after an
+.Nm
+unless the user ID is 0 (root).
+.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 EXAMPLES
+To become user username and use the same environment as in original shell, execute:
+.Bd -literal -offset indent
+su username
+.Ed
+.Pp
+To become user username and use environment as if full login would be performed,
+execute:
+.Bd -literal -offset indent
+su -l username
+.Ed
+.Pp
+When a
+.Fl c
+option is included
+.Em after
+the
+.Ar login
+name it is not a
+.Nm
+option, because any arguments after the
+.Ar login
+are passed to the shell.
+(See
+.Xr csh 1 ,
+.Xr ksh 1
+or
+.Xr sh 1
+for details.)
+To execute arbitrary command with privileges of user
+.Em username ,
+execute:
+.Bd -literal -offset indent
+su username -c "command args"
+.Ed
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr kinit 1 ,
+.Xr login 1 ,
+.Xr sh 1 ,
+.Xr skey 1 ,
+.Xr setusercontext 3 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr environ 7 ,
+.Xr kerberos 8
+.Sh HISTORY
+A
+.Nm
+command existed in
+.At v5
+(and probably earlier).
--- /dev/null
+/* $NetBSD: su.c,v 1.69 2011/08/31 16:24:58 plunky Exp $ */
+
+/*
+ * Copyright (c) 1988 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1988\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";*/
+#else
+__RCSID("$NetBSD: su.c,v 1.69 2011/08/31 16:24:58 plunky Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#ifdef SKEY
+#include <skey.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include <util.h>
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+#ifdef KERBEROS5
+#include <krb5.h>
+#endif
+
+#ifdef ALLOW_GROUP_CHANGE
+#include "grutil.h"
+#endif
+#include "suutil.h"
+
+#ifdef KERBEROS5
+#define ARGSTRX "-Kdflm"
+static int kerberos5(char *, const char *, uid_t);
+int use_kerberos = 1;
+#else
+#define ARGSTRX "-dflm"
+#endif
+
+#ifndef SU_GROUP
+#define SU_GROUP "wheel"
+#endif
+
+#define GROUP_PASSWORD "Group Password:"
+
+#ifdef LOGIN_CAP
+#define ARGSTR ARGSTRX "c:"
+#else
+#define ARGSTR ARGSTRX
+#endif
+
+static int check_ingroup(int, const char *, const char *, int);
+
+int
+main(int argc, char **argv)
+{
+ extern char **environ;
+ struct passwd *pwd;
+ char *p;
+#ifdef BSD4_4
+ struct timeval tp;
+#endif
+ uid_t ruid;
+ int asme, ch, asthem, fastlogin, prio, gohome;
+ enum { UNSET, YES, NO } iscsh = UNSET;
+ const char *user, *shell, *avshell;
+ char *username, **np;
+ char *userpass, *class;
+ char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN];
+ time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
+#ifdef LOGIN_CAP
+ login_cap_t *lc;
+#endif
+#ifdef ALLOW_GROUP_CHANGE
+ char *gname;
+#endif
+
+ (void)setprogname(argv[0]);
+ asme = asthem = fastlogin = 0;
+ gohome = 1;
+ shell = class = NULL;
+ while ((ch = getopt(argc, argv, ARGSTR)) != -1)
+ switch((char)ch) {
+#ifdef KERBEROS5
+ case 'K':
+ use_kerberos = 0;
+ break;
+#endif
+#ifdef LOGIN_CAP
+ case 'c':
+ class = optarg;
+ break;
+#endif
+ case 'd':
+ asme = 0;
+ asthem = 1;
+ gohome = 0;
+ break;
+ case 'f':
+ fastlogin = 1;
+ break;
+ case '-':
+ case 'l':
+ asme = 0;
+ asthem = 1;
+ break;
+ case 'm':
+ asme = 1;
+ asthem = 0;
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+#ifdef ALLOW_GROUP_CHANGE
+ "usage: %s [%s] [login[:group] [shell arguments]]\n",
+#else
+ "usage: %s [%s] [login [shell arguments]]\n",
+#endif
+ getprogname(), ARGSTR);
+ exit(EXIT_FAILURE);
+ }
+ argv += optind;
+
+ /* Lower the priority so su runs faster */
+ errno = 0;
+ prio = getpriority(PRIO_PROCESS, 0);
+ if (errno)
+ prio = 0;
+ if (prio > -2)
+ (void)setpriority(PRIO_PROCESS, 0, -2);
+ openlog("su", 0, LOG_AUTH);
+
+ /* get current login name and shell */
+ ruid = getuid();
+ username = getlogin();
+ if (username == NULL || (pwd = getpwnam(username)) == NULL ||
+ pwd->pw_uid != ruid)
+ pwd = getpwuid(ruid);
+ if (pwd == NULL)
+ errx(EXIT_FAILURE, "who are you?");
+ username = estrdup(pwd->pw_name);
+ userpass = estrdup(pwd->pw_passwd);
+
+ if (asme) {
+ if (pwd->pw_shell && *pwd->pw_shell) {
+ (void)estrlcpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
+ shell = shellbuf;
+ } else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+ }
+ /* get target login information, default to root */
+ user = *argv ? *argv : "root";
+ np = *argv ? argv : argv - 1;
+
+#ifdef ALLOW_GROUP_CHANGE
+ if ((p = strchr(user, ':')) != NULL) {
+ *p = '\0';
+ gname = ++p;
+ }
+ else
+ gname = NULL;
+
+#ifdef ALLOW_EMPTY_USER
+ if (user[0] == '\0' && gname != NULL)
+ user = username;
+#endif
+#endif
+ if ((pwd = getpwnam(user)) == NULL)
+ errx(EXIT_FAILURE, "unknown login `%s'", user);
+
+#ifdef LOGIN_CAP
+ /* force the usage of specified class */
+ if (class) {
+ if (ruid)
+ errx(EXIT_FAILURE, "Only root may use -c");
+
+ pwd->pw_class = class;
+ }
+ if ((lc = login_getclass(pwd->pw_class)) == NULL)
+ errx(EXIT_FAILURE, "Unknown class %s\n", pwd->pw_class);
+
+ pw_warntime = (time_t)login_getcaptime(lc, "password-warn",
+ _PASSWORD_WARNDAYS * SECSPERDAY,
+ _PASSWORD_WARNDAYS * SECSPERDAY);
+#endif
+
+ if (ruid
+#ifdef KERBEROS5
+ && (!use_kerberos || kerberos5(username, user, pwd->pw_uid))
+#endif
+ ) {
+ char *pass = pwd->pw_passwd;
+ int ok = pwd->pw_uid != 0;
+
+#ifdef SU_ROOTAUTH
+ /*
+ * Allow those in group rootauth to su to root, by supplying
+ * their own password.
+ */
+ if (!ok) {
+ if ((ok = check_ingroup(-1, SU_ROOTAUTH, username, 0))) {
+ pass = userpass;
+ user = username;
+ }
+ }
+#endif
+ /*
+ * Only allow those in group SU_GROUP to su to root,
+ * but only if that group has any members.
+ * If SU_GROUP has no members, allow anyone to su root
+ */
+ if (!ok)
+ ok = check_ingroup(-1, SU_GROUP, username, 1);
+ if (!ok)
+ errx(EXIT_FAILURE,
+ "you are not listed in the correct secondary group (%s) to su %s.",
+ SU_GROUP, user);
+ /* if target requires a password, verify it */
+ if (*pass && pwd->pw_uid != ruid) { /* XXX - OK? */
+ p = getpass("Password:");
+#ifdef SKEY
+ if (strcasecmp(p, "s/key") == 0) {
+ if (skey_haskey(user))
+ errx(EXIT_FAILURE,
+ "Sorry, you have no s/key.");
+ else {
+ if (skey_authenticate(user)) {
+ goto badlogin;
+ }
+ }
+
+ } else
+#endif
+ if (strcmp(pass, crypt(p, pass)) != 0) {
+#ifdef SKEY
+ badlogin:
+#endif
+ (void)fprintf(stderr, "Sorry\n");
+ syslog(LOG_WARNING,
+ "BAD SU %s to %s%s", username,
+ pwd->pw_name, ontty());
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (asme) {
+ /* if asme and non-standard target shell, must be root */
+ if (chshell(pwd->pw_shell) == 0 && ruid)
+ errx(EXIT_FAILURE, "permission denied (shell).");
+ } else if (pwd->pw_shell && *pwd->pw_shell) {
+ shell = pwd->pw_shell;
+ iscsh = UNSET;
+ } else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+
+ if ((p = strrchr(shell, '/')) != NULL)
+ avshell = p+1;
+ else
+ avshell = shell;
+
+ /* if we're forking a csh, we want to slightly muck the args */
+ if (iscsh == UNSET)
+ iscsh = strstr(avshell, "csh") ? YES : NO;
+
+ /* set permissions */
+#ifdef LOGIN_CAP
+# ifdef ALLOW_GROUP_CHANGE
+ /* if we aren't changing users, keep the current group members */
+ if (ruid != pwd->pw_uid &&
+ setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) == -1)
+ err(EXIT_FAILURE, "setting user context");
+
+ addgroup(lc, gname, pwd, ruid, GROUP_PASSWORD);
+
+ if (setusercontext(lc, pwd, pwd->pw_uid,
+ (u_int)(asthem ? (LOGIN_SETPRIORITY | LOGIN_SETUMASK) : 0) |
+ LOGIN_SETRESOURCES | LOGIN_SETUSER) == -1)
+ err(EXIT_FAILURE, "setting user context");
+# else
+ if (setusercontext(lc, pwd, pwd->pw_uid,
+ (u_int)(asthem ? (LOGIN_SETPRIORITY | LOGIN_SETUMASK) : 0) |
+ LOGIN_SETRESOURCES | LOGIN_SETGROUP | LOGIN_SETUSER) == -1)
+ err(EXIT_FAILURE, "setting user context");
+# endif
+#else
+ if (setgid(pwd->pw_gid) == -1)
+ err(EXIT_FAILURE, "setgid");
+ /* if we aren't changing users, keep the current group members */
+ if (ruid != pwd->pw_uid && initgroups(user, pwd->pw_gid) != 0)
+ errx(EXIT_FAILURE, "initgroups failed");
+# ifdef ALLOW_GROUP_CHANGE
+ addgroup(/*EMPTY*/, gname, pwd, ruid, GROUP_PASSWORD);
+# endif
+ if (setuid(pwd->pw_uid) == -1)
+ err(EXIT_FAILURE, "setuid");
+#endif
+
+ if (!asme) {
+ if (asthem) {
+ p = getenv("TERM");
+ /* Create an empty environment */
+ environ = emalloc(sizeof(char *));
+ environ[0] = NULL;
+#ifdef LOGIN_CAP
+ if (setusercontext(lc, pwd, pwd->pw_uid,
+ LOGIN_SETPATH) == -1)
+ err(EXIT_FAILURE, "setting user context");
+#else
+ (void)setenv("PATH", _PATH_DEFPATH, 1);
+#endif
+ if (p)
+ (void)setenv("TERM", p, 1);
+ if (gohome && chdir(pwd->pw_dir) == -1)
+ errx(EXIT_FAILURE, "no directory");
+ }
+
+ if (asthem || pwd->pw_uid) {
+ (void)setenv("LOGNAME", pwd->pw_name, 1);
+ (void)setenv("USER", pwd->pw_name, 1);
+ }
+ (void)setenv("HOME", pwd->pw_dir, 1);
+ (void)setenv("SHELL", shell, 1);
+ }
+ (void)setenv("SU_FROM", username, 1);
+
+ if (iscsh == YES) {
+ if (fastlogin)
+ *np-- = __UNCONST("-f");
+ if (asme)
+ *np-- = __UNCONST("-m");
+ } else {
+ if (fastlogin)
+ (void)unsetenv("ENV");
+ }
+
+ if (asthem) {
+ avshellbuf[0] = '-';
+ (void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1);
+ avshell = avshellbuf;
+ } else if (iscsh == YES) {
+ /* csh strips the first character... */
+ avshellbuf[0] = '_';
+ (void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1);
+ avshell = avshellbuf;
+ }
+ *np = __UNCONST(avshell);
+
+#ifdef BSD4_4
+ if (pwd->pw_change || pwd->pw_expire)
+ (void)gettimeofday(&tp, NULL);
+ if (pwd->pw_change) {
+ if (tp.tv_sec >= pwd->pw_change) {
+ (void)printf("%s -- %s's password has expired.\n",
+ (ruid ? "Sorry" : "Note"), user);
+ if (ruid != 0)
+ exit(EXIT_FAILURE);
+ } else if (pwd->pw_change - tp.tv_sec < pw_warntime)
+ (void)printf("Warning: %s's password expires on %s",
+ user, ctime(&pwd->pw_change));
+ }
+ if (pwd->pw_expire) {
+ if (tp.tv_sec >= pwd->pw_expire) {
+ (void)printf("%s -- %s's account has expired.\n",
+ (ruid ? "Sorry" : "Note"), user);
+ if (ruid != 0)
+ exit(EXIT_FAILURE);
+ } else if (pwd->pw_expire - tp.tv_sec <
+ _PASSWORD_WARNDAYS * SECSPERDAY)
+ (void)printf("Warning: %s's account expires on %s",
+ user, ctime(&pwd->pw_expire));
+ }
+#endif
+ if (ruid != 0)
+ syslog(LOG_NOTICE, "%s to %s%s",
+ username, pwd->pw_name, ontty());
+
+ /* Raise our priority back to what we had before */
+ (void)setpriority(PRIO_PROCESS, 0, prio);
+
+ (void)execv(shell, np);
+ err(EXIT_FAILURE, "%s", shell);
+ /* NOTREACHED */
+}
+
+
+#ifdef KERBEROS5
+static int
+kerberos5(char *username, const char *user, uid_t uid)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal princ = NULL;
+ krb5_ccache ccache, ccache2;
+ char *cc_name;
+ const char *filename;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return 1;
+
+ if (strcmp(user, "root") == 0)
+ ret = krb5_make_principal(context, &princ,
+ NULL, username, "root", NULL);
+ else
+ ret = krb5_make_principal(context, &princ,
+ NULL, user, NULL);
+ if (ret)
+ goto fail;
+ if (!krb5_kuserok(context, princ, user) && !uid) {
+ warnx("kerberos5: not in %s's ACL.", user);
+ goto fail;
+ }
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache);
+ if (ret)
+ goto fail;
+ ret = krb5_verify_user_lrealm(context, princ, ccache, NULL, TRUE,
+ NULL);
+ if (ret) {
+ krb5_cc_destroy(context, ccache);
+ switch (ret) {
+ case KRB5_LIBOS_PWDINTR :
+ break;
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ case KRB5KRB_AP_ERR_MODIFIED:
+ krb5_warnx(context, "Password incorrect");
+ break;
+ default :
+ krb5_warn(context, ret, "krb5_verify_user");
+ break;
+ }
+ goto fail;
+ }
+ ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2);
+ if (ret) {
+ krb5_cc_destroy(context, ccache);
+ goto fail;
+ }
+ ret = krb5_cc_copy_cache(context, ccache, ccache2);
+ if (ret) {
+ krb5_cc_destroy(context, ccache);
+ krb5_cc_destroy(context, ccache2);
+ goto fail;
+ }
+
+ filename = krb5_cc_get_name(context, ccache2);
+ (void)easprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2),
+ filename);
+ if (chown(filename, uid, NOGROUP) == -1) {
+ warn("chown %s", filename);
+ free(cc_name);
+ krb5_cc_destroy(context, ccache);
+ krb5_cc_destroy(context, ccache2);
+ goto fail;
+ }
+
+ (void)setenv("KRB5CCNAME", cc_name, 1);
+ free(cc_name);
+ krb5_cc_close(context, ccache2);
+ krb5_cc_destroy(context, ccache);
+ return 0;
+
+ fail:
+ if (princ != NULL)
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ return 1;
+}
+#endif /* KERBEROS5 */
+
+static int
+check_ingroup(int gid, const char *gname, const char *user, int ifempty)
+{
+ struct group *gr;
+ char **g;
+#ifdef SU_INDIRECT_GROUP
+ char **gr_mem;
+ int n = 0;
+ int i = 0;
+#endif
+ int ok = 0;
+
+ if (gname == NULL)
+ gr = getgrgid((gid_t) gid);
+ else
+ gr = getgrnam(gname);
+
+ /*
+ * XXX we are relying on the fact that we only set ifempty when
+ * calling to check for SU_GROUP and that is the only time a
+ * missing group is acceptable.
+ */
+ if (gr == NULL)
+ return ifempty;
+ if (!*gr->gr_mem) /* empty */
+ return ifempty;
+
+ /*
+ * Ok, first see if user is in gr_mem
+ */
+ for (g = gr->gr_mem; *g; ++g) {
+ if (strcmp(*g, user) == 0)
+ return 1; /* ok */
+#ifdef SU_INDIRECT_GROUP
+ ++n; /* count them */
+#endif
+ }
+#ifdef SU_INDIRECT_GROUP
+ /*
+ * No.
+ * Now we need to duplicate the gr_mem list, and recurse for
+ * each member to see if it is a group, and if so whether user is
+ * in it.
+ */
+ gr_mem = emalloc((n + 1) * sizeof (char *));
+ for (g = gr->gr_mem, i = 0; *g; ++g) {
+ gr_mem[i] = estrdup(*g);
+ i++;
+ }
+ gr_mem[i++] = NULL;
+
+ for (g = gr_mem; ok == 0 && *g; ++g) {
+ /*
+ * If we get this far we don't accept empty/missing groups.
+ */
+ ok = check_ingroup(-1, *g, user, 0);
+ }
+ for (g = gr_mem; *g; ++g) {
+ free(*g);
+ }
+ free(gr_mem);
+#endif
+ return ok;
+}
--- /dev/null
+/* $NetBSD: su_pam.c,v 1.16 2010/10/02 10:55:36 tron Exp $ */
+
+/*
+ * Copyright (c) 1988 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1988\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";*/
+#else
+__RCSID("$NetBSD: su_pam.c,v 1.16 2010/10/02 10:55:36 tron Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include <util.h>
+#include <login_cap.h>
+
+#include <security/pam_appl.h>
+#include <security/openpam.h> /* for openpam_ttyconv() */
+
+#ifdef ALLOW_GROUP_CHANGE
+#include "grutil.h"
+#endif
+#include "suutil.h"
+
+static const struct pam_conv pamc = { &openpam_ttyconv, NULL };
+
+#define ARGSTRX "-dflm"
+
+#ifdef LOGIN_CAP
+#define ARGSTR ARGSTRX "c:"
+#else
+#define ARGSTR ARGSTRX
+#endif
+
+static void logit(const char *, ...);
+
+int
+main(int argc, char **argv)
+{
+ extern char **environ;
+ struct passwd *pwd, pwres;
+ char *p;
+ uid_t ruid;
+ int asme, ch, asthem, fastlogin, prio, gohome;
+ u_int setwhat;
+ enum { UNSET, YES, NO } iscsh = UNSET;
+ const char *user, *shell, *avshell;
+ char *username, *class;
+ char **np;
+ char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN];
+ int pam_err;
+ char hostname[MAXHOSTNAMELEN];
+ char *tty;
+ const char *func;
+ const void *newuser;
+ login_cap_t *lc;
+ pam_handle_t *pamh = NULL;
+ char pwbuf[1024];
+#ifdef PAM_DEBUG
+ extern int _openpam_debug;
+
+ _openpam_debug = 1;
+#endif
+#ifdef ALLOW_GROUP_CHANGE
+ char *gname;
+#endif
+
+ (void)setprogname(argv[0]);
+ asme = asthem = fastlogin = 0;
+ gohome = 1;
+ shell = class = NULL;
+ while ((ch = getopt(argc, argv, ARGSTR)) != -1)
+ switch((char)ch) {
+ case 'c':
+ class = optarg;
+ break;
+ case 'd':
+ asme = 0;
+ asthem = 1;
+ gohome = 0;
+ break;
+ case 'f':
+ fastlogin = 1;
+ break;
+ case '-':
+ case 'l':
+ asme = 0;
+ asthem = 1;
+ break;
+ case 'm':
+ asme = 1;
+ asthem = 0;
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+#ifdef ALLOW_GROUP_CHANGE
+ "Usage: %s [%s] [login[:group] [shell arguments]]\n",
+#else
+ "Usage: %s [%s] [login [shell arguments]]\n",
+#endif
+ getprogname(), ARGSTR);
+ exit(EXIT_FAILURE);
+ }
+ argv += optind;
+
+ /* Lower the priority so su runs faster */
+ errno = 0;
+ prio = getpriority(PRIO_PROCESS, 0);
+ if (errno)
+ prio = 0;
+ if (prio > -2)
+ (void)setpriority(PRIO_PROCESS, 0, -2);
+ openlog("su", 0, LOG_AUTH);
+
+ /* get current login name and shell */
+ ruid = getuid();
+ username = getlogin();
+ if (username == NULL ||
+ getpwnam_r(username, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
+ pwd == NULL || pwd->pw_uid != ruid) {
+ if (getpwuid_r(ruid, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0)
+ pwd = NULL;
+ }
+ if (pwd == NULL)
+ errx(EXIT_FAILURE, "who are you?");
+ username = estrdup(pwd->pw_name);
+
+ if (asme) {
+ if (pwd->pw_shell && *pwd->pw_shell) {
+ (void)estrlcpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
+ shell = shellbuf;
+ } else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+ }
+ /* get target login information, default to root */
+ user = *argv ? *argv : "root";
+ np = *argv ? argv : argv - 1;
+
+#ifdef ALLOW_GROUP_CHANGE
+ if ((p = strchr(user, ':')) != NULL) {
+ *p = '\0';
+ gname = ++p;
+ }
+ else
+ gname = NULL;
+
+#ifdef ALLOW_EMPTY_USER
+ if (user[0] == '\0')
+ user = username;
+#endif
+#endif
+ if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
+ pwd == NULL)
+ errx(EXIT_FAILURE, "unknown login %s", user);
+
+ /*
+ * PAM initialization
+ */
+#define PAM_END(msg) do { func = msg; goto done;} /* NOTREACHED */ while (/*CONSTCOND*/0)
+
+ if ((pam_err = pam_start("su", user, &pamc, &pamh)) != PAM_SUCCESS) {
+ if (pamh != NULL)
+ PAM_END("pam_start");
+ /* Things went really bad... */
+ syslog(LOG_ERR, "pam_start failed: %s",
+ pam_strerror(pamh, pam_err));
+ errx(EXIT_FAILURE, "pam_start failed");
+ }
+
+#define PAM_END_ITEM(item) PAM_END("pam_set_item(" # item ")")
+#define PAM_SET_ITEM(item, var) \
+ if ((pam_err = pam_set_item(pamh, (item), (var))) != PAM_SUCCESS) \
+ PAM_END_ITEM(item)
+
+ /*
+ * Fill hostname, username and tty
+ */
+ PAM_SET_ITEM(PAM_RUSER, username);
+ if (gethostname(hostname, sizeof(hostname)) != -1)
+ PAM_SET_ITEM(PAM_RHOST, hostname);
+
+ if ((tty = ttyname(STDERR_FILENO)) != NULL)
+ PAM_SET_ITEM(PAM_TTY, tty);
+
+ /*
+ * Authentication
+ */
+ if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
+ syslog(LOG_WARNING, "BAD SU %s to %s%s: %s",
+ username, user, ontty(), pam_strerror(pamh, pam_err));
+ (void)pam_end(pamh, pam_err);
+ errx(EXIT_FAILURE, "Sorry: %s", pam_strerror(pamh, pam_err));
+ }
+
+ /*
+ * Authorization
+ */
+ switch(pam_err = pam_acct_mgmt(pamh, 0)) {
+ case PAM_NEW_AUTHTOK_REQD:
+ pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+ if (pam_err != PAM_SUCCESS)
+ PAM_END("pam_chauthok");
+ break;
+ case PAM_SUCCESS:
+ break;
+ default:
+ PAM_END("pam_acct_mgmt");
+ break;
+ }
+
+ /*
+ * pam_authenticate might have changed the target user.
+ * refresh pwd and user
+ */
+ pam_err = pam_get_item(pamh, PAM_USER, &newuser);
+ if (pam_err != PAM_SUCCESS) {
+ syslog(LOG_WARNING,
+ "pam_get_item(PAM_USER): %s", pam_strerror(pamh, pam_err));
+ } else {
+ user = (char *)__UNCONST(newuser);
+ if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
+ pwd == NULL) {
+ (void)pam_end(pamh, pam_err);
+ syslog(LOG_ERR, "unknown login: %s", username);
+ errx(EXIT_FAILURE, "unknown login: %s", username);
+ }
+ }
+
+#define ERRX_PAM_END(args) do { \
+ (void)pam_end(pamh, pam_err); \
+ errx args; \
+} while (/* CONSTCOND */0)
+
+#define ERR_PAM_END(args) do { \
+ (void)pam_end(pamh, pam_err); \
+ err args; \
+} while (/* CONSTCOND */0)
+
+ /* force the usage of specified class */
+ if (class) {
+ if (ruid)
+ ERRX_PAM_END((EXIT_FAILURE, "Only root may use -c"));
+
+ pwd->pw_class = class;
+ }
+
+ if ((lc = login_getclass(pwd->pw_class)) == NULL)
+ ERRX_PAM_END((EXIT_FAILURE,
+ "Unknown class %s\n", pwd->pw_class));
+
+ if (asme) {
+ /* if asme and non-standard target shell, must be root */
+ if (chshell(pwd->pw_shell) == 0 && ruid)
+ ERRX_PAM_END((EXIT_FAILURE,
+ "permission denied (shell)."));
+ } else if (pwd->pw_shell && *pwd->pw_shell) {
+ shell = pwd->pw_shell;
+ iscsh = UNSET;
+ } else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+
+ if ((p = strrchr(shell, '/')) != NULL)
+ avshell = p + 1;
+ else
+ avshell = shell;
+
+ /* if we're forking a csh, we want to slightly muck the args */
+ if (iscsh == UNSET)
+ iscsh = strstr(avshell, "csh") ? YES : NO;
+
+ /*
+ * Initialize the supplemental groups before pam gets to them,
+ * so that other pam modules get a chance to add more when
+ * we do setcred. Note, we don't relinguish our set-userid yet
+ */
+ /* if we aren't changing users, keep the current group members */
+ if (ruid != pwd->pw_uid &&
+ setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) == -1)
+ ERR_PAM_END((EXIT_FAILURE, "setting user context"));
+
+#ifdef ALLOW_GROUP_CHANGE
+ addgroup(lc, gname, pwd, ruid, "Group Password:");
+#endif
+ if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
+ PAM_END("pam_setcred");
+
+ /*
+ * Manage session.
+ */
+ if (asthem) {
+ pid_t pid, xpid;
+ int status = 1;
+ struct sigaction sa, sa_int, sa_pipe, sa_quit;
+ int fds[2];
+
+ if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
+ PAM_END("pam_open_session");
+
+ /*
+ * In order to call pam_close_session after the
+ * command terminates, we need to fork.
+ */
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_IGN;
+ (void)sigemptyset(&sa.sa_mask);
+ (void)sigaction(SIGINT, &sa, &sa_int);
+ (void)sigaction(SIGQUIT, &sa, &sa_quit);
+ (void)sigaction(SIGPIPE, &sa, &sa_pipe);
+ sa.sa_handler = SIG_DFL;
+ (void)sigaction(SIGTSTP, &sa, NULL);
+ /*
+ * Use a pipe to guarantee the order of execution of
+ * the parent and the child.
+ */
+ if (pipe(fds) == -1) {
+ warn("pipe failed");
+ goto out;
+ }
+
+ switch (pid = fork()) {
+ case -1:
+ logit("fork failed (%s)", strerror(errno));
+ goto out;
+
+ case 0: /* Child */
+ (void)close(fds[1]);
+ (void)read(fds[0], &status, 1);
+ (void)close(fds[0]);
+ (void)sigaction(SIGINT, &sa_int, NULL);
+ (void)sigaction(SIGQUIT, &sa_quit, NULL);
+ (void)sigaction(SIGPIPE, &sa_pipe, NULL);
+ break;
+
+ default:
+ sa.sa_handler = SIG_IGN;
+ (void)sigaction(SIGTTOU, &sa, NULL);
+ (void)close(fds[0]);
+ (void)setpgid(pid, pid);
+ (void)tcsetpgrp(STDERR_FILENO, pid);
+ (void)close(fds[1]);
+ (void)sigaction(SIGPIPE, &sa_pipe, NULL);
+ /*
+ * Parent: wait for the child to terminate
+ * and call pam_close_session.
+ */
+ while ((xpid = waitpid(pid, &status, WUNTRACED))
+ == pid) {
+ if (WIFSTOPPED(status)) {
+ (void)kill(getpid(), SIGSTOP);
+ (void)tcsetpgrp(STDERR_FILENO,
+ getpgid(pid));
+ (void)kill(pid, SIGCONT);
+ status = 1;
+ continue;
+ }
+ break;
+ }
+
+ (void)tcsetpgrp(STDERR_FILENO, getpgid(0));
+
+ if (xpid == -1) {
+ logit("Error waiting for pid %d (%s)", pid,
+ strerror(errno));
+ } else if (xpid != pid) {
+ /* Can't happen. */
+ logit("Wrong PID: %d != %d", pid, xpid);
+ }
+out:
+ pam_err = pam_setcred(pamh, PAM_DELETE_CRED);
+ if (pam_err != PAM_SUCCESS)
+ logit("pam_setcred: %s",
+ pam_strerror(pamh, pam_err));
+ pam_err = pam_close_session(pamh, 0);
+ if (pam_err != PAM_SUCCESS)
+ logit("pam_close_session: %s",
+ pam_strerror(pamh, pam_err));
+ (void)pam_end(pamh, pam_err);
+ exit(WEXITSTATUS(status));
+ break;
+ }
+ }
+
+ /*
+ * The child: starting here, we don't have to care about
+ * handling PAM issues if we exit, the parent will do the
+ * job when we exit.
+ */
+#undef PAM_END
+#undef ERR_PAM_END
+#undef ERRX_PAM_END
+
+ if (!asme) {
+ if (asthem) {
+ char **pamenv;
+
+ p = getenv("TERM");
+ /*
+ * Create an empty environment
+ */
+ environ = emalloc(sizeof(char *));
+ environ[0] = NULL;
+
+ /*
+ * Add PAM environement, before the LOGIN_CAP stuff:
+ * if the login class is unspecified, we'll get the
+ * same data from PAM, if -c was used, the specified
+ * class must override PAM.
+ */
+ if ((pamenv = pam_getenvlist(pamh)) != NULL) {
+ char **envitem;
+
+ /*
+ * XXX Here FreeBSD filters out
+ * SHELL, LOGNAME, MAIL, CDPATH, IFS, PATH
+ * how could we get untrusted data here?
+ */
+ for (envitem = pamenv; *envitem; envitem++) {
+ if (putenv(*envitem) == -1)
+ free(*envitem);
+ }
+
+ free(pamenv);
+ }
+
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH |
+ LOGIN_SETENV | LOGIN_SETUMASK) == -1)
+ err(EXIT_FAILURE, "setting user context");
+ if (p)
+ (void)setenv("TERM", p, 1);
+ if (gohome && chdir(pwd->pw_dir) == -1)
+ errx(EXIT_FAILURE, "no directory");
+ }
+
+ if (asthem || pwd->pw_uid) {
+ (void)setenv("LOGNAME", pwd->pw_name, 1);
+ (void)setenv("USER", pwd->pw_name, 1);
+ }
+ (void)setenv("HOME", pwd->pw_dir, 1);
+ (void)setenv("SHELL", shell, 1);
+ }
+ (void)setenv("SU_FROM", username, 1);
+
+ if (iscsh == YES) {
+ if (fastlogin)
+ *np-- = __UNCONST("-f");
+ if (asme)
+ *np-- = __UNCONST("-m");
+ } else {
+ if (fastlogin)
+ (void)unsetenv("ENV");
+ }
+
+ if (asthem) {
+ avshellbuf[0] = '-';
+ (void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1);
+ avshell = avshellbuf;
+ } else if (iscsh == YES) {
+ /* csh strips the first character... */
+ avshellbuf[0] = '_';
+ (void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1);
+ avshell = avshellbuf;
+ }
+ *np = __UNCONST(avshell);
+
+ if (ruid != 0)
+ syslog(LOG_NOTICE, "%s to %s%s",
+ username, pwd->pw_name, ontty());
+
+ /* Raise our priority back to what we had before */
+ (void)setpriority(PRIO_PROCESS, 0, prio);
+
+ /*
+ * Set user context, except for umask, and the stuff
+ * we have done before.
+ */
+ setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
+ LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP);
+
+ /*
+ * 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) == -1)
+ err(EXIT_FAILURE, "setusercontext");
+
+ (void)execv(shell, np);
+ err(EXIT_FAILURE, "%s", shell);
+done:
+ logit("%s: %s", func, pam_strerror(pamh, pam_err));
+ (void)pam_end(pamh, pam_err);
+ return EXIT_FAILURE;
+}
+
+static void
+logit(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+}
--- /dev/null
+/* $NetBSD: suutil.c,v 1.1 2007/10/17 21:05:39 christos Exp $ */
+
+/*
+ * Copyright (c) 1988 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. 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.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: suutil.c,v 1.1 2007/10/17 21:05:39 christos Exp $");
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include "suutil.h"
+
+int
+chshell(const char *sh)
+{
+ const char *cp;
+
+ setusershell();
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, sh) == 0)
+ return 1;
+ return 0;
+}
+
+char *
+ontty(void)
+{
+ char *p;
+ static char buf[MAXPATHLEN + 4];
+
+ buf[0] = 0;
+ if ((p = ttyname(STDERR_FILENO)) != NULL)
+ (void)snprintf(buf, sizeof buf, " on %s", p);
+ return buf;
+}
+
--- /dev/null
+/* $NetBSD: suutil.h,v 1.1 2007/10/17 21:05:40 christos Exp $ */
+
+/*
+ * Copyright (c) 1988 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. 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.
+ */
+
+__BEGIN_DECLS
+int chshell(const char *);
+char *ontty(void);
+__END_DECLS