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