]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/login.c
scriptreplay: cleanup usage()
[thirdparty/util-linux.git] / login-utils / login.c
CommitLineData
6dbe3af9 1/*
0d28a157
KZ
2 * login(1)
3 *
4 * This program is derived from 4.3 BSD software and is subject to the
5 * copyright notice below.
6 *
ee74f262
KZ
7 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
8 * Rewritten to PAM-only version.
9 *
0d28a157
KZ
10 * Michael Glad (glad@daimi.dk)
11 * Computer Science Department, Aarhus University, Denmark
12 * 1990-07-04
13 *
6dbe3af9
KZ
14 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms are permitted
18 * provided that the above copyright notice and this paragraph are
19 * duplicated in all such forms and that any documentation,
20 * advertising materials, and other materials related to such
21 * distribution and use acknowledge that the software was developed
22 * by the University of California, Berkeley. The name of the
23 * University may not be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
218b1dd6 27 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
6dbe3af9 28 */
6dbe3af9 29#include <sys/param.h>
fd6b7a7f 30#include <stdio.h>
6dbe3af9
KZ
31#include <ctype.h>
32#include <unistd.h>
33#include <getopt.h>
34#include <memory.h>
fd6b7a7f 35#include <time.h>
6dbe3af9
KZ
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <sys/resource.h>
39#include <sys/file.h>
40#include <termios.h>
41#include <string.h>
6dbe3af9 42#include <sys/ioctl.h>
fd6b7a7f 43#include <sys/wait.h>
6dbe3af9
KZ
44#include <signal.h>
45#include <errno.h>
46#include <grp.h>
47#include <pwd.h>
b4b919fe 48#include <utmpx.h>
6578ced7
RM
49#ifdef HAVE_LASTLOG_H
50# include <lastlog.h>
51#endif
6dbe3af9 52#include <stdlib.h>
6dbe3af9 53#include <sys/syslog.h>
cd8c4d03
TS
54#ifdef HAVE_LINUX_MAJOR_H
55# include <linux/major.h>
56#endif
6dbe3af9 57#include <netdb.h>
905045d4 58#include <security/pam_appl.h>
fe2c9909
WJ
59#ifdef HAVE_SECURITY_PAM_MISC_H
60# include <security/pam_misc.h>
61#elif defined(HAVE_SECURITY_OPENPAM_H)
62# include <security/openpam.h>
63#endif
4d8fc09c 64#include <sys/sendfile.h>
ab71156c 65
f8bdba2f
KZ
66#ifdef HAVE_LIBAUDIT
67# include <libaudit.h>
68#endif
0aeb57ac 69
cb5acd69
KZ
70#include "c.h"
71#include "setproctitle.h"
66ee8158 72#include "pathnames.h"
8abcf290 73#include "strutils.h"
7eda085c 74#include "nls.h"
984a6096 75#include "env.h"
3e31a2df 76#include "xalloc.h"
e12c9866 77#include "all-io.h"
5759de0c 78#include "fileutils.h"
70aaa730 79#include "ttyutils.h"
4f5f35fc 80#include "pwdutils.h"
6dbe3af9 81
4d8fc09c
KZ
82#include "logindefs.h"
83
905045d4 84#define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
fd6b7a7f 85
48f09788
KZ
86#define LOGIN_MAX_TRIES 3
87#define LOGIN_EXIT_TIMEOUT 5
88#define LOGIN_TIMEOUT 60
fd6b7a7f 89
fd6b7a7f 90#ifdef USE_TTY_GROUP
ab71156c 91# define TTY_MODE 0620
fd6b7a7f 92#else
ab71156c 93# define TTY_MODE 0600
fd6b7a7f
KZ
94#endif
95
ab71156c 96#define TTYGRPNAME "tty" /* name of group to own ttys */
99f7c131
KZ
97#define VCS_PATH_MAX 64
98
99/*
100 * Login control struct
101 */
102struct login_context {
103 const char *tty_path; /* ttyname() return value */
104 const char *tty_name; /* tty_path without /dev prefix */
105 const char *tty_number; /* end of the tty_path */
738246ed 106 mode_t tty_mode; /* chmod() mode */
99f7c131 107
088d4876
KZ
108 const char *username; /* points to PAM, pwd or cmd_username */
109 char *cmd_username; /* username specified on command line */
110
3eb8b797 111
67e70761 112 struct passwd *pwd; /* user info */
4f5f35fc 113 char *pwdbuf; /* pwd strings */
67e70761 114
eab72c4e
KZ
115 pam_handle_t *pamh; /* PAM handler */
116 struct pam_conv conv; /* PAM conversation */
117
99f7c131
KZ
118#ifdef LOGIN_CHOWN_VCS
119 char vcsn[VCS_PATH_MAX]; /* virtual console name */
120 char vcsan[VCS_PATH_MAX];
121#endif
d20337ed 122
80591bf6
BS
123 char *thishost; /* this machine */
124 char *thisdomain; /* this machine's domain */
125 char *hostname; /* remote machine */
126 char hostaddress[16]; /* remote address */
0180264f
KZ
127
128 pid_t pid;
80591bf6 129 int quiet; /* 1 if hush file exists */
eab72c4e 130
a2de6177 131 unsigned int remote:1, /* login -h */
92e386ca 132 nohost:1, /* login -H */
241e4565
KZ
133 noauth:1, /* login -f */
134 keep_env:1; /* login -p */
99f7c131 135};
726f69e2 136
6dbe3af9 137/*
80591bf6 138 * This bounds the time given to login. Not a define, so it can
6dbe3af9
KZ
139 * be patched on machines where it's too small.
140 */
c9baf5da 141static unsigned int timeout = LOGIN_TIMEOUT;
a169a454
KZ
142static int child_pid = 0;
143static volatile int got_sig = 0;
f17bda66 144static char timeout_msg[128];
6dbe3af9 145
2f595c00
KZ
146#ifdef LOGIN_CHOWN_VCS
147/* true if the filedescriptor fd is a console tty, very Linux specific */
148static int is_consoletty(int fd)
149{
150 struct stat stb;
151
152 if ((fstat(fd, &stb) >= 0)
153 && (major(stb.st_rdev) == TTY_MAJOR)
154 && (minor(stb.st_rdev) < 64)) {
155 return 1;
156 }
157 return 0;
158}
159#endif
160
161
9abc9dab
KZ
162/*
163 * Robert Ambrose writes:
164 * A couple of my users have a problem with login processes hanging around
165 * soaking up pts's. What they seem to hung up on is trying to write out the
166 * message 'Login timed out after %d seconds' when the connection has already
167 * been dropped.
80591bf6 168 * What I did was add a second timeout while trying to write the message, so
9abc9dab
KZ
169 * the process just exits if the second timeout expires.
170 */
a40f08ef
KZ
171static void __attribute__ ((__noreturn__))
172timedout2(int sig __attribute__ ((__unused__)))
9abc9dab
KZ
173{
174 struct termios ti;
175
176 /* reset echo */
177 tcgetattr(0, &ti);
178 ti.c_lflag |= ECHO;
179 tcsetattr(0, TCSANOW, &ti);
f17bda66 180 _exit(EXIT_SUCCESS); /* %% */
9abc9dab
KZ
181}
182
183static void timedout(int sig __attribute__ ((__unused__)))
184{
185 signal(SIGALRM, timedout2);
186 alarm(10);
f17bda66 187 ignore_result( write(STDERR_FILENO, timeout_msg, strlen(timeout_msg)) );
9abc9dab
KZ
188 signal(SIGALRM, SIG_IGN);
189 alarm(0);
190 timedout2(0);
191}
192
193/*
194 * This handler allows to inform a shell about signals to login. If you have
80591bf6
BS
195 * (root) permissions, you can kill all login children by one signal to the
196 * login process.
9abc9dab 197 *
80591bf6
BS
198 * Also, a parent who is session leader is able (before setsid() in the child)
199 * to inform the child when the controlling tty goes away (e.g. modem hangup).
9abc9dab
KZ
200 */
201static void sig_handler(int signal)
202{
203 if (child_pid)
204 kill(-child_pid, signal);
205 else
206 got_sig = 1;
207 if (signal == SIGTERM)
208 kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
209}
210
f950752b 211/*
80591bf6
BS
212 * Let us delay all exit() calls when the user is not authenticated
213 * or the session not fully initialized (loginpam_session()).
f950752b 214 */
a40f08ef 215static void __attribute__ ((__noreturn__)) sleepexit(int eval)
9abc9dab 216{
c9baf5da 217 sleep((unsigned int)getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT));
9abc9dab
KZ
218 exit(eval);
219}
220
92e386ca
KZ
221static const char *get_thishost(struct login_context *cxt, const char **domain)
222{
f0196a13
KZ
223 if (!cxt->thishost) {
224 cxt->thishost = xgethostname();
225 if (!cxt->thishost) {
92e386ca
KZ
226 if (domain)
227 *domain = NULL;
228 return NULL;
229 }
92e386ca
KZ
230 cxt->thisdomain = strchr(cxt->thishost, '.');
231 if (cxt->thisdomain)
232 *cxt->thisdomain++ = '\0';
233 }
234
235 if (domain)
236 *domain = cxt->thisdomain;
237 return cxt->thishost;
238}
239
4d8fc09c 240/*
80591bf6 241 * Output the /etc/motd file.
4d8fc09c 242 *
80591bf6
BS
243 * It determines the name of a login announcement file and outputs it to the
244 * user's terminal at login time. The MOTD_FILE configuration option is a
245 * colon-delimited list of filenames. An empty MOTD_FILE option disables
246 * message-of-the-day printing completely.
4d8fc09c 247 */
9abc9dab
KZ
248static void motd(void)
249{
34bb8eea 250 char *motdlist, *motdfile;
4d8fc09c 251 const char *mb;
9abc9dab 252
4d8fc09c
KZ
253 mb = getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE);
254 if (!mb || !*mb)
9abc9dab 255 return;
4d8fc09c
KZ
256
257 motdlist = xstrdup(mb);
258
34bb8eea
KZ
259 for (motdfile = strtok(motdlist, ":"); motdfile;
260 motdfile = strtok(NULL, ":")) {
261
4d8fc09c
KZ
262 struct stat st;
263 int fd;
264
4d8fc09c
KZ
265 fd = open(motdfile, O_RDONLY, 0);
266 if (fd < 0)
267 continue;
a64e2682
SK
268 if (!fstat(fd, &st) && st.st_size)
269 sendfile(fileno(stdout), fd, NULL, st.st_size);
4d8fc09c
KZ
270 close(fd);
271 }
34bb8eea
KZ
272
273 free(motdlist);
9abc9dab 274}
99f7c131
KZ
275
276/*
80591bf6
BS
277 * Nice and simple code provided by Linus Torvalds 16-Feb-93.
278 * Non-blocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
99f7c131
KZ
279 *
280 * He writes: "Login performs open() on a tty in a blocking mode.
281 * In some cases it may make login wait in open() for carrier infinitely,
282 * for example if the line is a simplistic case of a three-wire serial
80591bf6 283 * connection. I believe login should open the line in non-blocking mode,
99f7c131 284 * leaving the decision to make a connection to getty (where it actually
80591bf6 285 * belongs)."
99f7c131
KZ
286 */
287static void open_tty(const char *tty)
ab71156c 288{
1d4ad1de
KZ
289 int i, fd, flags;
290
291 fd = open(tty, O_RDWR | O_NONBLOCK);
292 if (fd == -1) {
960cf573 293 syslog(LOG_ERR, _("FATAL: can't reopen tty: %m"));
66020e56 294 sleepexit(EXIT_FAILURE);
1d4ad1de 295 }
eb63b9b8 296
8bee984a
KZ
297 if (!isatty(fd)) {
298 close(fd);
299 syslog(LOG_ERR, _("FATAL: %s is not a terminal"), tty);
66020e56 300 sleepexit(EXIT_FAILURE);
8bee984a
KZ
301 }
302
1d4ad1de
KZ
303 flags = fcntl(fd, F_GETFL);
304 flags &= ~O_NONBLOCK;
305 fcntl(fd, F_SETFL, flags);
e09947b5 306
1d4ad1de
KZ
307 for (i = 0; i < fd; i++)
308 close(i);
309 for (i = 0; i < 3; i++)
310 if (fd != i)
311 dup2(fd, i);
312 if (fd >= 3)
313 close(fd);
6dbe3af9
KZ
314}
315
ff0392a0
KZ
316#define chown_err(_what, _uid, _gid) \
317 syslog(LOG_ERR, _("chown (%s, %lu, %lu) failed: %m"), \
318 (_what), (unsigned long) (_uid), (unsigned long) (_gid))
319
320#define chmod_err(_what, _mode) \
321 syslog(LOG_ERR, _("chmod (%s, %u) failed: %m"), (_what), (_mode))
322
323static void chown_tty(struct login_context *cxt)
324{
45d0a30e 325 const char *grname;
ff0392a0
KZ
326 uid_t uid = cxt->pwd->pw_uid;
327 gid_t gid = cxt->pwd->pw_gid;
328
45d0a30e
KZ
329 grname = getlogindefs_str("TTYGROUP", TTYGRPNAME);
330 if (grname && *grname) {
10b3219a
SK
331 struct group *gr = getgrnam(grname);
332 if (gr) /* group by name */
333 gid = gr->gr_gid;
334 else /* group by ID */
335 gid = (gid_t) getlogindefs_num("TTYGROUP", gid);
45d0a30e 336 }
ff0392a0
KZ
337 if (fchown(0, uid, gid)) /* tty */
338 chown_err(cxt->tty_name, uid, gid);
738246ed
KZ
339 if (fchmod(0, cxt->tty_mode))
340 chmod_err(cxt->tty_name, cxt->tty_mode);
ff0392a0
KZ
341
342#ifdef LOGIN_CHOWN_VCS
343 if (is_consoletty(0)) {
2f595c00
KZ
344 if (chown(cxt->vcsn, uid, gid)) /* vcs */
345 chown_err(cxt->vcsn, uid, gid);
346 if (chmod(cxt->vcsn, cxt->tty_mode))
347 chmod_err(cxt->vcsn, cxt->tty_mode);
348
80591bf6 349 if (chown(cxt->vcsan, uid, gid)) /* vcsa */
2f595c00
KZ
350 chown_err(cxt->vcsan, uid, gid);
351 if (chmod(cxt->vcsan, cxt->tty_mode))
352 chmod_err(cxt->vcsan, cxt->tty_mode);
ff0392a0
KZ
353 }
354#endif
355}
356
99f7c131 357/*
9e930041 358 * Reads the current terminal path and initializes cxt->tty_* variables.
99f7c131
KZ
359 */
360static void init_tty(struct login_context *cxt)
ab71156c 361{
99f7c131
KZ
362 struct stat st;
363 struct termios tt, ttt;
364
738246ed
KZ
365 cxt->tty_mode = (mode_t) getlogindefs_num("TTYPERM", TTY_MODE);
366
285c1f3a 367 get_terminal_name(&cxt->tty_path, &cxt->tty_name, &cxt->tty_number);
24f4bbff 368
99f7c131
KZ
369 /*
370 * In case login is suid it was possible to use a hardlink as stdin
371 * and exploit races for a local root exploit. (Wojciech Purczynski).
372 *
373 * More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
374 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
7007991f 375 * All of this is a problem only when login is suid, which it isn't.
99f7c131
KZ
376 */
377 if (!cxt->tty_path || !*cxt->tty_path ||
378 lstat(cxt->tty_path, &st) != 0 || !S_ISCHR(st.st_mode) ||
379 (st.st_nlink > 1 && strncmp(cxt->tty_path, "/dev/", 5)) ||
380 access(cxt->tty_path, R_OK | W_OK) != 0) {
66020e56 381
24f4bbff 382 syslog(LOG_ERR, _("FATAL: bad tty"));
66020e56 383 sleepexit(EXIT_FAILURE);
24f4bbff 384 }
99f7c131 385
99f7c131 386#ifdef LOGIN_CHOWN_VCS
d4cba553
KZ
387 if (cxt->tty_number) {
388 /* find names of Virtual Console devices, for later mode change */
389 snprintf(cxt->vcsn, sizeof(cxt->vcsn), "/dev/vcs%s", cxt->tty_number);
390 snprintf(cxt->vcsan, sizeof(cxt->vcsan), "/dev/vcsa%s", cxt->tty_number);
391 }
99f7c131
KZ
392#endif
393
394 tcgetattr(0, &tt);
395 ttt = tt;
396 ttt.c_cflag &= ~HUPCL;
397
738246ed 398 if ((fchown(0, 0, 0) || fchmod(0, cxt->tty_mode)) && errno != EROFS) {
99f7c131
KZ
399
400 syslog(LOG_ERR, _("FATAL: %s: change permissions failed: %m"),
401 cxt->tty_path);
402 sleepexit(EXIT_FAILURE);
403 }
404
405 /* Kill processes left on this tty */
c5bb244e 406 tcsetattr(0, TCSANOW, &ttt);
99f7c131 407
2e703564 408 /*
9e930041 409 * Let's close file descriptors before vhangup
2e703564
KZ
410 * https://lkml.org/lkml/2012/6/5/145
411 */
412 close(STDIN_FILENO);
413 close(STDOUT_FILENO);
414 close(STDERR_FILENO);
415
3fd1f771 416 signal(SIGHUP, SIG_IGN); /* so vhangup() won't kill us */
99f7c131
KZ
417 vhangup();
418 signal(SIGHUP, SIG_DFL);
419
420 /* open stdin,stdout,stderr to the tty */
421 open_tty(cxt->tty_path);
422
423 /* restore tty modes */
424 tcsetattr(0, TCSAFLUSH, &tt);
24f4bbff
KZ
425}
426
99f7c131 427
364cda48 428/*
80591bf6 429 * Logs failed login attempts in _PATH_BTMP, if it exists.
364cda48
KZ
430 * Must be called only with username the name of an actual user.
431 * The most common login failure is to give password instead of username.
432 */
a2de6177 433static void log_btmp(struct login_context *cxt)
ab71156c 434{
b4b919fe 435 struct utmpx ut;
75d4dbb0 436 struct timeval tv;
364cda48
KZ
437
438 memset(&ut, 0, sizeof(ut));
439
ac5c12fd 440 str2memcpy(ut.ut_user,
3eb8b797 441 cxt->username ? cxt->username : "(unknown)",
364cda48
KZ
442 sizeof(ut.ut_user));
443
d20337ed 444 if (cxt->tty_number)
ac5c12fd 445 str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
d20337ed 446 if (cxt->tty_name)
ac5c12fd 447 str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
364cda48 448
75d4dbb0
KZ
449 gettimeofday(&tv, NULL);
450 ut.ut_tv.tv_sec = tv.tv_sec;
451 ut.ut_tv.tv_usec = tv.tv_usec;
364cda48 452
ab71156c 453 ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
0180264f 454 ut.ut_pid = cxt->pid;
c3f974a1 455
d20337ed 456 if (cxt->hostname) {
ac5c12fd 457 str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
0468860d 458 if (*cxt->hostaddress)
d20337ed 459 memcpy(&ut.ut_addr_v6, cxt->hostaddress,
ab71156c 460 sizeof(ut.ut_addr_v6));
364cda48 461 }
6e3bc8a6 462
b4b919fe 463 updwtmpx(_PATH_BTMP, &ut);
364cda48 464}
b2452358 465
b2452358 466
f8bdba2f 467#ifdef HAVE_LIBAUDIT
67e70761 468static void log_audit(struct login_context *cxt, int status)
f8bdba2f 469{
f8bdba2f 470 int audit_fd;
67e70761 471 struct passwd *pwd = cxt->pwd;
f8bdba2f
KZ
472
473 audit_fd = audit_open();
474 if (audit_fd == -1)
475 return;
c3f974a1
KZ
476 if (!pwd && cxt->username)
477 pwd = getpwnam(cxt->username);
478
479 audit_log_acct_message(audit_fd,
480 AUDIT_USER_LOGIN,
481 NULL,
482 "login",
483 cxt->username ? cxt->username : "(unknown)",
484 pwd ? pwd->pw_uid : (unsigned int) -1,
485 cxt->hostname,
486 NULL,
487 cxt->tty_name,
488 status);
f8bdba2f
KZ
489
490 close(audit_fd);
491}
67e70761
KZ
492#else /* !HAVE_LIBAUDIT */
493# define log_audit(cxt, status)
ab71156c 494#endif /* HAVE_LIBAUDIT */
f8bdba2f 495
3761d0bb
KZ
496static void log_lastlog(struct login_context *cxt)
497{
6f7eba20 498 struct sigaction sa, oldsa_xfsz;
3761d0bb
KZ
499 struct lastlog ll;
500 time_t t;
501 int fd;
502
67e70761
KZ
503 if (!cxt->pwd)
504 return;
505
1a83c00d
KZ
506 if (cxt->pwd->pw_uid > (uid_t) getlogindefs_num("LASTLOG_UID_MAX", ULONG_MAX))
507 return;
508
6f7eba20
KZ
509 /* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
510 memset(&sa, 0, sizeof(sa));
511 sa.sa_handler = SIG_IGN;
512 sigaction(SIGXFSZ, &sa, &oldsa_xfsz);
513
3761d0bb
KZ
514 fd = open(_PATH_LASTLOG, O_RDWR, 0);
515 if (fd < 0)
6f7eba20 516 goto done;
3761d0bb 517
7488d4c0
KZ
518 if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1)
519 goto done;
3761d0bb
KZ
520
521 /*
80591bf6 522 * Print last log message.
3761d0bb
KZ
523 */
524 if (!cxt->quiet) {
525 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
526 ll.ll_time != 0) {
527 time_t ll_time = (time_t) ll.ll_time;
528
529 printf(_("Last login: %.*s "), 24 - 5, ctime(&ll_time));
530 if (*ll.ll_host != '\0')
531 printf(_("from %.*s\n"),
532 (int)sizeof(ll.ll_host), ll.ll_host);
533 else
534 printf(_("on %.*s\n"),
535 (int)sizeof(ll.ll_line), ll.ll_line);
536 }
7488d4c0
KZ
537 if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1)
538 goto done;
3761d0bb
KZ
539 }
540
541 memset((char *)&ll, 0, sizeof(ll));
542
543 time(&t);
544 ll.ll_time = t; /* ll_time is always 32bit */
545
546 if (cxt->tty_name)
ac5c12fd 547 str2memcpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
3761d0bb 548 if (cxt->hostname)
ac5c12fd 549 str2memcpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));
3761d0bb
KZ
550
551 if (write_all(fd, (char *)&ll, sizeof(ll)))
552 warn(_("write lastlog failed"));
7488d4c0 553done:
6f7eba20
KZ
554 if (fd >= 0)
555 close(fd);
556
557 sigaction(SIGXFSZ, &oldsa_xfsz, NULL); /* restore original setting */
3761d0bb
KZ
558}
559
6e3bc8a6 560/*
80591bf6 561 * Update wtmp and utmp logs.
6e3bc8a6
KZ
562 */
563static void log_utmp(struct login_context *cxt)
564{
b4b919fe
RM
565 struct utmpx ut;
566 struct utmpx *utp;
6e3bc8a6
KZ
567 struct timeval tv;
568
b4b919fe
RM
569 utmpxname(_PATH_UTMP);
570 setutxent();
6e3bc8a6
KZ
571
572 /* Find pid in utmp.
573 *
574 * login sometimes overwrites the runlevel entry in /var/run/utmp,
575 * confusing sysvinit. I added a test for the entry type, and the
576 * problem was gone. (In a runlevel entry, st_pid is not really a pid
80591bf6 577 * but some number calculated from the previous and current runlevel.)
6e3bc8a6
KZ
578 * -- Michael Riepe <michael@stud.uni-hannover.de>
579 */
b4b919fe 580 while ((utp = getutxent()))
6e3bc8a6
KZ
581 if (utp->ut_pid == cxt->pid
582 && utp->ut_type >= INIT_PROCESS
583 && utp->ut_type <= DEAD_PROCESS)
584 break;
585
586 /* If we can't find a pre-existing entry by pid, try by line.
80591bf6 587 * BSD network daemons may rely on this. */
98193daa 588 if (utp == NULL && cxt->tty_name) {
b4b919fe 589 setutxent();
6e3bc8a6 590 ut.ut_type = LOGIN_PROCESS;
ac5c12fd 591 str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
b4b919fe 592 utp = getutxline(&ut);
6e3bc8a6
KZ
593 }
594
98193daa 595 /* If we can't find a pre-existing entry by pid and line, try it by id.
80591bf6 596 * Very stupid telnetd daemons don't set up utmp at all. (kzak) */
98193daa 597 if (utp == NULL && cxt->tty_number) {
b4b919fe 598 setutxent();
98193daa 599 ut.ut_type = DEAD_PROCESS;
ac5c12fd 600 str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
b4b919fe 601 utp = getutxid(&ut);
98193daa
KZ
602 }
603
6e3bc8a6
KZ
604 if (utp)
605 memcpy(&ut, utp, sizeof(ut));
606 else
607 /* some gettys/telnetds don't initialize utmp... */
608 memset(&ut, 0, sizeof(ut));
609
d4cba553 610 if (cxt->tty_number && ut.ut_id[0] == 0)
ac5c12fd 611 str2memcpy(ut.ut_id, cxt->tty_number, sizeof(ut.ut_id));
d4cba553 612 if (cxt->username)
ac5c12fd 613 str2memcpy(ut.ut_user, cxt->username, sizeof(ut.ut_user));
d4cba553 614 if (cxt->tty_name)
ac5c12fd 615 str2memcpy(ut.ut_line, cxt->tty_name, sizeof(ut.ut_line));
6e3bc8a6 616
6e3bc8a6
KZ
617 gettimeofday(&tv, NULL);
618 ut.ut_tv.tv_sec = tv.tv_sec;
619 ut.ut_tv.tv_usec = tv.tv_usec;
6e3bc8a6
KZ
620 ut.ut_type = USER_PROCESS;
621 ut.ut_pid = cxt->pid;
622 if (cxt->hostname) {
ac5c12fd 623 str2memcpy(ut.ut_host, cxt->hostname, sizeof(ut.ut_host));
0468860d 624 if (*cxt->hostaddress)
6e3bc8a6
KZ
625 memcpy(&ut.ut_addr_v6, cxt->hostaddress,
626 sizeof(ut.ut_addr_v6));
627 }
628
b4b919fe
RM
629 pututxline(&ut);
630 endutxent();
6e3bc8a6 631
b4b919fe 632 updwtmpx(_PATH_WTMP, &ut);
6e3bc8a6
KZ
633}
634
516b00c4
KZ
635static void log_syslog(struct login_context *cxt)
636{
637 struct passwd *pwd = cxt->pwd;
638
d4cba553
KZ
639 if (!cxt->tty_name)
640 return;
641
516b00c4
KZ
642 if (!strncmp(cxt->tty_name, "ttyS", 4))
643 syslog(LOG_INFO, _("DIALUP AT %s BY %s"),
644 cxt->tty_name, pwd->pw_name);
645
646 if (!pwd->pw_uid) {
647 if (cxt->hostname)
648 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
649 cxt->tty_name, cxt->hostname);
650 else
651 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), cxt->tty_name);
652 } else {
653 if (cxt->hostname)
654 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"),
655 cxt->tty_name, pwd->pw_name, cxt->hostname);
656 else
657 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), cxt->tty_name,
658 pwd->pw_name);
659 }
660}
661
53e9eda3 662/* encapsulate stupid "void **" pam_get_item() API */
088d4876 663static int loginpam_get_username(pam_handle_t *pamh, const char **name)
53e9eda3 664{
088d4876 665 const void *item = (const void *)*name;
53e9eda3
KZ
666 int rc;
667 rc = pam_get_item(pamh, PAM_USER, &item);
088d4876 668 *name = (const char *)item;
53e9eda3
KZ
669 return rc;
670}
53e9eda3 671
a40f08ef 672static void loginpam_err(pam_handle_t *pamh, int retcode)
905045d4
KZ
673{
674 const char *msg = pam_strerror(pamh, retcode);
675
676 if (msg) {
677 fprintf(stderr, "\n%s\n", msg);
678 syslog(LOG_ERR, "%s", msg);
679 }
680 pam_end(pamh, retcode);
f950752b 681 sleepexit(EXIT_FAILURE);
905045d4
KZ
682}
683
92e386ca 684/*
e6b32e7d
KZ
685 * Composes "<host> login: " string; or returns "login: " if -H is given or
686 * LOGIN_PLAIN_PROMPT=yes configured.
92e386ca
KZ
687 */
688static const char *loginpam_get_prompt(struct login_context *cxt)
689{
690 const char *host;
691 char *prompt, *dflt_prompt = _("login: ");
692 size_t sz;
693
e6b32e7d
KZ
694 if (cxt->nohost)
695 return dflt_prompt; /* -H on command line */
696
697 if (getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
92e386ca
KZ
698 return dflt_prompt;
699
e6b32e7d
KZ
700 if (!(host = get_thishost(cxt, NULL)))
701 return dflt_prompt;
92e386ca 702
e6b32e7d 703 sz = strlen(host) + 1 + strlen(dflt_prompt) + 1;
92e386ca
KZ
704 prompt = xmalloc(sz);
705 snprintf(prompt, sz, "%s %s", host, dflt_prompt);
706
707 return prompt;
708}
709
eab72c4e
KZ
710static pam_handle_t *init_loginpam(struct login_context *cxt)
711{
712 pam_handle_t *pamh = NULL;
713 int rc;
714
715 /*
716 * username is initialized to NULL and if specified on the command line
80591bf6 717 * it is set. Therefore, we are safe not setting it to anything.
eab72c4e
KZ
718 */
719 rc = pam_start(cxt->remote ? "remote" : "login",
720 cxt->username, &cxt->conv, &pamh);
721 if (rc != PAM_SUCCESS) {
722 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, rc));
723 syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
724 pam_strerror(pamh, rc));
f950752b 725 sleepexit(EXIT_FAILURE);
eab72c4e
KZ
726 }
727
728 /* hostname & tty are either set to NULL or their correct values,
80591bf6 729 * depending on how much we know. */
eab72c4e
KZ
730 rc = pam_set_item(pamh, PAM_RHOST, cxt->hostname);
731 if (is_pam_failure(rc))
732 loginpam_err(pamh, rc);
733
734 rc = pam_set_item(pamh, PAM_TTY, cxt->tty_name);
735 if (is_pam_failure(rc))
736 loginpam_err(pamh, rc);
737
738 /*
739 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM so that
740 * the "login: " prompt gets localized. Unfortunately, PAM doesn't have
741 * an interface to specify the "Password: " string (yet).
742 */
92e386ca 743 rc = pam_set_item(pamh, PAM_USER_PROMPT, loginpam_get_prompt(cxt));
eab72c4e
KZ
744 if (is_pam_failure(rc))
745 loginpam_err(pamh, rc);
746
80591bf6 747 /* We don't need the original username. We have to follow PAM. */
eab72c4e
KZ
748 cxt->username = NULL;
749 cxt->pamh = pamh;
750
751 return pamh;
752}
753
a2de6177
KZ
754static void loginpam_auth(struct login_context *cxt)
755{
7d18972b
KZ
756 int rc, show_unknown;
757 unsigned int retries, failcount = 0;
cea8ec53
KZ
758 const char *hostname = cxt->hostname ? cxt->hostname :
759 cxt->tty_name ? cxt->tty_name : "<unknown>";
a2de6177
KZ
760 pam_handle_t *pamh = cxt->pamh;
761
762 /* if we didn't get a user on the command line, set it to NULL */
763 loginpam_get_username(pamh, &cxt->username);
764
cea8ec53 765 show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
fab1f671 766 retries = getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES);
cea8ec53 767
a2de6177
KZ
768 /*
769 * There may be better ways to deal with some of these conditions, but
770 * at least this way I don't think we'll be giving away information...
771 *
772 * Perhaps someday we can trust that all PAM modules will pay attention
fab1f671 773 * to failure count and get rid of LOGIN_MAX_TRIES?
a2de6177
KZ
774 */
775 rc = pam_authenticate(pamh, 0);
776
fab1f671 777 while ((++failcount < retries) &&
a2de6177
KZ
778 ((rc == PAM_AUTH_ERR) ||
779 (rc == PAM_USER_UNKNOWN) ||
780 (rc == PAM_CRED_INSUFFICIENT) ||
781 (rc == PAM_AUTHINFO_UNAVAIL))) {
782
cea8ec53
KZ
783 if (rc == PAM_USER_UNKNOWN && !show_unknown)
784 /*
80591bf6
BS
785 * Logging unknown usernames may be a security issue if
786 * a user enters her password instead of her login name.
cea8ec53
KZ
787 */
788 cxt->username = NULL;
789 else
790 loginpam_get_username(pamh, &cxt->username);
a2de6177
KZ
791
792 syslog(LOG_NOTICE,
7d18972b 793 _("FAILED LOGIN %u FROM %s FOR %s, %s"),
cea8ec53
KZ
794 failcount, hostname,
795 cxt->username ? cxt->username : "(unknown)",
a2de6177
KZ
796 pam_strerror(pamh, rc));
797
798 log_btmp(cxt);
799 log_audit(cxt, 0);
800
801 fprintf(stderr, _("Login incorrect\n\n"));
802
803 pam_set_item(pamh, PAM_USER, NULL);
804 rc = pam_authenticate(pamh, 0);
805 }
806
807 if (is_pam_failure(rc)) {
808
cea8ec53
KZ
809 if (rc == PAM_USER_UNKNOWN && !show_unknown)
810 cxt->username = NULL;
811 else
812 loginpam_get_username(pamh, &cxt->username);
a2de6177
KZ
813
814 if (rc == PAM_MAXTRIES)
815 syslog(LOG_NOTICE,
7d18972b 816 _("TOO MANY LOGIN TRIES (%u) FROM %s FOR %s, %s"),
cea8ec53
KZ
817 failcount, hostname,
818 cxt->username ? cxt->username : "(unknown)",
a2de6177
KZ
819 pam_strerror(pamh, rc));
820 else
821 syslog(LOG_NOTICE,
822 _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
cea8ec53
KZ
823 hostname,
824 cxt->username ? cxt->username : "(unknown)",
a2de6177
KZ
825 pam_strerror(pamh, rc));
826
827 log_btmp(cxt);
828 log_audit(cxt, 0);
829
830 fprintf(stderr, _("\nLogin incorrect\n"));
831 pam_end(pamh, rc);
f950752b 832 sleepexit(EXIT_SUCCESS);
a2de6177
KZ
833 }
834}
835
98306fc5
KZ
836static void loginpam_acct(struct login_context *cxt)
837{
838 int rc;
839 pam_handle_t *pamh = cxt->pamh;
840
841 rc = pam_acct_mgmt(pamh, 0);
842
843 if (rc == PAM_NEW_AUTHTOK_REQD)
844 rc = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
845
846 if (is_pam_failure(rc))
847 loginpam_err(pamh, rc);
848
849 /*
80591bf6 850 * Grab the user information out of the password file for future use.
98306fc5
KZ
851 * First get the username that we are actually using, though.
852 */
853 rc = loginpam_get_username(pamh, &cxt->username);
854 if (is_pam_failure(rc))
855 loginpam_err(pamh, rc);
856
857 if (!cxt->username || !*cxt->username) {
858 warnx(_("\nSession setup problem, abort."));
9c6167c3 859 syslog(LOG_ERR, _("NULL user name. Abort."));
98306fc5 860 pam_end(pamh, PAM_SYSTEM_ERR);
f950752b 861 sleepexit(EXIT_FAILURE);
98306fc5
KZ
862 }
863}
864
34f7ea15 865/*
80591bf6 866 * Note that the position of the pam_setcred() call is discussable:
34f7ea15 867 *
80591bf6 868 * - the PAM docs recommend pam_setcred() before pam_open_session()
34f7ea15
KZ
869 * - but the original RFC http://www.opengroup.org/rfc/mirror-rfc/rfc86.0.txt
870 * uses pam_setcred() after pam_open_session()
871 *
872 * The old login versions (before year 2011) followed the RFC. This is probably
80591bf6
BS
873 * not optimal, because there could be a dependence between some session modules
874 * and the user's credentials.
34f7ea15
KZ
875 *
876 * The best is probably to follow openssh and call pam_setcred() before and
877 * after pam_open_session(). -- kzak@redhat.com (18-Nov-2011)
878 *
879 */
59a184d9
KZ
880static void loginpam_session(struct login_context *cxt)
881{
882 int rc;
883 pam_handle_t *pamh = cxt->pamh;
884
34f7ea15 885 rc = pam_setcred(pamh, PAM_ESTABLISH_CRED);
59a184d9
KZ
886 if (is_pam_failure(rc))
887 loginpam_err(pamh, rc);
888
34f7ea15
KZ
889 rc = pam_open_session(pamh, 0);
890 if (is_pam_failure(rc)) {
891 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
892 loginpam_err(pamh, rc);
893 }
894
895 rc = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
59a184d9
KZ
896 if (is_pam_failure(rc)) {
897 pam_close_session(pamh, 0);
898 loginpam_err(pamh, rc);
899 }
900}
901
a169a454 902/*
80591bf6
BS
903 * Detach the controlling terminal, fork, restore syslog stuff, and create
904 * a new session.
a169a454
KZ
905 */
906static void fork_session(struct login_context *cxt)
907{
908 struct sigaction sa, oldsa_hup, oldsa_term;
909
910 signal(SIGALRM, SIG_DFL);
911 signal(SIGQUIT, SIG_DFL);
912 signal(SIGTSTP, SIG_IGN);
913
914 memset(&sa, 0, sizeof(sa));
915 sa.sa_handler = SIG_IGN;
916 sigaction(SIGINT, &sa, NULL);
917
918 sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
919
920 /*
80591bf6
BS
921 * Detach the controlling tty.
922 * We don't need the tty in a parent who only waits for a child.
923 * The child calls setsid() that detaches from the tty as well.
a169a454
KZ
924 */
925 ioctl(0, TIOCNOTTY, NULL);
926
927 /*
80591bf6
BS
928 * We have to beware of SIGTERM, because leaving a PAM session
929 * without pam_close_session() is a pretty bad thing.
a169a454
KZ
930 */
931 sa.sa_handler = sig_handler;
932 sigaction(SIGHUP, &sa, NULL);
933 sigaction(SIGTERM, &sa, &oldsa_term);
934
935 closelog();
936
937 /*
80591bf6 938 * We must fork before setuid(), because we need to call
a169a454
KZ
939 * pam_close_session() as root.
940 */
941 child_pid = fork();
942 if (child_pid < 0) {
a169a454
KZ
943 warn(_("fork failed"));
944
945 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
946 pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
f950752b 947 sleepexit(EXIT_FAILURE);
a169a454
KZ
948 }
949
950 if (child_pid) {
951 /*
80591bf6 952 * parent - wait for child to finish, then clean up session
a169a454
KZ
953 */
954 close(0);
955 close(1);
956 close(2);
f4b03edb
KZ
957 free_getlogindefs_data();
958
a169a454
KZ
959 sa.sa_handler = SIG_IGN;
960 sigaction(SIGQUIT, &sa, NULL);
961 sigaction(SIGINT, &sa, NULL);
962
963 /* wait as long as any child is there */
964 while (wait(NULL) == -1 && errno == EINTR) ;
965 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
966
967 pam_setcred(cxt->pamh, PAM_DELETE_CRED);
968 pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
969 exit(EXIT_SUCCESS);
970 }
971
972 /*
973 * child
974 */
975 sigaction(SIGHUP, &oldsa_hup, NULL); /* restore old state */
976 sigaction(SIGTERM, &oldsa_term, NULL);
977 if (got_sig)
978 exit(EXIT_FAILURE);
979
980 /*
455fe9a0 981 * Problem: if the user's shell is a shell like ash that doesn't do
a169a454
KZ
982 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
983 * process in the pgrp, will kill us.
984 */
985
986 /* start new session */
987 setsid();
988
989 /* make sure we have a controlling tty */
990 open_tty(cxt->tty_path);
991 openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
992
993 /*
994 * TIOCSCTTY: steal tty from other process group.
995 */
996 if (ioctl(0, TIOCSCTTY, 1))
997 syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
998 signal(SIGINT, SIG_DFL);
999}
1000
241e4565
KZ
1001/*
1002 * Initialize $TERM, $HOME, ...
1003 */
1004static void init_environ(struct login_context *cxt)
1005{
1006 struct passwd *pwd = cxt->pwd;
86cf1ff4 1007 char *termenv, **env;
241e4565
KZ
1008 char tmp[PATH_MAX];
1009 int len, i;
1010
1011 termenv = getenv("TERM");
86cf1ff4
KZ
1012 if (termenv)
1013 termenv = xstrdup(termenv);
241e4565
KZ
1014
1015 /* destroy environment unless user has requested preservation (-p) */
1016 if (!cxt->keep_env) {
fea1cbf7 1017 environ = xmalloc(sizeof(char *));
241e4565
KZ
1018 memset(environ, 0, sizeof(char *));
1019 }
1020
984a6096
SK
1021 xsetenv("HOME", pwd->pw_dir, 0); /* legal to override */
1022 xsetenv("USER", pwd->pw_name, 1);
1023 xsetenv("SHELL", pwd->pw_shell, 1);
1024 xsetenv("TERM", termenv ? termenv : "dumb", 1);
86cf1ff4 1025 free(termenv);
241e4565 1026
05d8868d 1027 if (pwd->pw_uid) {
984a6096
SK
1028 if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH) != 0)
1029 err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
607e6b7c 1030
05d8868d
KZ
1031 } else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0 &&
1032 logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT) != 0) {
984a6096 1033 err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
05d8868d 1034 }
9f7293ea 1035
241e4565
KZ
1036 /* mailx will give a funny error msg if you forget this one */
1037 len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
06fa5817 1038 if (len > 0 && (size_t) len < sizeof(tmp))
984a6096 1039 xsetenv("MAIL", tmp, 0);
241e4565
KZ
1040
1041 /* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
1042 * not allow modifying it.
1043 */
984a6096 1044 xsetenv("LOGNAME", pwd->pw_name, 1);
241e4565
KZ
1045
1046 env = pam_getenvlist(cxt->pamh);
1047 for (i = 0; env && env[i]; i++)
1048 putenv(env[i]);
1049}
1050
cbbd5185 1051/*
80591bf6 1052 * This is called for the -h option, initializes cxt->{hostname,hostaddress}.
cbbd5185
KZ
1053 */
1054static void init_remote_info(struct login_context *cxt, char *remotehost)
1055{
92e386ca
KZ
1056 const char *domain;
1057 char *p;
cbbd5185
KZ
1058 struct addrinfo hints, *info = NULL;
1059
1060 cxt->remote = 1;
1061
92e386ca 1062 get_thishost(cxt, &domain);
cbbd5185 1063
92e386ca
KZ
1064 if (domain && (p = strchr(remotehost, '.')) &&
1065 strcasecmp(p + 1, domain) == 0)
cbbd5185
KZ
1066 *p = '\0';
1067
1068 cxt->hostname = xstrdup(remotehost);
1069
1070 memset(&hints, 0, sizeof(hints));
1071 hints.ai_flags = AI_ADDRCONFIG;
1072 cxt->hostaddress[0] = 0;
1073
1074 if (getaddrinfo(cxt->hostname, NULL, &hints, &info) == 0 && info) {
1075 if (info->ai_family == AF_INET) {
1076 struct sockaddr_in *sa =
1077 (struct sockaddr_in *) info->ai_addr;
1078
1079 memcpy(cxt->hostaddress, &(sa->sin_addr), sizeof(sa->sin_addr));
1080
1081 } else if (info->ai_family == AF_INET6) {
1082 struct sockaddr_in6 *sa =
1083 (struct sockaddr_in6 *) info->ai_addr;
1c8792f1
KZ
1084#ifdef IN6_IS_ADDR_V4MAPPED
1085 if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) {
1086 const uint8_t *bytes = sa->sin6_addr.s6_addr;
1087 struct in_addr addr = { *(const in_addr_t *) (bytes + 12) };
cbbd5185 1088
1c8792f1
KZ
1089 memcpy(cxt->hostaddress, &addr, sizeof(struct in_addr));
1090 } else
1091#endif
1092 memcpy(cxt->hostaddress, &(sa->sin6_addr), sizeof(sa->sin6_addr));
cbbd5185
KZ
1093 }
1094 freeaddrinfo(info);
1095 }
1096}
1097
7491906d
RM
1098static void __attribute__((__noreturn__)) usage(void)
1099{
1100 fputs(USAGE_HEADER, stdout);
1101 printf(_(" %s [-p] [-h <host>] [-H] [[-f] <username>]\n"), program_invocation_short_name);
1102 fputs(USAGE_SEPARATOR, stdout);
1103 fputs(_("Begin a session on the system.\n"), stdout);
62cd916f
RM
1104
1105 fputs(USAGE_OPTIONS, stdout);
1106 puts(_(" -p do not destroy the environment"));
1107 puts(_(" -f skip a second login authentication"));
1108 puts(_(" -h <host> hostname to be used for utmp logging"));
1109 puts(_(" -H suppress hostname in the login prompt"));
1110 printf(" --help %s\n", USAGE_OPTSTR_HELP);
1111 printf(" -V, --version %s\n", USAGE_OPTSTR_VERSION);
7491906d
RM
1112 printf(USAGE_MAN_TAIL("login(1)"));
1113 exit(EXIT_SUCCESS);
1114}
1115
ab71156c 1116int main(int argc, char **argv)
6dbe3af9 1117{
fc32d43e 1118 int c;
241e4565 1119 int cnt;
ab71156c
KZ
1120 char *childArgv[10];
1121 char *buff;
1122 int childArgc = 0;
1123 int retcode;
1b76608e 1124 struct sigaction act;
4f5f35fc 1125 struct passwd *pwd;
67e70761 1126
0180264f 1127 struct login_context cxt = {
fe2c9909
WJ
1128 .tty_mode = TTY_MODE, /* tty chmod() */
1129 .pid = getpid(), /* PID */
1130#ifdef HAVE_SECURITY_PAM_MISC_H
1131 .conv = { misc_conv, NULL } /* Linux-PAM conversation function */
1132#elif defined(HAVE_SECURITY_OPENPAM_H)
1133 .conv = { openpam_ttyconv, NULL } /* OpenPAM conversation function */
1134#endif
1135
0180264f 1136 };
ab71156c 1137
7491906d
RM
1138 /* the only two longopts to satisfy UL standards */
1139 enum { HELP_OPTION = CHAR_MAX + 1 };
1140 static const struct option longopts[] = {
1141 {"help", no_argument, NULL, HELP_OPTION},
1142 {"version", no_argument, NULL, 'V'},
1143 {NULL, 0, NULL, 0}
1144 };
1145
c9baf5da 1146 timeout = (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);
9abd9cde 1147
f17bda66
TS
1148 setlocale(LC_ALL, "");
1149 bindtextdomain(PACKAGE, LOCALEDIR);
1150 textdomain(PACKAGE);
1151
1152 /* TRANSLATORS: The standard value for %u is 60. */
1153 snprintf(timeout_msg, sizeof(timeout_msg),
1154 _("%s: timed out after %u seconds"),
1155 program_invocation_short_name, timeout);
1156
ab71156c 1157 signal(SIGALRM, timedout);
1b76608e
CQ
1158 (void) sigaction(SIGALRM, NULL, &act);
1159 act.sa_flags &= ~SA_RESTART;
1160 sigaction(SIGALRM, &act, NULL);
c9baf5da 1161 alarm(timeout);
ab71156c
KZ
1162 signal(SIGQUIT, SIG_IGN);
1163 signal(SIGINT, SIG_IGN);
1164
ab71156c
KZ
1165 setpriority(PRIO_PROCESS, 0, 0);
1166 initproctitle(argc, argv);
1167
1168 /*
1169 * -p is used by getty to tell login not to destroy the environment
1170 * -f is used to skip a second login authentication
1171 * -h is used by other servers to pass the name of the remote
1172 * host to login so that it may be placed in utmp and wtmp
1173 */
7491906d 1174 while ((c = getopt_long(argc, argv, "fHh:pV", longopts, NULL)) != -1)
fc32d43e 1175 switch (c) {
ab71156c 1176 case 'f':
a2de6177 1177 cxt.noauth = 1;
ab71156c
KZ
1178 break;
1179
92e386ca
KZ
1180 case 'H':
1181 cxt.nohost = 1;
1182 break;
1183
ab71156c
KZ
1184 case 'h':
1185 if (getuid()) {
1186 fprintf(stderr,
63578526 1187 _("login: -h is for superuser only\n"));
ab71156c 1188 exit(EXIT_FAILURE);
ea6c190a 1189 }
cbbd5185 1190 init_remote_info(&cxt, optarg);
ab71156c
KZ
1191 break;
1192
1193 case 'p':
241e4565 1194 cxt.keep_env = 1;
ab71156c
KZ
1195 break;
1196
0effd19e 1197 case 'V':
2c308875 1198 print_version(EXIT_SUCCESS);
7491906d
RM
1199 case HELP_OPTION:
1200 usage();
ab71156c 1201 default:
7491906d 1202 errtryhelp(EXIT_FAILURE);
ea6c190a 1203 }
ab71156c
KZ
1204 argc -= optind;
1205 argv += optind;
1206
1207 if (*argv) {
1208 char *p = *argv;
088d4876
KZ
1209
1210 /* username from command line */
1211 cxt.cmd_username = xstrdup(p);
1212 /* used temporary, it'll be replaced by username from PAM or/and cxt->pwd */
1213 cxt.username = cxt.cmd_username;
ab71156c 1214
80591bf6
BS
1215 /* Wipe the name - some people mistype their password here. */
1216 /* (Of course we are too late, but perhaps this helps a little...) */
ab71156c
KZ
1217 while (*p)
1218 *p++ = ' ';
1219 }
1220
5759de0c 1221 for (cnt = get_fd_tabsize() - 1; cnt > 2; cnt--)
ab71156c
KZ
1222 close(cnt);
1223
99f7c131 1224 setpgrp(); /* set pgid to pid this means that setsid() will fail */
c5bb244e 1225 init_tty(&cxt);
11784a84 1226
ab71156c 1227 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
364cda48 1228
4ab0df0a 1229 init_loginpam(&cxt);
fd6b7a7f 1230
a2de6177
KZ
1231 /* login -f, then the user has already been authenticated */
1232 cxt.noauth = cxt.noauth && getuid() == 0 ? 1 : 0;
ab71156c 1233
a2de6177
KZ
1234 if (!cxt.noauth)
1235 loginpam_auth(&cxt);
6dbe3af9 1236
ab71156c 1237 /*
98306fc5
KZ
1238 * Authentication may be skipped (for example, during krlogin, rlogin,
1239 * etc...), but it doesn't mean that we can skip other account checks.
80591bf6
BS
1240 * The account could be disabled or the password has expired (although
1241 * the kerberos ticket is valid). -- kzak@redhat.com (22-Feb-2006)
ab71156c 1242 */
98306fc5 1243 loginpam_acct(&cxt);
a7507436 1244
4f5f35fc
KZ
1245 cxt.pwd = xgetpwnam(cxt.username, &cxt.pwdbuf);
1246 if (!cxt.pwd) {
ab71156c 1247 warnx(_("\nSession setup problem, abort."));
9c6167c3
KZ
1248 syslog(LOG_ERR, _("Invalid user name \"%s\". Abort."),
1249 cxt.username);
4ab0df0a 1250 pam_end(cxt.pamh, PAM_SYSTEM_ERR);
f950752b 1251 sleepexit(EXIT_FAILURE);
2b6fc908 1252 }
e09947b5 1253
67e70761 1254 pwd = cxt.pwd;
3eb8b797 1255 cxt.username = pwd->pw_name;
e09947b5 1256
ab71156c 1257 /*
a7507436 1258 * Initialize the supplementary group list. This should be done before
80591bf6 1259 * pam_setcred, because PAM modules might add groups during that call.
a7507436 1260 *
80591bf6 1261 * For root we don't call initgroups, instead we call setgroups with
a7507436
KZ
1262 * group 0. This avoids the need to step through the whole group file,
1263 * which can cause problems if NIS, NIS+, LDAP or something similar
1264 * is used and the machine has network problems.
ab71156c 1265 */
a7507436
KZ
1266 retcode = pwd->pw_uid ? initgroups(cxt.username, pwd->pw_gid) : /* user */
1267 setgroups(0, NULL); /* root */
1268 if (retcode < 0) {
1269 syslog(LOG_ERR, _("groups initialization failed: %m"));
ab71156c 1270 warnx(_("\nSession setup problem, abort."));
4ab0df0a 1271 pam_end(cxt.pamh, PAM_SYSTEM_ERR);
f950752b 1272 sleepexit(EXIT_FAILURE);
ab71156c
KZ
1273 }
1274
59a184d9 1275 /*
80591bf6 1276 * Open PAM session (after successful authentication and account check).
59a184d9
KZ
1277 */
1278 loginpam_session(&cxt);
e09947b5 1279
ab71156c
KZ
1280 /* committed to login -- turn off timeout */
1281 alarm((unsigned int)0);
1282
1283 endpwent();
1284
29cc2a55 1285 cxt.quiet = get_hushlogin_status(pwd, 1);
e09947b5 1286
6e3bc8a6 1287 log_utmp(&cxt);
67e70761 1288 log_audit(&cxt, 1);
3761d0bb 1289 log_lastlog(&cxt);
e09947b5 1290
ff0392a0 1291 chown_tty(&cxt);
ab71156c 1292
ab71156c
KZ
1293 if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
1294 syslog(LOG_ALERT, _("setgid() failed"));
1295 exit(EXIT_FAILURE);
6dbe3af9 1296 }
5c36a0eb 1297
c6f23b3b 1298 if (pwd->pw_shell == NULL || *pwd->pw_shell == '\0')
ab71156c 1299 pwd->pw_shell = _PATH_BSHELL;
e09947b5 1300
516b00c4 1301 init_environ(&cxt); /* init $HOME, $TERM ... */
ab71156c 1302
3eb8b797 1303 setproctitle("login", cxt.username);
ab71156c 1304
516b00c4 1305 log_syslog(&cxt);
ab71156c 1306
3761d0bb 1307 if (!cxt.quiet) {
ab71156c
KZ
1308 motd();
1309
1310#ifdef LOGIN_STAT_MAIL
1311 /*
1312 * This turns out to be a bad idea: when the mail spool
1313 * is NFS mounted, and the NFS connection hangs, the
1314 * login hangs, even root cannot login.
1315 * Checking for mail should be done from the shell.
1316 */
1317 {
1318 struct stat st;
1319 char *mail;
1320
1321 mail = getenv("MAIL");
1322 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1323 if (st.st_mtime > st.st_atime)
1324 printf(_("You have new mail.\n"));
1325 else
1326 printf(_("You have mail.\n"));
1327 }
1328 }
d162fcb5 1329#endif
ab71156c
KZ
1330 }
1331
ab71156c 1332 /*
80591bf6
BS
1333 * Detach the controlling terminal, fork, and create a new session
1334 * and reinitialize syslog stuff.
ab71156c 1335 */
a169a454 1336 fork_session(&cxt);
ab71156c 1337
80591bf6 1338 /* discard permissions last so we can't get killed and drop core */
ab71156c
KZ
1339 if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1340 syslog(LOG_ALERT, _("setuid() failed"));
1341 exit(EXIT_FAILURE);
1342 }
1343
1344 /* wait until here to change directory! */
1345 if (chdir(pwd->pw_dir) < 0) {
1346 warn(_("%s: change directory failed"), pwd->pw_dir);
91d0a913
KZ
1347
1348 if (!getlogindefs_bool("DEFAULT_HOME", 1))
1349 exit(0);
ab71156c
KZ
1350 if (chdir("/"))
1351 exit(EXIT_FAILURE);
1352 pwd->pw_dir = "/";
1353 printf(_("Logging in with home = \"/\".\n"));
1354 }
1355
1356 /* if the shell field has a space: treat it like a shell script */
1357 if (strchr(pwd->pw_shell, ' ')) {
1aaee548 1358 xasprintf(&buff, "exec %s", pwd->pw_shell);
ab71156c
KZ
1359 childArgv[childArgc++] = "/bin/sh";
1360 childArgv[childArgc++] = "-sh";
1361 childArgv[childArgc++] = "-c";
1362 childArgv[childArgc++] = buff;
1363 } else {
cbbd5185
KZ
1364 char tbuf[PATH_MAX + 2], *p;
1365
ab71156c
KZ
1366 tbuf[0] = '-';
1367 xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1368 p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);
1369
1370 childArgv[childArgc++] = pwd->pw_shell;
2f228f8c 1371 childArgv[childArgc++] = xstrdup(tbuf);
ab71156c
KZ
1372 }
1373
1374 childArgv[childArgc++] = NULL;
1375
1376 execvp(childArgv[0], childArgv + 1);
1377
1378 if (!strcmp(childArgv[0], "/bin/sh"))
1379 warn(_("couldn't exec shell script"));
1380 else
1381 warn(_("no shell"));
1382
1383 exit(EXIT_SUCCESS);
6dbe3af9 1384}