xref: /aosp_15_r20/external/toybox/toys/other/login.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* login.c - Start a session on the system.
2  *
3  * Copyright 2012 Elie De Brauwer <[email protected]>
4  *
5  * No support for PAM/securetty/selinux/login script/issue/utmp
6  * Relies on libcrypt for hash calculation.
7 
8 USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
9 
10 config LOGIN
11   bool "login"
12   default y
13   help
14     usage: login [-p] [-h host] [-f USERNAME] [USERNAME]
15 
16     Log in as a user, prompting for username and password if necessary.
17 
18     -p	Preserve environment
19     -h	The name of the remote host for this login
20     -f	login as USERNAME without authentication
21 */
22 
23 #define FOR_login
24 #include "toys.h"
25 
26 GLOBALS(
27   char *h, *f;
28 
29   int login_timeout, login_fail_timeout;
30 )
31 
login_timeout_handler(int sig)32 static void login_timeout_handler(int sig __attribute__((unused)))
33 {
34   printf("\nLogin timed out after %d seconds.\n", TT.login_timeout);
35   xexit();
36 }
37 
login_main(void)38 void login_main(void)
39 {
40   int count, tty = tty_fd();
41   char *username, *pass = 0, *ss;
42   struct passwd *pwd = 0;
43 
44   // we read user/password from stdin, but tty can be stderr?
45   if (tty == -1) error_exit("no tty");
46 
47   openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
48   xsignal(SIGALRM, login_timeout_handler);
49 
50   if (TT.f) username = TT.f;
51   else username = *toys.optargs;
52   for (count = 0; count < 3; count++) {
53     alarm(TT.login_timeout = 60);
54     tcflush(0, TCIFLUSH);
55 
56     if (!username) {
57       if (gethostname(toybuf, sizeof(toybuf)-1)) *toybuf = 0;
58       printf("%s%slogin: ", *toybuf ? toybuf : "", *toybuf ? " " : "");
59       fflush(stdout);
60 
61       if(!fgets(toybuf, sizeof(toybuf)-1, stdin)) xexit();
62 
63       // Remove trailing \n and so on
64       for (ss = toybuf; *ss; ss++) if (*ss<=' ' || *ss==':') break;
65       *ss = 0;
66       if (!*(username = toybuf)) {
67         username = 0;
68         continue;
69       }
70     }
71 
72     // If user exists and isn't locked
73     if ((pwd = getpwnam(username))) {
74       // Pre-authenticated or passwordless
75       if (TT.f || !*pwd->pw_passwd) break;
76 
77       // fetch shadow password if necessary
78       if (*(pass = pwd->pw_passwd) == 'x') {
79         struct spwd *spwd = getspnam (username);
80 
81         if (spwd) {
82           pass = spwd->sp_pwdp;
83 
84           // empty shadow password
85           if (pass && !*pass) break;
86         }
87       }
88     } else if (TT.f) error_exit("bad -f '%s'", TT.f);
89 
90     // Verify password. (Prompt for password _before_ checking disable state.)
91     if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
92       int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss);
93 
94       // password go bye-bye now.
95       memset(toybuf, 0, sizeof(toybuf));
96       if (x) break;
97     }
98 
99     syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", username,
100       ttyname(tty), TT.h ? "from " : "", TT.h ? : "");
101 
102     sleep(3);
103     puts("Login incorrect");
104 
105     username = 0;
106     pwd = 0;
107   }
108 
109   alarm(0);
110   if (!pwd) error_exit("max retries (3)");
111 
112   // Check twice because "this file exists" is a security test, and in
113   // theory filehandle exhaustion or other error could make open/read fail.
114   if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
115     ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
116     puts ((ss && *ss) ? ss : "nologin");
117     free(ss);
118     toys.exitval = 1;
119 
120     return;
121   }
122 
123   if (fchown(tty, pwd->pw_uid, pwd->pw_gid) || fchmod(tty, 0600))
124     printf("can't claim tty");
125   xsetuser(pwd);
126   reset_env(pwd, !FLAG(p));
127 
128   // Message of the day
129   if ((ss = readfile("/etc/motd", 0, 0))) puts(ss);
130 
131   syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
132     ttyname(tty), TT.h ? "from" : "", TT.h ? : "");
133 
134   // not using xexec(), login calls absolute path from filesystem so must exec()
135   execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0);
136   perror_exit("exec shell '%s'", pwd->pw_shell);
137 }
138