Add full PAM support for account management and sessions.

The PAM_FAIL_CHECK and PAM_END macros in su.c came from the util-linux
package's PAM patches to the BSD login.c

Submitted by:	"David J. MacKenzie" <djm@web.us.uu.net>
This commit is contained in:
Mark Murray 2001-03-27 19:40:51 +00:00
parent b2f6bdeeaa
commit 5bc9d93db3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=74874
12 changed files with 686 additions and 184 deletions

View File

@ -3,30 +3,84 @@
# This file controls the authentication methods that login and other
# utilities use. See pam(8) for a description of its format.
#
# Note: the final entry must say "required" -- otherwise, things don't
# work quite right. If you delete the final entry, be sure to change
# "sufficient" to "required" in the entry before it.
#
# $FreeBSD$
#
# service-name module-type control-flag module-path arguments
#
# module-type:
# auth: prompt for a password to authenticate that the user is
# who they say they are, and set any credentials.
# account: non-authentication based authorization, based on time,
# resources, etc.
# session: housekeeping before and/or after login.
# password: update authentication tokens.
#
# control-flag: How libpam handles success or failure of the module.
# required: success is required, and on failure all remaining
# modules are run.
# requisite: success is required, and on failure no remaining
# modules are run.
# sufficient: success is sufficient, and if no previous required
# module failed, no remaining modules are run.
# optional: ignored unless the other modules return PAM_IGNORE.
#
# arguments:
# Passed to the module; module-specific plus some generic ones:
# debug: syslog debug info.
# no_warn: return no warning messages to the application.
# use_first_pass: try authentication using password from the
# preceding auth module.
# try_first_pass: first try authentication using password from
# the preceding auth module, and if that fails
# prompt for a new password.
# use_mapped_pass: convert cleartext password to a crypto key.
# expose_account: allow printing more info about the user when
# prompting.
#
# Each final entry must say "required" -- otherwise, things don't
# work quite right. If you delete a final entry, be sure to change
# "sufficient" to "required" in the entry before it.
# If the user can authenticate with S/Key, that's sufficient; allow clear
# password. Try kerberos, then try plain unix password.
login auth sufficient pam_skey.so
login auth requisite pam_cleartext_pass_ok.so
#login auth sufficient pam_kerberosIV.so try_first_pass
#login auth sufficient pam_krb5.so
login auth required pam_unix.so try_first_pass
#login account required pam_krb5.so
login account required pam_unix.so
#login session required pam_krb5.so
login password required pam_permit.so
login session required pam_permit.so
# Same requirement for ftpd as login
ftpd auth sufficient pam_skey.so
ftpd auth requisite pam_cleartext_pass_ok.so
#ftpd auth sufficient pam_kerberosIV.so try_first_pass
rsh auth required pam_permit.so
rsh account required pam_unix.so
rsh session required pam_permit.so
#su auth sufficient pam_krb5.so
su auth required pam_unix.so try_first_pass
#su account required pam_krb5.so
su account required pam_unix.so
#su session required pam_krb5.so
su password required pam_permit.so
su session required pam_permit.so
# Native ftpd.
#ftpd auth sufficient pam_krb5.so
ftpd auth required pam_unix.so try_first_pass
#ftpd account required pam_krb5.so
ftpd account required pam_unix.so
#ftpd session required pam_krb5.so
# OpenSSH with PAM support requires similar modules. The session one is
# a bit strange, though...
sshd auth sufficient pam_skey.so
#sshd auth sufficient pam_kerberosIV.so try_first_pass
# PROftpd.
#ftp auth sufficient pam_krb5.so
ftp auth required pam_unix.so try_first_pass
#ftp account required pam_krb5.so
ftp account required pam_unix.so
#ftp session required pam_krb5.so
#sshd auth sufficient pam_krb5.so
sshd auth required pam_unix.so try_first_pass
#sshd account required pam_krb5.so
sshd account required pam_unix.so
sshd password required pam_permit.so
#sshd session required pam_krb5.so
sshd session required pam_permit.so
# Don't break startx
@ -35,15 +89,14 @@ xserver auth required pam_permit.so
# XDM is difficult; it fails or moans unless there are modules for each
# of the four management groups; auth, account, session and password.
xdm auth required pam_unix.so
#xdm auth sufficient pam_kerberosIV.so try_first_pass
xdm account required pam_unix.so try_first_pass
xdm account required pam_unix.so
xdm session required pam_deny.so
xdm password required pam_deny.so
# Mail services
imap auth required pam_unix.so try_first_pass
pop3 auth required pam_unix.so try_first_pass
#imap auth required pam_unix.so try_first_pass
#pop3 auth required pam_unix.so try_first_pass
# If we don't match anything else, default to using getpwnam().
other auth required pam_unix.so try_first_pass
other account required pam_unix.so try_first_pass
other account required pam_unix.so

