login.c (4812B)
- // SPDX-FileCopyrightText: 2022 Haelwenn (lanodan) Monnier <contact+skeud@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- // for explicit_bzero, initgroups
- #define _DEFAULT_SOURCE
- #ifdef __linux__
- // I love linux extensions (no)
- #include <shadow.h> /* getspnam */
- #endif
- #include "common.h" // skeud_getpass, skeud_crypt_check
- #include <assert.h> // assert
- #include <errno.h> // errno
- #include <grp.h> // getgrnam, initgroups
- #include <pwd.h> // getpwnam
- #include <stdbool.h> // bool
- #include <stdio.h> // fprintf, perror
- #include <stdlib.h> // abort, setenv
- #include <string.h> // strcmp, explicit_bzero
- #include <sys/stat.h> // fchmod
- #include <unistd.h> // getuid, getopt, opt*, chdir, setuid, setgid, fchown
- #define TTY_GROUP "tty"
- #define TTY_PERMS 0600
- extern char **environ;
- char *envclear[] = {NULL};
- int
- main(int argc, char *argv[])
- {
- bool opt_f = false;
- bool opt_p = false;
- int c = EOF;
- char *username = NULL;
- struct passwd *pwent = NULL;
- char *shell = "/bin/sh";
- if(getuid() != 0)
- {
- fprintf(stderr, "login: Not super-user\n");
- return 1;
- }
- /* flawfinder: ignore CWE-120, CWE-20 */
- while((c = getopt(argc, argv, ":f:p")) != EOF)
- {
- switch(c)
- {
- case 'f': // user already authenticated
- opt_f = true;
- username = optarg;
- break;
- case 'p': // preserve environment
- opt_p = true;
- break;
- case ':':
- fprintf(stderr, "login: Option -%c requires an operand\n", optopt);
- return 1;
- case '?':
- fprintf(stderr, "login: Unrecognized option: '-%c'\n", optopt);
- return 1;
- default:
- fprintf(stderr, "login: Unknown getopt state, aborting\n");
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(!opt_f)
- {
- if(argc == 1)
- {
- username = argv[0];
- }
- else if(argc > 1)
- {
- fprintf(stderr, "login: Too many arguments given.\n");
- return 1;
- }
- }
- else
- {
- if(argc > 0)
- {
- fprintf(stderr, "login: Too many arguments given.\n");
- return 1;
- }
- }
- if(username == NULL)
- {
- size_t len = 0;
- printf("username: ");
- ssize_t got = getline(&username, &len, stdin);
- if(got < 0)
- {
- if(errno != 0) perror("login: getline");
- free(username);
- return 1;
- }
- username[got] = 0;
- username[got - 1] = 0;
- }
- assert(username != NULL);
- errno = 0;
- pwent = getpwnam(username);
- if(errno != 0)
- {
- perror("login: getpwnam");
- }
- if(!opt_f)
- {
- char *pw_hash = NULL;
- if(pwent && pwent->pw_passwd)
- {
- pw_hash = pwent->pw_passwd;
- }
- #ifdef __linux__
- // Always fetched to avoid potentially leaking passwd contents
- errno = 0;
- struct spwd *swent = getspnam(username);
- if(errno != 0)
- {
- perror("login: getspnam");
- }
- else
- {
- if(pw_hash && strcmp(pw_hash, "x") == 0)
- {
- pw_hash = swent->sp_pwdp;
- }
- explicit_bzero(swent, sizeof(swent));
- swent = NULL;
- }
- #endif /* __linux__ */
- char *password = NULL;
- ssize_t got = skeud_getpass(&password);
- if(got < 0)
- {
- free(username);
- free(password);
- return 1;
- }
- bool valid_p = skeud_crypt_check(pw_hash, password);
- explicit_bzero(password, got);
- free(password);
- if(pw_hash) explicit_bzero(pw_hash, sizeof(pw_hash));
- if(!valid_p)
- {
- free(username);
- sleep(2);
- fprintf(stderr, "login: Invalid username or password\n");
- return 1;
- }
- }
- if(!opt_p)
- {
- char *term = getenv("TERM");
- environ = envclear;
- if(term)
- {
- setenv("TERM", term, 1);
- }
- }
- if(pwent != NULL)
- {
- int tty_gid = pwent->pw_gid;
- struct group *tty_group = getgrnam(TTY_GROUP);
- if(tty_group == NULL)
- {
- perror("login: getgrnam");
- }
- else
- {
- tty_gid = tty_group->gr_gid;
- }
- /* considers that STDIN_FILENO is close enough to the current tty */
- if(fchown(STDIN_FILENO, pwent->pw_uid, tty_gid) < 0)
- {
- perror("login: fchown");
- }
- if(fchmod(STDIN_FILENO, TTY_PERMS))
- {
- perror("login: fchmod");
- }
- if(setgid(pwent->pw_gid) < 0)
- {
- perror("login: setgid");
- }
- if(initgroups(pwent->pw_name, pwent->pw_gid) < 0)
- {
- perror("login: initgroups");
- }
- if(setuid(pwent->pw_uid) < 0)
- {
- perror("login: setuid");
- }
- if(pwent->pw_shell != NULL)
- {
- shell = pwent->pw_shell;
- }
- if(pwent->pw_dir != NULL)
- {
- setenv("HOME", pwent->pw_dir, 1);
- if(chdir(pwent->pw_dir) != 0)
- {
- perror("login: chdir");
- }
- }
- explicit_bzero(pwent, sizeof(pwent));
- pwent = NULL;
- }
- setenv("USER", username, 1);
- setenv("LOGNAME", username, 1);
- setenv("SHELL", shell, 1);
- setenv("IFS", " \t\n", 1);
- free(username);
- errno = 0;
- /* flawfinder: ignore CWE-78 */
- if(execl(shell, shell, "-l", NULL) < 0)
- {
- if(errno == ENOENT)
- {
- perror("login: execve");
- return 127;
- }
- else
- {
- perror("login: execve");
- return 126;
- }
- }
- }