]> Zhao Yanbai Git Server - minix.git/commitdiff
import NetBSD su
authorBen Gras <ben@minix3.org>
Tue, 27 Mar 2012 00:19:46 +0000 (02:19 +0200)
committerBen Gras <ben@minix3.org>
Tue, 27 Mar 2012 00:19:46 +0000 (02:19 +0200)
12 files changed:
commands/Makefile
commands/su/Makefile [deleted file]
commands/su/su.c [deleted file]
man/man1/Makefile
man/man1/su.1 [deleted file]
tools/nbsd_ports
usr.bin/su/Makefile [new file with mode: 0644]
usr.bin/su/su.1 [new file with mode: 0644]
usr.bin/su/su.c [new file with mode: 0644]
usr.bin/su/su_pam.c [new file with mode: 0644]
usr.bin/su/suutil.c [new file with mode: 0644]
usr.bin/su/suutil.h [new file with mode: 0644]

index bc24a4642b9a49f3b4523164144280e816423723..a07e9456230d20a45e67cc30914697d28abf41a4 100644 (file)
@@ -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 (file)
index 7b28851..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-PROG=  su
-BINMODE= 4755
-MAN=
-LDADD+=        -lcrypt
-
-
-.include <bsd.prog.mk>
diff --git a/commands/su/su.c b/commands/su/su.c
deleted file mode 100644 (file)
index 9209c4d..0000000
+++ /dev/null
@@ -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);
-}
index d293380facbdc0108bec6124e5ee8948e7540491..b6fa4d0c3794de979e20abab6487924a8ec835c0 100644 (file)
@@ -16,7 +16,7 @@ MAN=  ash.1 at.1 banner.1 basename.1 \
        profile.1 ps.1 pwd.1 rcp.1 readall.1 recwave.1 \
        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 (file)
index 0b212e0..0000000
+++ /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 <kjb@cs.vu.nl>
index 92adeb83b9a607ad07a23097e75dd46a1eb90754..b3ed779e12614b91518a71b5c717b2a69cf82189 100644 (file)
@@ -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 (file)
index 0000000..cc3ac73
--- /dev/null
@@ -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>
diff --git a/usr.bin/su/su.1 b/usr.bin/su/su.1
new file mode 100644 (file)
index 0000000..59a8775
--- /dev/null
@@ -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 (file)
index 0000000..eeba32c
--- /dev/null
@@ -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;
+}
diff --git a/usr.bin/su/su_pam.c b/usr.bin/su/su_pam.c
new file mode 100644 (file)
index 0000000..fc612af
--- /dev/null
@@ -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);
+}
diff --git a/usr.bin/su/suutil.c b/usr.bin/su/suutil.c
new file mode 100644 (file)
index 0000000..ece5448
--- /dev/null
@@ -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;
+}
+
diff --git a/usr.bin/su/suutil.h b/usr.bin/su/suutil.h
new file mode 100644 (file)
index 0000000..d1f6465
--- /dev/null
@@ -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