View File

@ -18,9 +18,8 @@ LSDIR= ../../bin/ls
SRCS+= ls.c cmp.c print.c util.c
CFLAGS+=-Dmain=ls_main -I${.CURDIR}/${LSDIR}
.if defined(NOPAM)
CFLAGS+=-DNOPAM
.else
.if !defined(NOPAM)
CFLAGS+=-DUSE_PAM
DPADD+= ${LIBPAM}
LDADD+= ${MINUSLPAM}
.endif

View File

@ -95,7 +95,7 @@ static const char rcsid[] =
#include <skey.h>
#endif
#if !defined(NOPAM)
#ifdef USE_PAM
#include <security/pam_appl.h>
#endif
@ -182,8 +182,9 @@ char *ident = NULL;
static char ttyline[20];
char *tty = ttyline; /* for klogin */
#if !defined(NOPAM)
#ifdef USE_PAM
static int auth_pam __P((struct passwd**, const char*));
pam_handle_t *pamh = NULL;
#endif
char *pid_file = NULL;
@ -1033,6 +1034,9 @@ checkuser(fname, name, pwset)
static void
end_login()
{
#ifdef USE_PAM
int e;
#endif
(void) seteuid((uid_t)0);
if (logged_in)
@ -1041,13 +1045,22 @@ end_login()
#ifdef LOGIN_CAP
setusercontext(NULL, getpwuid(0), (uid_t)0,
LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
#endif
#ifdef USE_PAM
if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e));
if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
pamh = NULL;
#endif
logged_in = 0;
guest = 0;
dochroot = 0;
}
#if !defined(NOPAM)
#ifdef USE_PAM
/*
* the following code is stolen from imap-uw PAM authentication module and
@ -1166,19 +1179,34 @@ auth_pam(struct passwd **ppw, const char *pass)
break;
default:
syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e));
syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
rval = -1;
break;
}
if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
rval = -1;
if (rval == 0) {
e = pam_acct_mgmt(pamh, 0);
if (e == PAM_NEW_AUTHTOK_REQD) {
e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (e != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e));
rval = 1;
}
} else if (e != PAM_SUCCESS) {
rval = 1;
}
}
if (rval != 0) {
if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
}
pamh = NULL;
}
return rval;
}
#endif /* !defined(NOPAM) */
#endif /* USE_PAM */
void
pass(passwd)
@ -1189,6 +1217,9 @@ pass(passwd)
#ifdef LOGIN_CAP
login_cap_t *lc = NULL;
#endif
#ifdef USE_PAM
int e;
#endif
if (logged_in || askpasswd == 0) {
reply(503, "Login with USER first.");
@ -1200,7 +1231,7 @@ pass(passwd)
rval = 1; /* failure below */
goto skip;
}
#if !defined(NOPAM)
#ifdef USE_PAM
rval = auth_pam(&pw, passwd);
if (rval >= 0)
goto skip;
@ -1281,6 +1312,16 @@ pass(passwd)
(void) initgroups(pw->pw_name, pw->pw_gid);
#endif
#ifdef USE_PAM
if (pamh) {
if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
}
}
#endif
/* open wtmp before chroot */
ftpd_logwtmp(ttyline, pw->pw_name, remotehost);
logged_in = 1;

View File

@ -7,11 +7,16 @@ MAN= rshd.8
#CFLAGS+= -DCRYPT
# For login_cap handling
CFLAGS+=-DLOGIN_CAP -Wall
CFLAGS+= -Wall
DPADD+= ${LIBUTIL}
LDADD+= -lutil
.if !defined(NOPAM)
CFLAGS+= -DUSE_PAM
DPADD+= ${LIBPAM}
LDADD+= ${MINUSLPAM}
.endif
# IPv6 support
CFLAGS+= -DINET6

View File

@ -241,6 +241,16 @@ and is not preceded by a flag byte.
.It Pa /etc/login.conf
.It Ev $HOME Ns Pa /.rhosts
.It Pa /var/run/nologin
.It Pa /etc/pam.conf
if
.Nm
is configured with PAM support, it uses
.Pa /etc/pam.conf
entries with service name
.Dq rsh .
authentication modules requiring passwords (such as
.Nm pam_unix )
are not supported
.El
.Sh BUGS
The authentication procedure used here assumes the integrity

View File

