aboutsummaryrefslogtreecommitdiffstats
path: root/src/cerberus.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cerberus.c694
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();
}
-