]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/login.c
login: cleanup begin of the login.c file
[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 *
7 * Michael Glad (glad@daimi.dk)
8 * Computer Science Department, Aarhus University, Denmark
9 * 1990-07-04
10 *
6dbe3af9
KZ
11 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms are permitted
15 * provided that the above copyright notice and this paragraph are
16 * duplicated in all such forms and that any documentation,
17 * advertising materials, and other materials related to such
18 * distribution and use acknowledge that the software was developed
19 * by the University of California, Berkeley. The name of the
20 * University may not be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
24 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25 */
6dbe3af9 26#include <sys/param.h>
fd6b7a7f 27#include <stdio.h>
6dbe3af9
KZ
28#include <ctype.h>
29#include <unistd.h>
30#include <getopt.h>
31#include <memory.h>
fd6b7a7f 32#include <time.h>
6dbe3af9
KZ
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/resource.h>
36#include <sys/file.h>
37#include <termios.h>
38#include <string.h>
6dbe3af9 39#include <sys/ioctl.h>
fd6b7a7f 40#include <sys/wait.h>
6dbe3af9
KZ
41#include <signal.h>
42#include <errno.h>
43#include <grp.h>
44#include <pwd.h>
fd6b7a7f 45#include <utmp.h>
6dbe3af9
KZ
46#include <setjmp.h>
47#include <stdlib.h>
6dbe3af9
KZ
48#include <sys/syslog.h>
49#include <sys/sysmacros.h>
5476bf9b 50#include <linux/major.h>
6dbe3af9 51#include <netdb.h>
cb5acd69 52#include <lastlog.h>
905045d4
KZ
53#include <security/pam_appl.h>
54#include <security/pam_misc.h>
f8bdba2f
KZ
55#ifdef HAVE_LIBAUDIT
56# include <libaudit.h>
57#endif
0aeb57ac
FG
58#ifdef HAVE_CRYPT_H
59#include <crypt.h>
60#endif
61
cb5acd69
KZ
62#include "c.h"
63#include "setproctitle.h"
66ee8158 64#include "pathnames.h"
66ee8158 65#include "login.h"
8abcf290 66#include "strutils.h"
7eda085c 67#include "nls.h"
3e31a2df 68#include "xalloc.h"
6dbe3af9 69
9244d3c2 70#define PAM_MAX_LOGIN_TRIES 3
905045d4 71#define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
fd6b7a7f 72
fd6b7a7f
KZ
73#define SLEEP_EXIT_TIMEOUT 5
74
fd6b7a7f
KZ
75#ifdef USE_TTY_GROUP
76# define TTY_MODE 0620
77#else
78# define TTY_MODE 0600
79#endif
80
6dbe3af9 81#define TTYGRPNAME "tty" /* name of group to own ttys */
726f69e2
KZ
82
83#ifndef MAXPATHLEN
84# define MAXPATHLEN 1024
6dbe3af9
KZ
85#endif
86
87/*
88 * This bounds the time given to login. Not a define so it can
89 * be patched on machines where it's too small.
90 */
cb5acd69 91int timeout = 60;
6dbe3af9 92
48d7b13a
KZ
93struct passwd *pwd;
94
e8f26419 95static struct passwd pwdcopy;
cb5acd69 96char hostaddress[16]; /* used in checktty.c */
ea6c190a 97sa_family_t hostfamily; /* used in checktty.c */
364cda48
KZ
98char *hostname; /* idem */
99static char *username, *tty_name, *tty_number;
66ee8158
KZ
100static char thishost[100];
101static int failures = 1;
1d4ad1de 102static pid_t pid;
6dbe3af9 103
cb5acd69
KZ
104static void timedout (int);
105static void sigint (int);
106static void motd (void);
107static void dolastlog (int quiet);
108
eb63b9b8
KZ
109/* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
110/* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
111 He writes: "Login performs open() on a tty in a blocking mode.
112 In some cases it may make login wait in open() for carrier infinitely,
113 for example if the line is a simplistic case of a three-wire serial
114 connection. I believe login should open the line in the non-blocking mode
115 leaving the decision to make a connection to getty (where it actually
116 belongs). */
66ee8158 117static void
364cda48 118opentty(const char * tty) {
1d4ad1de
KZ
119 int i, fd, flags;
120
121 fd = open(tty, O_RDWR | O_NONBLOCK);
122 if (fd == -1) {
960cf573 123 syslog(LOG_ERR, _("FATAL: can't reopen tty: %m"));
66020e56 124 sleepexit(EXIT_FAILURE);
1d4ad1de 125 }
eb63b9b8 126
8bee984a
KZ
127 if (!isatty(fd)) {
128 close(fd);
129 syslog(LOG_ERR, _("FATAL: %s is not a terminal"), tty);
66020e56 130 sleepexit(EXIT_FAILURE);
8bee984a
KZ
131 }
132
1d4ad1de
KZ
133 flags = fcntl(fd, F_GETFL);
134 flags &= ~O_NONBLOCK;
135 fcntl(fd, F_SETFL, flags);
e09947b5 136
1d4ad1de
KZ
137 for (i = 0; i < fd; i++)
138 close(i);
139 for (i = 0; i < 3; i++)
140 if (fd != i)
141 dup2(fd, i);
142 if (fd >= 3)
143 close(fd);
6dbe3af9
KZ
144}
145
24f4bbff
KZ
146/* In case login is suid it was possible to use a hardlink as stdin
147 and exploit races for a local root exploit. (Wojciech Purczynski). */
148/* More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn);
149 here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */
150/* All of this is a problem only when login is suid, which it isnt. */
151static void
152check_ttyname(char *ttyn) {
153 struct stat statbuf;
154
8bee984a
KZ
155 if (ttyn == NULL
156 || *ttyn == '\0'
157 || lstat(ttyn, &statbuf)
24f4bbff 158 || !S_ISCHR(statbuf.st_mode)
62eabbf4
KZ
159 || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))
160 || (access(ttyn, R_OK | W_OK) != 0)) {
66020e56 161
24f4bbff 162 syslog(LOG_ERR, _("FATAL: bad tty"));
66020e56 163 sleepexit(EXIT_FAILURE);
24f4bbff
KZ
164 }
165}
166
48d7b13a 167#ifdef LOGIN_CHOWN_VCS
fd6b7a7f
KZ
168/* true if the filedescriptor fd is a console tty, very Linux specific */
169static int
364cda48 170consoletty(int fd) {
fd6b7a7f
KZ
171 struct stat stb;
172
e09947b5 173 if ((fstat(fd, &stb) >= 0)
fd6b7a7f
KZ
174 && (major(stb.st_rdev) == TTY_MAJOR)
175 && (minor(stb.st_rdev) < 64)) {
176 return 1;
177 }
fd6b7a7f
KZ
178 return 0;
179}
48d7b13a 180#endif
fd6b7a7f 181
364cda48
KZ
182/*
183 * Log failed login attempts in _PATH_BTMP if that exists.
184 * Must be called only with username the name of an actual user.
185 * The most common login failure is to give password instead of username.
186 */
364cda48
KZ
187static void
188logbtmp(const char *line, const char *username, const char *hostname) {
189 struct utmp ut;
75d4dbb0 190 struct timeval tv;
364cda48
KZ
191
192 memset(&ut, 0, sizeof(ut));
193
194 strncpy(ut.ut_user, username ? username : "(unknown)",
195 sizeof(ut.ut_user));
196
197 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
198 xstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
199
200#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
75d4dbb0
KZ
201 gettimeofday(&tv, NULL);
202 ut.ut_tv.tv_sec = tv.tv_sec;
203 ut.ut_tv.tv_usec = tv.tv_usec;
364cda48
KZ
204#else
205 {
206 time_t t;
207 time(&t);
208 ut.ut_time = t; /* ut_time is not always a time_t */
209 }
210#endif
211
212 ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
1d4ad1de 213 ut.ut_pid = pid;
364cda48
KZ
214 if (hostname) {
215 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
216 if (hostaddress[0])
ea6c190a 217 memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
364cda48 218 }
48d7b13a 219#if HAVE_UPDWTMP /* bad luck for ancient systems */
364cda48
KZ
220 updwtmp(_PATH_BTMP, &ut);
221#endif
222}
b2452358
KZ
223
224
225static int child_pid = 0;
226static volatile int got_sig = 0;
227
228/*
229 * This handler allows to inform a shell about signals to login. If you have
230 * (root) permissions you can kill all login childrent by one signal to login
231 * process.
232 *
233 * Also, parent who is session leader is able (before setsid() in child) to
234 * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
235 */
236static void
237sig_handler(int signal)
238{
239 if(child_pid)
240 kill(-child_pid, signal);
241 else
242 got_sig = 1;
243 if(signal == SIGTERM)
244 kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
245}
246
f8bdba2f
KZ
247#ifdef HAVE_LIBAUDIT
248static void
249logaudit(const char *tty, const char *username, const char *hostname,
250 struct passwd *pwd, int status)
251{
f8bdba2f
KZ
252 int audit_fd;
253
254 audit_fd = audit_open();
255 if (audit_fd == -1)
256 return;
e797d832 257 if (!pwd && username)
f8bdba2f 258 pwd = getpwnam(username);
f8bdba2f 259
8ccf0b25
SG
260 audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN,
261 NULL, "login", username ? username : "(unknown)",
878a3697 262 pwd ? pwd->pw_uid : (unsigned int) -1, hostname, NULL, tty, status);
f8bdba2f
KZ
263
264 close(audit_fd);
265}
266#else /* ! HAVE_LIBAUDIT */
267# define logaudit(tty, username, hostname, pwd, status)
268#endif /* HAVE_LIBAUDIT */
269
53e9eda3 270/* encapsulate stupid "void **" pam_get_item() API */
905045d4 271static int loginpam_get_username(pam_handle_t *pamh, char **name)
53e9eda3
KZ
272{
273 const void *item = (void *) *name;
274 int rc;
275 rc = pam_get_item(pamh, PAM_USER, &item);
276 *name = (char *) item;
277 return rc;
278}
53e9eda3 279
905045d4
KZ
280static int loginpam_err(pam_handle_t *pamh, int retcode)
281{
282 const char *msg = pam_strerror(pamh, retcode);
283
284 if (msg) {
285 fprintf(stderr, "\n%s\n", msg);
286 syslog(LOG_ERR, "%s", msg);
287 }
288 pam_end(pamh, retcode);
289 exit(EXIT_FAILURE);
290
291}
292
3c0e680c
KZ
293/*
294 * We need to check effective UID/GID. For example $HOME could be on root
295 * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
296 * The open(2) seems as the surest solution.
297 * -- kzak@redhat.com (10-Apr-2009)
298 */
299int
300effective_access(const char *path, int mode)
301{
302 int fd = open(path, mode);
303 if (fd != -1)
304 close(fd);
305 return fd == -1 ? -1 : 0;
306}
307
6dbe3af9 308int
5c36a0eb 309main(int argc, char **argv)
6dbe3af9 310{
22853e4a 311 extern int optind;
fd6b7a7f
KZ
312 extern char *optarg, **environ;
313 struct group *gr;
314 register int ch;
315 register char *p;
abd0a5c9 316 int fflag, hflag, pflag, cnt;
5c36a0eb
KZ
317 int quietlog, passwd_req;
318 char *domain, *ttyn;
8bee984a 319 char tbuf[MAXPATHLEN + 2];
fd6b7a7f 320 char *termenv;
24f4bbff
KZ
321 char *childArgv[10];
322 char *buff;
fd6b7a7f 323 int childArgc = 0;
fd6b7a7f
KZ
324 int retcode;
325 pam_handle_t *pamh = NULL;
326 struct pam_conv conv = { misc_conv, NULL };
b2452358 327 struct sigaction sa, oldsa_hup, oldsa_term;
48d7b13a 328#ifdef LOGIN_CHOWN_VCS
66ee8158
KZ
329 char vcsn[20], vcsan[20];
330#endif
6dbe3af9 331
1d4ad1de
KZ
332 pid = getpid();
333
fd6b7a7f 334 signal(SIGALRM, timedout);
649efbb3 335 siginterrupt(SIGALRM,1); /* we have to interrupt syscalls like ioclt() */
fd6b7a7f
KZ
336 alarm((unsigned int)timeout);
337 signal(SIGQUIT, SIG_IGN);
338 signal(SIGINT, SIG_IGN);
7eda085c
KZ
339
340 setlocale(LC_ALL, "");
341 bindtextdomain(PACKAGE, LOCALEDIR);
342 textdomain(PACKAGE);
e09947b5 343
fd6b7a7f 344 setpriority(PRIO_PROCESS, 0, 0);
5c36a0eb 345 initproctitle(argc, argv);
e09947b5 346
fd6b7a7f
KZ
347 /*
348 * -p is used by getty to tell login not to destroy the environment
e09947b5 349 * -f is used to skip a second login authentication
fd6b7a7f
KZ
350 * -h is used by other servers to pass the name of the remote
351 * host to login so that it may be placed in utmp and wtmp
352 */
353 gethostname(tbuf, sizeof(tbuf));
c07ebfa1 354 xstrncpy(thishost, tbuf, sizeof(thishost));
c0f19ccf 355 domain = strchr(tbuf, '.');
e09947b5 356
364cda48 357 username = tty_name = hostname = NULL;
fd6b7a7f
KZ
358 fflag = hflag = pflag = 0;
359 passwd_req = 1;
5c36a0eb 360
ffc43748 361 while ((ch = getopt(argc, argv, "fh:p")) != -1)
fd6b7a7f
KZ
362 switch (ch) {
363 case 'f':
364 fflag = 1;
365 break;
e09947b5 366
fd6b7a7f
KZ
367 case 'h':
368 if (getuid()) {
369 fprintf(stderr,
7eda085c 370 _("login: -h for super-user only.\n"));
66020e56 371 exit(EXIT_FAILURE);
fd6b7a7f
KZ
372 }
373 hflag = 1;
c0f19ccf 374 if (domain && (p = strchr(optarg, '.')) &&
fd6b7a7f
KZ
375 strcasecmp(p, domain) == 0)
376 *p = 0;
364cda48 377
7eda085c 378 hostname = strdup(optarg); /* strdup: Ambrose C. Li */
364cda48 379 {
ea6c190a
KZ
380 struct addrinfo hints, *info = NULL;
381
382 memset(&hints, 0, sizeof(hints));
383 hints.ai_flags = AI_ADDRCONFIG;
384
385 hostaddress[0] = 0;
386
387 if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) {
388 if (info->ai_family == AF_INET) {
389 struct sockaddr_in *sa =
390 (struct sockaddr_in *) info->ai_addr;
391 memcpy(hostaddress, &(sa->sin_addr),
392 sizeof(sa->sin_addr));
393 }
394 else if (info->ai_family == AF_INET6) {
395 struct sockaddr_in6 *sa =
396 (struct sockaddr_in6 *) info->ai_addr;
397 memcpy(hostaddress, &(sa->sin6_addr),
398 sizeof(sa->sin6_addr));
399 }
400 hostfamily = info->ai_family;
401 freeaddrinfo(info);
402 }
fd6b7a7f
KZ
403 }
404 break;
e09947b5 405
fd6b7a7f
KZ
406 case 'p':
407 pflag = 1;
408 break;
409
410 case '?':
411 default:
412 fprintf(stderr,
7eda085c 413 _("usage: login [-fp] [username]\n"));
66020e56 414 exit(EXIT_FAILURE);
fd6b7a7f
KZ
415 }
416 argc -= optind;
417 argv += optind;
abd0a5c9 418
fd6b7a7f 419 if (*argv) {
5c36a0eb
KZ
420 char *p = *argv;
421 username = strdup(p);
abd0a5c9 422
5c36a0eb
KZ
423 /* wipe name - some people mistype their password here */
424 /* (of course we are too late, but perhaps this helps a little ..) */
425 while(*p)
426 *p++ = ' ';
abd0a5c9 427 }
22853e4a 428
fd6b7a7f
KZ
429 for (cnt = getdtablesize(); cnt > 2; cnt--)
430 close(cnt);
e09947b5 431
8bee984a
KZ
432 /* note that libc checks that the file descriptor is a terminal, so we don't
433 * have to call isatty() here */
fd6b7a7f 434 ttyn = ttyname(0);
24f4bbff
KZ
435 check_ttyname(ttyn);
436
364cda48
KZ
437 if (strncmp(ttyn, "/dev/", 5) == 0)
438 tty_name = ttyn+5;
439 else
440 tty_name = ttyn;
441
442 if (strncmp(ttyn, "/dev/tty", 8) == 0)
443 tty_number = ttyn+8;
444 else {
fd6b7a7f 445 char *p = ttyn;
fd6b7a7f 446 while (*p && !isdigit(*p)) p++;
364cda48 447 tty_number = p;
fd6b7a7f 448 }
364cda48 449
48d7b13a 450#ifdef LOGIN_CHOWN_VCS
364cda48
KZ
451 /* find names of Virtual Console devices, for later mode change */
452 snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number);
453 snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number);
66ee8158 454#endif
fd6b7a7f 455
1d4ad1de 456 /* set pgid to pid */
fd6b7a7f 457 setpgrp();
1d4ad1de 458 /* this means that setsid() will fail */
e09947b5 459
fd6b7a7f
KZ
460 {
461 struct termios tt, ttt;
e09947b5 462
fd6b7a7f
KZ
463 tcgetattr(0, &tt);
464 ttt = tt;
465 ttt.c_cflag &= ~HUPCL;
66ee8158 466
364cda48 467 /* These can fail, e.g. with ttyn on a read-only filesystem */
11784a84
KZ
468 if (fchown(0, 0, 0)) {
469 ; /* glibc warn_unused_result */
470 }
471
453b3614 472 fchmod(0, TTY_MODE);
364cda48
KZ
473
474 /* Kill processes left on this tty */
475 tcsetattr(0,TCSAFLUSH,&ttt);
476 signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
477 vhangup();
478 signal(SIGHUP, SIG_DFL);
1d4ad1de
KZ
479
480 /* open stdin,stdout,stderr to the tty */
fd6b7a7f 481 opentty(ttyn);
e09947b5 482
1d4ad1de 483 /* restore tty modes */
fd6b7a7f
KZ
484 tcsetattr(0,TCSAFLUSH,&tt);
485 }
22853e4a 486
fd6b7a7f
KZ
487 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
488
22853e4a
KZ
489 /*
490 * username is initialized to NULL
491 * and if specified on the command line it is set.
492 * Therefore, we are safe not setting it to anything
493 */
fd6b7a7f 494
067f5343 495 retcode = pam_start(hflag?"remote":"login",username, &conv, &pamh);
fd6b7a7f 496 if(retcode != PAM_SUCCESS) {
66020e56 497 warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, retcode));
e8f26419
KZ
498 syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
499 pam_strerror(pamh, retcode));
66020e56 500 exit(EXIT_FAILURE);
fd6b7a7f 501 }
905045d4 502
fd6b7a7f 503 /* hostname & tty are either set to NULL or their correct values,
905045d4
KZ
504 * depending on how much we know
505 */
fd6b7a7f 506 retcode = pam_set_item(pamh, PAM_RHOST, hostname);
905045d4
KZ
507 if (is_pam_failure(retcode))
508 loginpam_err(pamh, retcode);
509
364cda48 510 retcode = pam_set_item(pamh, PAM_TTY, tty_name);
905045d4
KZ
511 if (is_pam_failure(retcode))
512 loginpam_err(pamh, retcode);
7eda085c 513
22853e4a
KZ
514 /*
515 * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
516 * so that the "login: " prompt gets localized. Unfortunately,
517 * PAM doesn't have an interface to specify the "Password: " string
518 * (yet).
519 */
eb63b9b8 520 retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
905045d4
KZ
521 if (is_pam_failure(retcode))
522 loginpam_err(pamh, retcode);
eb63b9b8 523
30568eec
KZ
524 if (username) {
525 /* we need't the original username. We have to follow PAM. */
526 free(username);
527 username = NULL;
528 }
529
fd6b7a7f
KZ
530 /* if fflag == 1, then the user has already been authenticated */
531 if (fflag && (getuid() == 0))
532 passwd_req = 0;
533 else
534 passwd_req = 1;
6dbe3af9 535
fd6b7a7f
KZ
536 if(passwd_req == 1) {
537 int failcount=0;
538
364cda48 539 /* if we didn't get a user on the command line, set it to NULL */
905045d4 540 loginpam_get_username(pamh, &username);
364cda48 541
fd6b7a7f
KZ
542 /* there may be better ways to deal with some of these
543 conditions, but at least this way I don't think we'll
544 be giving away information... */
545 /* Perhaps someday we can trust that all PAM modules will
546 pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
547
548 retcode = pam_authenticate(pamh, 0);
549 while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
550 ((retcode == PAM_AUTH_ERR) ||
551 (retcode == PAM_USER_UNKNOWN) ||
552 (retcode == PAM_CRED_INSUFFICIENT) ||
553 (retcode == PAM_AUTHINFO_UNAVAIL))) {
905045d4 554 loginpam_get_username(pamh, &username);
364cda48 555
7eda085c 556 syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
364cda48
KZ
557 failcount, hostname, username, pam_strerror(pamh, retcode));
558 logbtmp(tty_name, username, hostname);
f8bdba2f 559 logaudit(tty_name, username, hostname, NULL, 0);
364cda48 560
7eda085c 561 fprintf(stderr,_("Login incorrect\n\n"));
fd6b7a7f
KZ
562 pam_set_item(pamh,PAM_USER,NULL);
563 retcode = pam_authenticate(pamh, 0);
6dbe3af9
KZ
564 }
565
905045d4
KZ
566 if (is_pam_failure(retcode)) {
567 loginpam_get_username(pamh, &username);
6dbe3af9 568
364cda48 569 if (retcode == PAM_MAXTRIES)
7eda085c
KZ
570 syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
571 "%s, %s"), failcount, hostname, username,
2b6fc908 572 pam_strerror(pamh, retcode));
fd6b7a7f 573 else
7eda085c 574 syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
2b6fc908 575 hostname, username, pam_strerror(pamh, retcode));
364cda48 576 logbtmp(tty_name, username, hostname);
f8bdba2f 577 logaudit(tty_name, username, hostname, NULL, 0);
6dbe3af9 578
7eda085c 579 fprintf(stderr,_("\nLogin incorrect\n"));
fd6b7a7f 580 pam_end(pamh, retcode);
66020e56 581 exit(EXIT_SUCCESS);
fd6b7a7f 582 }
987195ce 583 }
726f69e2 584
987195ce
KZ
585 /*
586 * Authentication may be skipped (for example, during krlogin, rlogin, etc...),
587 * but it doesn't mean that we can skip other account checks. The account
588 * could be disabled or password expired (althought kerberos ticket is valid).
589 * -- kzak@redhat.com (22-Feb-2006)
590 */
591 retcode = pam_acct_mgmt(pamh, 0);
6dbe3af9 592
905045d4 593 if (retcode == PAM_NEW_AUTHTOK_REQD)
987195ce 594 retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
905045d4
KZ
595 if (is_pam_failure(retcode))
596 loginpam_err(pamh, retcode);
987195ce 597
1d4ad1de
KZ
598 /*
599 * Grab the user information out of the password file for future usage
600 * First get the username that we are actually using, though.
601 */
905045d4
KZ
602 retcode = loginpam_get_username(pamh, &username);
603 if (is_pam_failure(retcode))
604 loginpam_err(pamh, retcode);
e8f26419
KZ
605
606 if (!username || !*username) {
66020e56 607 warnx(_("\nSession setup problem, abort."));
e8f26419
KZ
608 syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
609 __FUNCTION__, __LINE__);
610 pam_end(pamh, PAM_SYSTEM_ERR);
66020e56 611 exit(EXIT_FAILURE);
e8f26419
KZ
612 }
613 if (!(pwd = getpwnam(username))) {
66020e56 614 warnx(_("\nSession setup problem, abort."));
e8f26419 615 syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
612721db 616 username, __FUNCTION__, __LINE__);
e8f26419 617 pam_end(pamh, PAM_SYSTEM_ERR);
66020e56 618 exit(EXIT_FAILURE);
e8f26419
KZ
619 }
620
1d4ad1de
KZ
621 /*
622 * Create a copy of the pwd struct - otherwise it may get
623 * clobbered by PAM
624 */
e8f26419
KZ
625 memcpy(&pwdcopy, pwd, sizeof(*pwd));
626 pwd = &pwdcopy;
627 pwd->pw_name = strdup(pwd->pw_name);
628 pwd->pw_passwd = strdup(pwd->pw_passwd);
629 pwd->pw_gecos = strdup(pwd->pw_gecos);
630 pwd->pw_dir = strdup(pwd->pw_dir);
631 pwd->pw_shell = strdup(pwd->pw_shell);
632 if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
633 !pwd->pw_dir || !pwd->pw_shell) {
66020e56 634 warnx(_("out of memory"));
e8f26419
KZ
635 syslog(LOG_ERR, "Out of memory");
636 pam_end(pamh, PAM_SYSTEM_ERR);
66020e56 637 exit(EXIT_FAILURE);
c07ebfa1 638 }
e8f26419 639 username = pwd->pw_name;
c07ebfa1
KZ
640
641 /*
642 * Initialize the supplementary group list.
643 * This should be done before pam_setcred because
644 * the PAM modules might add groups during pam_setcred.
645 */
e8f26419
KZ
646 if (initgroups(username, pwd->pw_gid) < 0) {
647 syslog(LOG_ERR, "initgroups: %m");
66020e56 648 warnx(_("\nSession setup problem, abort."));
e8f26419 649 pam_end(pamh, PAM_SYSTEM_ERR);
66020e56 650 exit(EXIT_FAILURE);
e8f26419 651 }
6dbe3af9 652
c07ebfa1 653 retcode = pam_open_session(pamh, 0);
905045d4
KZ
654 if (is_pam_failure(retcode))
655 loginpam_err(pamh, retcode);
6dbe3af9 656
c07ebfa1 657 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
905045d4 658 if (is_pam_failure(retcode)) {
f2c7ae1d 659 pam_close_session(pamh, 0);
905045d4
KZ
660 loginpam_err(pamh, retcode);
661 }
6dbe3af9 662
fd6b7a7f
KZ
663 /* committed to login -- turn off timeout */
664 alarm((unsigned int)0);
e09947b5 665
fd6b7a7f 666 endpwent();
e09947b5 667
fd6b7a7f
KZ
668 /* This requires some explanation: As root we may not be able to
669 read the directory of the user if it is on an NFS mounted
670 filesystem. We temporarily set our effective uid to the user-uid
e09947b5
KZ
671 making sure that we keep root privs. in the real uid.
672
fd6b7a7f
KZ
673 A portable solution would require a fork(), but we rely on Linux
674 having the BSD setreuid() */
e09947b5 675
fd6b7a7f
KZ
676 {
677 char tmpstr[MAXPATHLEN];
678 uid_t ruid = getuid();
679 gid_t egid = getegid();
2b6fc908
KZ
680
681 /* avoid snprintf - old systems do not have it, or worse,
682 have a libc in which snprintf is the same as sprintf */
683 if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
684 quietlog = 0;
685 else {
686 sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
687 setregid(-1, pwd->pw_gid);
688 setreuid(0, pwd->pw_uid);
3c0e680c 689 quietlog = (effective_access(tmpstr, O_RDONLY) == 0);
2b6fc908
KZ
690 setuid(0); /* setreuid doesn't do it alone! */
691 setreuid(ruid, 0);
692 setregid(-1, egid);
693 }
fd6b7a7f 694 }
e09947b5 695
fd6b7a7f
KZ
696 /* for linux, write entries in utmp and wtmp */
697 {
698 struct utmp ut;
fd6b7a7f 699 struct utmp *utp;
75d4dbb0 700 struct timeval tv;
e09947b5 701
fd6b7a7f
KZ
702 utmpname(_PATH_UTMP);
703 setutent();
5c36a0eb 704
1d4ad1de 705 /* Find pid in utmp.
5c36a0eb
KZ
706login sometimes overwrites the runlevel entry in /var/run/utmp,
707confusing sysvinit. I added a test for the entry type, and the problem
708was gone. (In a runlevel entry, st_pid is not really a pid but some number
709calculated from the previous and current runlevel).
710Michael Riepe <michael@stud.uni-hannover.de>
711 */
712 while ((utp = getutent()))
1d4ad1de 713 if (utp->ut_pid == pid
5c36a0eb
KZ
714 && utp->ut_type >= INIT_PROCESS
715 && utp->ut_type <= DEAD_PROCESS)
716 break;
7eda085c
KZ
717
718 /* If we can't find a pre-existing entry by pid, try by line.
719 BSD network daemons may rely on this. (anonymous) */
720 if (utp == NULL) {
721 setutent();
722 ut.ut_type = LOGIN_PROCESS;
364cda48 723 strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
d03dd608 724 utp = getutline(&ut);
7eda085c 725 }
e09947b5 726
fd6b7a7f
KZ
727 if (utp) {
728 memcpy(&ut, utp, sizeof(ut));
729 } else {
730 /* some gettys/telnetds don't initialize utmp... */
731 memset(&ut, 0, sizeof(ut));
6dbe3af9 732 }
e09947b5 733
fd6b7a7f 734 if (ut.ut_id[0] == 0)
364cda48 735 strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
e09947b5 736
fd6b7a7f 737 strncpy(ut.ut_user, username, sizeof(ut.ut_user));
364cda48 738 xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
7eda085c 739#ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */
75d4dbb0
KZ
740 gettimeofday(&tv, NULL);
741 ut.ut_tv.tv_sec = tv.tv_sec;
742 ut.ut_tv.tv_usec = tv.tv_usec;
7eda085c
KZ
743#else
744 {
364cda48
KZ
745 time_t t;
746 time(&t);
747 ut.ut_time = t; /* ut_time is not always a time_t */
7eda085c
KZ
748 /* glibc2 #defines it as ut_tv.tv_sec */
749 }
750#endif
fd6b7a7f 751 ut.ut_type = USER_PROCESS;
1d4ad1de 752 ut.ut_pid = pid;
fd6b7a7f 753 if (hostname) {
364cda48
KZ
754 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
755 if (hostaddress[0])
ea6c190a 756 memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
6dbe3af9 757 }
e09947b5 758
fd6b7a7f
KZ
759 pututline(&ut);
760 endutent();
5c36a0eb 761
48d7b13a 762#if HAVE_UPDWTMP
5c36a0eb 763 updwtmp(_PATH_WTMP, &ut);
5c36a0eb
KZ
764#else
765 /* Probably all this locking below is just nonsense,
766 and the short version is OK as well. */
e09947b5 767 {
22853e4a 768 int lf, wtmp;
fd6b7a7f
KZ
769 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
770 flock(lf, LOCK_EX);
771 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
772 write(wtmp, (char *)&ut, sizeof(ut));
773 close(wtmp);
774 }
775 flock(lf, LOCK_UN);
776 close(lf);
777 }
778 }
c07ebfa1 779#endif
5c36a0eb 780 }
e09947b5 781
f8bdba2f 782 logaudit(tty_name, username, hostname, pwd, 1);
fd6b7a7f 783 dolastlog(quietlog);
e09947b5 784
11784a84
KZ
785 if (fchown(0, pwd->pw_uid,
786 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
787 warn(_("change terminal owner failed"));
788
453b3614 789 fchmod(0, TTY_MODE);
fd6b7a7f 790
48d7b13a 791#ifdef LOGIN_CHOWN_VCS
e09947b5 792 /* if tty is one of the VC's then change owner and mode of the
fd6b7a7f
KZ
793 special /dev/vcs devices as well */
794 if (consoletty(0)) {
11784a84
KZ
795
796 if (chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid)))
797 warn(_("change terminal owner failed"));
798 if (chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid)))
799 warn(_("change terminal owner failed"));
800
fd6b7a7f
KZ
801 chmod(vcsn, TTY_MODE);
802 chmod(vcsan, TTY_MODE);
803 }
66ee8158 804#endif
fd6b7a7f 805
81e9987b
KZ
806 if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
807 syslog(LOG_ALERT, _("setgid() failed"));
808 exit(EXIT_FAILURE);
809 }
810
e09947b5 811
fd6b7a7f
KZ
812 if (*pwd->pw_shell == '\0')
813 pwd->pw_shell = _PATH_BSHELL;
e09947b5 814
fd6b7a7f
KZ
815 /* preserve TERM even without -p flag */
816 {
817 char *ep;
e09947b5 818
fd6b7a7f
KZ
819 if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
820 termenv = "dumb";
821 }
e09947b5 822
fd6b7a7f
KZ
823 /* destroy environment unless user has requested preservation */
824 if (!pflag)
825 {
6dbe3af9
KZ
826 environ = (char**)malloc(sizeof(char*));
827 memset(environ, 0, sizeof(char*));
fd6b7a7f 828 }
e09947b5 829
fd6b7a7f
KZ
830 setenv("HOME", pwd->pw_dir, 0); /* legal to override */
831 if(pwd->pw_uid)
832 setenv("PATH", _PATH_DEFPATH, 1);
833 else
834 setenv("PATH", _PATH_DEFPATH_ROOT, 1);
e09947b5 835
fd6b7a7f
KZ
836 setenv("SHELL", pwd->pw_shell, 1);
837 setenv("TERM", termenv, 1);
e09947b5 838
fd6b7a7f 839 /* mailx will give a funny error msg if you forget this one */
5c36a0eb
KZ
840 {
841 char tmp[MAXPATHLEN];
2b6fc908
KZ
842 /* avoid snprintf */
843 if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
844 sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
845 setenv("MAIL",tmp,0);
846 }
847 }
e09947b5 848
fd6b7a7f
KZ
849 /* LOGNAME is not documented in login(1) but
850 HP-UX 6.5 does it. We'll not allow modifying it.
851 */
852 setenv("LOGNAME", pwd->pw_name, 1);
6dbe3af9 853
fd6b7a7f
KZ
854 {
855 int i;
364cda48 856 char ** env = pam_getenvlist(pamh);
6dbe3af9 857
fd6b7a7f 858 if (env != NULL) {
364cda48
KZ
859 for (i=0; env[i]; i++) {
860 putenv(env[i]);
861 /* D(("env[%d] = %s", i,env[i])); */
fd6b7a7f 862 }
6dbe3af9 863 }
fd6b7a7f 864 }
5c36a0eb 865
5c36a0eb 866 setproctitle("login", username);
e09947b5 867
364cda48
KZ
868 if (!strncmp(tty_name, "ttyS", 4))
869 syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
e09947b5 870
fd6b7a7f
KZ
871 /* allow tracking of good logins.
872 -steve philp (sphilp@mail.alliance.net) */
e09947b5 873
fd6b7a7f
KZ
874 if (pwd->pw_uid == 0) {
875 if (hostname)
7eda085c 876 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
364cda48 877 tty_name, hostname);
fd6b7a7f 878 else
364cda48 879 syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
fd6b7a7f 880 } else {
e09947b5
KZ
881 if (hostname)
882 syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name,
fd6b7a7f 883 pwd->pw_name, hostname);
e09947b5
KZ
884 else
885 syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name,
fd6b7a7f
KZ
886 pwd->pw_name);
887 }
e09947b5 888
fd6b7a7f 889 if (!quietlog) {
fd6b7a7f 890 motd();
d162fcb5 891
48d7b13a 892#ifdef LOGIN_STAT_MAIL
d162fcb5
KZ
893 /*
894 * This turns out to be a bad idea: when the mail spool
895 * is NFS mounted, and the NFS connection hangs, the
896 * login hangs, even root cannot login.
897 * Checking for mail should be done from the shell.
898 */
899 {
900 struct stat st;
901 char *mail;
e09947b5 902
d162fcb5
KZ
903 mail = getenv("MAIL");
904 if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
e8f26419
KZ
905 if (st.st_mtime > st.st_atime)
906 printf(_("You have new mail.\n"));
907 else
908 printf(_("You have mail.\n"));
d162fcb5 909 }
2b6fc908 910 }
d162fcb5 911#endif
fd6b7a7f 912 }
e09947b5 913
fd6b7a7f
KZ
914 signal(SIGALRM, SIG_DFL);
915 signal(SIGQUIT, SIG_DFL);
fd6b7a7f 916 signal(SIGTSTP, SIG_IGN);
5c36a0eb 917
b2452358
KZ
918 memset(&sa, 0, sizeof(sa));
919 sa.sa_handler = SIG_IGN;
920 sigaction(SIGINT, &sa, NULL);
921
922 sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
923
924 /*
925 * detach the controlling tty
926 * -- we needn't the tty in parent who waits for child only.
927 * The child calls setsid() that detach from the tty as well.
928 */
929 ioctl(0, TIOCNOTTY, NULL);
930
931 /*
932 * We have care about SIGTERM, because leave PAM session without
933 * pam_close_session() is pretty bad thing.
934 */
935 sa.sa_handler = sig_handler;
936 sigaction(SIGHUP, &sa, NULL);
937 sigaction(SIGTERM, &sa, &oldsa_term);
938
939 closelog();
940
c07ebfa1
KZ
941 /*
942 * We must fork before setuid() because we need to call
5c36a0eb
KZ
943 * pam_close_session() as root.
944 */
e09947b5 945
b2452358
KZ
946 child_pid = fork();
947 if (child_pid < 0) {
5c36a0eb 948 /* error in fork() */
66020e56 949 warn(_("failure forking"));
905045d4
KZ
950 pam_setcred(pamh, PAM_DELETE_CRED);
951 pam_end(pamh, pam_close_session(pamh, 0));
66020e56 952 exit(EXIT_FAILURE);
1d4ad1de
KZ
953 }
954
b2452358 955 if (child_pid) {
5c36a0eb 956 /* parent - wait for child to finish, then cleanup session */
b2452358
KZ
957 close(0);
958 close(1);
959 close(2);
960 sa.sa_handler = SIG_IGN;
961 sigaction(SIGQUIT, &sa, NULL);
962 sigaction(SIGINT, &sa, NULL);
963
964 /* wait as long as any child is there */
965 while(wait(NULL) == -1 && errno == EINTR)
966 ;
17274233 967 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
905045d4
KZ
968 pam_setcred(pamh, PAM_DELETE_CRED);
969 pam_end(pamh, pam_close_session(pamh, 0));
66020e56 970 exit(EXIT_SUCCESS);
5c36a0eb 971 }
1d4ad1de 972
5c36a0eb 973 /* child */
b2452358
KZ
974
975 /* restore to old state */
976 sigaction(SIGHUP, &oldsa_hup, NULL);
977 sigaction(SIGTERM, &oldsa_term, NULL);
978 if(got_sig)
66020e56 979 exit(EXIT_FAILURE);
b2452358 980
1d4ad1de
KZ
981 /*
982 * Problem: if the user's shell is a shell like ash that doesnt do
983 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
984 * process in the pgrp, will kill us.
985 */
986
987 /* start new session */
c07ebfa1 988 setsid();
1d4ad1de
KZ
989
990 /* make sure we have a controlling tty */
364cda48 991 opentty(ttyn);
1d4ad1de
KZ
992 openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
993
994 /*
995 * TIOCSCTTY: steal tty from other process group.
996 */
997 if (ioctl(0, TIOCSCTTY, 1))
998 syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
5c36a0eb 999 signal(SIGINT, SIG_DFL);
e09947b5 1000
fd6b7a7f
KZ
1001 /* discard permissions last so can't get killed and drop core */
1002 if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
7eda085c 1003 syslog(LOG_ALERT, _("setuid() failed"));
66020e56 1004 exit(EXIT_FAILURE);
fd6b7a7f 1005 }
e09947b5 1006
fd6b7a7f
KZ
1007 /* wait until here to change directory! */
1008 if (chdir(pwd->pw_dir) < 0) {
66020e56 1009 warn(_("%s: change directory failed"), pwd->pw_dir);
5c36a0eb 1010 if (chdir("/"))
66020e56 1011 exit(EXIT_FAILURE);
fd6b7a7f 1012 pwd->pw_dir = "/";
7eda085c 1013 printf(_("Logging in with home = \"/\".\n"));
fd6b7a7f 1014 }
e09947b5 1015
fd6b7a7f
KZ
1016 /* if the shell field has a space: treat it like a shell script */
1017 if (strchr(pwd->pw_shell, ' ')) {
3e31a2df 1018 buff = xmalloc(strlen(pwd->pw_shell) + 6);
6dbe3af9 1019
fd6b7a7f
KZ
1020 strcpy(buff, "exec ");
1021 strcat(buff, pwd->pw_shell);
1022 childArgv[childArgc++] = "/bin/sh";
1023 childArgv[childArgc++] = "-sh";
1024 childArgv[childArgc++] = "-c";
1025 childArgv[childArgc++] = buff;
1026 } else {
6dbe3af9 1027 tbuf[0] = '-';
c0f19ccf 1028 xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
fd6b7a7f
KZ
1029 p + 1 : pwd->pw_shell),
1030 sizeof(tbuf)-1);
e09947b5 1031
fd6b7a7f
KZ
1032 childArgv[childArgc++] = pwd->pw_shell;
1033 childArgv[childArgc++] = tbuf;
1034 }
1035
1036 childArgv[childArgc++] = NULL;
1037
2b6fc908
KZ
1038 execvp(childArgv[0], childArgv + 1);
1039
7eda085c 1040 if (!strcmp(childArgv[0], "/bin/sh"))
66020e56 1041 warn(_("couldn't exec shell script"));
2b6fc908 1042 else
66020e56 1043 warn(_("no shell"));
fd6b7a7f 1044
66020e56 1045 exit(EXIT_SUCCESS);
6dbe3af9
KZ
1046}
1047
df1dddf9
KZ
1048/*
1049 * Robert Ambrose writes:
1050 * A couple of my users have a problem with login processes hanging around
1051 * soaking up pts's. What they seem to hung up on is trying to write out the
1052 * message 'Login timed out after %d seconds' when the connection has already
1053 * been dropped.
1054 * What I did was add a second timeout while trying to write the message so
1055 * the process just exits if the second timeout expires.
1056 */
1057
1058static void
878a3697 1059timedout2(int sig __attribute__((__unused__))) {
ad3a3bfc 1060 struct termios ti;
e09947b5 1061
df1dddf9 1062 /* reset echo */
ad3a3bfc 1063 tcgetattr(0, &ti);
df1dddf9 1064 ti.c_lflag |= ECHO;
ad3a3bfc 1065 tcsetattr(0, TCSANOW, &ti);
66020e56 1066 exit(EXIT_SUCCESS); /* %% */
df1dddf9
KZ
1067}
1068
1069static void
878a3697 1070timedout(int sig __attribute__((__unused__))) {
df1dddf9
KZ
1071 signal(SIGALRM, timedout2);
1072 alarm(10);
f14f02f5 1073 /* TRANSLATORS: The standard value for %d is 60. */
66020e56 1074 warnx(_("timed out after %d seconds"), timeout);
df1dddf9
KZ
1075 signal(SIGALRM, SIG_IGN);
1076 alarm(0);
1077 timedout2(0);
6dbe3af9
KZ
1078}
1079
6dbe3af9
KZ
1080jmp_buf motdinterrupt;
1081
1082void
66ee8158
KZ
1083motd(void) {
1084 int fd, nchars;
1085 void (*oldint)(int);
fd6b7a7f 1086 char tbuf[8192];
e09947b5 1087
fd6b7a7f
KZ
1088 if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
1089 return;
1090 oldint = signal(SIGINT, sigint);
1091 if (setjmp(motdinterrupt) == 0)
11784a84
KZ
1092 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) {
1093 if (write(fileno(stdout), tbuf, nchars)) {
1094 ; /* glibc warn_unused_result */
1095 }
1096 }
fd6b7a7f
KZ
1097 signal(SIGINT, oldint);
1098 close(fd);
6dbe3af9
KZ
1099}
1100
fd6b7a7f 1101void
878a3697 1102sigint(int sig __attribute__((__unused__))) {
fd6b7a7f 1103 longjmp(motdinterrupt, 1);
6dbe3af9
KZ
1104}
1105
6dbe3af9 1106void
66ee8158 1107dolastlog(int quiet) {
fd6b7a7f
KZ
1108 struct lastlog ll;
1109 int fd;
e09947b5 1110
fd6b7a7f 1111 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
5c36a0eb 1112 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
fd6b7a7f
KZ
1113 if (!quiet) {
1114 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1115 ll.ll_time != 0) {
d03dd608
KZ
1116 time_t ll_time = (time_t) ll.ll_time;
1117
1118 printf(_("Last login: %.*s "),
1119 24-5, ctime(&ll_time));
e09947b5 1120
d03dd608
KZ
1121 if (*ll.ll_host != '\0')
1122 printf(_("from %.*s\n"),
1123 (int)sizeof(ll.ll_host), ll.ll_host);
1124 else
1125 printf(_("on %.*s\n"),
1126 (int)sizeof(ll.ll_line), ll.ll_line);
fd6b7a7f 1127 }
5c36a0eb 1128 lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
6dbe3af9 1129 }
fd6b7a7f 1130 memset((char *)&ll, 0, sizeof(ll));
a063e16c
KZ
1131
1132 {
1133 time_t t;
1134 time(&t);
1135 ll.ll_time = t; /* ll_time is always 32bit */
1136 }
1137
364cda48 1138 xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line));
c07ebfa1
KZ
1139 if (hostname)
1140 xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1141
11784a84
KZ
1142 if (write(fd, (char *)&ll, sizeof(ll)) < 0)
1143 warn(_("write lastlog failed"));
fd6b7a7f
KZ
1144 close(fd);
1145 }
6dbe3af9
KZ
1146}
1147
1148void
364cda48 1149badlogin(const char *name) {
7eda085c
KZ
1150 if (failures == 1) {
1151 if (hostname)
1152 syslog(LOG_NOTICE, _("LOGIN FAILURE FROM %s, %s"),
1153 hostname, name);
1154 else
1155 syslog(LOG_NOTICE, _("LOGIN FAILURE ON %s, %s"),
364cda48 1156 tty_name, name);
7eda085c
KZ
1157 } else {
1158 if (hostname)
1159 syslog(LOG_NOTICE, _("%d LOGIN FAILURES FROM %s, %s"),
1160 failures, hostname, name);
1161 else
1162 syslog(LOG_NOTICE, _("%d LOGIN FAILURES ON %s, %s"),
364cda48 1163 failures, tty_name, name);
7eda085c 1164 }
6dbe3af9
KZ
1165}
1166
e8f26419 1167/* Should not be called from PAM code... */
6dbe3af9 1168void
66ee8158 1169sleepexit(int eval) {
fd6b7a7f
KZ
1170 sleep(SLEEP_EXIT_TIMEOUT);
1171 exit(eval);
6dbe3af9 1172}