@ -76,9 +76,27 @@ static const char rcsid[] =
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#ifdef LOGIN_CAP
#include <login_cap.h>
#endif
#ifdef USE_PAM
#include <security/pam_appl.h>
#include <sys/wait.h>
static int export_pam_environment __P((void));
static int ok_to_export __P((const char *));
static pam_handle_t *pamh;
static char **environ_pam;
#define PAM_END { \
if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
syslog(LOG_ERR|LOG_AUTH, "pam_setcred: %s", pam_strerror(pamh, retcode)); \
if ((retcode = pam_close_session(pamh,0)) != PAM_SUCCESS) \
syslog(LOG_ERR|LOG_AUTH, "pam_close_session: %s", pam_strerror(pamh, retcode)); \
if ((retcode = pam_end(pamh, retcode)) != PAM_SUCCESS) \
syslog(LOG_ERR|LOG_AUTH, "pam_end: %s", pam_strerror(pamh, retcode)); \
}
#endif /* USE_PAM */
/* wrapper for KAME-special getnameinfo() */
#ifndef NI_WITHSCOPEID
@ -188,6 +206,20 @@ main(argc, argv)
return(0);
}
#ifdef USE_PAM
/*
* We can't have a conversation with the client over the rsh connection.
* You must use auth methods that don't require one, like pam_rhosts.
*/
int null_conv(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr)
{
syslog(LOG_ERR, "PAM conversation is not supported");
return PAM_CONV_ERR;
}
#endif /* USE_PAM */
char username[20] = "USER=";
char homedir[64] = "HOME=";
char shell[64] = "SHELL=";
@ -216,9 +248,11 @@ doit(fromp)
int rc;
int pv1[2], pv2[2];
#endif
#ifdef LOGIN_CAP
login_cap_t *lc;
#endif
#ifdef USE_PAM
static struct pam_conv conv = { null_conv, NULL };
int retcode;
#endif /* USE_PAM */
(void) signal(SIGINT, SIG_DFL);
(void) signal(SIGQUIT, SIG_DFL);
@ -229,7 +263,7 @@ doit(fromp)
&& af != AF_INET6
#endif
) {
syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", af);
syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
exit(1);
}
err = getnameinfo((struct sockaddr *)fromp, fromp->su_len, numericname,
@ -341,6 +375,52 @@ doit(fromp)
getstr(locuser, sizeof(locuser), "locuser");
getstr(cmdbuf, sizeof(cmdbuf), "command");
#ifdef USE_PAM
retcode = pam_start("rsh", locuser, &conv, &pamh);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR|LOG_AUTH, "pam_start: %s", pam_strerror(pamh, retcode));
error("Login incorrect.\n");
exit(1);
}
retcode = pam_set_item (pamh, PAM_RUSER, remuser);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_RUSER): %s", pam_strerror(pamh, retcode));
error("Login incorrect.\n");
exit(1);
}
retcode = pam_set_item (pamh, PAM_RHOST, fromhost);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode));
error("Login incorrect.\n");
exit(1);
}
retcode = pam_set_item (pamh, PAM_TTY, "tty");
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode));
error("Login incorrect.\n");
exit(1);
}
retcode = pam_authenticate(pamh, 0);
if (retcode == PAM_SUCCESS) {
if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &cp)) == PAM_SUCCESS) {
strncpy(locuser, cp, sizeof(locuser));
locuser[sizeof(locuser) - 1] = '\0';
} else
syslog(LOG_ERR|LOG_AUTH, "pam_get_item(PAM_USER): %s",
pam_strerror(pamh, retcode));
retcode = pam_acct_mgmt(pamh, 0);
}
if (retcode != PAM_SUCCESS) {
syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'",
remuser, fromhost, locuser, pam_strerror(pamh, retcode), cmdbuf);
error("Login incorrect.\n");
exit(1);
}
#endif /* USE_PAM */
setpwent();
pwd = getpwnam(locuser);
if (pwd == NULL) {
@ -349,13 +429,36 @@ doit(fromp)
remuser, fromhost, locuser, cmdbuf);
if (errorstr == NULL)
errorstr = "Login incorrect.\n";
goto fail;
error(errorstr, fromhost);
exit(1);
}
#ifdef LOGIN_CAP
#ifndef USE_PAM
if (errorstr ||
(pwd->pw_expire && time(NULL) >= pwd->pw_expire) ||
iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0,
remuser, locuser) < 0) {
if (__rcmd_errstr)
syslog(LOG_INFO|LOG_AUTH,
"%s@%s as %s: permission denied (%s). cmd='%.80s'",
remuser, fromhost, locuser, __rcmd_errstr,
cmdbuf);
else
syslog(LOG_INFO|LOG_AUTH,
"%s@%s as %s: permission denied. cmd='%.80s'",
remuser, fromhost, locuser, cmdbuf);
if (errorstr == NULL)
errorstr = "Login incorrect.\n";
error(errorstr, fromhost);
exit(1);
}
#endif /* USE_PAM */
lc = login_getpwclass(pwd);
#endif
if (pwd->pw_uid)
auth_checknologin(lc);
if (chdir(pwd->pw_dir) < 0) {
#ifdef LOGIN_CAP
if (chdir("/") < 0 ||
login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
syslog(LOG_INFO|LOG_AUTH,
@ -364,44 +467,9 @@ doit(fromp)
error("No remote home directory.\n");
exit(0);
}
#else
(void) chdir("/");
#ifdef notdef
syslog(LOG_INFO|LOG_AUTH,
"%s@%s as %s: no home directory. cmd='%.80s'",
remuser, fromhost, locuser, cmdbuf);
error("No remote directory.\n");
exit(1);
#endif
#endif
pwd->pw_dir = "/";
}
if (errorstr ||
(pwd->pw_expire && time(NULL) >= pwd->pw_expire) ||
iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0,
remuser, locuser) < 0) {
if (__rcmd_errstr)
syslog(LOG_INFO|LOG_AUTH,
"%s@%s as %s: permission denied (%s). cmd='%.80s'",
remuser, fromhost, locuser, __rcmd_errstr,
cmdbuf);
else
syslog(LOG_INFO|LOG_AUTH,
"%s@%s as %s: permission denied. cmd='%.80s'",
remuser, fromhost, locuser, cmdbuf);
fail:
if (errorstr == NULL)
errorstr = "Login incorrect.\n";
error(errorstr, fromhost);
exit(1);
}
if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
error("Logins currently disabled.\n");
exit(1);
}
#ifdef LOGIN_CAP
if (lc != NULL && fromp->su_family == AF_INET) { /*XXX*/
char remote_ip[MAXHOSTNAMELEN];
@ -421,13 +489,30 @@ doit(fromp)
exit(1);
}
}
#endif /* !LOGIN_CAP */
#if BSD > 43
/* before fork, while we're session leader */
if (setlogin(pwd->pw_name) < 0)
syslog(LOG_ERR, "setlogin() failed: %m");
#endif
/*
* PAM modules might add supplementary groups in
* pam_setcred(), so initialize them first.
* But we need to open the session as root.
*/
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
syslog(LOG_ERR, "setusercontext: %m");
exit(1);
}
#ifdef USE_PAM
if ((retcode = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, retcode));
} else if ((retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode));
}
#endif /* USE_PAM */
(void) write(STDERR_FILENO, "\0", 1);
sent_null = 1;
@ -569,6 +654,9 @@ doit(fromp)
(doencrypt && FD_ISSET(pv1[0], &readfrom)) ||
#endif
FD_ISSET(pv[0], &readfrom));
#ifdef USE_PAM
PAM_END;
#endif /* USE_PAM */
exit(0);
}
setpgrp(0, getpid());
@ -586,9 +674,38 @@ doit(fromp)
dup2(pv[1], 2);
close(pv[1]);
}
#ifdef USE_PAM
else {
pid = fork();
if (pid == -1) {
error("Can't fork; try again.\n");
exit(1);
}
if (pid) {
/* Parent. */
wait(NULL);
PAM_END;
exit(0);
}
}
#endif /* USE_PAM */
if (*pwd->pw_shell == '\0')
pwd->pw_shell = _PATH_BSHELL;
environ = envinit;
#ifdef USE_PAM
/*
* Add any environmental variables that the
* PAM modules may have set.
*/
environ_pam = pam_getenvlist(pamh);
if (environ_pam)
export_pam_environment();
if ((retcode = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
syslog(LOG_ERR|LOG_AUTH, "pam_end: %s", pam_strerror(pamh, retcode));
#endif /* USE_PAM */
strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
strcat(path, _PATH_DEFPATH);
strncat(shell, pwd->pw_shell, sizeof(shell)-7);
@ -598,17 +715,13 @@ doit(fromp)
cp++;
else
cp = pwd->pw_shell;
#ifdef LOGIN_CAP
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) {
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETGROUP) != 0) {
syslog(LOG_ERR, "setusercontext: %m");
exit(1);
}
login_close(lc);
#else
(void) setgid((gid_t)pwd->pw_gid);
initgroups(pwd->pw_name, pwd->pw_gid);
(void) setuid((uid_t)pwd->pw_uid);
#endif
endpwent();
if (log_success || pwd->pw_uid == 0) {
syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
@ -717,6 +830,51 @@ topdomain(h)
return (maybe);
}
#ifdef USE_PAM
static int
export_pam_environment()
{
char **pp;
for (pp = environ_pam; *pp != NULL; pp++) {
if (ok_to_export(*pp))
(void) putenv(*pp);
free(*pp);
}
return PAM_SUCCESS;
}
/*
* Sanity checks on PAM environmental variables:
* - Make sure there is an '=' in the string.
* - Make sure the string doesn't run on too long.
* - Do not export certain variables. This list was taken from the
* Solaris pam_putenv(3) man page.
*/
static int
ok_to_export(s)
const char *s;
{
static const char *noexport[] = {
"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
"IFS", "PATH", NULL
};
const char **pp;
size_t n;
if (strlen(s) > 1024 || strchr(s, '=') == NULL)
return 0;
if (strncmp(s, "LD_", 3) == 0)
return 0;
for (pp = noexport; *pp != NULL; pp++) {
n = strlen(*pp);
if (s[n] == '=' && strncmp(s, *pp, n) == 0)
return 0;
}
return 1;
}
#endif /* USE_PAM */
void
usage()
{

View File

@ -10,9 +10,8 @@ CFLAGS+=-Wall -DLOGIN_ACCESS -DLOGALL
DPADD= ${LIBUTIL} ${LIBCRYPT}
LDADD= -lutil -lcrypt
.if defined(NOPAM)
CFLAGS+= -DNO_PAM
.else
.if !defined(NOPAM)
CFLAGS+= -DUSE_PAM
DPADD+= ${LIBPAM}
LDADD+= ${MINUSLPAM}
.endif

View File

@ -177,6 +177,13 @@ system mailboxes
makes login quieter
.It Pa /etc/auth.conf
configure authentication services
.It Pa /etc/pam.conf
if
.Nm
is configured with PAM support, it uses
.Pa /etc/pam.conf
entries with service name
.Dq login
.El
.Sh SEE ALSO
.Xr builtin 1 ,

View File

@ -78,10 +78,11 @@ static const char rcsid[] =
#include <unistd.h>
#include <utmp.h>
#ifndef NO_PAM
#ifdef USE_PAM
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#endif
#include <sys/wait.h>
#endif /* USE_PAM */
#include "pathnames.h"
@ -104,11 +105,23 @@ void timedout __P((int));
int login_access __P((char *, char *));
void login_fbtab __P((char *, uid_t, gid_t));
#ifndef NO_PAM
#ifdef USE_PAM
static int auth_pam __P((void));
static int export_pam_environment __P((void));
static int ok_to_export __P((const char *));
#endif
static pam_handle_t *pamh = NULL;
static char **environ_pam;
#define PAM_END { \
if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \
if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \
syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \
if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \
}
#endif /* USE_PAM */
static int auth_traditional __P((void));
extern void login __P((struct utmp *));
static void usage __P((void));
@ -130,9 +143,6 @@ struct passwd *pwd;
int failures;
char *term, *envinit[1], *hostname, *username, *tty;
char full_hostname[MAXHOSTNAMELEN];
#ifndef NO_PAM
static char **environ_pam;
#endif
int
main(argc, argv)
@ -155,6 +165,10 @@ main(argc, argv)
char tname[sizeof(_PATH_TTY) + 10];
char *shell = NULL;
login_cap_t *lc = NULL;
#ifdef USE_PAM
pid_t pid;
int e;
#endif /* USE_PAM */
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
@ -314,19 +328,19 @@ main(argc, argv)
(void)setpriority(PRIO_PROCESS, 0, -4);
#ifndef NO_PAM
#ifdef USE_PAM
/*
* Try to authenticate using PAM. If a PAM system error
* occurs, perhaps because of a botched configuration,
* then fall back to using traditional Unix authentication.
*/
if ((rval = auth_pam()) == -1)
#endif /* NO_PAM */
#endif /* USE_PAM */
rval = auth_traditional();
(void)setpriority(PRIO_PROCESS, 0, 0);
#ifndef NO_PAM
#ifdef USE_PAM
/*
* PAM authentication may have changed "pwd" to the
* entry for the template user. Check again to see if
@ -334,7 +348,7 @@ main(argc, argv)
*/
if (pwd != NULL && pwd->pw_uid == 0)
rootlogin = 1;
#endif /* NO_PAM */
#endif /* USE_PAM */
ttycheck:
/*
@ -553,14 +567,54 @@ main(argc, argv)
if (!pflag)
environ = envinit;
#ifndef NO_PAM
#ifdef USE_PAM
/*
* Add any environmental variables that the
* PAM modules may have set.
*/
if (environ_pam)
export_pam_environment();
#endif
if (pamh) {
environ_pam = pam_getenvlist(pamh);
if (environ_pam)
export_pam_environment();
}
#endif /* USE_PAM */
/*
* PAM modules might add supplementary groups during pam_setcred().
*/
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
syslog(LOG_ERR, "setusercontext() failed - exiting");
exit(1);
}
#ifdef USE_PAM
if (pamh) {
if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
}
/*
* We must fork() before setuid() because we need to call
* pam_close_session() as root.
*/
pid = fork();
if (pid < 0) {
err(1, "fork");
PAM_END;
exit(0);
} else if (pid) {
/* parent - wait for child to finish, then cleanup session */
wait(NULL);
PAM_END;
exit(0);
} else {
if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
}
}
#endif /* USE_PAM */
/*
* We don't need to be root anymore, so
@ -571,7 +625,7 @@ main(argc, argv)
exit(1);
}
if (setusercontext(lc, pwd, pwd->pw_uid,
LOGIN_SETALL & ~LOGIN_SETLOGIN) != 0) {
LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
syslog(LOG_ERR, "setusercontext() failed - exiting");
exit(1);
}
@ -666,7 +720,7 @@ auth_traditional()
return rval;
}
#ifndef NO_PAM
#ifdef USE_PAM
/*
* Attempt to authenticate the user using PAM. Returns 0 if the user is
* authenticated, or 1 if not authenticated. If some sort of PAM system
@ -677,7 +731,6 @@ auth_traditional()
static int
auth_pam()
{
pam_handle_t *pamh = NULL;
const char *tmpl_user;
const void *item;
int rval;
@ -728,11 +781,6 @@ auth_pam()
} else
syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
pam_strerror(pamh, e));
if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) !=
PAM_SUCCESS)
syslog(LOG_ERR, "Couldn't establish credentials: %s",
pam_strerror(pamh, e));
environ_pam = pam_getenvlist(pamh);
rval = 0;
break;
@ -743,13 +791,29 @@ auth_pam()
break;
default:
syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e));
syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
rval = -1;
break;
}
if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
rval = -1;
if (rval == 0) {
e = pam_acct_mgmt(pamh, 0);
if (e == PAM_NEW_AUTHTOK_REQD) {
e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (e != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e));
rval = 1;
}
} else if (e != PAM_SUCCESS) {
rval = 1;
}
}
if (rval != 0) {
if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
}
pamh = NULL;
}
return rval;
}
@ -796,7 +860,7 @@ ok_to_export(s)
}
return 1;
}
#endif /* NO_PAM */
#endif /* USE_PAM */
static void
usage()
@ -807,7 +871,7 @@ usage()
/*
* Allow for authentication style and/or kerberos instance
* */
*/
#define NBUFSIZ UT_NAMESIZE + 64

