⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 su.c

📁 《linux应用开发技术详解》的配套代码
💻 C
📖 第 1 页 / 共 2 页
字号:
      environ = (char **) xmalloc (2 * sizeof (char *));
      environ[0] = 0;
      if (term)
	xputenv (concat ("TERM", "=", term));
      if (display)
	xputenv (concat ("DISPLAY", "=", display));
      if (xauthority)
	xputenv (concat ("XAUTHORITY", "=", xauthority));
      xputenv (concat ("HOME", "=", pw->pw_dir));
      xputenv (concat ("SHELL", "=", shell));
      xputenv (concat ("USER", "=", pw->pw_name));
      xputenv (concat ("LOGNAME", "=", pw->pw_name));
      xputenv (concat ("PATH", "=", (pw->pw_uid
				     ? DEFAULT_LOGIN_PATH
				     : DEFAULT_ROOT_LOGIN_PATH)));
    }
  else
    {
      /* Set HOME, SHELL, and if not becoming a super-user,
	 USER and LOGNAME.  */
      if (change_environment)
	{
	  xputenv (concat ("HOME", "=", pw->pw_dir));
	  xputenv (concat ("SHELL", "=", shell));
	  if (pw->pw_uid)
	    {
	      xputenv (concat ("USER", "=", pw->pw_name));
	      xputenv (concat ("LOGNAME", "=", pw->pw_name));
	    }
	}
    }
}

/* Become the user and group(s) specified by PW.  */

static void
change_identity (const struct passwd *pw)
{
#ifdef HAVE_INITGROUPS
  errno = 0;
  if (initgroups (pw->pw_name, pw->pw_gid) == -1)
    error (EXIT_FAILURE, errno, _("cannot set groups"));
  endgrent ();
#endif
#ifdef USE_PAM
  retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
  if (retval != PAM_SUCCESS)
    error (1, 0, pam_strerror(pamh, retval));
#endif /* USE_PAM */
  if (setgid (pw->pw_gid))
    error (EXIT_FAILURE, errno, _("cannot set group id"));
  if (setuid (pw->pw_uid))
    error (EXIT_FAILURE, errno, _("cannot set user id"));
}

#ifdef USE_PAM
static int caught=0;
/* Signal handler for parent process later */
static void su_catch_sig(int sig)
{
  ++caught;
}

int
pam_copyenv (pam_handle_t *pamh)
{
  char **env;

  env = pam_getenvlist(pamh);
  if(env) {
    while(*env) {
	xputenv(*env);
	env++;
    }
  }
  return(0);
}
#endif

/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
   If COMMAND is nonzero, pass it to the shell with the -c option.
   If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
   arguments.  */

static void
run_shell (const char *shell, const char *command, char **additional_args, const struct passwd *pw)
{
  const char **args;
  int argno = 1;
#ifdef USE_PAM
  int child;
  sigset_t ourset, oldset;
  struct sigaction oldaction;
  int status;

  retval = pam_open_session(pamh,0);
  if (retval != PAM_SUCCESS) {
    fprintf (stderr, "could not open session\n");
    exit (1);
  }

/* do this at the last possible moment, because environment variables may
   be passed even in the session phase
*/
  if(pam_copyenv(pamh) != PAM_SUCCESS)
     fprintf (stderr, "error copying PAM environment\n");
  
  /* parent only */
  sigfillset(&ourset);
  if (sigprocmask(SIG_BLOCK, &ourset, &oldset)) {
    fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME);
    caught = 1;
  }
  if (!caught) {
    struct sigaction action;
    action.sa_handler = su_catch_sig;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    sigemptyset(&ourset);
    if (sigaddset(&ourset, SIGTERM)
        || sigaddset(&ourset, SIGALRM)
        || sigaction(SIGTERM, &action, &oldaction)
        || sigprocmask(SIG_UNBLOCK, &ourset, NULL)) {
      fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME);
      caught = 1;
    }
  }
  child = fork();
  if (child == 0) {  /* child shell */
  sigprocmask(SIG_SETMASK, &oldset, NULL);
  if (!caught) sigaction (SIGTERM, &oldaction, NULL);
  change_identity (pw);
  pam_end(pamh, 0);
#endif

  if (additional_args)
    args = (const char **) xmalloc (sizeof (char *)
				    * (10 + elements (additional_args)));
  else
    args = (const char **) xmalloc (sizeof (char *) * 10);
  if (simulate_login)
    {
      char *arg0;
      char *shell_basename;

      if(chdir(pw->pw_dir))
	      error(0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);

      shell_basename = base_name (shell);
      arg0 = xmalloc (strlen (shell_basename) + 2);
      arg0[0] = '-';
      strcpy (arg0 + 1, shell_basename);
      args[0] = arg0;
    }
  else
    args[0] = base_name (shell);
  if (fast_startup)
    args[argno++] = "-f";
  if (command)
    {
      args[argno++] = "-c";
      args[argno++] = command;
    }
  if (additional_args)
    for (; *additional_args; ++additional_args)
      args[argno++] = *additional_args;
  args[argno] = NULL;
  execv (shell, (char **) args);

  {
    int exit_status = (errno == ENOENT ? 127 : 126);
    error (0, errno, "%s", shell);
    exit (exit_status);
  }
#ifdef USE_PAM
  } else if (child == -1) {
      fprintf(stderr, "can not fork user shell: %s", strerror(errno));
      exit(1);
  }
  if (!caught) {
    do {
      int pid;

      pid = waitpid(-1, &status, WUNTRACED);

      if (WIFSTOPPED(status)) {
          kill(getpid(), SIGSTOP);
          /* once we get here, we must have resumed */
          kill(pid, SIGCONT);
      }
    } while (WIFSTOPPED(status));
  }

  if (caught) {
    fprintf(stderr, "\nSession terminated, killing shell...");
    kill (child, SIGTERM);
  }
  retval = pam_close_session(pamh, 0);
  PAM_BAIL_P;
  retval = pam_end(pamh, PAM_SUCCESS);
  PAM_BAIL_P;
  if (caught) {
    sleep(2);
    kill(child, SIGKILL);
    fprintf(stderr, " ...killed.\n");
    exit(-1);
  }
  exit (WEXITSTATUS(status));
