xref: /aosp_15_r20/external/toybox/toys/pending/chsh.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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 
7 USE_CHSH(NEWTOY(chsh, ">1R:s:a", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
8 
9 config 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 
26 GLOBALS(
27   char *s, *R;
28 )
29 
chsh_main()30 void 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