diff options
Diffstat (limited to 'toys/pending/chsh.c')
-rw-r--r-- | toys/pending/chsh.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/toys/pending/chsh.c b/toys/pending/chsh.c new file mode 100644 index 00000000..bf409783 --- /dev/null +++ b/toys/pending/chsh.c @@ -0,0 +1,77 @@ +/* chsh.c - Change login shell. + * + * Copyright 2021 Michael Christensen + * + * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/chsh.html + +USE_CHSH(NEWTOY(chsh, "s:", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) + +config CHSH + bool "chsh" + default y + help + usage: chsh [-s SHELL] [USER] + + Change user's login shell. + + -s Use SHELL instead of prompting + + Non-root users can only change their own shell to one listed in /etc/shells. +*/ + +#define FOR_chsh +#include "toys.h" + +GLOBALS( + char *s; +) + +void chsh_main() +{ + FILE *file; + char *user, *line, *shell, *encrypted; + struct passwd *passwd_info; + struct spwd *shadow_info; + + // Get uid user information, may be discarded later + + if ((user = *toys.optargs)) { + passwd_info = xgetpwnam(user); + if (geteuid() && strcmp(passwd_info->pw_name, user)) + error_exit("Permission denied\n"); + } else { + passwd_info = xgetpwuid(getuid()); + user = passwd_info->pw_name; + } + + // Get a password, encrypt it, wipe it, and check it + if (mlock(toybuf, sizeof(toybuf))) perror_exit("mlock"); + if (!(shadow_info = getspnam(passwd_info->pw_name))) perror_exit("getspnam"); + if (read_password(toybuf, sizeof(toybuf), "Password: ")) perror_exit("woaj"); //xexit(); + if (!(encrypted = crypt(toybuf, shadow_info->sp_pwdp))) perror_exit("crypt"); + memset(toybuf, 0, sizeof(toybuf)); + munlock(toybuf, sizeof(toybuf)); // prevents memset from "optimizing" away. + if (strcmp(encrypted, shadow_info->sp_pwdp)) perror_exit("Bad password"); + + // Get new shell (either -s or interactive) + file = xfopen("/etc/shells", "r"); + if (toys.optflags) shell = TT.s; + else { + xprintf("Changing the login shell for %s\n" + "Enter the new value, or press ENTER for default\n" + " Login shell [%s]: ", user, passwd_info->pw_shell); + if (!(shell = xgetline(stdin))) xexit(); + } + + // Verify supplied shell in /etc/shells, or get default shell + if (strlen(shell)) + while ((line = xgetline(file)) && strcmp(shell, line)) free(line); + else do line = xgetline(file); while (line && *line != '/'); + if (!line) error_exit("Shell not found in '/etc/shells'"); + + // Update /etc/passwd + passwd_info->pw_shell = line; + if (-1 == update_password("/etc/passwd", user, NULL)) perror_exit("Failed to remove passwd entry"); + file = xfopen("/etc/passwd", "a"); + if (putpwent(passwd_info, file)) perror_exit("putwent"); +} |