diff options
Diffstat (limited to 'src/cerberus.c')
| -rw-r--r-- | src/cerberus.c | 694 |
1 files changed, 337 insertions, 357 deletions
diff --git a/src/cerberus.c b/src/cerberus.c index 0cd06b4..9fccbf9 100644 --- a/src/cerberus.c +++ b/src/cerberus.c @@ -1,7 +1,7 @@ /** * cerberus – Minimal login program * - * Copyright © 2013, 2014, 2015, 2016, 2020 Mattias Andrée (maandree@kth.se) + * Copyright © 2013, 2014, 2015, 2016, 2020 Mattias Andrée (m@maandree.se) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,7 +72,7 @@ static char skip_auth = 0; /** * The passphrase */ -char* passphrase = NULL; +char *passphrase = NULL; #endif @@ -81,59 +81,59 @@ char* passphrase = NULL; * * @param s The number of seconds to sleep */ -static void xsleep(unsigned int s) +static void +xsleep(unsigned int s) { - sigset_t sigset; - sigset_t old_sigset; - - sigfillset(&sigset); - sigprocmask(SIG_BLOCK, &sigset, &old_sigset); - - while ((s = sleep(s))); - - sigprocmask(SIG_SETMASK, &old_sigset, NULL); + sigset_t sigset; + sigset_t old_sigset; + + sigfillset(&sigset); + sigprocmask(SIG_BLOCK, &sigset, &old_sigset); + + while ((s = sleep(s))); + + sigprocmask(SIG_SETMASK, &old_sigset, NULL); } /** - * Mane method + * Main method * * @param argc The number of command line arguments * @param argv The command line arguments * @return Return code */ -int main(int argc, char** argv) +int +main(int argc, char **argv) { - char* tty_device = ttyname(STDIN_FILENO); - - do_login(argc, argv); - - /* Ignore signals */ - signal(SIGQUIT, SIG_IGN); - signal(SIGINT, SIG_IGN); - - /* Wait for the login shell and all grandchildren to exit */ - while ((wait(NULL) == -1) && (errno == EINTR)) - ; - - /* Regain access to the terminal */ - if (tty_device) - { - int fd = open(tty_device, O_RDWR | O_NONBLOCK); - if (fd != 0) dup2(fd, 0); - if (fd != 1) dup2(fd, 1); - if (fd != 2) dup2(fd, 2); - } - - /* Reset terminal ownership and mode */ - chown_tty(0, tty_group, 0); - - /* Close login session */ - close_login_session(); - - /* Run logout hook */ - exec_hook(HOOK_LOGOUT, argc, argv); - return 0; + char *tty_device = ttyname(STDIN_FILENO); + + do_login(argc, argv); + + /* Ignore signals */ + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + + /* Wait for the login shell and all grandchildren to exit */ + while (wait(NULL) == -1 && errno == EINTR); + + /* Regain access to the terminal */ + if (tty_device) { + int fd = open(tty_device, O_RDWR | O_NONBLOCK); + if (fd != 0) dup2(fd, 0); + if (fd != 1) dup2(fd, 1); + if (fd != 2) dup2(fd, 2); + } + + /* Reset terminal ownership and mode */ + chown_tty(0, tty_group, 0); + + /* Close login session */ + close_login_session(); + + /* Run logout hook */ + exec_hook(HOOK_LOGOUT, argc, argv); + return 0; } @@ -144,33 +144,32 @@ int main(int argc, char** argv) * @param argc The number of command line arguments * @param argv The command line arguments */ -void exec_hook(int hook, int argc, char** argv) +void +exec_hook(int hook, int argc, char **argv) { - static char cerberusrc[] = CERBERUSRC; - static char hooks[][7] = - { - [HOOK_LOGIN] = "login", - [HOOK_LOGOUT] = "logout", - [HOOK_DENIED] = "denied", - [HOOK_VERIFY] = "verify", - }; - char** args; - int i; - - args = malloc((size_t)(argc + 2) * sizeof(char*)); - if (args == NULL) - { - perror("malloc"); - return; - } - - args[0] = cerberusrc; - args[1] = hooks[hook]; - for (i = 1; i < argc; i++) - args[i + 1] = argv[i]; - args[argc + 1] = NULL; - - execv(CERBERUSRC, args); + static char cerberusrc[] = CERBERUSRC; + static char hooks[][7] = { + [HOOK_LOGIN] = "login", + [HOOK_LOGOUT] = "logout", + [HOOK_DENIED] = "denied", + [HOOK_VERIFY] = "verify", + }; + char **args; + int i; + + args = malloc((size_t)(argc + 2) * sizeof(char*)); + if (!args) { + perror("malloc"); + return; + } + + args[0] = cerberusrc; + args[1] = hooks[hook]; + for (i = 1; i < argc; i++) + args[i + 1] = argv[i]; + args[argc + 1] = NULL; + + execv(CERBERUSRC, args); } @@ -182,30 +181,28 @@ void exec_hook(int hook, int argc, char** argv) * @param argv The command line arguments * @return The exit value of the hook */ -int fork_exec_wait_hook(int hook, int argc, char** argv) +int +fork_exec_wait_hook(int hook, int argc, char **argv) { - pid_t pid, reaped; - int status; - pid = fork(); - if (pid == -1) - return -1; - if (pid == 0) - { - close(STDIN_FILENO); - exec_hook(hook, argc, argv); - _exit(138); - } - for (;;) - { - reaped = wait(&status); - if (reaped == -1) - { - perror("wait"); - return -1; + pid_t pid, reaped; + int status; + pid = fork(); + if (pid == -1) + return -1; + if (pid == 0) { + close(STDIN_FILENO); + exec_hook(hook, argc, argv); + _exit(138); + } + for (;;) { + reaped = wait(&status); + if (reaped == -1) { + perror("wait"); + return -1; + } + if (reaped == pid) + return status == 138 ? -1 : status; } - if (reaped == pid) - return status == 138 ? -1 : status; - } } @@ -215,246 +212,229 @@ int fork_exec_wait_hook(int hook, int argc, char** argv) * @param argc The number of command line arguments * @param argv The command line arguments */ -void do_login(int argc, char** argv) +void +do_login(int argc, char **argv) { - char* username = NULL; - char* hostname = NULL; - char preserve_env = 0; - int ret; - #ifdef USE_TTY_GROUP - struct group* group; - #endif - - - #if AUTH > 0 - /* Disable echoing */ - passphrase_disable_echo1(STDIN_FILENO /* Will be the terminal. */); - /* This should be done as early and quickly as possible so as little - as possible of the passphrase gets leaked to the output if the user - begins entering the passphrase directly after the username. */ - #endif - - - /* Set process group ID */ - setpgrp(); - - - /* Parse command line arguments */ - { - char double_dashed = 0; - char hostname_on_next = 0; - int i; - for (i = 1; i < argc; i++) - { - char *arg = *(argv + i); - char c; - - if (*arg == 0) - ; - else if ((*arg == '-') && (double_dashed == 0)) - while ((c = *(++arg))) - if (c == 'H') - ; - else if (c == 'V') - _exit(2); - else if (c == 'p') - preserve_env = 1; - else if (c == 'h') - { - if (*(arg + 1)) - hostname = arg + 1; - else - hostname_on_next = 1; - break; - } - else if (c == 'f') - { - if (*(arg + 1)) - username = arg + 1; - skip_auth = 1; - break; - } - else if (c == '-') - { - double_dashed = 1; - break; - } - else - printf("%s: unrecognised options: -%c\n", *argv, c); - else if (hostname_on_next) - { - hostname = arg; - hostname_on_next = 0; - } - else - username = arg; - } - } - - - /* Change that a username has been specified */ - if (username == NULL) - { - printf("%s: no username specified\n", *argv); - sleep(ERROR_SLEEP); - _exit(2); - } - - - /* Verify that the user may login */ - ret = fork_exec_wait_hook(HOOK_VERIFY, argc, argv); - if ((ret >= 0) && WIFEXITED(ret) && (WEXITSTATUS(ret) == 1)) - { - sleep(ERROR_SLEEP); - _exit(2); - } - - - if (skip_auth) - { - /* Reset terminal settings */ - passphrase_reenable_echo1(STDIN_FILENO); - - /* Only root may bypass authentication */ - if (getuid()) + char *username = NULL; + char *hostname = NULL; + char preserve_env = 0; + int ret; +#ifdef USE_TTY_GROUP + struct group *group; +#endif + + +#if AUTH > 0 + /* Disable echoing */ + passphrase_disable_echo1(STDIN_FILENO /* Will be the terminal. */); + /* This should be done as early and quickly as possible so as little + as possible of the passphrase gets leaked to the output if the user + begins entering the passphrase directly after the username. */ +#endif + + + /* Set process group ID */ + setpgrp(); + + + /* Parse command line arguments */ { - printf("%s: only root by use the -f option\n", *argv); - sleep(ERROR_SLEEP); - _exit(2); + char double_dashed = 0; + char hostname_on_next = 0; + int i; + for (i = 1; i < argc; i++) { + char *arg = *(argv + i); + char c; + + if (*arg == 0) { + continue; + } else if (*arg == '-' && !double_dashed) { + while ((c = *++arg)) + if (c == 'H') { + continue; + } else if (c == 'V') { + _exit(2); + } else if (c == 'p') { + preserve_env = 1; + } else if (c == 'h') { + if (arg[1]) + hostname = arg + 1; + else + hostname_on_next = 1; + break; + } else if (c == 'f') { + if (arg[1]) + username = arg + 1; + skip_auth = 1; + break; + } else if (c == '-') { + double_dashed = 1; + break; + } else { + printf("%s: unrecognised options: -%c\n", *argv, c); + } + } else if (hostname_on_next) { + hostname = arg; + hostname_on_next = 0; + } else { + username = arg; + } + } + } + + + /* Change that a username has been specified */ + if (!username) { + printf("%s: no username specified\n", *argv); + sleep(ERROR_SLEEP); + _exit(2); + } + + + /* Verify that the user may login */ + ret = fork_exec_wait_hook(HOOK_VERIFY, argc, argv); + if (ret >= 0 && WIFEXITED(ret) && WEXITSTATUS(ret) == 1) { + sleep(ERROR_SLEEP); + _exit(2); + } + + + if (skip_auth) { + /* Reset terminal settings */ + passphrase_reenable_echo1(STDIN_FILENO); + + /* Only root may bypass authentication */ + if (getuid()) { + printf("%s: only root by use the -f option\n", *argv); + sleep(ERROR_SLEEP); + _exit(2); + } + } else { + /* Print ant we want a passphrase, if -f has not been used */ + printf("Passphrase: "); + fflush(stdout); } - } - /* Print ant we want a passphrase, if -f has not been used */ - else - { - printf("Passphrase: "); - fflush(stdout); - } - /* Done early to make to program look like it is even faster than it is */ - - - /* Make sure nopony is spying */ - #ifdef USE_TTY_GROUP - if ((group = getgrnam(TTY_GROUP))) - tty_group = group->gr_gid; - endgrent(); - #endif - secure_tty(tty_group); - - #if AUTH > 0 - if (skip_auth == 0) - { - /* Redisable echoing */ - passphrase_disable_echo1(STDIN_FILENO); - } - #endif - - - /* Set up clean quiting and time out */ - signal(SIGALRM, timeout_quit); - signal(SIGQUIT, user_quit); - signal(SIGINT, user_quit); - siginterrupt(SIGALRM, 1); - siginterrupt(SIGQUIT, 1); - siginterrupt(SIGINT, 1); - #if AUTH > 0 - alarm(TIMEOUT_SECONDS); - #endif - - - /* Get user information */ - if ((entry = getpwnam(username)) == NULL) - { - if (errno == EIO /* seriously...? */ || !errno) - printf("User does not exist\n"); - else if (errno) - perror("getpwnam"); - sleep(ERROR_SLEEP); - _exit(1); - } - endpwent(); - username = entry->pw_name; - - - /* Verify passphrase or other token, if -f has not been used */ - ret = 2; - #if AUTH == 0 - (void) hostname; - #else - initialise_login(hostname, username, read_passphrase); - if (skip_auth == 0) - ret = authenticate_login(); - /* Passphrase entered, turn off timeout */ - alarm(0); - #endif - if (ret == 2) - printf("(auto-authenticated)\n"); - if (ret == 0) - { - preexit(); - fork_exec_wait_hook(HOOK_DENIED, argc, argv); - xsleep(FAILURE_SLEEP); - _exit(1); - } - - preexit(); - - - /* Verify account, such as that it is enabled */ - verify_account(); - - - /* Run login hook */ - fork_exec_wait_hook(HOOK_LOGIN, argc, argv); - - - /* Partial login */ - chown_tty(entry->pw_uid, tty_group, 0); - chdir_home(entry); - ensure_shell(entry); - set_environ(entry, preserve_env); - open_login_session(); - - - /* Stop signal handling */ - signal(SIGALRM, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTSTP, SIG_IGN); - - - child_pid = fork(); - /* vfork cannot be used as the child changes the user, - the parent would not be able to chown the TTY */ - - if (child_pid == -1) - { - perror("fork"); - close_login_session(); - sleep(ERROR_SLEEP); - _exit(1); - } - else if (child_pid) - return; /* Do not go beyond this in the parent */ - - /* In case the shell does not do this */ - setsid(); - - /* Set controlling terminal */ - if (ioctl(STDIN_FILENO, TIOCSCTTY, 1)) - perror("TIOCSCTTY"); - signal(SIGINT, SIG_DFL); - - /* Partial login */ - ret = entry->pw_uid - ? initgroups(username, entry->pw_gid) /* supplemental groups for user, can require network */ - : setgroups(0, NULL); /* supplemental groups for root, does not require netork */ - if (ret == -1) - { - perror(entry->pw_uid ? "initgroups" : "setgroups"); - sleep(ERROR_SLEEP); - _exit(1); - } - set_user(entry); - exec_shell(entry); + /* Done early to make to program look like it is even faster than it is */ + + + /* Make sure nobody is spying */ +#ifdef USE_TTY_GROUP + if ((group = getgrnam(TTY_GROUP))) + tty_group = group->gr_gid; + endgrent(); +#endif + secure_tty(tty_group); + +#if AUTH > 0 + if (!skip_auth) { + /* Redisable echoing */ + passphrase_disable_echo1(STDIN_FILENO); + } +#endif + + + /* Set up clean quiting and time out */ + signal(SIGALRM, timeout_quit); + signal(SIGQUIT, user_quit); + signal(SIGINT, user_quit); + siginterrupt(SIGALRM, 1); + siginterrupt(SIGQUIT, 1); + siginterrupt(SIGINT, 1); +#if AUTH > 0 + alarm(TIMEOUT_SECONDS); +#endif + + + /* Get user information */ + if (!(entry = getpwnam(username))) { + if (errno == EIO /* seriously...? */ || !errno) + printf("User does not exist\n"); + else if (errno) + perror("getpwnam"); + sleep(ERROR_SLEEP); + _exit(1); + } + endpwent(); + username = entry->pw_name; + + + /* Verify passphrase or other token, if -f has not been used */ + ret = 2; +#if AUTH == 0 + (void) hostname; +#else + initialise_login(hostname, username, read_passphrase); + if (skip_auth == 0) + ret = authenticate_login(); + /* Passphrase entered, turn off timeout */ + alarm(0); +#endif + if (ret == 2) + printf("(auto-authenticated)\n"); + if (ret == 0) { + preexit(); + fork_exec_wait_hook(HOOK_DENIED, argc, argv); + xsleep(FAILURE_SLEEP); + _exit(1); + } + + preexit(); + + + /* Verify account, such as that it is enabled */ + verify_account(); + + + /* Run login hook */ + fork_exec_wait_hook(HOOK_LOGIN, argc, argv); + + + /* Partial login */ + chown_tty(entry->pw_uid, tty_group, 0); + chdir_home(entry); + ensure_shell(entry); + set_environ(entry, preserve_env); + open_login_session(); + + + /* Stop signal handling */ + signal(SIGALRM, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTSTP, SIG_IGN); + + + child_pid = fork(); + /* vfork cannot be used as the child changes the user, + the parent would not be able to chown the TTY */ + + if (child_pid == -1) { + perror("fork"); + close_login_session(); + sleep(ERROR_SLEEP); + _exit(1); + } else if (child_pid) { + return; /* Do not go beyond this in the parent */ + } + + /* In case the shell does not do this */ + setsid(); + + /* Set controlling terminal */ + if (ioctl(STDIN_FILENO, TIOCSCTTY, 1)) + perror("TIOCSCTTY"); + signal(SIGINT, SIG_DFL); + + /* Partial login */ + ret = entry->pw_uid + ? initgroups(username, entry->pw_gid) /* supplemental groups for user, can require network */ + : setgroups(0, NULL); /* supplemental groups for root, does not require netork */ + if (ret == -1) { + perror(entry->pw_uid ? "initgroups" : "setgroups"); + sleep(ERROR_SLEEP); + _exit(1); + } + set_user(entry); + exec_shell(entry); } @@ -463,16 +443,16 @@ void do_login(int argc, char** argv) /** * Called before the process exits, to do cleanup */ -void preexit(void) +void +preexit(void) { - if (skip_auth == 0) - { - /* Wipe and free the passphrase from the memory */ - destroy_passphrase(); - - /* Reset terminal settings */ - passphrase_reenable_echo1(STDIN_FILENO); - } + if (!skip_auth) { + /* Wipe and free the passphrase from the memory */ + destroy_passphrase(); + + /* Reset terminal settings */ + passphrase_reenable_echo1(STDIN_FILENO); + } } @@ -481,16 +461,16 @@ void preexit(void) * * @return The entered passphrase */ -char* read_passphrase(void) +char * +read_passphrase(void) { - passphrase = passphrase_read2(STDIN_FILENO, PASSPHRASE_READ_EXISTING); - if (passphrase == NULL) - { - perror("passphrase_read"); - sleep(ERROR_SLEEP); - _exit(1); - } - return passphrase; + passphrase = passphrase_read2(STDIN_FILENO, PASSPHRASE_READ_EXISTING); + if (!passphrase) { + perror("passphrase_read"); + sleep(ERROR_SLEEP); + _exit(1); + } + return passphrase; } #endif @@ -503,14 +483,14 @@ char* read_passphrase(void) /** * Wipe and free the passphrase if it is allocated */ -void destroy_passphrase(void) +void +destroy_passphrase(void) { - if (passphrase) - { - passphrase_wipe(passphrase, strlen(passphrase)); - free(passphrase); - passphrase = NULL; - } + if (passphrase) { + passphrase_wipe(passphrase, strlen(passphrase)); + free(passphrase); + passphrase = NULL; + } } @@ -520,8 +500,8 @@ void destroy_passphrase(void) #ifdef __GNUC__ __attribute__((destructor)) #endif -static void passphrase_destructor(void) +static void +passphrase_destructor(void) { - destroy_passphrase(); + destroy_passphrase(); } - |
