From 4de51eedad5c2244aac7f2cacbeeef2a2a8c0591 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Tue, 27 Mar 2012 02:19:46 +0200 Subject: [PATCH] import NetBSD su --- commands/Makefile | 2 +- commands/su/Makefile | 7 - commands/su/su.c | 149 ----------- man/man1/Makefile | 2 +- man/man1/su.1 | 58 ----- tools/nbsd_ports | 1 + usr.bin/su/Makefile | 71 ++++++ usr.bin/su/su.1 | 386 ++++++++++++++++++++++++++++ usr.bin/su/su.c | 585 +++++++++++++++++++++++++++++++++++++++++++ usr.bin/su/su_pam.c | 560 +++++++++++++++++++++++++++++++++++++++++ usr.bin/su/suutil.c | 63 +++++ usr.bin/su/suutil.h | 35 +++ 12 files changed, 1703 insertions(+), 216 deletions(-) delete mode 100644 commands/su/Makefile delete mode 100644 commands/su/su.c delete mode 100644 man/man1/su.1 create mode 100644 usr.bin/su/Makefile create mode 100644 usr.bin/su/su.1 create mode 100644 usr.bin/su/su.c create mode 100644 usr.bin/su/su_pam.c create mode 100644 usr.bin/su/suutil.c create mode 100644 usr.bin/su/suutil.h diff --git a/commands/Makefile b/commands/Makefile index bc24a4642..a07e94562 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -26,7 +26,7 @@ SUBDIR= add_route arp ash at awk \ reboot remsync rev rget rlogin \ rotate rsh rshd sed service setup shar acksize \ sleep slip sort spell split srccrc \ - stty su sum svclog swifi sync synctree sysenv \ + stty sum svclog swifi sync synctree sysenv \ syslogd tail tar tcpd tcpdp tcpstat tee telnet \ telnetd term termcap tget time touch tr \ truncate tsort tty udpstat umount uname unexpand \ diff --git a/commands/su/Makefile b/commands/su/Makefile deleted file mode 100644 index 7b2885193..000000000 --- a/commands/su/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -PROG= su -BINMODE= 4755 -MAN= -LDADD+= -lcrypt - - -.include diff --git a/commands/su/su.c b/commands/su/su.c deleted file mode 100644 index 9209c4db4..000000000 --- a/commands/su/su.c +++ /dev/null @@ -1,149 +0,0 @@ -/* su - become super-user Author: Patrick van Kleef */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if __minix_vmd -#include -#endif -#include - -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); -} diff --git a/man/man1/Makefile b/man/man1/Makefile index d293380fa..b6fa4d0c3 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -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 \ ref.1 remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \ shar.1 acksize.1 sleep.1 sort.1 spell.1 \ - split.1 stty.1 su.1 sum.1 svc.1 \ + split.1 stty.1 sum.1 svc.1 \ synctree.1 sysenv.1 sz.1 tail.1 tee.1 telnet.1 template.1 \ term.1 termcap.1 tget.1 time.1 tr.1 true.1 \ truncate.1 tsort.1 tty.1 umount.1 uname.1 unexpand.1 \ diff --git a/man/man1/su.1 b/man/man1/su.1 deleted file mode 100644 index 0b212e099..000000000 --- a/man/man1/su.1 +++ /dev/null @@ -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 diff --git a/tools/nbsd_ports b/tools/nbsd_ports index 92adeb83b..b3ed779e1 100644 --- a/tools/nbsd_ports +++ b/tools/nbsd_ports @@ -2,6 +2,7 @@ # Timestamp in UTC,minixpath,netbsdpath # minixpath: path in Minix source tree (starting from /usr/src/) # netbsdpath: path in BSD source tree (starting from src/) +2012/02/10 16:16:12,usr.bin/su 2011/12/25 06:09:09,sys/arch/i386/stand 2012/02/10 16:16:12,share/zoneinfo 2012/02/10 16:16:12,share/misc diff --git a/usr.bin/su/Makefile b/usr.bin/su/Makefile new file mode 100644 index 000000000..cc3ac73b6 --- /dev/null +++ b/usr.bin/su/Makefile @@ -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 + +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 diff --git a/usr.bin/su/su.1 b/usr.bin/su/su.1 new file mode 100644 index 000000000..59a877578 --- /dev/null +++ b/usr.bin/su/su.1 @@ -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). diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c new file mode 100644 index 000000000..eeba32c12 --- /dev/null +++ b/usr.bin/su/su.c @@ -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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SKEY +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOGIN_CAP +#include +#endif + +#ifdef KERBEROS5 +#include +#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; +} diff --git a/usr.bin/su/su_pam.c b/usr.bin/su/su_pam.c new file mode 100644 index 000000000..fc612afff --- /dev/null +++ b/usr.bin/su/su_pam.c @@ -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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* 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); +} diff --git a/usr.bin/su/suutil.c b/usr.bin/su/suutil.c new file mode 100644 index 000000000..ece54483b --- /dev/null +++ b/usr.bin/su/suutil.c @@ -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 +__RCSID("$NetBSD: suutil.c,v 1.1 2007/10/17 21:05:39 christos Exp $"); + +#include +#include +#include +#include +#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; +} + diff --git a/usr.bin/su/suutil.h b/usr.bin/su/suutil.h new file mode 100644 index 000000000..d1f646595 --- /dev/null +++ b/usr.bin/su/suutil.h @@ -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