* src/su.c: Extract the authentication from the main function.

This commit is contained in:
nekral-guest 2011-06-13 18:26:16 +00:00
parent dc90c77285
commit 3abd71c526
2 changed files with 186 additions and 177 deletions

View File

@ -16,6 +16,7 @@
* src/su.c: Define shellstr before the environment so that
restricted_shell is called only once. This will allow moving the
environment definition after the switch to the new user.
* src/su.c: Extract the authentication from the main function.
2011-06-10 Nicolas François <nicolas.francois@centraliens.net>

362
src/su.c
View File

@ -114,6 +114,7 @@ static RETSIGTYPE kill_child (int unused(s));
static RETSIGTYPE die (int);
static bool iswheel (const char *);
#endif /* !USE_PAM */
static void check_perms (void);
#ifndef USE_PAM
/*
@ -406,6 +407,189 @@ static void usage (int status)
exit (status);
}
/*
* check_perms - check permissions to switch to the user 'name'
*
* In case of subsystem login, the user is first authenticated in the
* caller's root subsystem, and then in the user's target subsystem.
*/
static void check_perms (void)
{
/*
* The password file entries for the user is gotten and the account
* validated.
*/
pw = xgetpwnam (name);
if (NULL == pw) {
(void) fprintf (stderr, _("Unknown id: %s\n"), name);
closelog ();
exit (1);
}
#ifndef USE_PAM
spwd = NULL;
if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
spwd = getspnam (name); /* !USE_PAM, no need for xgetspnam */
if (NULL != spwd) {
pw->pw_passwd = spwd->sp_pwdp;
}
}
#endif /* !USE_PAM */
pwent = *pw;
#ifndef USE_PAM
/*
* BSD systems only allow "wheel" to SU to root. USG systems don't,
* so we make this a configurable option.
*/
/* The original Shadow 3.3.2 did this differently. Do it like BSD:
*
* - check for UID 0 instead of name "root" - there are systems with
* several root accounts under different names,
*
* - check the contents of /etc/group instead of the current group
* set (you must be listed as a member, GID 0 is not sufficient).
*
* In addition to this traditional feature, we now have complete su
* access control (allow, deny, no password, own password). Thanks
* to Chris Evans <lady0110@sable.ox.ac.uk>.
*/
if (!amroot) {
if ( (0 == pwent.pw_uid)
&& getdef_bool ("SU_WHEEL_ONLY")
&& !iswheel (oldname)) {
fprintf (stderr,
_("You are not authorized to su %s\n"),
name);
exit (1);
}
#ifdef SU_ACCESS
switch (check_su_auth (oldname, name)) {
case 0: /* normal su, require target user's password */
break;
case 1: /* require no password */
pwent.pw_passwd = ""; /* XXX warning: const */
break;
case 2: /* require own password */
puts (_("(Enter your own password)"));
pwent.pw_passwd = oldpass;
break;
default: /* access denied (-1) or unexpected value */
fprintf (stderr,
_("You are not authorized to su %s\n"),
name);
exit (1);
}
#endif /* SU_ACCESS */
}
#endif /* !USE_PAM */
(void) signal (SIGINT, SIG_IGN);
(void) signal (SIGQUIT, SIG_IGN);
#ifdef USE_PAM
ret = pam_authenticate (pamh, 0);
if (PAM_SUCCESS != ret) {
SYSLOG ((LOG_ERR, "pam_authenticate: %s",
pam_strerror (pamh, ret)));
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
(void) pam_end (pamh, ret);
su_failure (tty);
}
ret = pam_acct_mgmt (pamh, 0);
if (PAM_SUCCESS != ret) {
if (amroot) {
fprintf (stderr,
_("%s: %s\n(Ignored)\n"),
Prog, pam_strerror (pamh, ret));
} else if (PAM_NEW_AUTHTOK_REQD == ret) {
ret = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (PAM_SUCCESS != ret) {
SYSLOG ((LOG_ERR, "pam_chauthtok: %s",
pam_strerror (pamh, ret)));
fprintf (stderr,
_("%s: %s\n"),
Prog, pam_strerror (pamh, ret));
(void) pam_end (pamh, ret);
su_failure (tty);
}
} else {
SYSLOG ((LOG_ERR, "pam_acct_mgmt: %s",
pam_strerror (pamh, ret)));
fprintf (stderr,
_("%s: %s\n"),
Prog, pam_strerror (pamh, ret));
(void) pam_end (pamh, ret);
su_failure (tty);
}
}
#else /* !USE_PAM */
/*
* Set up a signal handler in case the user types QUIT.
*/
die (0);
oldsig = signal (SIGQUIT, die);
/*
* See if the system defined authentication method is being used.
* The first character of an administrator defined method is an '@'
* character.
*/
if ( !amroot
&& (pw_auth (pwent.pw_passwd, name, PW_SU, (char *) 0) != 0)) {
SYSLOG (((pwent.pw_uid != 0)? LOG_NOTICE : LOG_WARN,
"Authentication failed for %s", name));
fprintf(stderr, _("%s: Authentication failure\n"), Prog);
su_failure (tty);
}
(void) signal (SIGQUIT, oldsig);
/*
* Check to see if the account is expired. root gets to ignore any
* expired accounts, but normal users can't become a user with an
* expired password.
*/
if ((!amroot) && (NULL != spwd)) {
(void) expire (&pwent, spwd);
}
/*
* Check to see if the account permits "su". root gets to ignore any
* restricted accounts, but normal users can't become a user if
* there is a "SU" entry in the /etc/porttime file denying access to
* the account.
*/
if (!amroot) {
if (!isttytime (pwent.pw_name, "SU", time ((time_t *) 0))) {
SYSLOG (((0 != pwent.pw_uid) ? LOG_WARN : LOG_CRIT,
"SU by %s to restricted account %s",
oldname, name));
fprintf (stderr,
_("%s: You are not authorized to su at that time\n"),
Prog);
su_failure (tty);
}
}
#endif /* !USE_PAM */
(void) signal (SIGINT, SIG_DFL);
(void) signal (SIGQUIT, SIG_DFL);
/*
* Even if --shell is specified, the subsystem login test is based on
* the shell specified in /etc/passwd (not the one specified with
* --shell, which will be the one executed in the chroot later).
*/
if ('*' == pwent.pw_shell[0]) { /* subsystem root required */
subsystem (&pwent); /* change to the subsystem root */
endpwent (); /* close the old password databases */
endspent ();
return check_perms (); /* authenticate in the subsystem */
}
}
/*
* su - switch user id
*
@ -626,183 +810,7 @@ int main (int argc, char **argv)
}
#endif /* USE_PAM */
top:
/*
* This is the common point for validating a user whose name is
* known. It will be reached either by normal processing, or if the
* user is to be logged into a subsystem root.
*
* The password file entries for the user is gotten and the account
* validated.
*/
pw = xgetpwnam (name);
if (NULL == pw) {
(void) fprintf (stderr, _("Unknown id: %s\n"), name);
closelog ();
exit (1);
}
#ifndef USE_PAM
spwd = NULL;
if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
spwd = getspnam (name); /* !USE_PAM, no need for xgetspnam */
if (NULL != spwd) {
pw->pw_passwd = spwd->sp_pwdp;
}
}
#endif /* !USE_PAM */
pwent = *pw;
#ifndef USE_PAM
/*
* BSD systems only allow "wheel" to SU to root. USG systems don't,
* so we make this a configurable option.
*/
/* The original Shadow 3.3.2 did this differently. Do it like BSD:
*
* - check for UID 0 instead of name "root" - there are systems with
* several root accounts under different names,
*
* - check the contents of /etc/group instead of the current group
* set (you must be listed as a member, GID 0 is not sufficient).
*
* In addition to this traditional feature, we now have complete su
* access control (allow, deny, no password, own password). Thanks
* to Chris Evans <lady0110@sable.ox.ac.uk>.
*/
if (!amroot) {
if ( (0 == pwent.pw_uid)
&& getdef_bool ("SU_WHEEL_ONLY")
&& !iswheel (oldname)) {
fprintf (stderr,
_("You are not authorized to su %s\n"),
name);
exit (1);
}
#ifdef SU_ACCESS
switch (check_su_auth (oldname, name)) {
case 0: /* normal su, require target user's password */
break;
case 1: /* require no password */
pwent.pw_passwd = ""; /* XXX warning: const */
break;
case 2: /* require own password */
puts (_("(Enter your own password)"));
pwent.pw_passwd = oldpass;
break;
default: /* access denied (-1) or unexpected value */
fprintf (stderr,
_("You are not authorized to su %s\n"),
name);
exit (1);
}
#endif /* SU_ACCESS */
}
#endif /* !USE_PAM */
(void) signal (SIGINT, SIG_IGN);
(void) signal (SIGQUIT, SIG_IGN);
#ifdef USE_PAM
ret = pam_authenticate (pamh, 0);
if (PAM_SUCCESS != ret) {
SYSLOG ((LOG_ERR, "pam_authenticate: %s",
pam_strerror (pamh, ret)));
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
(void) pam_end (pamh, ret);
su_failure (tty);
}
ret = pam_acct_mgmt (pamh, 0);
if (PAM_SUCCESS != ret) {
if (amroot) {
fprintf (stderr,
_("%s: %s\n(Ignored)\n"),
Prog, pam_strerror (pamh, ret));
} else if (PAM_NEW_AUTHTOK_REQD == ret) {
ret = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (PAM_SUCCESS != ret) {
SYSLOG ((LOG_ERR, "pam_chauthtok: %s",
pam_strerror (pamh, ret)));
fprintf (stderr,
_("%s: %s\n"),
Prog, pam_strerror (pamh, ret));
(void) pam_end (pamh, ret);
su_failure (tty);
}
} else {
SYSLOG ((LOG_ERR, "pam_acct_mgmt: %s",
pam_strerror (pamh, ret)));
fprintf (stderr,
_("%s: %s\n"),
Prog, pam_strerror (pamh, ret));
(void) pam_end (pamh, ret);
su_failure (tty);
}
}
#else /* !USE_PAM */
/*
* Set up a signal handler in case the user types QUIT.
*/
die (0);
oldsig = signal (SIGQUIT, die);
/*
* See if the system defined authentication method is being used.
* The first character of an administrator defined method is an '@'
* character.
*/
if ( !amroot
&& (pw_auth (pwent.pw_passwd, name, PW_SU, (char *) 0) != 0)) {
SYSLOG (((pwent.pw_uid != 0)? LOG_NOTICE : LOG_WARN,
"Authentication failed for %s", name));
fprintf(stderr, _("%s: Authentication failure\n"), Prog);
su_failure (tty);
}
(void) signal (SIGQUIT, oldsig);
/*
* Check to see if the account is expired. root gets to ignore any
* expired accounts, but normal users can't become a user with an
* expired password.
*/
if ((!amroot) && (NULL != spwd)) {
(void) expire (&pwent, spwd);
}
/*
* Check to see if the account permits "su". root gets to ignore any
* restricted accounts, but normal users can't become a user if
* there is a "SU" entry in the /etc/porttime file denying access to
* the account.
*/
if (!amroot) {
if (!isttytime (pwent.pw_name, "SU", time ((time_t *) 0))) {
SYSLOG (((0 != pwent.pw_uid) ? LOG_WARN : LOG_CRIT,
"SU by %s to restricted account %s",
oldname, name));
fprintf (stderr,
_("%s: You are not authorized to su at that time\n"),
Prog);
su_failure (tty);
}
}
#endif /* !USE_PAM */
(void) signal (SIGINT, SIG_DFL);
(void) signal (SIGQUIT, SIG_DFL);
/*
* Even if --shell is specified, the subsystem login test is based on
* the shell specified in /etc/passwd (not the one specified with
* --shell, which will be the one executed in the chroot later).
*/
if ('*' == pwent.pw_shell[0]) { /* subsystem root required */
subsystem (&pwent); /* change to the subsystem root */
endpwent (); /* close the old password databases */
endspent ();
goto top; /* authenticate in the subsystem */
}
check_perms ();
/* If the user do not want to change the environment,
* use the current SHELL.