📄 su.c
字号:
/* su for GNU. Run a shell with substitute user and group IDs. Copyright (C) 92, 93, 94, 95, 1996 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 along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* Run a shell with the real and effective UID and GID and groups of USER, default `root'. The shell run is taken from USER's password entry, /bin/sh if none is specified there. If the account has a password, su prompts for a password unless run by a user with real UID 0. Does not change the current directory. Sets `HOME' and `SHELL' from the password entry for USER, and if USER is not root, sets `USER' and `LOGNAME' to USER. The subshell is not a login shell. If one or more ARGs are given, they are passed as additional arguments to the subshell. Does not handle /bin/sh or other shells specially (setting argv[0] to "-su", passing -c only to certain shells, etc.). I don't see the point in doing that, and it's ugly. This program intentionally does not support a "wheel group" that restricts who can su to UID 0 accounts. RMS considers that to be fascist. Options: -, -l, --login Make the subshell a login shell. Unset all environment variables except TERM, HOME and SHELL (set as above), and USER and LOGNAME (set unconditionally as above), and set PATH to a default value. Change to USER's home directory. Prepend "-" to the shell's name. -c, --commmand=COMMAND Pass COMMAND to the subshell with a -c option instead of starting an interactive shell. -f, --fast Pass the -f option to the subshell. -m, -p, --preserve-environment Do not change HOME, USER, LOGNAME, SHELL. Run $SHELL instead of USER's shell from /etc/passwd unless not the superuser and USER's shell is restricted. Overridden by --login and --shell. -s, --shell=shell Run SHELL instead of USER's shell from /etc/passwd unless not the superuser and USER's shell is restricted. Compile-time options: -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog. -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog. -DSYSLOG_NON_ROOT Log all su's, not just those to root (UID 0). Never logs attempted su's to nonexistent accounts. Written by David MacKenzie <djm@gnu.ai.mit.edu>. */#include <config.h>#include <stdio.h>#include <getopt.h>#include <sys/types.h>#include <pwd.h>#include <grp.h>#include "system.h"#if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG)#include <syslog.h>#else /* !HAVE_SYSLOG_H */#ifdef SYSLOG_SUCCESS#undef SYSLOG_SUCCESS#endif#ifdef SYSLOG_FAILURE#undef SYSLOG_FAILURE#endif#ifdef SYSLOG_NON_ROOT#undef SYSLOG_NON_ROOT#endif#endif /* !HAVE_SYSLOG_H */#ifdef _POSIX_VERSION#include <limits.h>#else /* not _POSIX_VERSION */struct passwd *getpwuid ();struct group *getgrgid ();uid_t getuid ();#include <sys/param.h>#endif /* not _POSIX_VERSION */#ifndef HAVE_ENDGRENT# define endgrent() ((void) 0)/* [<][>][^][v][top][bottom][index][help] */#endif#ifndef HAVE_ENDPWENT# define endpwent() ((void) 0)/* [<][>][^][v][top][bottom][index][help] */#endif#ifdef HAVE_SHADOW_H#include <shadow.h>#endif#include "error.h"#ifdef HAVE_PATHS_H#include <paths.h>#endif/* The default PATH for simulated logins to non-superuser accounts. */#ifdef _PATH_DEFPATH#define DEFAULT_LOGIN_PATH _PATH_DEFPATH#else#define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"#endif/* The default PATH for simulated logins to superuser accounts. */#ifdef _PATH_DEFPATH_ROOT#define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT#else#define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"#endif/* The shell to run if none is given in the user's passwd entry. */#define DEFAULT_SHELL "/bin/sh"/* The user to become if none is specified. */#define DEFAULT_USER "root"char *crypt ();char *getpass ();char *getusershell ();void endusershell ();void setusershell ();char *basename ();char *xmalloc ();char *xrealloc ();char *xstrdup ();extern char **environ;/* The name this program was run with. */char *program_name;/* If nonzero, display usage information and exit. */static int show_help;/* If nonzero, print the version on standard output and exit. */static int show_version;/* If nonzero, pass the `-f' option to the subshell. */static int fast_startup;/* If nonzero, simulate a login instead of just starting a shell. */static int simulate_login;/* If nonzero, change some environment vars to indicate the user su'd to. */static int change_environment;static struct option const longopts[] ={ {"command", required_argument, 0, 'c'}, {"fast", no_argument, &fast_startup, 1}, {"help", no_argument, &show_help, 1}, {"login", no_argument, &simulate_login, 1}, {"preserve-environment", no_argument, &change_environment, 0}, {"shell", required_argument, 0, 's'}, {"version", no_argument, &show_version, 1}, {0, 0, 0, 0}};/* Add VAL to the environment, checking for out of memory errors. */static voidxputenv (const char *val)/* [<][>][^][v][top][bottom][index][help] */{ if (putenv (val)) error (1, 0, _("virtual memory exhausted"));}/* Return a newly-allocated string whose contents concatenate those of S1, S2, S3. */static char *concat (const char *s1, const char *s2, const char *s3)/* [<][>][^][v][top][bottom][index][help] */{ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); char *result = (char *) xmalloc (len1 + len2 + len3 + 1); strcpy (result, s1); strcpy (result + len1, s2); strcpy (result + len1 + len2, s3); result[len1 + len2 + len3] = 0; return result;}/* Return the number of elements in ARR, a null-terminated array. */static intelements (char **arr)/* [<][>][^][v][top][bottom][index][help] */{ int n = 0; for (n = 0; *arr; ++arr) ++n; return n;}#if defined (SYSLOG_SUCCESS) || defined (SYSLOG_FAILURE)/* Log the fact that someone has run su to the user given by PW; if SUCCESSFUL is nonzero, they gave the correct password, etc. */static voidlog_su (const struct passwd *pw, int successful)/* [<][>][^][v][top][bottom][index][help] */{ const char *new_user, *old_user, *tty;#ifndef SYSLOG_NON_ROOT if (pw->pw_uid) return;#endif new_user = pw->pw_name; /* The utmp entry (via getlogin) is probably the best way to identify the user, especially if someone su's from a su-shell. */ old_user = getlogin (); if (old_user == NULL) old_user = ""; tty = ttyname (2); if (tty == NULL) tty = ""; /* 4.2BSD openlog doesn't have the third parameter. */ openlog (basename (program_name), 0#ifdef LOG_AUTH , LOG_AUTH#endif ); syslog (LOG_NOTICE,#ifdef SYSLOG_NON_ROOT "%s(to %s) %s on %s",#else "%s%s on %s",#endif successful ? "" : "FAILED SU ",#ifdef SYSLOG_NON_ROOT new_user,#endif old_user, tty); closelog ();}#endif/* Ask the user for a password. Return 1 if the user gives the correct password for entry PW, 0 if not. Return 1 without asking for a password if run by UID 0 or if PW has an empty password. */static intcorrect_password (const struct passwd *pw)/* [<][>][^][v][top][bottom][index][help] */{ char *unencrypted, *encrypted, *correct;#ifdef HAVE_SHADOW_H /* Shadow passwd stuff for SVR3 and maybe other systems. */ struct spwd *sp = getspnam (pw->pw_name); endspent (); if (sp) correct = sp->sp_pwdp; else#endif correct = pw->pw_passwd; if (getuid () == 0 || correct == 0 || correct[0] == '\0') return 1; unencrypted = getpass (_("Password:"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -