import NetBSD su
This commit is contained in:
parent
4f78dfcabc
commit
4de51eedad
12 changed files with 1703 additions and 216 deletions
|
@ -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 \
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
PROG= su
|
|
||||||
BINMODE= 4755
|
|
||||||
MAN=
|
|
||||||
LDADD+= -lcrypt
|
|
||||||
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
|
149
commands/su/su.c
149
commands/su/su.c
|
@ -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);
|
|
||||||
}
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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>
|
|
|
@ -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
71
usr.bin/su/Makefile
Normal 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
386
usr.bin/su/su.1
Normal 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
585
usr.bin/su/su.c
Normal 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
560
usr.bin/su/su_pam.c
Normal 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
63
usr.bin/su/suutil.c
Normal 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
35
usr.bin/su/suutil.h
Normal 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
|
Loading…
Reference in a new issue