#endif /* USE_PAM */
}

/* Return 1 if SHELL is a restricted shell (one not returned by
   getusershell), else 0, meaning it is a standard shell.  */

static int
restricted_shell (const char *shell)
{
  char *line;

  setusershell ();
  while ((line = getusershell ()) != NULL)
    {
      if (*line != '#' && strcmp (line, shell) == 0)
	{
	  endusershell ();
	  return 0;
	}
    }
  endusershell ();
  return 1;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
	     program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... [-] [USER [ARG]...]\n"), program_name);
      fputs (_("\
Change the effective user id and group id to that of USER.\n\
\n\
  -, -l, --login               make the shell a login shell\n\
  -c, --commmand=COMMAND       pass a single COMMAND to the shell with -c\n\
  -f, --fast                   pass -f to the shell (for csh or tcsh)\n\
  -m, --preserve-environment   do not reset environment variables\n\
  -p                           same as -m\n\
  -s, --shell=SHELL            run SHELL if /etc/shells allows it\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
A mere - implies -l.   If USER not given, assume root.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
      close_stdout ();
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int optc;
  const char *new_user = DEFAULT_USER;
  char *command = 0;
  char **additional_args = 0;
  char *shell = 0;
  struct passwd *pw;
  struct passwd pw_copy;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  fast_startup = 0;
  simulate_login = 0;
  change_environment = 1;

  while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, NULL)) != -1)
    {
      switch (optc)
	{
	case 0:
	  break;

	case 'c':
	  command = optarg;
	  break;

	case 'f':
	  fast_startup = 1;
	  break;

	case 'l':
	  simulate_login = 1;
	  break;

	case 'm':
	case 'p':
	  change_environment = 0;
	  break;

	case 's':
	  shell = optarg;
	  break;

	case_GETOPT_HELP_CHAR;

	case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

	default:
	  usage (EXIT_FAILURE);
	}
    }

  if (optind < argc && !strcmp (argv[optind], "-"))
    {
      simulate_login = 1;
      ++optind;
    }
  if (optind < argc)
    new_user = argv[optind++];
  if (optind < argc)
    additional_args = argv + optind;

  pw = getpwnam (new_user);
  if (pw == 0)
    error (EXIT_FAILURE, 0, _("user %s does not exist"), new_user);
  endpwent ();

  /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
     is a username that is retrieved via NIS (YP), but that doesn't have
     a default shell listed.  */
  if (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
    pw->pw_shell = (char *) DEFAULT_SHELL;

  /* Make a copy of the password information and point pw at the local
     copy instead.  Otherwise, some systems (e.g. Linux) would clobber
     the static data through the getlogin call from log_su.  */
  pw_copy = *pw;
  pw = &pw_copy;
  pw->pw_name = xstrdup (pw->pw_name);
  pw->pw_dir = xstrdup (pw->pw_dir);
  pw->pw_shell = xstrdup (pw->pw_shell);

  if (!correct_password (pw))
    {
#ifdef SYSLOG_FAILURE
      log_su (pw, 0);
#endif
      error (EXIT_FAILURE, 0, _("incorrect password"));
    }
#ifdef SYSLOG_SUCCESS
  else
    {
      log_su (pw, 1);
    }
#endif

  if (shell == 0 && change_environment == 0)
    shell = getenv ("SHELL");
  if (shell != 0 && getuid () && restricted_shell (pw->pw_shell))
    {
      /* The user being su'd to has a nonstandard shell, and so is
	 probably a uucp account or has restricted access.  Don't
	 compromise the account by allowing access with a standard
	 shell.  */
      error (0, 0, _("using restricted shell %s"), pw->pw_shell);
      shell = 0;
    }
  if (shell == 0)
    {
      shell = xstrdup (pw->pw_shell);
    }
  modify_environment (pw, shell);


#ifdef USE_PAM
  setfsuid(pw->pw_uid);
  setfsgid(pw->pw_gid);
#else
  change_identity (pw);
#endif

  run_shell (shell, command, additional_args, pw);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -