import NetBSD su

This commit is contained in:
Ben Gras 2012-03-27 02:19:46 +02:00
parent 4f78dfcabc
commit 4de51eedad
12 changed files with 1703 additions and 216 deletions

View file

@ -26,7 +26,7 @@ SUBDIR= add_route arp ash at awk \
reboot remsync rev rget rlogin \ reboot remsync rev rget rlogin \
rotate rsh rshd sed service setup shar acksize \ rotate rsh rshd sed service setup shar acksize \
sleep slip sort spell split srccrc \ 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 \ syslogd tail tar tcpd tcpdp tcpstat tee telnet \
telnetd term termcap tget time touch tr \ telnetd term termcap tget time touch tr \
truncate tsort tty udpstat umount uname unexpand \ truncate tsort tty udpstat umount uname unexpand \

View file

@ -1,7 +0,0 @@
PROG= su
BINMODE= 4755
MAN=
LDADD+= -lcrypt
.include <bsd.prog.mk>

View file

@ -1,149 +0,0 @@
/* 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);
}

View file

@ -16,7 +16,7 @@ MAN= ash.1 at.1 banner.1 basename.1 \
profile.1 ps.1 pwd.1 rcp.1 readall.1 recwave.1 \ profile.1 ps.1 pwd.1 rcp.1 readall.1 recwave.1 \
ref.1 remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \ ref.1 remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \
shar.1 acksize.1 sleep.1 sort.1 spell.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 \ 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 \ term.1 termcap.1 tget.1 time.1 tr.1 true.1 \
truncate.1 tsort.1 tty.1 umount.1 uname.1 unexpand.1 \ truncate.1 tsort.1 tty.1 umount.1 uname.1 unexpand.1 \

View file

@ -1,58 +0,0 @@
.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>

View file

@ -2,6 +2,7 @@
# Timestamp in UTC,minixpath,netbsdpath # Timestamp in UTC,minixpath,netbsdpath
# minixpath: path in Minix source tree (starting from /usr/src/) # minixpath: path in Minix source tree (starting from /usr/src/)
# netbsdpath: path in BSD source tree (starting from 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 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/zoneinfo
2012/02/10 16:16:12,share/misc 2012/02/10 16:16:12,share/misc

71
usr.bin/su/Makefile Normal file
View file

@ -0,0 +1,71 @@
# $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>

386
usr.bin/su/su.1 Normal file
View file

@ -0,0 +1,386 @@
.\" 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).

585
usr.bin/su/su.c Normal file
View file

@ -0,0 +1,585 @@
/* $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;
}

560
usr.bin/su/su_pam.c Normal file
View file

@ -0,0 +1,560 @@
/* $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);
}

63
usr.bin/su/suutil.c Normal file
View file

@ -0,0 +1,63 @@
/* $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;
}

35
usr.bin/su/suutil.h Normal file
View file

@ -0,0 +1,35 @@
/* $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