1 /* login.c - Start a session on the system.
2 *
3 * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
4 *
5 * No support for PAM/securetty/selinux/login script/issue/utmp
6 * Relies on libcrypt for hash calculation.
7 *
8 * TODO: this command predates "pending" but needs cleanup. It #defines
9 * random stuff, calls exit() form a signal handler... yeah.
10
11 USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
12
13 config LOGIN
14 bool "login"
15 default y
16 depends on TOYBOX_SHADOW
17 help
18 usage: login [-p] [-h host] [-f USERNAME] [USERNAME]
19
20 Log in as a user, prompting for username and password if necessary.
21
22 -p Preserve environment
23 -h The name of the remote host for this login
24 -f login as USERNAME without authentication
25 */
26
27 #define FOR_login
28 #include "toys.h"
29
GLOBALS(char * hostname;char * username;int login_timeout,login_fail_timeout;)30 GLOBALS(
31 char *hostname;
32 char *username;
33
34 int login_timeout, login_fail_timeout;
35 )
36
37 static void login_timeout_handler(int sig __attribute__((unused)))
38 {
39 printf("\nLogin timed out after %d seconds.\n", TT.login_timeout);
40 exit(0);
41 }
42
login_main(void)43 void login_main(void)
44 {
45 char *forbid[] = {
46 "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
47 "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
48 "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL"
49 };
50 int hh = toys.optflags&FLAG_h, count, tty;
51 char uu[33], *username, *pass = 0, *ss;
52 struct passwd *pwd = 0;
53
54 for (tty=0; tty<3; tty++) if (isatty(tty)) break;
55 if (tty == 3) error_exit("no tty");
56
57 for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]);
58
59 openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
60 xsignal(SIGALRM, login_timeout_handler);
61
62 if (TT.username) username = TT.username;
63 else username = *toys.optargs;
64 for (count = 0; count < 3; count++) {
65 alarm(TT.login_timeout = 60);
66 tcflush(0, TCIFLUSH);
67
68 if (!username) {
69 int i;
70
71 memset(username = uu, 0, sizeof(uu));
72 gethostname(uu, sizeof(uu)-1);
73 printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : "");
74 fflush(stdout);
75
76 if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1);
77
78 // Remove trailing \n and so on
79 for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0;
80 if (!*uu) {
81 username = 0;
82 continue;
83 }
84 }
85
86 // If user exists and isn't locked
87 pwd = getpwnam(username);
88 if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') {
89
90 // Pre-authenticated or passwordless
91 if (TT.username || !*pwd->pw_passwd) break;
92
93 // fetch shadow password if necessary
94 if (*(pass = pwd->pw_passwd) == 'x') {
95 struct spwd *spwd = getspnam (username);
96
97 if (spwd) pass = spwd->sp_pwdp;
98 }
99 } else if (TT.username) error_exit("bad -f '%s'", TT.username);
100
101 // Verify password. (Prompt for password _before_ checking disable state.)
102 if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
103 int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss);
104
105 // password go bye-bye now.
106 memset(toybuf, 0, sizeof(toybuf));
107 if (x) break;
108 }
109
110 syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name,
111 ttyname(tty), hh ? "from " : "", hh ? TT.hostname : "");
112
113 sleep(3);
114 puts("Login incorrect");
115
116 username = 0;
117 pwd = 0;
118 }
119
120 alarm(0);
121 // This had password data in it, and we reuse for motd below
122 memset(toybuf, 0, sizeof(toybuf));
123
124 if (!pwd) error_exit("max retries (3)");
125
126 // Check twice because "this file exists" is a security test, and in
127 // theory filehandle exhaustion or other error could make open/read fail.
128 if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
129 ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
130 puts ((ss && *ss) ? ss : "nologin");
131 free(ss);
132 toys.exitval = 1;
133
134 return;
135 }
136
137 xsetuser(pwd);
138
139 if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir);
140
141 if (!(toys.optflags&FLAG_p)) {
142 char *term = getenv("TERM");
143
144 clearenv();
145 if (term) setenv("TERM", term, 1);
146 }
147
148 setenv("USER", pwd->pw_name, 1);
149 setenv("LOGNAME", pwd->pw_name, 1);
150 setenv("HOME", pwd->pw_dir, 1);
151 setenv("SHELL", pwd->pw_shell, 1);
152
153 // Message of the day
154 if ((ss = readfile("/etc/motd", 0, 0))) {
155 puts(ss);
156 free(ss);
157 }
158
159 syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
160 ttyname(tty), hh ? "from" : "", hh ? TT.hostname : "");
161
162 // not using xexec(), login calls absolute path from filesystem so must exec()
163 execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0);
164 perror_exit("exec shell '%s'", pwd->pw_shell);
165 }
166