chsh.c 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  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. USE_CHSH(NEWTOY(chsh, "s:", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
  7. config CHSH
  8. bool "chsh"
  9. default n
  10. help
  11. usage: chsh [-s SHELL] [USER]
  12. Change user's login shell.
  13. -s Use SHELL instead of prompting
  14. Non-root users can only change their own shell to one listed in /etc/shells.
  15. */
  16. #define FOR_chsh
  17. #include "toys.h"
  18. GLOBALS(
  19. char *s;
  20. )
  21. void chsh_main()
  22. {
  23. FILE *file;
  24. char *user, *line, *shell, *encrypted;
  25. struct passwd *passwd_info;
  26. struct spwd *shadow_info;
  27. // Get uid user information, may be discarded later
  28. if ((user = *toys.optargs)) {
  29. passwd_info = xgetpwnam(user);
  30. if (geteuid() && strcmp(passwd_info->pw_name, user))
  31. error_exit("Permission denied\n");
  32. } else {
  33. passwd_info = xgetpwuid(getuid());
  34. user = passwd_info->pw_name;
  35. }
  36. // Get a password, encrypt it, wipe it, and check it
  37. if (mlock(toybuf, sizeof(toybuf))) perror_exit("mlock");
  38. if (!(shadow_info = getspnam(passwd_info->pw_name))) perror_exit("getspnam");
  39. if (read_password(toybuf, sizeof(toybuf), "Password: ")) perror_exit("woaj"); //xexit();
  40. if (!(encrypted = crypt(toybuf, shadow_info->sp_pwdp))) perror_exit("crypt");
  41. memset(toybuf, 0, sizeof(toybuf));
  42. munlock(toybuf, sizeof(toybuf)); // prevents memset from "optimizing" away.
  43. if (strcmp(encrypted, shadow_info->sp_pwdp)) perror_exit("Bad password");
  44. // Get new shell (either -s or interactive)
  45. file = xfopen("/etc/shells", "r");
  46. if (toys.optflags) shell = TT.s;
  47. else {
  48. xprintf("Changing the login shell for %s\n"
  49. "Enter the new value, or press ENTER for default\n"
  50. " Login shell [%s]: ", user, passwd_info->pw_shell);
  51. if (!(shell = xgetline(stdin))) xexit();
  52. }
  53. // Verify supplied shell in /etc/shells, or get default shell
  54. if (strlen(shell))
  55. while ((line = xgetline(file)) && strcmp(shell, line)) free(line);
  56. else do line = xgetline(file); while (line && *line != '/');
  57. if (!line) error_exit("Shell not found in '/etc/shells'");
  58. // Update /etc/passwd
  59. passwd_info->pw_shell = line;
  60. if (-1 == update_password("/etc/passwd", user, NULL)) perror_exit("Failed to remove passwd entry");
  61. file = xfopen("/etc/passwd", "a");
  62. if (putpwent(passwd_info, file)) perror_exit("putwent");
  63. }