View File

@ -4,9 +4,18 @@
PROG= su
SRCS= su.c
COPTS+= -DLOGIN_CAP -DSKEY
DPADD= ${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
LDADD= -lutil -lskey -lmd -lcrypt
DPADD+= ${LIBUTIL}
LDADD+= -lutil
.if !defined(NOPAM)
CFLAGS+= -DUSE_PAM
DPADD+= ${LIBPAM}
LDADD+= ${MINUSLPAM}
.else
COPTS+= -DSKEY
DPADD+= ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
LDADD+= -lskey -lmd -lcrypt
.endif
.if defined(WHEELSU)
COPTS+= -DWHEELSU

View File

@ -173,6 +173,13 @@ to remind one of its awesome power.
.Bl -tag -width /etc/auth.conf -compact
.It Pa /etc/auth.conf
configure authentication services
.It Pa /etc/pam.conf
if
.Nm
is configured with PAM support, it uses
.Pa /etc/pam.conf
entries with service name
.Dq su
.El
.Sh SEE ALSO
.Xr csh 1 ,

View File

@ -60,36 +60,47 @@ static const char rcsid[] =
#include <syslog.h>
#include <unistd.h>
#include <libutil.h>
#ifdef LOGIN_CAP
#include <login_cap.h>
#endif
#ifdef USE_PAM
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <signal.h>
#include <sys/wait.h>
static int export_pam_environment __P((void));
static int ok_to_export __P((const char *));
static pam_handle_t *pamh = NULL;
static char **environ_pam;
#define PAM_END { \
if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) { \
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode)); \
} \
if ((retcode = pam_end(pamh,retcode)) != PAM_SUCCESS) { \
syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, retcode)); \
} \
}
#else /* !USE_PAM */
#ifdef SKEY
#include <skey.h>
#endif
#endif /* USE_PAM */
#ifdef KERBEROS
#include <openssl/des.h>
#include <krb.h>
#include <netdb.h>
#ifdef LOGIN_CAP
#define ARGSTR "-Kflmc:"
#else
#define ARGSTR "-Kflm"
#endif
static int kerberos(char *username, char *user, int uid, char *pword);
static int koktologin(char *name, char *toname);
int use_kerberos = 1;
#else /* !KERBEROS */
#ifdef LOGIN_CAP
#define ARGSTR "-flmc:"
#else
#define ARGSTR "-flm"
#endif
#endif /* KERBEROS */
char *ontty __P((void));
@ -107,17 +118,24 @@ main(argc, argv)
char *targetpass;
int iswheelsu;
#endif /* WHEELSU */
char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
struct group *gr;
char *p, *user, *shell=NULL, *username, *cleanenv = NULL, **nargv, **np;
uid_t ruid;
gid_t gid;
int asme, ch, asthem, fastlogin, prio, i;
enum { UNSET, YES, NO } iscsh = UNSET;
#ifdef LOGIN_CAP
login_cap_t *lc;
char *class=NULL;
int setwhat;
#endif
#ifdef USE_PAM
int retcode;
struct pam_conv conv = { misc_conv, NULL };
char myhost[MAXHOSTNAMELEN + 1], *mytty;
int statusp=0;
int child_pid, child_pgrp, ret_pid;
#else /* !USE_PAM */
char **g;
struct group *gr;
#endif /* USE_PAM */
#ifdef KERBEROS
char *k;
#endif
@ -147,11 +165,9 @@ main(argc, argv)
asme = 1;
asthem = 0;
break;
#ifdef LOGIN_CAP
case 'c':
class = optarg;
break;
#endif
case '?':
default:
usage();
@ -161,8 +177,7 @@ main(argc, argv)
user = argv[optind++];
if (strlen(user) > MAXLOGNAME - 1) {
(void)fprintf(stderr, "su: username too long.\n");
exit(1);
errx(1, "username too long");
}
if (user == NULL)
@ -189,7 +204,7 @@ main(argc, argv)
if (errno)
prio = 0;
(void)setpriority(PRIO_PROCESS, 0, -2);
openlog("su", LOG_CONS, 0);
openlog("su", LOG_CONS, LOG_AUTH);
/* get current login name and shell */
ruid = getuid();
@ -214,11 +229,61 @@ main(argc, argv)
}
}
#ifdef USE_PAM
retcode = pam_start("su", user, &conv, &pamh);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
}
gethostname(myhost, sizeof(myhost));
retcode = pam_set_item(pamh, PAM_RHOST, myhost);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode));
errx(1, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode));
}
mytty = ttyname(STDERR_FILENO);
if (!mytty)
mytty = "tty";
retcode = pam_set_item(pamh, PAM_TTY, mytty);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode));
errx(1, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode));
}
if (ruid) {
retcode = pam_authenticate(pamh, 0);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &p)) == PAM_SUCCESS) {
user = p;
} else
syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
pam_strerror(pamh, retcode));
retcode = pam_acct_mgmt(pamh, 0);
if (retcode == PAM_NEW_AUTHTOK_REQD) {
retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
}
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_acct_mgmt: %s", pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
}
#endif /* USE_PAM */
/* get target login information, default to root */
if ((pwd = getpwnam(user)) == NULL) {
errx(1, "unknown login: %s", user);
}
#ifdef LOGIN_CAP
if (class==NULL) {
lc = login_getpwclass(pwd);
} else {
@ -228,8 +293,8 @@ main(argc, argv)
if (lc == NULL)
errx(1, "unknown class: %s", class);
}
#endif
#ifndef USE_PAM
#ifdef WHEELSU
targetpass = strdup(pwd->pw_passwd);
#endif /* WHEELSU */
@ -280,18 +345,18 @@ main(argc, argv)
#ifdef WHEELSU
|| (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
#endif /* WHEELSU */
)) {
#else
))
#else /* !SKEY */
p = getpass("Password:");
if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
#endif
if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd)))
#endif /* SKEY */
{
#ifdef KERBEROS
if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
#endif
{
fprintf(stderr, "Sorry\n");
{
syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
exit(1);
errx(1, "Sorry");
}
}
#ifdef WHEELSU
@ -301,17 +366,17 @@ main(argc, argv)
#endif /* WHEELSU */
}
if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
fprintf(stderr, "Sorry - account expired\n");
syslog(LOG_AUTH|LOG_WARNING,
"BAD SU %s to %s%s", username,
user, ontty());
exit(1);
errx(1, "Sorry - account expired");
}
}
#endif /* USE_PAM */
if (asme) {
/* if asme and non-standard target shell, must be root */
if (!chshell(pwd->pw_shell) && ruid)
if (ruid && !chshell(pwd->pw_shell))
errx(1, "permission denied (shell).");
} else if (pwd->pw_shell && *pwd->pw_shell) {
shell = pwd->pw_shell;
@ -334,7 +399,49 @@ main(argc, argv)
(void)setpriority(PRIO_PROCESS, 0, prio);
#ifdef LOGIN_CAP
/*
* PAM modules might add supplementary groups in
* pam_setcred(), so initialize them first.
*/
if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
err(1, "setusercontext");
#ifdef USE_PAM
retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
if (retcode != PAM_SUCCESS) {
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode));
}
/*
* We must fork() before setuid() because we need to call
* pam_setcred(pamh, PAM_DELETE_CRED) as root.
*/
statusp = 1;
switch ((child_pid = fork())) {
default:
while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
if (WIFSTOPPED(statusp)) {
child_pgrp = tcgetpgrp(1);
kill(getpid(), SIGSTOP);
tcsetpgrp(1, child_pgrp);
kill(child_pid, SIGCONT);
statusp = 1;
continue;
}
break;
}
if (ret_pid == -1)
err(1, "waitpid");
PAM_END;
exit(statusp);
case -1:
err(1, "fork");
PAM_END;
exit (1);
case 0:
#endif /* USE_PAM */
/*
* Set all user context except for:
* Environmental variables
@ -343,7 +450,7 @@ main(argc, argv)
* Path
*/
setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
LOGIN_SETLOGIN | LOGIN_SETPATH);
LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP);
/*
* Don't touch resource/priority settings if -m has been
@ -353,15 +460,6 @@ main(argc, argv)
setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
err(1, "setusercontext");
#else
/* set permissions */
if (setgid(pwd->pw_gid) < 0)
err(1, "setgid");
if (initgroups(user, pwd->pw_gid))
errx(1, "initgroups failed");
if (setuid(pwd->pw_uid) < 0)
err(1, "setuid");
#endif
if (!asme) {
if (asthem) {
@ -369,16 +467,20 @@ main(argc, argv)
#ifdef KERBEROS
k = getenv("KRBTKFILE");
#endif
if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
errx(1, "calloc");
cleanenv[0] = NULL;
environ = cleanenv;
#ifdef LOGIN_CAP
environ = &cleanenv;
#ifdef USE_PAM
/*
* Add any environmental variables that the
* PAM modules may have set.
*/
environ_pam = pam_getenvlist(pamh);
if (environ_pam)
export_pam_environment();
#endif /* USE_PAM */
/* set the su'd user's environment & umask */
setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
#else
(void)setenv("PATH", _PATH_DEFPATH, 1);
#endif
if (p)
(void)setenv("TERM", p, 1);
#ifdef KERBEROS
@ -393,6 +495,9 @@ main(argc, argv)
(void)setenv("HOME", pwd->pw_dir, 1);
(void)setenv("SHELL", shell, 1);
}
login_close(lc);
if (iscsh == YES) {
if (fastlogin)
*np-- = "-f";
@ -404,20 +509,65 @@ main(argc, argv)
*np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
if (ruid != 0)
syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
syslog(LOG_NOTICE, "%s to %s%s",
username, user, ontty());
login_close(lc);
execv(shell, np);
err(1, "%s", shell);
#ifdef USE_PAM
}
#endif /* USE_PAM */
}
#ifdef USE_PAM
static int
export_pam_environment()
{
char **pp;
for (pp = environ_pam; *pp != NULL; pp++) {
if (ok_to_export(*pp))
(void) putenv(*pp);
free(*pp);
}
return PAM_SUCCESS;
}
/*
* Sanity checks on PAM environmental variables:
* - Make sure there is an '=' in the string.
* - Make sure the string doesn't run on too long.
* - Do not export certain variables. This list was taken from the
* Solaris pam_putenv(3) man page.
*/
static int
ok_to_export(s)
const char *s;
{
static const char *noexport[] = {
"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
"IFS", "PATH", NULL
};
const char **pp;
size_t n;
if (strlen(s) > 1024 || strchr(s, '=') == NULL)
return 0;
if (strncmp(s, "LD_", 3) == 0)
return 0;
for (pp = noexport; *pp != NULL; pp++) {
n = strlen(*pp);
if (s[n] == '=' && strncmp(s, *pp, n) == 0)
return 0;
}
return 1;
}
#endif /* USE_PAM */
static void
usage()
{
(void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR);
exit(1);
errx(1, "usage: su [%s] [login [args]]", ARGSTR);
}
int
@ -501,7 +651,7 @@ kerberos(username, user, uid, pword)
return (1);
}
warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
syslog(LOG_NOTICE|LOG_AUTH,
syslog(LOG_NOTICE,
"BAD Kerberos SU: %s to %s%s: %s",
username, user, ontty(), krb_err_txt[kerno]);
return (1);
@ -528,13 +678,13 @@ kerberos(username, user, uid, pword)
if (kerno == KDC_PR_UNKNOWN) {
warnx("Warning: TGT not verified.");
syslog(LOG_NOTICE|LOG_AUTH,
syslog(LOG_NOTICE,
"%s to %s%s, TGT not verified (%s); %s.%s not registered?",
username, user, ontty(), krb_err_txt[kerno],
"rcmd", savehost);
} else if (kerno != KSUCCESS) {
warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
syslog(LOG_NOTICE, "failed su: %s to %s%s: %s",
username, user, ontty(), krb_err_txt[kerno]);
dest_tkt();
return (1);
@ -548,9 +698,9 @@ kerberos(username, user, uid, pword)
if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
&authdata, "")) != KSUCCESS) {
warnx("kerberos: unable to verify rcmd ticket: %s\n",
warnx("kerberos: unable to verify rcmd ticket: %s",
krb_err_txt[kerno]);
syslog(LOG_NOTICE|LOG_AUTH,
syslog(LOG_NOTICE,
"failed su: %s to %s%s: %s", username,
user, ontty(), krb_err_txt[kerno]);
dest_tkt();