10f66f451Sopenharmony_ci/* chsh.c - Change login shell.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2021 Michael Christensen
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/chsh.html
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_CHSH(NEWTOY(chsh, "s:", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig CHSH
100f66f451Sopenharmony_ci  bool "chsh"
110f66f451Sopenharmony_ci  default n
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: chsh [-s SHELL] [USER]
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Change user's login shell.
160f66f451Sopenharmony_ci
170f66f451Sopenharmony_ci    -s	Use SHELL instead of prompting
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci    Non-root users can only change their own shell to one listed in /etc/shells.
200f66f451Sopenharmony_ci*/
210f66f451Sopenharmony_ci
220f66f451Sopenharmony_ci#define FOR_chsh
230f66f451Sopenharmony_ci#include "toys.h"
240f66f451Sopenharmony_ci
250f66f451Sopenharmony_ciGLOBALS(
260f66f451Sopenharmony_ci  char *s;
270f66f451Sopenharmony_ci)
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_civoid chsh_main()
300f66f451Sopenharmony_ci{
310f66f451Sopenharmony_ci  FILE *file;
320f66f451Sopenharmony_ci  char *user, *line, *shell, *encrypted;
330f66f451Sopenharmony_ci  struct passwd *passwd_info;
340f66f451Sopenharmony_ci  struct spwd *shadow_info;
350f66f451Sopenharmony_ci
360f66f451Sopenharmony_ci  // Get uid user information, may be discarded later
370f66f451Sopenharmony_ci
380f66f451Sopenharmony_ci  if ((user = *toys.optargs)) {
390f66f451Sopenharmony_ci    passwd_info = xgetpwnam(user);
400f66f451Sopenharmony_ci    if (geteuid() && strcmp(passwd_info->pw_name, user))
410f66f451Sopenharmony_ci      error_exit("Permission denied\n");
420f66f451Sopenharmony_ci  } else {
430f66f451Sopenharmony_ci    passwd_info = xgetpwuid(getuid());
440f66f451Sopenharmony_ci    user = passwd_info->pw_name;
450f66f451Sopenharmony_ci  }
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_ci  // Get a password, encrypt it, wipe it, and check it
480f66f451Sopenharmony_ci  if (mlock(toybuf, sizeof(toybuf))) perror_exit("mlock");
490f66f451Sopenharmony_ci  if (!(shadow_info = getspnam(passwd_info->pw_name))) perror_exit("getspnam");
500f66f451Sopenharmony_ci  if (read_password(toybuf, sizeof(toybuf), "Password: ")) perror_exit("woaj"); //xexit();
510f66f451Sopenharmony_ci  if (!(encrypted = crypt(toybuf, shadow_info->sp_pwdp))) perror_exit("crypt");
520f66f451Sopenharmony_ci  memset(toybuf, 0, sizeof(toybuf));
530f66f451Sopenharmony_ci  munlock(toybuf, sizeof(toybuf)); // prevents memset from "optimizing" away.
540f66f451Sopenharmony_ci  if (strcmp(encrypted, shadow_info->sp_pwdp)) perror_exit("Bad password");
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci  // Get new shell (either -s or interactive)
570f66f451Sopenharmony_ci  file = xfopen("/etc/shells", "r");
580f66f451Sopenharmony_ci  if (toys.optflags) shell = TT.s;
590f66f451Sopenharmony_ci  else {
600f66f451Sopenharmony_ci    xprintf("Changing the login shell for %s\n"
610f66f451Sopenharmony_ci            "Enter the new value, or press ENTER for default\n"
620f66f451Sopenharmony_ci            "    Login shell [%s]: ", user, passwd_info->pw_shell);
630f66f451Sopenharmony_ci    if (!(shell = xgetline(stdin))) xexit();
640f66f451Sopenharmony_ci  }
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_ci  // Verify supplied shell in /etc/shells, or get default shell
670f66f451Sopenharmony_ci  if (strlen(shell))
680f66f451Sopenharmony_ci    while ((line = xgetline(file)) && strcmp(shell, line)) free(line);
690f66f451Sopenharmony_ci  else do line = xgetline(file); while (line && *line != '/');
700f66f451Sopenharmony_ci  if (!line) error_exit("Shell not found in '/etc/shells'");
710f66f451Sopenharmony_ci
720f66f451Sopenharmony_ci  // Update /etc/passwd
730f66f451Sopenharmony_ci  passwd_info->pw_shell = line;
740f66f451Sopenharmony_ci  if (-1 == update_password("/etc/passwd", user, NULL)) perror_exit("Failed to remove passwd entry");
750f66f451Sopenharmony_ci  file = xfopen("/etc/passwd", "a");
760f66f451Sopenharmony_ci  if (putpwent(passwd_info, file)) perror_exit("putwent");
770f66f451Sopenharmony_ci}
78