2007-10-07 17:14:02 +05:30
|
|
|
/*
|
2021-12-05 21:05:27 +05:30
|
|
|
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
|
|
|
|
* SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz
|
|
|
|
* SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko
|
|
|
|
* SPDX-FileCopyrightText: 2007 - 2013, Nicolas François
|
2007-10-07 17:14:02 +05:30
|
|
|
*
|
2021-12-05 21:05:27 +05:30
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2007-10-07 17:14:02 +05:30
|
|
|
*/
|
2008-04-27 06:10:09 +05:30
|
|
|
|
|
|
|
/* Some parts substantially derived from an ancestor of:
|
|
|
|
su for GNU. Run a shell with substitute user and group IDs.
|
|
|
|
|
2007-10-27 19:20:40 +05:30
|
|
|
Copyright (C) 1992-2003 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2008-04-27 06:10:09 +05:30
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA. */
|
2007-10-27 19:20:40 +05:30
|
|
|
|
2007-10-07 17:14:02 +05:30
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2007-11-11 05:16:11 +05:30
|
|
|
#ident "$Id$"
|
2007-10-07 17:17:01 +05:30
|
|
|
|
2020-08-29 01:46:11 +05:30
|
|
|
#include <getopt.h>
|
2007-10-07 17:16:34 +05:30
|
|
|
#include <grp.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <signal.h>
|
2007-10-07 17:14:02 +05:30
|
|
|
#include <stdio.h>
|
2007-10-07 17:16:34 +05:30
|
|
|
#include <sys/types.h>
|
2011-06-13 23:57:40 +05:30
|
|
|
#include <unistd.h>
|
|
|
|
#ifndef USE_PAM
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#endif /* !USE_PAM */
|
2023-02-05 03:11:18 +05:30
|
|
|
|
|
|
|
#include "alloc.h"
|
2007-10-07 17:14:02 +05:30
|
|
|
#include "prototypes.h"
|
|
|
|
#include "defines.h"
|
|
|
|
#include "pwauth.h"
|
|
|
|
#include "getdef.h"
|
2007-10-07 17:16:34 +05:30
|
|
|
#ifdef USE_PAM
|
|
|
|
#include "pam_defs.h"
|
* src/newgrp.c, src/chfn.c, src/groupmems.c, src/usermod.c,
src/userdel.c, src/chpasswd.c, src/grpck.c, src/gpasswd.c,
src/groupdel.c, src/chgpasswd.c, src/vipw.c, src/useradd.c,
src/su.c, src/groupmod.c, src/passwd.c, src/pwck.c,
src/groupadd.c, src/chage.c, src/login.c, src/faillog.c,
src/sulogin.c, src/chsh.c, src/pwconv.c: Added splint annotations.
* src/userdel.c, src/pwconv.c, src/lastlog.c, src/grpck.c,
src/vipw.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/login.c,
src/sulogin.c, src/usermod.c: Use return instead of exit at the
end of main().
* src/gpasswd.c, src/passwd.c, src/faillog.c: Use the exitcodes.h
exit codes.
* src/chpasswd.c: Added missing ||.
* src/nologin.c: Do not include exitcodes.h.
* src/nologin.c: Added brackets.
* src/nologin.c: Avoid assignments in comparisons.
2009-05-01 03:09:38 +05:30
|
|
|
#endif /* USE_PAM */
|
|
|
|
/*@-exitarg@*/
|
|
|
|
#include "exitcodes.h"
|
2021-11-29 05:07:53 +05:30
|
|
|
#include "shadowlog.h"
|
* src/newgrp.c, src/chfn.c, src/groupmems.c, src/usermod.c,
src/userdel.c, src/chpasswd.c, src/grpck.c, src/gpasswd.c,
src/groupdel.c, src/chgpasswd.c, src/vipw.c, src/useradd.c,
src/su.c, src/groupmod.c, src/passwd.c, src/pwck.c,
src/groupadd.c, src/chage.c, src/login.c, src/faillog.c,
src/sulogin.c, src/chsh.c, src/pwconv.c: Added splint annotations.
* src/userdel.c, src/pwconv.c, src/lastlog.c, src/grpck.c,
src/vipw.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/login.c,
src/sulogin.c, src/usermod.c: Use return instead of exit at the
end of main().
* src/gpasswd.c, src/passwd.c, src/faillog.c: Use the exitcodes.h
exit codes.
* src/chpasswd.c: Added missing ||.
* src/nologin.c: Do not include exitcodes.h.
* src/nologin.c: Added brackets.
* src/nologin.c: Avoid assignments in comparisons.
2009-05-01 03:09:38 +05:30
|
|
|
|
2007-10-07 17:14:02 +05:30
|
|
|
/*
|
|
|
|
* Global variables
|
|
|
|
*/
|
* src/newgrp.c, src/userdel.c, src/grpck.c, src/gpasswd.c,
src/newusers.c, src/pwconv.c, src/chpasswd.c, src/logoutd.c,
src/chfn.c, src/groupmems.c, src/usermod.c, src/pwunconv.c,
src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/useradd.c,
src/su.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/chage.c,
src/groupadd.c, src/login.c, src/grpconv.c, src/groups.c,
src/grpunconv.c, src/chsh.c: Prog changed to a constant string.
2010-08-23 01:06:09 +05:30
|
|
|
const char *Prog;
|
2011-08-15 03:14:46 +05:30
|
|
|
static /*@observer@*/const char *caller_tty = NULL; /* Name of tty SU is run from */
|
2011-06-13 23:57:23 +05:30
|
|
|
static bool caller_is_root = false;
|
|
|
|
static uid_t caller_uid;
|
2011-06-13 23:56:47 +05:30
|
|
|
#ifndef USE_PAM
|
2011-08-15 03:14:46 +05:30
|
|
|
static bool caller_on_console = false;
|
2011-06-13 23:56:47 +05:30
|
|
|
#ifdef SU_ACCESS
|
2011-08-15 03:14:46 +05:30
|
|
|
static /*@only@*/char *caller_pass;
|
2011-06-13 23:56:47 +05:30
|
|
|
#endif
|
|
|
|
#endif /* !USE_PAM */
|
2020-08-29 01:46:04 +05:30
|
|
|
static bool doshell = false;
|
2011-06-13 23:57:23 +05:30
|
|
|
static bool fakelogin = false;
|
2011-08-15 03:14:46 +05:30
|
|
|
static /*@observer@*/const char *shellstr;
|
|
|
|
static /*@null@*/char *command = NULL;
|
2011-06-13 23:56:47 +05:30
|
|
|
|
* src/newgrp.c, src/userdel.c, src/grpck.c, src/gpasswd.c,
src/newusers.c, src/pwconv.c, src/chpasswd.c, src/logoutd.c,
src/chfn.c, src/groupmems.c, src/usermod.c, src/pwunconv.c,
src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/useradd.c,
src/su.c, src/groupmod.c, src/passwd.c, src/pwck.c,
src/groupadd.c, src/chage.c, src/login.c, src/grpconv.c,
src/groups.c, src/grpunconv.c, src/chsh.c: Prog is now global (not
static to the file) so that it can be used by the helper functions
of libmisc.
* lib/prototypes.h: Added extern char *Prog.
* libmisc/find_new_gid.c, libmisc/find_new_uid.c: Indicate the
program name with the warning.
2008-09-06 18:21:53 +05:30
|
|
|
|
2007-10-07 17:14:14 +05:30
|
|
|
/* not needed by sulog.c anymore */
|
|
|
|
static char name[BUFSIZ];
|
2011-06-13 23:56:47 +05:30
|
|
|
static char caller_name[BUFSIZ];
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2007-10-07 17:17:11 +05:30
|
|
|
/* If nonzero, change some environment vars to indicate the user su'd to. */
|
2011-06-13 23:57:23 +05:30
|
|
|
static bool change_environment = true;
|
2007-10-07 17:16:34 +05:30
|
|
|
|
2007-10-07 17:17:11 +05:30
|
|
|
#ifdef USE_PAM
|
2016-07-02 21:41:09 +05:30
|
|
|
static char kill_msg[256];
|
|
|
|
static char wait_msg[256];
|
2007-10-07 17:16:34 +05:30
|
|
|
static pam_handle_t *pamh = NULL;
|
2011-06-05 20:11:15 +05:30
|
|
|
static int caught = 0;
|
2009-09-09 03:03:14 +05:30
|
|
|
/* PID of the child, in case it needs to be killed */
|
|
|
|
static pid_t pid_child = 0;
|
2007-10-07 17:16:34 +05:30
|
|
|
#endif
|
2007-10-07 17:14:02 +05:30
|
|
|
|
|
|
|
/*
|
|
|
|
* External identifiers
|
|
|
|
*/
|
|
|
|
|
2011-06-17 02:51:29 +05:30
|
|
|
extern char **newenvp; /* libmisc/env.c */
|
|
|
|
extern size_t newenvc; /* libmisc/env.c */
|
2007-10-07 17:14:02 +05:30
|
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
|
2011-06-17 02:51:29 +05:30
|
|
|
static void execve_shell (const char *shellname,
|
2009-09-09 02:09:15 +05:30
|
|
|
char *args[],
|
|
|
|
char *const envp[]);
|
2009-09-09 03:03:14 +05:30
|
|
|
#ifdef USE_PAM
|
2021-12-30 21:06:18 +05:30
|
|
|
static void kill_child (int unused(s));
|
2011-06-13 23:57:40 +05:30
|
|
|
static void prepare_pam_close_session (void);
|
2009-09-09 03:03:14 +05:30
|
|
|
#else /* !USE_PAM */
|
2021-12-30 21:06:18 +05:30
|
|
|
static void die (int);
|
2010-08-22 18:19:07 +05:30
|
|
|
static bool iswheel (const char *);
|
2009-09-09 02:09:15 +05:30
|
|
|
#endif /* !USE_PAM */
|
2011-06-17 02:51:29 +05:30
|
|
|
static bool restricted_shell (const char *shellname);
|
2023-02-08 00:20:36 +05:30
|
|
|
NORETURN static void su_failure (const char *tty, bool su_to_root);
|
2011-08-15 14:55:58 +05:30
|
|
|
static /*@only@*/struct passwd * check_perms (void);
|
2023-05-23 17:25:09 +05:30
|
|
|
static /*@only@*/struct passwd * do_check_perms (void);
|
2011-06-13 23:56:58 +05:30
|
|
|
#ifdef USE_PAM
|
2011-08-15 14:55:58 +05:30
|
|
|
static void check_perms_pam (const struct passwd *pw);
|
2011-06-13 23:56:58 +05:30
|
|
|
#else /* !USE_PAM */
|
2011-08-15 14:55:58 +05:30
|
|
|
static void check_perms_nopam (const struct passwd *pw);
|
2011-06-13 23:56:58 +05:30
|
|
|
#endif /* !USE_PAM */
|
2011-06-13 23:56:47 +05:30
|
|
|
static void save_caller_context (char **argv);
|
2011-06-13 23:57:23 +05:30
|
|
|
static void process_flags (int argc, char **argv);
|
2011-06-17 02:51:29 +05:30
|
|
|
static void set_environment (struct passwd *pw);
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2009-09-09 02:09:15 +05:30
|
|
|
#ifndef USE_PAM
|
2007-10-07 17:14:02 +05:30
|
|
|
/*
|
|
|
|
* die - set or reset termio modes.
|
|
|
|
*
|
2007-10-07 17:14:59 +05:30
|
|
|
* die() is called before processing begins. signal() is then called
|
|
|
|
* with die() as the signal handler. If signal later calls die() with a
|
|
|
|
* signal number, the terminal modes are then reset.
|
2007-10-07 17:14:02 +05:30
|
|
|
*/
|
2021-12-30 21:06:18 +05:30
|
|
|
static void die (int killed)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
|
|
|
static TERMIO sgtty;
|
|
|
|
|
* libmisc/limits.c: Avoid implicit conversion of integer to
boolean.
* libmisc/basename.c: Avoid implicit conversion of pointer to
boolean.
* libmisc/basename.c, lib/prototypes.h (Basename): Return a
constant string.
* libmisc/basename.c, libmisc/obscure.c, lib/prototypes.h,
libmisc/xmalloc.c, libmisc/getdate.h, libmisc/system.c,
libmisc/getgr_nam_gid.c, libmisc/failure.c, libmisc/valid.c: Add
splint annotations.
* libmisc/chowndir.c: Avoid memory leak.
* libmisc/chowndir.c: Do not check *printf/*puts return value.
* libmisc/chowntty.c: Avoid implicit conversion between integer
types.
* libmisc/obscure.c: Return a bool when possible instead of int.
* libmisc/shell.c: Do not check *printf/*puts return value.
* libmisc/shell.c: Do not check execle return value.
* libmisc/setupenv.c: Avoid implicit conversion between integer
types.
* libmisc/xmalloc.c: size should not be zero to avoid returning
NULL pointers.
* libmisc/hushed.c: Do not check *printf/*puts return value.
* libmisc/system.c: Avoid implicit conversion of integer to
boolean. safe_system last argument is a boolean.
* libmisc/system.c: Check return value of dup2.
* libmisc/system.c: Do not check *printf/*puts return value.
* libmisc/system.c: Do not check execve return value.
* libmisc/salt.c: Do not check *printf/*puts return value.
* libmisc/loginprompt.c: Do not check gethostname return value.
* libmisc/find_new_gid.c, libmisc/find_new_uid.c: Do not check
gr_rewind/pw_rewind return value.
* libmisc/ttytype.c: Limit the number of parsed characters in the
sscanf format.
* libmisc/ttytype.c: Test if a type was really read.
* libmisc/sub.c: Do not check *printf/*puts return value.
* libmisc/sub.c: Avoid implicit conversion of integer to boolean.
* src/userdel.c: Fix typo in comment.
* src/userdel.c: Avoid implicit conversion of boolean to integer.
* src/userdel.c: safe_system last argument is a boolean.
* src/newusers.c: Avoid implicit conversion of boolean to integer.
* src/newusers.c: Avoid implicit conversion of integer to boolean.
* src/usermod.c: Add brackets.
* src/usermod.c: Avoid implicit conversion of characters or
integers to booleans.
* src/vipw.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Add brackets.
* src/useradd.c: Avoid implicit conversion of characters or
integers to booleans.
2010-08-23 00:43:53 +05:30
|
|
|
if (killed != 0) {
|
2007-10-07 17:14:59 +05:30
|
|
|
STTY (0, &sgtty);
|
* libmisc/limits.c: Avoid implicit conversion of integer to
boolean.
* libmisc/basename.c: Avoid implicit conversion of pointer to
boolean.
* libmisc/basename.c, lib/prototypes.h (Basename): Return a
constant string.
* libmisc/basename.c, libmisc/obscure.c, lib/prototypes.h,
libmisc/xmalloc.c, libmisc/getdate.h, libmisc/system.c,
libmisc/getgr_nam_gid.c, libmisc/failure.c, libmisc/valid.c: Add
splint annotations.
* libmisc/chowndir.c: Avoid memory leak.
* libmisc/chowndir.c: Do not check *printf/*puts return value.
* libmisc/chowntty.c: Avoid implicit conversion between integer
types.
* libmisc/obscure.c: Return a bool when possible instead of int.
* libmisc/shell.c: Do not check *printf/*puts return value.
* libmisc/shell.c: Do not check execle return value.
* libmisc/setupenv.c: Avoid implicit conversion between integer
types.
* libmisc/xmalloc.c: size should not be zero to avoid returning
NULL pointers.
* libmisc/hushed.c: Do not check *printf/*puts return value.
* libmisc/system.c: Avoid implicit conversion of integer to
boolean. safe_system last argument is a boolean.
* libmisc/system.c: Check return value of dup2.
* libmisc/system.c: Do not check *printf/*puts return value.
* libmisc/system.c: Do not check execve return value.
* libmisc/salt.c: Do not check *printf/*puts return value.
* libmisc/loginprompt.c: Do not check gethostname return value.
* libmisc/find_new_gid.c, libmisc/find_new_uid.c: Do not check
gr_rewind/pw_rewind return value.
* libmisc/ttytype.c: Limit the number of parsed characters in the
sscanf format.
* libmisc/ttytype.c: Test if a type was really read.
* libmisc/sub.c: Do not check *printf/*puts return value.
* libmisc/sub.c: Avoid implicit conversion of integer to boolean.
* src/userdel.c: Fix typo in comment.
* src/userdel.c: Avoid implicit conversion of boolean to integer.
* src/userdel.c: safe_system last argument is a boolean.
* src/newusers.c: Avoid implicit conversion of boolean to integer.
* src/newusers.c: Avoid implicit conversion of integer to boolean.
* src/usermod.c: Add brackets.
* src/usermod.c: Avoid implicit conversion of characters or
integers to booleans.
* src/vipw.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Add brackets.
* src/useradd.c: Avoid implicit conversion of characters or
integers to booleans.
2010-08-23 00:43:53 +05:30
|
|
|
} else {
|
2007-10-07 17:14:59 +05:30
|
|
|
GTTY (0, &sgtty);
|
* libmisc/limits.c: Avoid implicit conversion of integer to
boolean.
* libmisc/basename.c: Avoid implicit conversion of pointer to
boolean.
* libmisc/basename.c, lib/prototypes.h (Basename): Return a
constant string.
* libmisc/basename.c, libmisc/obscure.c, lib/prototypes.h,
libmisc/xmalloc.c, libmisc/getdate.h, libmisc/system.c,
libmisc/getgr_nam_gid.c, libmisc/failure.c, libmisc/valid.c: Add
splint annotations.
* libmisc/chowndir.c: Avoid memory leak.
* libmisc/chowndir.c: Do not check *printf/*puts return value.
* libmisc/chowntty.c: Avoid implicit conversion between integer
types.
* libmisc/obscure.c: Return a bool when possible instead of int.
* libmisc/shell.c: Do not check *printf/*puts return value.
* libmisc/shell.c: Do not check execle return value.
* libmisc/setupenv.c: Avoid implicit conversion between integer
types.
* libmisc/xmalloc.c: size should not be zero to avoid returning
NULL pointers.
* libmisc/hushed.c: Do not check *printf/*puts return value.
* libmisc/system.c: Avoid implicit conversion of integer to
boolean. safe_system last argument is a boolean.
* libmisc/system.c: Check return value of dup2.
* libmisc/system.c: Do not check *printf/*puts return value.
* libmisc/system.c: Do not check execve return value.
* libmisc/salt.c: Do not check *printf/*puts return value.
* libmisc/loginprompt.c: Do not check gethostname return value.
* libmisc/find_new_gid.c, libmisc/find_new_uid.c: Do not check
gr_rewind/pw_rewind return value.
* libmisc/ttytype.c: Limit the number of parsed characters in the
sscanf format.
* libmisc/ttytype.c: Test if a type was really read.
* libmisc/sub.c: Do not check *printf/*puts return value.
* libmisc/sub.c: Avoid implicit conversion of integer to boolean.
* src/userdel.c: Fix typo in comment.
* src/userdel.c: Avoid implicit conversion of boolean to integer.
* src/userdel.c: safe_system last argument is a boolean.
* src/newusers.c: Avoid implicit conversion of boolean to integer.
* src/newusers.c: Avoid implicit conversion of integer to boolean.
* src/usermod.c: Add brackets.
* src/usermod.c: Avoid implicit conversion of characters or
integers to booleans.
* src/vipw.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Add brackets.
* src/useradd.c: Avoid implicit conversion of characters or
integers to booleans.
2010-08-23 00:43:53 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
* libmisc/limits.c: Avoid implicit conversion of integer to
boolean.
* libmisc/basename.c: Avoid implicit conversion of pointer to
boolean.
* libmisc/basename.c, lib/prototypes.h (Basename): Return a
constant string.
* libmisc/basename.c, libmisc/obscure.c, lib/prototypes.h,
libmisc/xmalloc.c, libmisc/getdate.h, libmisc/system.c,
libmisc/getgr_nam_gid.c, libmisc/failure.c, libmisc/valid.c: Add
splint annotations.
* libmisc/chowndir.c: Avoid memory leak.
* libmisc/chowndir.c: Do not check *printf/*puts return value.
* libmisc/chowntty.c: Avoid implicit conversion between integer
types.
* libmisc/obscure.c: Return a bool when possible instead of int.
* libmisc/shell.c: Do not check *printf/*puts return value.
* libmisc/shell.c: Do not check execle return value.
* libmisc/setupenv.c: Avoid implicit conversion between integer
types.
* libmisc/xmalloc.c: size should not be zero to avoid returning
NULL pointers.
* libmisc/hushed.c: Do not check *printf/*puts return value.
* libmisc/system.c: Avoid implicit conversion of integer to
boolean. safe_system last argument is a boolean.
* libmisc/system.c: Check return value of dup2.
* libmisc/system.c: Do not check *printf/*puts return value.
* libmisc/system.c: Do not check execve return value.
* libmisc/salt.c: Do not check *printf/*puts return value.
* libmisc/loginprompt.c: Do not check gethostname return value.
* libmisc/find_new_gid.c, libmisc/find_new_uid.c: Do not check
gr_rewind/pw_rewind return value.
* libmisc/ttytype.c: Limit the number of parsed characters in the
sscanf format.
* libmisc/ttytype.c: Test if a type was really read.
* libmisc/sub.c: Do not check *printf/*puts return value.
* libmisc/sub.c: Avoid implicit conversion of integer to boolean.
* src/userdel.c: Fix typo in comment.
* src/userdel.c: Avoid implicit conversion of boolean to integer.
* src/userdel.c: safe_system last argument is a boolean.
* src/newusers.c: Avoid implicit conversion of boolean to integer.
* src/newusers.c: Avoid implicit conversion of integer to boolean.
* src/usermod.c: Add brackets.
* src/usermod.c: Avoid implicit conversion of characters or
integers to booleans.
* src/vipw.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Avoid implicit conversion of integer to boolean.
* src/su.c: Add brackets.
* src/useradd.c: Avoid implicit conversion of characters or
integers to booleans.
2010-08-23 00:43:53 +05:30
|
|
|
if (killed != 0) {
|
2016-07-02 21:41:09 +05:30
|
|
|
_exit (128+killed);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-22 18:19:07 +05:30
|
|
|
static bool iswheel (const char *username)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
|
|
|
struct group *grp;
|
|
|
|
|
* lib/prototypes.h, configure.in, libmisc/Makefile.am,
libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c,
libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c:
Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(),
xgetgrgid(), and xgetspnam(). They allocate memory for the
returned structure and are more robust to successive calls. They
are implemented with the libc's getxxyyy_r() functions if
available.
* libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c,
libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c,
libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c,
src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c,
src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c,
src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c,
src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c,
src/groupadd.c, src/chage.c, src/login.c, src/suauth.c,
src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the
usage of one of the getpwnam(), getpwuid(), getgrnam(),
getgrgid(), and getspnam() functions. It was noticed on
http://bugs.debian.org/341230 that chfn and chsh use a passwd
structure after calling a pam function, which result in using
information from the passwd structure requested by pam, not the
original one. It is much easier to use the new xget... functions
to avoid these issues. I've checked which call to the original
get... functions could be left (reducing the scope of the
structure if possible), and I've left comments to ease future
reviews (e.g. /* local, no need for xgetpwnam */).
Note: the getpwent/getgrent calls should probably be checked also.
* src/groupdel.c, src/expiry.c: Fix typos in comments.
* src/groupmod.c: Re-indent.
* libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c,
lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup
functions (used by the xget... functions) from the <xx>io.c files
to the new <xx>mem.c files. This avoid linking some utils against
the SELinux library.
2007-11-19 04:45:26 +05:30
|
|
|
grp = getgrnam ("wheel"); /* !USE_PAM, no need for xgetgrnam */
|
2008-08-31 23:00:30 +05:30
|
|
|
if ( (NULL ==grp)
|
|
|
|
|| (NULL == grp->gr_mem)) {
|
2010-08-22 18:19:07 +05:30
|
|
|
return false;
|
2008-08-31 23:00:30 +05:30
|
|
|
}
|
2007-10-07 17:14:59 +05:30
|
|
|
return is_on_list (grp->gr_mem, username);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-09-09 03:03:14 +05:30
|
|
|
#else /* USE_PAM */
|
2021-12-30 21:06:18 +05:30
|
|
|
static void kill_child (int unused(s))
|
2009-09-09 02:09:15 +05:30
|
|
|
{
|
|
|
|
if (0 != pid_child) {
|
2013-07-28 18:08:12 +05:30
|
|
|
(void) kill (-pid_child, SIGKILL);
|
2016-07-02 21:41:09 +05:30
|
|
|
(void) write (STDERR_FILENO, kill_msg, strlen (kill_msg));
|
2009-09-09 02:09:15 +05:30
|
|
|
} else {
|
2016-07-02 21:41:09 +05:30
|
|
|
(void) write (STDERR_FILENO, wait_msg, strlen (wait_msg));
|
2009-09-09 02:09:15 +05:30
|
|
|
}
|
2016-07-02 21:41:09 +05:30
|
|
|
_exit (255);
|
2009-09-09 02:09:15 +05:30
|
|
|
}
|
2009-09-09 03:03:14 +05:30
|
|
|
#endif /* USE_PAM */
|
2009-09-09 02:09:15 +05:30
|
|
|
|
2007-10-07 17:17:11 +05:30
|
|
|
/* borrowed from GNU sh-utils' "su.c" */
|
2011-06-17 02:51:29 +05:30
|
|
|
static bool restricted_shell (const char *shellname)
|
2007-10-07 17:17:11 +05:30
|
|
|
{
|
2011-08-15 03:14:46 +05:30
|
|
|
/*@observer@*/const char *line;
|
2007-10-07 17:17:11 +05:30
|
|
|
|
|
|
|
setusershell ();
|
|
|
|
while ((line = getusershell ()) != NULL) {
|
2011-06-17 02:51:29 +05:30
|
|
|
if (('#' != *line) && (strcmp (line, shellname) == 0)) {
|
2007-10-07 17:17:11 +05:30
|
|
|
endusershell ();
|
2008-06-11 03:37:51 +05:30
|
|
|
return false;
|
2007-10-07 17:17:11 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
endusershell ();
|
2008-06-11 03:37:51 +05:30
|
|
|
return true;
|
2007-10-07 17:17:11 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2023-02-08 00:20:36 +05:30
|
|
|
NORETURN
|
|
|
|
static void
|
|
|
|
su_failure (const char *tty, bool su_to_root)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2011-06-13 23:56:47 +05:30
|
|
|
sulog (tty, false, caller_name, name); /* log failed attempt */
|
2008-08-31 23:00:30 +05:30
|
|
|
if (getdef_bool ("SYSLOG_SU_ENAB")) {
|
2011-06-13 23:56:26 +05:30
|
|
|
SYSLOG ((su_to_root ? LOG_NOTICE : LOG_INFO,
|
2008-06-11 03:37:51 +05:30
|
|
|
"- %s %s:%s", tty,
|
2011-06-13 23:56:47 +05:30
|
|
|
('\0' != caller_name[0]) ? caller_name : "???",
|
2008-06-11 03:37:51 +05:30
|
|
|
('\0' != name[0]) ? name : "???"));
|
2008-08-31 23:00:30 +05:30
|
|
|
}
|
2007-10-07 17:14:59 +05:30
|
|
|
closelog ();
|
2016-12-13 22:14:19 +05:30
|
|
|
|
|
|
|
#ifdef WITH_AUDIT
|
|
|
|
audit_fd = audit_open ();
|
|
|
|
audit_log_acct_message (audit_fd,
|
|
|
|
AUDIT_USER_ROLE_CHANGE,
|
|
|
|
NULL, /* Prog. name */
|
|
|
|
"su",
|
|
|
|
('\0' != caller_name[0]) ? caller_name : "???",
|
|
|
|
AUDIT_NO_ID,
|
|
|
|
"localhost",
|
|
|
|
NULL, /* addr */
|
|
|
|
tty,
|
|
|
|
0); /* result */
|
|
|
|
close (audit_fd);
|
|
|
|
#endif /* WITH_AUDIT */
|
|
|
|
|
2007-10-07 17:14:59 +05:30
|
|
|
exit (1);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
|
2009-07-20 19:30:50 +05:30
|
|
|
/*
|
|
|
|
* execve_shell - Execute a shell with execve, or interpret it with
|
|
|
|
* /bin/sh
|
|
|
|
*/
|
2011-06-17 02:51:29 +05:30
|
|
|
static void execve_shell (const char *shellname,
|
2009-09-09 02:16:43 +05:30
|
|
|
char *args[],
|
|
|
|
char *const envp[])
|
2009-07-20 19:30:50 +05:30
|
|
|
{
|
|
|
|
int err;
|
2023-02-01 18:20:48 +05:30
|
|
|
(void) execve (shellname, args, envp);
|
2009-07-20 19:30:50 +05:30
|
|
|
err = errno;
|
|
|
|
|
2011-06-17 02:51:29 +05:30
|
|
|
if (access (shellname, R_OK|X_OK) == 0) {
|
2009-07-20 19:30:50 +05:30
|
|
|
/*
|
|
|
|
* Assume this is a shell script (with no shebang).
|
|
|
|
* Interpret it with /bin/sh
|
|
|
|
*/
|
|
|
|
size_t n_args = 0;
|
|
|
|
char **targs;
|
|
|
|
while (NULL != args[n_args]) {
|
|
|
|
n_args++;
|
|
|
|
}
|
2023-02-05 03:11:18 +05:30
|
|
|
targs = XMALLOCARRAY (n_args + 3, char *);
|
2009-07-20 19:30:50 +05:30
|
|
|
targs[0] = "sh";
|
2009-07-22 19:05:57 +05:30
|
|
|
targs[1] = "-";
|
2011-06-17 02:51:29 +05:30
|
|
|
targs[2] = xstrdup (shellname);
|
2009-07-22 19:05:57 +05:30
|
|
|
targs[n_args+2] = NULL;
|
2009-07-20 19:30:50 +05:30
|
|
|
while (1 != n_args) {
|
2009-07-22 19:05:57 +05:30
|
|
|
targs[n_args+1] = args[n_args - 1];
|
2009-07-20 19:30:50 +05:30
|
|
|
n_args--;
|
|
|
|
}
|
|
|
|
|
2009-07-22 19:00:06 +05:30
|
|
|
(void) execve (SHELL, targs, envp);
|
2009-07-20 19:30:50 +05:30
|
|
|
} else {
|
|
|
|
errno = err;
|
|
|
|
}
|
|
|
|
}
|
2007-10-07 17:15:40 +05:30
|
|
|
|
2007-10-07 17:16:34 +05:30
|
|
|
#ifdef USE_PAM
|
2007-10-07 17:15:40 +05:30
|
|
|
/* Signal handler for parent process later */
|
2011-06-05 20:11:15 +05:30
|
|
|
static void catch_signals (int sig)
|
2007-10-07 17:15:40 +05:30
|
|
|
{
|
2011-06-05 20:11:15 +05:30
|
|
|
caught = sig;
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
|
|
|
|
2011-06-13 23:57:34 +05:30
|
|
|
/*
|
2011-06-13 23:57:40 +05:30
|
|
|
* prepare_pam_close_session - Fork and wait for the child to close the session
|
|
|
|
*
|
|
|
|
* Only the child returns. The parent will wait for the child to
|
|
|
|
* terminate and exit.
|
2007-10-07 17:15:40 +05:30
|
|
|
*/
|
2011-06-13 23:57:34 +05:30
|
|
|
static void prepare_pam_close_session (void)
|
2007-10-07 17:15:40 +05:30
|
|
|
{
|
|
|
|
sigset_t ourset;
|
|
|
|
int status;
|
|
|
|
int ret;
|
2021-10-23 19:06:00 +05:30
|
|
|
struct sigaction action;
|
|
|
|
|
|
|
|
/* reset SIGCHLD handling to default */
|
|
|
|
action.sa_handler = SIG_DFL;
|
|
|
|
sigemptyset (&action.sa_mask);
|
|
|
|
action.sa_flags = 0;
|
|
|
|
if (0 == caught && sigaction (SIGCHLD, &action, NULL) != 0) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: signal masking malfunction\n"),
|
|
|
|
Prog);
|
|
|
|
SYSLOG ((LOG_WARN, "Will not execute %s", shellstr));
|
|
|
|
closelog ();
|
|
|
|
exit (1);
|
|
|
|
/* Only the child returns. See above. */
|
|
|
|
}
|
2007-10-07 17:15:40 +05:30
|
|
|
|
2011-06-13 23:57:34 +05:30
|
|
|
pid_child = fork ();
|
|
|
|
if (pid_child == 0) { /* child shell */
|
|
|
|
return; /* Only the child will return from pam_create_session */
|
|
|
|
} else if ((pid_t)-1 == pid_child) {
|
2009-09-09 02:16:43 +05:30
|
|
|
(void) fprintf (stderr,
|
|
|
|
_("%s: Cannot fork user shell\n"),
|
|
|
|
Prog);
|
2007-10-07 17:17:11 +05:30
|
|
|
SYSLOG ((LOG_WARN, "Cannot execute %s", shellstr));
|
2007-10-07 17:15:40 +05:30
|
|
|
closelog ();
|
|
|
|
exit (1);
|
2011-06-13 23:57:34 +05:30
|
|
|
/* Only the child returns. See above. */
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
2011-06-13 23:57:34 +05:30
|
|
|
|
2007-10-07 17:15:40 +05:30
|
|
|
/* parent only */
|
|
|
|
sigfillset (&ourset);
|
2008-06-11 03:37:51 +05:30
|
|
|
if (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0) {
|
2009-09-09 02:16:43 +05:30
|
|
|
(void) fprintf (stderr,
|
|
|
|
_("%s: signal malfunction\n"),
|
|
|
|
Prog);
|
2011-06-05 20:11:15 +05:30
|
|
|
caught = SIGTERM;
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
2011-06-05 20:11:15 +05:30
|
|
|
if (0 == caught) {
|
2007-10-07 17:17:22 +05:30
|
|
|
action.sa_handler = catch_signals;
|
2007-10-07 17:15:40 +05:30
|
|
|
sigemptyset (&ourset);
|
|
|
|
|
2008-06-11 03:37:51 +05:30
|
|
|
if ( (sigaddset (&ourset, SIGTERM) != 0)
|
|
|
|
|| (sigaddset (&ourset, SIGALRM) != 0)
|
|
|
|
|| (sigaction (SIGTERM, &action, NULL) != 0)
|
2020-08-29 01:46:04 +05:30
|
|
|
|| ( !doshell /* handle SIGINT (Ctrl-C), SIGQUIT
|
|
|
|
* (Ctrl-\), and SIGTSTP (Ctrl-Z)
|
|
|
|
* since the child will not control
|
|
|
|
* the tty.
|
|
|
|
*/
|
2011-06-05 20:11:15 +05:30
|
|
|
&& ( (sigaddset (&ourset, SIGINT) != 0)
|
|
|
|
|| (sigaddset (&ourset, SIGQUIT) != 0)
|
|
|
|
|| (sigaddset (&ourset, SIGTSTP) != 0)
|
|
|
|
|| (sigaction (SIGINT, &action, NULL) != 0)
|
2011-06-13 23:56:52 +05:30
|
|
|
|| (sigaction (SIGQUIT, &action, NULL) != 0)
|
|
|
|
|| (sigaction (SIGTSTP, &action, NULL) != 0)))
|
2008-06-11 03:37:51 +05:30
|
|
|
|| (sigprocmask (SIG_UNBLOCK, &ourset, NULL) != 0)
|
2007-10-07 17:15:40 +05:30
|
|
|
) {
|
|
|
|
fprintf (stderr,
|
2009-09-09 02:16:43 +05:30
|
|
|
_("%s: signal masking malfunction\n"),
|
|
|
|
Prog);
|
2011-06-05 20:11:15 +05:30
|
|
|
caught = SIGTERM;
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-05 20:11:15 +05:30
|
|
|
if (0 == caught) {
|
|
|
|
bool stop = true;
|
|
|
|
|
2007-10-07 17:15:40 +05:30
|
|
|
do {
|
2008-06-14 02:32:07 +05:30
|
|
|
pid_t pid;
|
2011-06-05 20:11:15 +05:30
|
|
|
stop = true;
|
2007-10-07 17:15:40 +05:30
|
|
|
|
2020-09-21 03:32:58 +05:30
|
|
|
do {
|
|
|
|
pid = waitpid (-1, &status, WUNTRACED);
|
|
|
|
} while (pid != -1 && pid != pid_child);
|
2007-10-07 17:15:40 +05:30
|
|
|
|
2011-06-05 20:11:15 +05:30
|
|
|
/* When interrupted by signal, the signal will be
|
|
|
|
* forwarded to the child, and termination will be
|
|
|
|
* forced later.
|
|
|
|
*/
|
|
|
|
if ( ((pid_t)-1 == pid)
|
|
|
|
&& (EINTR == errno)
|
|
|
|
&& (SIGTSTP == caught)) {
|
2013-08-13 23:12:50 +05:30
|
|
|
caught = 0;
|
2011-06-05 20:11:15 +05:30
|
|
|
/* Except for SIGTSTP, which request to
|
|
|
|
* stop the child.
|
|
|
|
* We will SIGSTOP ourself on the next
|
|
|
|
* waitpid round.
|
|
|
|
*/
|
2011-06-13 23:57:34 +05:30
|
|
|
kill (pid_child, SIGSTOP);
|
2011-06-05 20:11:15 +05:30
|
|
|
stop = false;
|
|
|
|
} else if ( ((pid_t)-1 != pid)
|
|
|
|
&& (0 != WIFSTOPPED (status))) {
|
2008-05-18 19:11:56 +05:30
|
|
|
/* The child (shell) was suspended.
|
|
|
|
* Suspend su. */
|
2010-03-23 18:35:06 +05:30
|
|
|
kill (getpid (), SIGSTOP);
|
2008-05-18 19:11:56 +05:30
|
|
|
/* wake child when resumed */
|
2007-10-07 17:15:40 +05:30
|
|
|
kill (pid, SIGCONT);
|
2011-06-05 20:11:15 +05:30
|
|
|
stop = false;
|
2017-05-14 21:28:10 +05:30
|
|
|
} else if ( (pid_t)-1 != pid) {
|
2017-02-23 21:17:29 +05:30
|
|
|
pid_child = 0;
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
2011-06-05 20:11:15 +05:30
|
|
|
} while (!stop);
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
|
|
|
|
2017-02-23 21:17:29 +05:30
|
|
|
if (0 != caught && 0 != pid_child) {
|
2009-09-09 02:09:15 +05:30
|
|
|
(void) fputs ("\n", stderr);
|
|
|
|
(void) fputs (_("Session terminated, terminating shell..."),
|
|
|
|
stderr);
|
2013-07-28 18:08:12 +05:30
|
|
|
(void) kill (-pid_child, caught);
|
2013-08-13 23:18:53 +05:30
|
|
|
|
2016-09-15 21:24:42 +05:30
|
|
|
snprintf (kill_msg, sizeof kill_msg, _(" ...killed.\n"));
|
|
|
|
snprintf (wait_msg, sizeof wait_msg, _(" ...waiting for child to terminate.\n"));
|
2016-07-02 21:41:09 +05:30
|
|
|
|
2021-10-09 17:24:36 +05:30
|
|
|
/* Any signals other than SIGCHLD and SIGALRM will no longer have any effect,
|
|
|
|
* so it's time to block all of them. */
|
|
|
|
sigfillset (&ourset);
|
|
|
|
if (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0) {
|
|
|
|
fprintf (stderr, _("%s: signal masking malfunction\n"), Prog);
|
|
|
|
kill_child (0);
|
|
|
|
/* Never reach (_exit called). */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send SIGKILL to the child if it doesn't
|
|
|
|
* exit within 2 seconds (after SIGTERM) */
|
2013-08-13 23:18:53 +05:30
|
|
|
(void) signal (SIGALRM, kill_child);
|
2017-02-23 21:17:29 +05:30
|
|
|
(void) signal (SIGCHLD, catch_signals);
|
2013-08-13 23:18:53 +05:30
|
|
|
(void) alarm (2);
|
|
|
|
|
2021-10-09 17:24:36 +05:30
|
|
|
(void) sigdelset (&ourset, SIGALRM);
|
|
|
|
(void) sigdelset (&ourset, SIGCHLD);
|
|
|
|
|
|
|
|
while (0 == waitpid (pid_child, &status, WNOHANG)) {
|
|
|
|
sigsuspend (&ourset);
|
2017-02-23 21:17:29 +05:30
|
|
|
}
|
2021-10-09 17:24:36 +05:30
|
|
|
pid_child = 0;
|
2017-02-23 21:17:29 +05:30
|
|
|
|
2013-08-13 23:18:53 +05:30
|
|
|
(void) fputs (_(" ...terminated.\n"), stderr);
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
ret = pam_close_session (pamh, 0);
|
2008-06-11 03:37:51 +05:30
|
|
|
if (PAM_SUCCESS != ret) {
|
2007-10-07 17:15:40 +05:30
|
|
|
SYSLOG ((LOG_ERR, "pam_close_session: %s",
|
2011-06-13 23:57:34 +05:30
|
|
|
pam_strerror (pamh, ret)));
|
2007-10-07 17:17:01 +05:30
|
|
|
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
|
|
|
|
2011-06-13 23:57:46 +05:30
|
|
|
(void) pam_setcred (pamh, PAM_DELETE_CRED);
|
|
|
|
(void) pam_end (pamh, PAM_SUCCESS);
|
2007-10-07 17:15:40 +05:30
|
|
|
|
2008-06-11 03:37:51 +05:30
|
|
|
exit ((0 != WIFEXITED (status)) ? WEXITSTATUS (status)
|
|
|
|
: WTERMSIG (status) + 128);
|
2011-06-13 23:57:34 +05:30
|
|
|
/* Only the child returns. See above. */
|
2007-10-07 17:15:40 +05:30
|
|
|
}
|
2011-06-13 23:57:34 +05:30
|
|
|
#endif /* USE_PAM */
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2007-10-07 17:17:11 +05:30
|
|
|
/*
|
|
|
|
* usage - print command line syntax and exit
|
2011-06-13 23:57:40 +05:30
|
|
|
*/
|
* NEWS, src/userdel.c, src/lastlog.c, src/gpasswd.c,
src/newusers.c, src/chpasswd.c, src/groupmems.c, src/usermod.c,
src/chgpasswd.c, src/vipw.c, src/su.c, src/useradd.c,
src/groupmod.c, src/passwd.c, src/groupadd.c, src/chage.c,
src/faillog.c, src/chsh.c: If someone uses the -h/--help options,
the usage should not go to stderr nor should the utility exit with
non-zero status. All of the shadow utils do just this
unfortunately, so convert them over to sanity.
* man/groupmems.8.xml, man/gpasswd.1.xml: Added option -h/--help.
2009-09-05 04:32:33 +05:30
|
|
|
static void usage (int status)
|
2007-10-07 17:17:11 +05:30
|
|
|
{
|
2011-08-15 03:14:46 +05:30
|
|
|
(void)
|
2018-03-27 04:15:03 +05:30
|
|
|
fputs (_("Usage: su [options] [-] [username [args]]\n"
|
2008-01-25 02:24:42 +05:30
|
|
|
"\n"
|
|
|
|
"Options:\n"
|
|
|
|
" -c, --command COMMAND pass COMMAND to the invoked shell\n"
|
|
|
|
" -h, --help display this help message and exit\n"
|
|
|
|
" -, -l, --login make the shell a login shell\n"
|
|
|
|
" -m, -p,\n"
|
|
|
|
" --preserve-environment do not reset environment variables, and\n"
|
|
|
|
" keep the same shell\n"
|
|
|
|
" -s, --shell SHELL use SHELL instead of the default in passwd\n"
|
2018-03-27 04:15:03 +05:30
|
|
|
"\n"
|
|
|
|
"If no username is given, assume root.\n"), (E_SUCCESS != status) ? stderr : stdout);
|
* NEWS, src/userdel.c, src/lastlog.c, src/gpasswd.c,
src/newusers.c, src/chpasswd.c, src/groupmems.c, src/usermod.c,
src/chgpasswd.c, src/vipw.c, src/su.c, src/useradd.c,
src/groupmod.c, src/passwd.c, src/groupadd.c, src/chage.c,
src/faillog.c, src/chsh.c: If someone uses the -h/--help options,
the usage should not go to stderr nor should the utility exit with
non-zero status. All of the shadow utils do just this
unfortunately, so convert them over to sanity.
* man/groupmems.8.xml, man/gpasswd.1.xml: Added option -h/--help.
2009-09-05 04:32:33 +05:30
|
|
|
exit (status);
|
2007-10-07 17:17:11 +05:30
|
|
|
}
|
|
|
|
|
2011-06-13 23:56:52 +05:30
|
|
|
#ifdef USE_PAM
|
2011-08-15 14:55:58 +05:30
|
|
|
static void check_perms_pam (const struct passwd *pw)
|
2011-06-13 23:56:58 +05:30
|
|
|
{
|
2011-06-13 23:56:52 +05:30
|
|
|
int ret;
|
2011-06-13 23:56:58 +05:30
|
|
|
ret = pam_authenticate (pamh, 0);
|
|
|
|
if (PAM_SUCCESS != ret) {
|
2016-08-05 01:47:31 +05:30
|
|
|
SYSLOG (((pw->pw_uid != 0)? LOG_NOTICE : LOG_WARN, "pam_authenticate: %s",
|
2011-06-13 23:56:58 +05:30
|
|
|
pam_strerror (pamh, ret)));
|
|
|
|
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
|
|
|
|
(void) pam_end (pamh, ret);
|
|
|
|
su_failure (caller_tty, 0 == pw->pw_uid);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pam_acct_mgmt (pamh, 0);
|
|
|
|
if (PAM_SUCCESS != ret) {
|
|
|
|
if (caller_is_root) {
|
|
|
|
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 (caller_tty, 0 == pw->pw_uid);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SYSLOG ((LOG_ERR, "pam_acct_mgmt: %s",
|
2011-06-13 23:57:06 +05:30
|
|
|
pam_strerror (pamh, ret)));
|
2011-06-13 23:56:58 +05:30
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: %s\n"),
|
|
|
|
Prog, pam_strerror (pamh, ret));
|
|
|
|
(void) pam_end (pamh, ret);
|
|
|
|
su_failure (caller_tty, 0 == pw->pw_uid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-13 23:56:52 +05:30
|
|
|
#else /* !USE_PAM */
|
2011-08-15 14:55:58 +05:30
|
|
|
static void check_perms_nopam (const struct passwd *pw)
|
2011-06-13 23:56:58 +05:30
|
|
|
{
|
2011-08-15 14:55:58 +05:30
|
|
|
/*@observer@*/const struct spwd *spwd = NULL;
|
2011-08-15 03:14:46 +05:30
|
|
|
/*@observer@*/const char *password = pw->pw_passwd;
|
2021-12-30 21:06:18 +05:30
|
|
|
sighandler_t oldsig;
|
2011-06-13 23:56:16 +05:30
|
|
|
|
2011-06-13 23:57:06 +05:30
|
|
|
if (caller_is_root) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-29 08:46:03 +05:30
|
|
|
if (strcmp (pw->pw_passwd, "") == 0) {
|
2023-02-01 18:20:48 +05:30
|
|
|
const char *prevent_no_auth = getdef_str("PREVENT_NO_AUTH");
|
|
|
|
|
2021-08-18 23:36:02 +05:30
|
|
|
if (prevent_no_auth == NULL) {
|
2021-03-29 08:46:03 +05:30
|
|
|
prevent_no_auth = "superuser";
|
|
|
|
}
|
2021-08-18 23:36:02 +05:30
|
|
|
if (strcmp(prevent_no_auth, "yes") == 0) {
|
2021-03-29 08:46:03 +05:30
|
|
|
fprintf(stderr, _("Password field is empty, this is forbidden for all accounts.\n"));
|
|
|
|
exit(1);
|
2021-08-18 23:36:02 +05:30
|
|
|
} else if ((pw->pw_uid == 0)
|
2021-03-29 08:46:03 +05:30
|
|
|
&& (strcmp(prevent_no_auth, "superuser") == 0)) {
|
|
|
|
fprintf(stderr, _("Password field is empty, this is forbidden for super-user.\n"));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-13 23:56:16 +05:30
|
|
|
/*
|
|
|
|
* 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>.
|
|
|
|
*/
|
|
|
|
|
2011-06-13 23:57:06 +05:30
|
|
|
if ( (0 == pw->pw_uid)
|
|
|
|
&& getdef_bool ("SU_WHEEL_ONLY")
|
|
|
|
&& !iswheel (caller_name)) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("You are not authorized to su %s\n"),
|
|
|
|
name);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
spwd = getspnam (name); /* !USE_PAM, no need for xgetspnam */
|
2011-06-13 23:56:16 +05:30
|
|
|
#ifdef SU_ACCESS
|
2011-06-13 23:57:06 +05:30
|
|
|
if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
|
|
|
|
if (NULL != spwd) {
|
2011-08-15 03:14:46 +05:30
|
|
|
password = spwd->sp_pwdp;
|
2011-06-13 23:56:26 +05:30
|
|
|
}
|
2011-06-13 23:57:06 +05:30
|
|
|
}
|
2011-06-13 23:56:26 +05:30
|
|
|
|
2011-06-13 23:57:06 +05:30
|
|
|
switch (check_su_auth (caller_name, name, 0 == pw->pw_uid)) {
|
|
|
|
case 0: /* normal su, require target user's password */
|
|
|
|
break;
|
|
|
|
case 1: /* require no password */
|
2011-08-15 03:14:46 +05:30
|
|
|
password = ""; /* XXX warning: const */
|
2011-06-13 23:57:06 +05:30
|
|
|
break;
|
|
|
|
case 2: /* require own password */
|
2011-08-15 03:14:46 +05:30
|
|
|
(void) puts (_("(Enter your own password)"));
|
|
|
|
password = caller_pass;
|
2011-06-13 23:57:06 +05:30
|
|
|
break;
|
|
|
|
default: /* access denied (-1) or unexpected value */
|
|
|
|
fprintf (stderr,
|
|
|
|
_("You are not authorized to su %s\n"),
|
|
|
|
name);
|
|
|
|
exit (1);
|
2011-06-13 23:56:16 +05:30
|
|
|
}
|
2011-06-13 23:57:06 +05:30
|
|
|
#endif /* SU_ACCESS */
|
2011-06-13 23:56:16 +05:30
|
|
|
/*
|
|
|
|
* Set up a signal handler in case the user types QUIT.
|
|
|
|
*/
|
|
|
|
die (0);
|
|
|
|
oldsig = signal (SIGQUIT, die);
|
|
|
|
|
|
|
|
/*
|
2021-08-18 23:36:02 +05:30
|
|
|
* See if the system defined authentication method is being used.
|
2011-06-13 23:56:16 +05:30
|
|
|
* The first character of an administrator defined method is an '@'
|
|
|
|
* character.
|
|
|
|
*/
|
2023-02-01 07:20:14 +05:30
|
|
|
if (pw_auth (password, name, PW_SU, NULL) != 0) {
|
2011-06-13 23:56:26 +05:30
|
|
|
SYSLOG (((pw->pw_uid != 0)? LOG_NOTICE : LOG_WARN,
|
2011-06-13 23:56:16 +05:30
|
|
|
"Authentication failed for %s", name));
|
|
|
|
fprintf(stderr, _("%s: Authentication failure\n"), Prog);
|
2011-06-13 23:56:47 +05:30
|
|
|
su_failure (caller_tty, 0 == pw->pw_uid);
|
2011-06-13 23:56:16 +05:30
|
|
|
}
|
|
|
|
(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.
|
|
|
|
*/
|
2011-06-13 23:57:06 +05:30
|
|
|
if (NULL != spwd) {
|
2011-06-13 23:56:26 +05:30
|
|
|
(void) expire (pw, spwd);
|
2011-06-13 23:56:16 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2023-02-01 07:20:14 +05:30
|
|
|
if (!isttytime (name, "SU", time (NULL))) {
|
2011-06-13 23:57:06 +05:30
|
|
|
SYSLOG (((0 != pw->pw_uid) ? LOG_WARN : LOG_CRIT,
|
|
|
|
"SU by %s to restricted account %s",
|
|
|
|
caller_name, name));
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: You are not authorized to su at that time\n"),
|
|
|
|
Prog);
|
|
|
|
su_failure (caller_tty, 0 == pw->pw_uid);
|
2011-06-13 23:56:16 +05:30
|
|
|
}
|
2011-06-13 23:56:58 +05:30
|
|
|
}
|
2011-06-13 23:56:16 +05:30
|
|
|
#endif /* !USE_PAM */
|
|
|
|
|
2011-06-13 23:56:58 +05:30
|
|
|
/*
|
|
|
|
* check_perms - check permissions to switch to the user 'name'
|
|
|
|
*
|
2023-05-23 17:25:09 +05:30
|
|
|
* The user is authenticated in all subsystems iterately.
|
2011-06-13 23:56:58 +05:30
|
|
|
*/
|
2011-08-15 14:55:58 +05:30
|
|
|
static /*@only@*/struct passwd * check_perms (void)
|
2023-05-23 17:25:09 +05:30
|
|
|
{
|
|
|
|
struct passwd *pw = NULL;
|
|
|
|
|
|
|
|
while (pw == NULL)
|
|
|
|
pw = do_check_perms();
|
|
|
|
return pw;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do_check_perms - check permissions to switch to the user 'name'
|
|
|
|
*
|
|
|
|
* The user is authenticated in current subsystem, if any. Returns
|
|
|
|
* NULL if permissions have to be checked in next subsystem, in
|
|
|
|
* which case the subsystem has already been entered.
|
|
|
|
*/
|
|
|
|
static /*@only@*/struct passwd * do_check_perms (void)
|
2011-06-13 23:56:58 +05:30
|
|
|
{
|
2011-06-17 02:51:29 +05:30
|
|
|
#ifdef USE_PAM
|
2023-02-01 18:20:48 +05:30
|
|
|
const void *tmp_name;
|
2011-06-17 02:51:29 +05:30
|
|
|
int ret;
|
|
|
|
#endif /* !USE_PAM */
|
2011-06-13 23:56:58 +05:30
|
|
|
/*
|
|
|
|
* The password file entries for the user is gotten and the account
|
|
|
|
* validated.
|
|
|
|
*/
|
|
|
|
struct passwd *pw = xgetpwnam (name);
|
|
|
|
if (NULL == pw) {
|
2011-06-17 02:51:29 +05:30
|
|
|
(void) fprintf (stderr,
|
|
|
|
_("No passwd entry for user '%s'\n"), name);
|
2016-08-05 01:47:31 +05:30
|
|
|
SYSLOG ((LOG_NOTICE, "No passwd entry for user '%s'", name));
|
2011-06-17 02:51:29 +05:30
|
|
|
su_failure (caller_tty, true);
|
2011-06-13 23:56:58 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
(void) signal (SIGINT, SIG_IGN);
|
|
|
|
(void) signal (SIGQUIT, SIG_IGN);
|
2011-06-17 02:51:29 +05:30
|
|
|
|
2011-06-13 23:56:58 +05:30
|
|
|
#ifdef USE_PAM
|
|
|
|
check_perms_pam (pw);
|
2011-06-17 02:51:29 +05:30
|
|
|
/* PAM authentication can request a change of account */
|
2023-02-01 18:20:48 +05:30
|
|
|
ret = pam_get_item(pamh, PAM_USER, &tmp_name);
|
2011-06-17 02:51:29 +05:30
|
|
|
if (ret != PAM_SUCCESS) {
|
|
|
|
SYSLOG((LOG_ERR, "pam_get_item: internal PAM error\n"));
|
|
|
|
(void) fprintf (stderr,
|
|
|
|
"%s: Internal PAM error retrieving username\n",
|
|
|
|
Prog);
|
|
|
|
(void) pam_end (pamh, ret);
|
|
|
|
su_failure (caller_tty, 0 == pw->pw_uid);
|
|
|
|
}
|
|
|
|
if (strcmp (name, tmp_name) != 0) {
|
|
|
|
SYSLOG ((LOG_INFO,
|
|
|
|
"Change user from '%s' to '%s' as requested by PAM",
|
|
|
|
name, tmp_name));
|
2023-03-13 05:51:42 +05:30
|
|
|
if (strlcpy (name, tmp_name, sizeof(name)) >= sizeof(name)) {
|
|
|
|
fprintf (stderr, _("Overlong user name '%s'\n"),
|
|
|
|
tmp_name);
|
|
|
|
SYSLOG ((LOG_NOTICE, "Overlong user name '%s'",
|
|
|
|
tmp_name));
|
|
|
|
su_failure (caller_tty, true);
|
|
|
|
}
|
2011-06-17 02:51:29 +05:30
|
|
|
pw = xgetpwnam (name);
|
|
|
|
if (NULL == pw) {
|
|
|
|
(void) fprintf (stderr,
|
|
|
|
_("No passwd entry for user '%s'\n"),
|
|
|
|
name);
|
2016-08-05 01:47:31 +05:30
|
|
|
SYSLOG ((LOG_NOTICE,
|
2011-06-17 02:51:29 +05:30
|
|
|
"No passwd entry for user '%s'", name));
|
|
|
|
su_failure (caller_tty, true);
|
|
|
|
}
|
|
|
|
}
|
2011-06-13 23:56:58 +05:30
|
|
|
#else /* !USE_PAM */
|
2011-06-13 23:57:23 +05:30
|
|
|
check_perms_nopam (pw);
|
2011-06-13 23:56:58 +05:30
|
|
|
#endif /* !USE_PAM */
|
2011-06-17 02:51:29 +05:30
|
|
|
|
2011-06-13 23:56:16 +05:30
|
|
|
(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).
|
|
|
|
*/
|
2011-06-13 23:56:26 +05:30
|
|
|
if ('*' == pw->pw_shell[0]) { /* subsystem root required */
|
|
|
|
subsystem (pw); /* change to the subsystem root */
|
2011-06-13 23:56:16 +05:30
|
|
|
endpwent (); /* close the old password databases */
|
|
|
|
endspent ();
|
2011-08-15 03:14:46 +05:30
|
|
|
pw_free (pw);
|
2023-05-23 17:25:09 +05:30
|
|
|
return NULL; /* authenticate in the subsystem */
|
2011-06-13 23:56:16 +05:30
|
|
|
}
|
|
|
|
|
2011-06-13 23:56:26 +05:30
|
|
|
return pw;
|
2011-06-13 23:56:16 +05:30
|
|
|
}
|
|
|
|
|
2011-06-13 23:56:47 +05:30
|
|
|
/*
|
|
|
|
* save_caller_context - save information from the call context
|
|
|
|
*
|
|
|
|
* Save the program's name (Prog), caller's UID (caller_uid /
|
|
|
|
* caller_is_root), name (caller_name), and password (caller_pass),
|
|
|
|
* the TTY (ttyp), and whether su was called from a console
|
|
|
|
* (is_console) for further processing and before they might change.
|
|
|
|
*/
|
|
|
|
static void save_caller_context (char **argv)
|
|
|
|
{
|
2011-09-18 23:17:03 +05:30
|
|
|
struct passwd *pw = NULL;
|
|
|
|
#ifndef USE_PAM
|
|
|
|
#ifdef SU_ACCESS
|
2011-08-15 03:14:46 +05:30
|
|
|
const char *password = NULL;
|
2011-09-18 23:17:03 +05:30
|
|
|
#endif /* SU_ACCESS */
|
|
|
|
#endif /* !USE_PAM */
|
2011-06-13 23:56:47 +05:30
|
|
|
/*
|
|
|
|
* Get the program name. The program name is used as a prefix to
|
|
|
|
* most error messages.
|
|
|
|
*/
|
|
|
|
Prog = Basename (argv[0]);
|
2021-11-29 05:07:53 +05:30
|
|
|
log_set_progname(Prog);
|
|
|
|
log_set_logfd(stderr);
|
2011-06-13 23:56:47 +05:30
|
|
|
|
|
|
|
caller_uid = getuid ();
|
|
|
|
caller_is_root = (caller_uid == 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the tty name. Entries will be logged indicating that the user
|
|
|
|
* tried to change to the named new user from the current terminal.
|
|
|
|
*/
|
|
|
|
caller_tty = ttyname (0);
|
|
|
|
if ((isatty (0) != 0) && (NULL != caller_tty)) {
|
|
|
|
#ifndef USE_PAM
|
|
|
|
caller_on_console = console (caller_tty);
|
2011-09-18 23:17:03 +05:30
|
|
|
#endif /* !USE_PAM */
|
2011-06-13 23:56:47 +05:30
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Be more paranoid, like su from SimplePAMApps. --marekm
|
|
|
|
*/
|
|
|
|
if (!caller_is_root) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: must be run from a terminal\n"),
|
|
|
|
Prog);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
caller_tty = "???";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the user's real name. The current UID is used to determine
|
|
|
|
* who has executed su. That user ID must exist.
|
|
|
|
*/
|
|
|
|
pw = get_my_pwent ();
|
|
|
|
if (NULL == pw) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: Cannot determine your user name.\n"),
|
|
|
|
Prog);
|
|
|
|
SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
|
|
|
|
(unsigned long) caller_uid));
|
2011-06-17 02:51:29 +05:30
|
|
|
su_failure (caller_tty, true); /* unknown target UID*/
|
2011-06-13 23:56:47 +05:30
|
|
|
}
|
|
|
|
STRFCPY (caller_name, pw->pw_name);
|
|
|
|
|
|
|
|
#ifndef USE_PAM
|
|
|
|
#ifdef SU_ACCESS
|
|
|
|
/*
|
|
|
|
* Sort out the password of user calling su, in case needed later
|
|
|
|
* -- chris
|
|
|
|
*/
|
2011-08-15 03:14:46 +05:30
|
|
|
password = pw->pw_passwd;
|
2011-06-13 23:56:47 +05:30
|
|
|
if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
|
2011-08-15 14:55:58 +05:30
|
|
|
const struct spwd *spwd = getspnam (caller_name);
|
2011-06-13 23:56:47 +05:30
|
|
|
if (NULL != spwd) {
|
2011-08-15 03:14:46 +05:30
|
|
|
password = spwd->sp_pwdp;
|
2011-06-13 23:56:47 +05:30
|
|
|
}
|
|
|
|
}
|
2011-08-15 03:14:46 +05:30
|
|
|
free (caller_pass);
|
|
|
|
caller_pass = xstrdup (password);
|
2011-06-13 23:56:47 +05:30
|
|
|
#endif /* SU_ACCESS */
|
|
|
|
#endif /* !USE_PAM */
|
2011-08-15 03:14:46 +05:30
|
|
|
pw_free (pw);
|
2011-06-13 23:56:47 +05:30
|
|
|
}
|
|
|
|
|
2007-10-07 17:14:02 +05:30
|
|
|
/*
|
2011-06-13 23:57:23 +05:30
|
|
|
* process_flags - Process the command line arguments
|
2007-10-07 17:14:02 +05:30
|
|
|
*
|
2011-06-13 23:57:23 +05:30
|
|
|
* process_flags() interprets the command line arguments and sets
|
|
|
|
* the values that the user will be created with accordingly. The
|
|
|
|
* values are checked for sanity.
|
2007-10-07 17:14:02 +05:30
|
|
|
*/
|
2011-06-13 23:57:23 +05:30
|
|
|
static void process_flags (int argc, char **argv)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2020-08-29 01:46:11 +05:30
|
|
|
int c;
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"command", required_argument, NULL, 'c'},
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
|
|
|
{"login", no_argument, NULL, 'l'},
|
|
|
|
{"preserve-environment", no_argument, NULL, 'p'},
|
|
|
|
{"shell", required_argument, NULL, 's'},
|
|
|
|
{NULL, 0, NULL, '\0'}
|
|
|
|
};
|
|
|
|
|
|
|
|
while ((c = getopt_long (argc, argv, "c:hlmps:",
|
|
|
|
long_options, NULL)) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'c':
|
|
|
|
command = optarg;
|
|
|
|
break;
|
|
|
|
case 'h':
|
2011-06-13 23:57:23 +05:30
|
|
|
usage (E_SUCCESS);
|
2020-08-29 01:46:11 +05:30
|
|
|
break;
|
|
|
|
case 'l':
|
2011-06-13 23:57:23 +05:30
|
|
|
fakelogin = true;
|
2020-08-29 01:46:11 +05:30
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
case 'p':
|
2011-06-13 23:57:23 +05:30
|
|
|
/* This will only have an effect if the target
|
2023-02-16 17:24:14 +05:30
|
|
|
* user does not have a restricted shell, or if
|
2011-06-13 23:57:23 +05:30
|
|
|
* su is called by root.
|
|
|
|
*/
|
|
|
|
change_environment = false;
|
|
|
|
break;
|
2020-08-29 01:46:11 +05:30
|
|
|
case 's':
|
|
|
|
shellstr = optarg;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage (E_USAGE); /* NOT REACHED */
|
2007-10-07 17:17:11 +05:30
|
|
|
}
|
2011-06-13 23:57:23 +05:30
|
|
|
}
|
2007-12-26 20:40:48 +05:30
|
|
|
|
2020-08-29 01:46:11 +05:30
|
|
|
if ((optind < argc) && (strcmp (argv[optind], "-") == 0)) {
|
|
|
|
fakelogin = true;
|
|
|
|
optind++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optind < argc) {
|
|
|
|
STRFCPY (name, argv[optind++]); /* use this login id */
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-04-12 05:58:32 +05:30
|
|
|
if ('\0' == name[0]) { /* use default user */
|
2009-05-10 23:50:41 +05:30
|
|
|
struct passwd *root_pw = getpwnam ("root");
|
2009-04-12 05:58:32 +05:30
|
|
|
if ((NULL != root_pw) && (0 == root_pw->pw_uid)) {
|
|
|
|
(void) strcpy (name, "root");
|
|
|
|
} else {
|
2009-05-10 23:50:41 +05:30
|
|
|
root_pw = getpwuid (0);
|
2009-04-12 05:58:32 +05:30
|
|
|
if (NULL == root_pw) {
|
2009-05-10 23:50:41 +05:30
|
|
|
SYSLOG ((LOG_CRIT, "There is no UID 0 user."));
|
2011-06-13 23:56:47 +05:30
|
|
|
su_failure (caller_tty, true);
|
2009-04-12 05:58:32 +05:30
|
|
|
}
|
2009-05-10 23:50:41 +05:30
|
|
|
(void) strcpy (name, root_pw->pw_name);
|
2009-04-12 05:58:32 +05:30
|
|
|
}
|
2008-06-11 03:37:51 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2020-08-29 01:46:11 +05:30
|
|
|
doshell = (argc == optind); /* any arguments remaining? */
|
2008-06-11 03:37:51 +05:30
|
|
|
if (NULL != command) {
|
2020-08-29 01:46:04 +05:30
|
|
|
doshell = false;
|
2008-06-11 03:37:51 +05:30
|
|
|
}
|
2011-06-13 23:57:23 +05:30
|
|
|
}
|
|
|
|
|
2011-06-13 23:57:28 +05:30
|
|
|
static void set_environment (struct passwd *pw)
|
|
|
|
{
|
|
|
|
const char *cp;
|
|
|
|
/*
|
|
|
|
* If a new login is being set up, the old environment will be
|
|
|
|
* ignored and a new one created later on.
|
|
|
|
*/
|
|
|
|
if (change_environment && fakelogin) {
|
|
|
|
/*
|
|
|
|
* The terminal type will be left alone if it is present in
|
|
|
|
* the environment already.
|
|
|
|
*/
|
|
|
|
cp = getenv ("TERM");
|
|
|
|
if (NULL != cp) {
|
|
|
|
addenv ("TERM", cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For some terminals COLORTERM seems to be the only way
|
|
|
|
* for checking for that specific terminal. For instance,
|
|
|
|
* gnome-terminal sets its TERM as "xterm" but its
|
|
|
|
* COLORTERM as "gnome-terminal". The COLORTERM variable
|
|
|
|
* is also of use when running GNU screen since it sets
|
|
|
|
* TERM to "screen" but doesn't touch COLORTERM.
|
|
|
|
*/
|
|
|
|
cp = getenv ("COLORTERM");
|
|
|
|
if (NULL != cp) {
|
|
|
|
addenv ("COLORTERM", cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef USE_PAM
|
|
|
|
cp = getdef_str ("ENV_TZ");
|
|
|
|
if (NULL != cp) {
|
|
|
|
addenv (('/' == *cp) ? tz (cp) : cp, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The clock frequency will be reset to the login value if required
|
|
|
|
*/
|
|
|
|
cp = getdef_str ("ENV_HZ");
|
|
|
|
if (NULL != cp) {
|
|
|
|
addenv (cp, NULL); /* set the default $HZ, if one */
|
|
|
|
}
|
|
|
|
#endif /* !USE_PAM */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also leave DISPLAY and XAUTHORITY if present, else
|
|
|
|
* pam_xauth will not work.
|
|
|
|
*/
|
|
|
|
cp = getenv ("DISPLAY");
|
|
|
|
if (NULL != cp) {
|
|
|
|
addenv ("DISPLAY", cp);
|
|
|
|
}
|
|
|
|
cp = getenv ("XAUTHORITY");
|
|
|
|
if (NULL != cp) {
|
|
|
|
addenv ("XAUTHORITY", cp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char **envp = environ;
|
|
|
|
while (NULL != *envp) {
|
|
|
|
addenv (*envp, NULL);
|
|
|
|
envp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cp = getdef_str ((pw->pw_uid == 0) ? "ENV_SUPATH" : "ENV_PATH");
|
|
|
|
if (NULL == cp) {
|
|
|
|
addenv ((pw->pw_uid == 0) ? "PATH=/sbin:/bin:/usr/sbin:/usr/bin" : "PATH=/bin:/usr/bin", NULL);
|
|
|
|
} else if (strchr (cp, '=') != NULL) {
|
|
|
|
addenv (cp, NULL);
|
|
|
|
} else {
|
|
|
|
addenv ("PATH", cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getenv ("IFS") != NULL) { /* don't export user IFS ... */
|
|
|
|
addenv ("IFS= \t\n", NULL); /* ... instead, set a safe IFS */
|
|
|
|
}
|
|
|
|
|
|
|
|
environ = newenvp; /* make new environment active */
|
|
|
|
|
|
|
|
if (change_environment) {
|
|
|
|
if (fakelogin) {
|
2011-08-15 03:14:46 +05:30
|
|
|
if (shellstr != pw->pw_shell) {
|
|
|
|
free (pw->pw_shell);
|
|
|
|
pw->pw_shell = xstrdup (shellstr);
|
|
|
|
}
|
2011-06-13 23:57:28 +05:30
|
|
|
setup_env (pw);
|
|
|
|
} else {
|
|
|
|
addenv ("HOME", pw->pw_dir);
|
|
|
|
addenv ("USER", pw->pw_name);
|
|
|
|
addenv ("LOGNAME", pw->pw_name);
|
|
|
|
addenv ("SHELL", shellstr);
|
|
|
|
}
|
2018-06-25 19:30:17 +05:30
|
|
|
|
|
|
|
#ifdef USE_PAM
|
|
|
|
/* we need to setup the environment *after* pam_open_session(),
|
|
|
|
* else the UID is changed before stuff like pam_xauth could
|
|
|
|
* run, and we cannot access /etc/shadow and co
|
|
|
|
*/
|
|
|
|
/* update environment with all pam set variables */
|
|
|
|
char **envcp = pam_getenvlist (pamh);
|
|
|
|
if (NULL != envcp) {
|
|
|
|
while (NULL != *envcp) {
|
|
|
|
addenv (*envcp, NULL);
|
|
|
|
envcp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* !USE_PAM */
|
2011-06-13 23:57:28 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-06-13 23:57:23 +05:30
|
|
|
/*
|
|
|
|
* su - switch user id
|
|
|
|
*
|
|
|
|
* su changes the user's ids to the values for the specified user. if
|
|
|
|
* no new user name is specified, "root" or UID 0 is used by default.
|
|
|
|
*
|
|
|
|
* Any additional arguments are passed to the user's shell. In
|
|
|
|
* particular, the argument "-c" will cause the next argument to be
|
|
|
|
* interpreted as a command by the common shell programs.
|
|
|
|
*/
|
|
|
|
int main (int argc, char **argv)
|
|
|
|
{
|
|
|
|
const char *cp;
|
|
|
|
struct passwd *pw = NULL;
|
|
|
|
|
|
|
|
#ifdef USE_PAM
|
|
|
|
int ret;
|
2011-06-13 23:57:51 +05:30
|
|
|
#endif /* USE_PAM */
|
2011-06-13 23:57:23 +05:30
|
|
|
|
|
|
|
(void) setlocale (LC_ALL, "");
|
|
|
|
(void) bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
(void) textdomain (PACKAGE);
|
|
|
|
|
|
|
|
save_caller_context (argv);
|
|
|
|
|
|
|
|
OPENLOG ("su");
|
|
|
|
|
|
|
|
process_flags (argc, argv);
|
|
|
|
|
|
|
|
initenv ();
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2011-06-13 23:56:47 +05:30
|
|
|
#ifdef USE_PAM
|
2007-10-07 17:14:59 +05:30
|
|
|
ret = pam_start ("su", name, &conv, &pamh);
|
2008-06-11 03:37:51 +05:30
|
|
|
if (PAM_SUCCESS != ret) {
|
2007-10-07 17:14:59 +05:30
|
|
|
SYSLOG ((LOG_ERR, "pam_start: error %d", ret);
|
2011-06-13 23:57:34 +05:30
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: pam_start: error %d\n"),
|
|
|
|
Prog, ret));
|
2007-10-07 17:14:59 +05:30
|
|
|
exit (1);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
|
2023-02-01 18:20:48 +05:30
|
|
|
ret = pam_set_item (pamh, PAM_TTY, caller_tty);
|
2008-06-11 03:37:51 +05:30
|
|
|
if (PAM_SUCCESS == ret) {
|
2023-02-01 18:20:48 +05:30
|
|
|
ret = pam_set_item (pamh, PAM_RUSER, caller_name);
|
2008-06-11 03:37:51 +05:30
|
|
|
}
|
|
|
|
if (PAM_SUCCESS != ret) {
|
2007-10-07 17:14:59 +05:30
|
|
|
SYSLOG ((LOG_ERR, "pam_set_item: %s",
|
2011-06-13 23:57:34 +05:30
|
|
|
pam_strerror (pamh, ret)));
|
2007-10-07 17:17:01 +05:30
|
|
|
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
|
2007-10-07 17:14:59 +05:30
|
|
|
pam_end (pamh, ret);
|
|
|
|
exit (1);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2007-10-07 17:14:59 +05:30
|
|
|
#endif /* USE_PAM */
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2011-06-13 23:56:26 +05:30
|
|
|
pw = check_perms ();
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2023-02-16 17:24:14 +05:30
|
|
|
/* If the user does not want to change the environment,
|
2011-06-13 23:56:10 +05:30
|
|
|
* use the current SHELL.
|
|
|
|
* (unless another shell is required by the command line)
|
|
|
|
*/
|
|
|
|
if ((NULL == shellstr) && !change_environment) {
|
|
|
|
shellstr = getenv ("SHELL");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If su is not called by root, and the target user has a
|
|
|
|
* restricted shell, the environment must be changed and the shell
|
|
|
|
* must be the one specified in /etc/passwd.
|
|
|
|
*/
|
2011-06-13 23:56:47 +05:30
|
|
|
if ( !caller_is_root
|
2011-06-13 23:56:26 +05:30
|
|
|
&& restricted_shell (pw->pw_shell)) {
|
2011-06-13 23:56:10 +05:30
|
|
|
shellstr = NULL;
|
|
|
|
change_environment = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the shell is not set at this time, use the shell specified
|
|
|
|
* in /etc/passwd.
|
2011-06-13 23:55:57 +05:30
|
|
|
*/
|
2011-06-13 23:56:10 +05:30
|
|
|
if (NULL == shellstr) {
|
2011-08-15 14:55:58 +05:30
|
|
|
shellstr = pw->pw_shell;
|
2011-06-13 23:56:10 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the default shell.
|
|
|
|
*/
|
|
|
|
if ((NULL == shellstr) || ('\0' == shellstr[0])) {
|
|
|
|
shellstr = SHELL;
|
|
|
|
}
|
2011-06-13 23:55:57 +05:30
|
|
|
|
2011-06-13 23:57:12 +05:30
|
|
|
sulog (caller_tty, true, caller_name, name); /* save SU information */
|
|
|
|
if (getdef_bool ("SYSLOG_SU_ENAB")) {
|
|
|
|
SYSLOG ((LOG_INFO, "+ %s %s:%s", caller_tty,
|
|
|
|
('\0' != caller_name[0]) ? caller_name : "???",
|
|
|
|
('\0' != name[0]) ? name : "???"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_PAM
|
|
|
|
/* set primary group id and supplementary groups */
|
|
|
|
if (setup_groups (pw) != 0) {
|
|
|
|
pam_end (pamh, PAM_ABORT);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pam_setcred() may do things like resource limits, console groups,
|
|
|
|
* and much more, depending on the configured modules
|
|
|
|
*/
|
|
|
|
ret = pam_setcred (pamh, PAM_ESTABLISH_CRED);
|
|
|
|
if (PAM_SUCCESS != ret) {
|
|
|
|
SYSLOG ((LOG_ERR, "pam_setcred: %s", pam_strerror (pamh, ret)));
|
|
|
|
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
|
|
|
|
(void) pam_end (pamh, ret);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pam_open_session (pamh, 0);
|
|
|
|
if (PAM_SUCCESS != ret) {
|
|
|
|
SYSLOG ((LOG_ERR, "pam_open_session: %s",
|
2011-06-13 23:57:23 +05:30
|
|
|
pam_strerror (pamh, ret)));
|
2011-06-13 23:57:12 +05:30
|
|
|
fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
|
|
|
|
pam_setcred (pamh, PAM_DELETE_CRED);
|
|
|
|
(void) pam_end (pamh, ret);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
2011-06-13 23:57:34 +05:30
|
|
|
prepare_pam_close_session ();
|
|
|
|
|
2011-06-13 23:57:12 +05:30
|
|
|
/* become the new user */
|
|
|
|
if (change_uid (pw) != 0) {
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
#else /* !USE_PAM */
|
|
|
|
/* no limits if su from root (unless su must fake login's behavior) */
|
|
|
|
if (!caller_is_root || fakelogin) {
|
|
|
|
setup_limits (pw);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setup_uid_gid (pw, caller_on_console) != 0) {
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
#endif /* !USE_PAM */
|
|
|
|
|
2016-12-13 22:14:19 +05:30
|
|
|
#ifdef WITH_AUDIT
|
|
|
|
audit_fd = audit_open ();
|
|
|
|
audit_log_acct_message (audit_fd,
|
|
|
|
AUDIT_USER_ROLE_CHANGE,
|
|
|
|
NULL, /* Prog. name */
|
|
|
|
"su",
|
|
|
|
('\0' != caller_name[0]) ? caller_name : "???",
|
|
|
|
AUDIT_NO_ID,
|
|
|
|
"localhost",
|
|
|
|
NULL, /* addr */
|
|
|
|
caller_tty,
|
|
|
|
1); /* result */
|
|
|
|
close (audit_fd);
|
|
|
|
#endif /* WITH_AUDIT */
|
|
|
|
|
2011-06-13 23:57:28 +05:30
|
|
|
set_environment (pw);
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2020-08-29 01:46:04 +05:30
|
|
|
if (!doshell) {
|
2011-06-13 23:57:40 +05:30
|
|
|
/* There is no need for a controlling terminal.
|
|
|
|
* This avoids the callee to inject commands on
|
|
|
|
* the caller's tty. */
|
|
|
|
int err = -1;
|
|
|
|
|
|
|
|
#ifdef USE_PAM
|
|
|
|
/* When PAM is used, we are on the child */
|
|
|
|
err = setsid ();
|
|
|
|
#else
|
|
|
|
/* Otherwise, we cannot use setsid */
|
|
|
|
int fd = open ("/dev/tty", O_RDWR);
|
|
|
|
|
|
|
|
if (fd >= 0) {
|
2023-02-01 07:20:14 +05:30
|
|
|
err = ioctl (fd, TIOCNOTTY, (char *) NULL);
|
2011-06-13 23:57:40 +05:30
|
|
|
(void) close (fd);
|
2012-05-20 21:45:14 +05:30
|
|
|
} else if (ENXIO == errno) {
|
|
|
|
/* There are no controlling terminal already */
|
|
|
|
err = 0;
|
2011-06-13 23:57:40 +05:30
|
|
|
}
|
|
|
|
#endif /* USE_PAM */
|
|
|
|
|
|
|
|
if (-1 == err) {
|
|
|
|
(void) fprintf (stderr,
|
|
|
|
_("%s: Cannot drop the controlling terminal\n"),
|
|
|
|
Prog);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-08 18:33:36 +05:30
|
|
|
#ifdef USE_PAM
|
|
|
|
(void) pam_end (pamh, PAM_SUCCESS | PAM_DATA_SILENT);
|
|
|
|
#endif
|
2011-06-13 23:57:40 +05:30
|
|
|
|
2011-06-13 23:57:17 +05:30
|
|
|
endpwent ();
|
|
|
|
endspent ();
|
2007-10-07 17:14:02 +05:30
|
|
|
/*
|
|
|
|
* This is a workaround for Linux libc bug/feature (?) - the
|
2007-10-07 17:14:59 +05:30
|
|
|
* /dev/log file descriptor is open without the close-on-exec flag
|
|
|
|
* and used to be passed to the new shell. There is "fcntl(LogFile,
|
|
|
|
* F_SETFD, 1)" in libc/misc/syslog.c, but it is commented out (at
|
|
|
|
* least in 5.4.33). Why? --marekm
|
2007-10-07 17:14:02 +05:30
|
|
|
*/
|
2007-10-07 17:14:59 +05:30
|
|
|
closelog ();
|
2007-10-07 17:14:02 +05:30
|
|
|
|
|
|
|
/*
|
2007-10-07 17:14:59 +05:30
|
|
|
* See if the user has extra arguments on the command line. In that
|
|
|
|
* case they will be provided to the new user's shell as arguments.
|
2007-10-07 17:14:02 +05:30
|
|
|
*/
|
|
|
|
if (fakelogin) {
|
|
|
|
char *arg0;
|
|
|
|
|
2007-10-07 17:14:59 +05:30
|
|
|
cp = getdef_str ("SU_NAME");
|
2008-06-11 03:37:51 +05:30
|
|
|
if (NULL == cp) {
|
2007-10-07 17:17:11 +05:30
|
|
|
cp = Basename (shellstr);
|
2008-06-11 03:37:51 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2023-02-05 03:11:18 +05:30
|
|
|
arg0 = XMALLOCARRAY (strlen (cp) + 2, char);
|
2007-10-07 17:14:02 +05:30
|
|
|
arg0[0] = '-';
|
2007-10-07 17:14:59 +05:30
|
|
|
strcpy (arg0 + 1, cp);
|
2007-10-07 17:14:02 +05:30
|
|
|
cp = arg0;
|
2008-06-11 03:37:51 +05:30
|
|
|
} else {
|
2007-10-07 17:17:11 +05:30
|
|
|
cp = Basename (shellstr);
|
2008-06-11 03:37:51 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2020-08-29 01:46:04 +05:30
|
|
|
if (!doshell) {
|
2011-06-13 23:57:40 +05:30
|
|
|
int err;
|
2007-10-07 17:17:11 +05:30
|
|
|
/* Position argv to the remaining arguments */
|
2020-08-29 01:46:11 +05:30
|
|
|
argv += optind;
|
2008-06-11 03:37:51 +05:30
|
|
|
if (NULL != command) {
|
2007-10-07 17:17:22 +05:30
|
|
|
argv -= 2;
|
|
|
|
argv[0] = "-c";
|
|
|
|
argv[1] = command;
|
|
|
|
}
|
2007-10-07 17:14:14 +05:30
|
|
|
/*
|
2007-10-07 17:17:11 +05:30
|
|
|
* Use the shell and create an argv
|
2007-10-07 17:14:59 +05:30
|
|
|
* with the rest of the command line included.
|
2007-10-07 17:14:14 +05:30
|
|
|
*/
|
2010-03-31 03:02:36 +05:30
|
|
|
argv[-1] = cp;
|
2020-08-29 01:45:56 +05:30
|
|
|
execve_shell (shellstr, &argv[-1], environ);
|
|
|
|
err = errno;
|
|
|
|
(void) fprintf (stderr,
|
|
|
|
_("Cannot execute %s\n"), shellstr);
|
2011-06-13 23:57:40 +05:30
|
|
|
errno = err;
|
|
|
|
} else {
|
|
|
|
(void) shell (shellstr, cp, environ);
|
2007-10-07 17:14:14 +05:30
|
|
|
}
|
2011-06-13 23:57:40 +05:30
|
|
|
|
2011-08-15 14:55:58 +05:30
|
|
|
pw_free (pw);
|
|
|
|
|
2011-06-13 23:57:40 +05:30
|
|
|
return (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2008-06-11 03:37:51 +05:30
|
|
|
|