aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/make.sh2
-rw-r--r--toys.h3
-rw-r--r--toys/login.c329
3 files changed, 333 insertions, 1 deletions
diff --git a/scripts/make.sh b/scripts/make.sh
index 52380ecf..c5cee5bb 100755
--- a/scripts/make.sh
+++ b/scripts/make.sh
@@ -121,7 +121,7 @@ do_loudly()
}
do_loudly ${CROSS_COMPILE}${CC} $CFLAGS -I . -o toybox_unstripped $OPTIMIZE \
- main.c lib/*.c $TOYFILES -Wl,--as-needed,-lutil,--no-as-needed || exit 1
+ main.c lib/*.c $TOYFILES -Wl,--as-needed,-lutil,--no-as-needed -Wl,--as-needed,-lcrypt,--no-as-needed || exit 1
do_loudly ${CROSS_COMPILE}${STRIP} toybox_unstripped -o toybox || exit 1
# gcc 4.4's strip command is buggy, and doesn't set the executable bit on
# its output the way SUSv4 suggests it do so.
diff --git a/toys.h b/toys.h
index 814db1ea..aa43ae7e 100644
--- a/toys.h
+++ b/toys.h
@@ -8,6 +8,7 @@
#include "lib/portability.h"
+#include <crypt.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
@@ -21,6 +22,7 @@
#include <pwd.h>
#include <sched.h>
#include <setjmp.h>
+#include <shadow.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -37,6 +39,7 @@
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
+#include <syslog.h>
#include <unistd.h>
#include <utime.h>
#include <utmpx.h>
diff --git a/toys/login.c b/toys/login.c
new file mode 100644
index 00000000..ca62081a
--- /dev/null
+++ b/toys/login.c
@@ -0,0 +1,329 @@
+/* vi: set sw=4 ts=4:
+ *
+ * login.c - Start a session on the system.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * Not in SUSv4.
+ * No support for PAM/securetty/selinux/login script/issue/utmp
+ * Relies on libcrypt for hash calculation.
+
+USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN))
+
+config LOGIN
+ bool "login"
+ default y
+ help
+ usage: login [-p] [-h host] [[-f] username]
+
+ Establish a new session with the system.
+ -p Preserve environment
+ -h The name of the remote host for this login
+ -f Do not perform authentication
+*/
+
+#include "toys.h"
+
+#define LOGIN_TIMEOUT 60
+#define LOGIN_FAIL_TIMEOUT 3
+#define USER_NAME_MAX_SIZE 32
+#define HOSTNAME_SIZE 32
+
+DEFINE_GLOBALS(
+ char * hostname;
+)
+#define TT this.login
+
+static void login_timeout_handler(int sig __attribute__((unused)))
+{
+ printf("\nLogin timed out after %d seconds.\n", LOGIN_TIMEOUT);
+ exit(0);
+}
+
+static const char *forbid[] = {
+ "BASH_ENV",
+ "ENV",
+ "HOME",
+ "IFS",
+ "LD_LIBRARY_PATH",
+ "LD_PRELOAD",
+ "LD_TRACE_LOADED_OBJECTS",
+ "LD_BIND_NOW",
+ "LD_AOUT_LIBRARY_PATH",
+ "LD_AOUT_PRELOAD",
+ "LD_NOWARN",
+ "LD_KEEPDIR",
+ "SHELL",
+ NULL
+};
+
+// Unset dangerous environment variables.
+void sanitize_env()
+{
+ const char **p = forbid;
+ do {
+ unsetenv(*p);
+ p++;
+ } while (*p);
+}
+
+int read_password(char * buff, int buflen)
+{
+ int i = 0;
+ struct termios termio, oldtermio;
+ tcgetattr(0, &oldtermio);
+ tcflush(0, TCIFLUSH);
+ termio = oldtermio;
+
+ termio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
+ termio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
+ tcsetattr(0, TCSANOW, &termio);
+
+ fputs("Password: ", stdout);
+ fflush(stdout);
+
+ while (1) {
+ int ret = read(0, &buff[i], 1);
+ if ( ret < 0 )
+ {
+ buff[0] = 0;
+ tcsetattr(0, TCSANOW, &oldtermio);
+ return 1;
+ }
+ else if ( ret == 0 || buff[i] == '\n' ||
+ buff[i] == '\r' || buflen == i+1)
+ {
+ buff[i] = '\0';
+ break;
+ }
+ i++;
+ }
+
+ tcsetattr(0, TCSANOW, &oldtermio);
+ puts("\n");
+ fflush(stdout);
+ return 0;
+}
+
+int verify_password(char * pwd)
+{
+ char * pass;
+
+ if (read_password(toybuf, sizeof(toybuf)))
+ return 1;
+ if (!pwd)
+ return 1;
+ if (pwd[0] == '!' || pwd[0] == '*')
+ return 1;
+
+ pass = crypt(toybuf, pwd);
+ if (pass != NULL && strcmp(pass, pwd)==0)
+ return 0;
+
+ return 1;
+}
+
+void read_user(char * buff, int size)
+{
+ char hostname[HOSTNAME_SIZE+1];
+ int i = 0;
+ hostname[HOSTNAME_SIZE] = 0;
+ if(!gethostname(hostname, HOSTNAME_SIZE))
+ fputs(hostname, stdout);
+
+ fputs(" login: ", stdout);
+ fflush(stdout);
+
+ do {
+ buff[0] = getchar();
+ if (buff[0] == EOF)
+ exit(EXIT_FAILURE);
+ } while (isblank(buff[0]));
+
+ if (buff[0] != '\n')
+ if(!fgets(&buff[1], HOSTNAME_SIZE-1, stdin))
+ _exit(1);
+
+ while(i<HOSTNAME_SIZE-1 && isgraph(buff[i]))
+ {
+ i++;
+ }
+ buff[i] = 0;
+}
+
+void handle_nologin(void)
+{
+ int fd = open("/etc/nologin", O_RDONLY);
+ int size;
+ if (fd == -1)
+ return;
+
+ size = readall(fd, toybuf,sizeof(toybuf)-1);
+ toybuf[size] = 0;
+ if (!size)
+ puts("System closed for routine maintenance\n");
+ else
+ puts(toybuf);
+
+ close(fd);
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+}
+
+void handle_motd(void)
+{
+ int fd = open("/etc/motd", O_RDONLY);
+ int size;
+ if (fd == -1)
+ return;
+
+ size = readall(fd, toybuf,sizeof(toybuf)-1);
+ toybuf[size] = 0;
+ puts(toybuf);
+
+ close(fd);
+ fflush(stdout);
+}
+
+int change_identity(const struct passwd *pwd)
+{
+ if (initgroups(pwd->pw_name,pwd->pw_gid))
+ return 1;
+ if (setgid(pwd->pw_uid))
+ return 1;
+ if (setuid(pwd->pw_uid))
+ return 1;
+
+ return 0;
+}
+
+void spawn_shell(const char *shell)
+{
+ const char * exec_name = strrchr(shell,'/');
+ if (exec_name)
+ exec_name++;
+ else
+ exec_name = shell;
+
+ snprintf(toybuf,sizeof(toybuf)-1, "-%s", shell);
+ execl(shell, toybuf, NULL);
+ error_exit("Failed to spawn shell");
+}
+
+void setup_environment(const struct passwd *pwd, int clear_env)
+{
+ if (chdir(pwd->pw_dir))
+ printf("can't chdir to home directory: %s\n", pwd->pw_dir);
+
+ if (clear_env)
+ {
+ const char * term = getenv("TERM");
+ clearenv();
+ if (term) setenv("TERM", term, 1);
+ }
+
+ setenv("USER", pwd->pw_name, 1);
+ setenv("LOGNAME", pwd->pw_name, 1);
+ setenv("HOME", pwd->pw_dir, 1);
+ setenv("SHELL", pwd->pw_shell, 1);
+}
+
+void login_main(void)
+{
+ int f_flag = (toys.optflags & 4) >> 2;
+ int p_flag = (toys.optflags & 2) >> 1;
+ int h_flag = toys.optflags & 1;
+ char username[USER_NAME_MAX_SIZE+1];
+ struct passwd * pwd = NULL;
+ struct spwd * spwd = NULL;
+ char *pass = NULL;
+ int auth_fail_cnt = 0;
+
+ if (f_flag && toys.optc != 1)
+ error_exit("-f requires username");
+
+ if (geteuid() != 0 )
+ error_exit("Cannot possibly work without effective root");
+
+ if (!isatty(0) || !isatty(1) || !isatty(2))
+ error_exit("Not connected to a tty");
+
+ openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
+ signal(SIGALRM, login_timeout_handler);
+ alarm(LOGIN_TIMEOUT);
+ sanitize_env();
+
+ while (1) {
+ tcflush(0, TCIFLUSH);
+
+ username[USER_NAME_MAX_SIZE] = 0;
+ if (toys.optargs[0])
+ strncpy(username, toys.optargs[0], USER_NAME_MAX_SIZE);
+ else {
+ read_user(username, USER_NAME_MAX_SIZE+1);
+ if (username[0] == 0)
+ continue;
+ }
+
+ pwd = getpwnam(username);
+ if (!pwd)
+ goto query_pass; // Non-existing user
+
+ if (pwd->pw_passwd[0] == '!' || pwd->pw_passwd[0] == '*')
+ goto query_pass; // Locked account
+
+ if (f_flag)
+ break; // Pre-authenticated
+
+ if (pwd->pw_passwd[0] == '\0')
+ break; // Password-less account
+
+ pass = pwd->pw_passwd;
+ if (pwd->pw_passwd[0] == 'x') {
+ spwd = getspnam (username);
+ if (spwd)
+ pass = spwd->sp_pwdp;
+ }
+
+ query_pass:
+ if (!verify_password(pass))
+ break;
+
+ f_flag = 0;
+ syslog(LOG_WARNING, "invalid password for '%s' on %s %s %s", username,
+ ttyname(0),
+ (h_flag)?"from":"",
+ (h_flag)?TT.hostname:"");
+
+ sleep(LOGIN_FAIL_TIMEOUT);
+ puts("Login incorrect");
+
+ if (++auth_fail_cnt == 3)
+ {
+ error_exit("Maximum number of tries exceeded (%d)\n", auth_fail_cnt);
+ }
+
+ username[0] = 0;
+ pwd = NULL;
+ spwd = NULL;
+ }
+
+ alarm(0);
+
+ if (pwd->pw_uid)
+ handle_nologin();
+
+ if (change_identity(pwd))
+ error_exit("Failed to change identity");
+
+ setup_environment(pwd, !p_flag);
+
+ handle_motd();
+
+ syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
+ ttyname(0),
+ (h_flag)?"from":"",
+ (h_flag)?TT.hostname:"");
+
+ spawn_shell(pwd->pw_shell);
+}