toybox/toys/pending/chsh.c
<<
>>
Prefs
   1/* chsh.c - Change login shell.
   2 *
   3 * Copyright 2021 Michael Christensen
   4 *
   5 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/chsh.html
   6
   7USE_CHSH(NEWTOY(chsh, ">1R:s:a", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
   8
   9config CHSH
  10  bool "chsh"
  11  default n
  12  help
  13    usage: chsh [-s SHELL] [-R CHROOT_DIR] [USER]
  14
  15    Change user's login shell.
  16
  17    -s  Use SHELL instead of prompting
  18    -R  Act on CHROOT_DIR instead of host
  19
  20    Non-root users can only change their own shell to one listed in /etc/shells.
  21*/
  22
  23#define FOR_chsh
  24#include "toys.h"
  25
  26GLOBALS(
  27  char *s, *R;
  28)
  29
  30void chsh_main()
  31{
  32  FILE *file;
  33  char *user, *line, *shell, *encrypted;
  34  struct passwd *passwd_info;
  35  struct spwd *shadow_info;
  36
  37  // Get uid user information, may be discarded later
  38
  39  if ((user = *toys.optargs)) {
  40    if (strcmp((passwd_info = xgetpwnam(user))->pw_name, user))
  41      if (geteuid()) errno = EPERM, error_exit(0);
  42  } else user = (passwd_info = xgetpwuid(getuid()))->pw_name;
  43
  44  // Get a password, encrypt it, wipe it, and check it
  45  if (mlock(toybuf, sizeof(toybuf))) perror_exit("mlock");
  46  if (!(shadow_info = getspnam(passwd_info->pw_name))) perror_exit("getspnam");
  47  if (read_password(toybuf, sizeof(toybuf), "Password: ")) *toybuf = 0;
  48  if (!(encrypted = crypt(toybuf, shadow_info->sp_pwdp))) perror_exit("crypt");
  49  memset(toybuf, 0, sizeof(toybuf));
  50  munlock(toybuf, sizeof(toybuf)); // prevents memset from "optimizing" away.
  51  if (strcmp(encrypted, shadow_info->sp_pwdp)) perror_exit("Bad password");
  52
  53  // Get new shell (either -s or interactive)
  54  file = xfopen("/etc/shells", "r");
  55  if (toys.optflags) shell = TT.s;
  56  else {
  57    xprintf("Login shell for %s [%s]:", user, passwd_info->pw_shell);
  58    if (!(shell = xgetline(stdin))) xexit();
  59    if (!*shell) xexit();
  60  }
  61
  62  // Verify supplied shell in /etc/shells, or get default shell
  63  if (*shell) while ((line = xgetline(file)) && strcmp(shell, line)) free(line);
  64  else do line = xgetline(file); while (line && *line != '/');
  65  if (!line) error_exit("Shell not found in '/etc/shells'");
  66
  67  // Update /etc/passwd
  68  if (!update_password("/etc/passwd", user, line,6)) perror_exit("/etc/passwd");
  69}
  70