Upload Tizen:Base source
[framework/base/util-linux-ng.git] / login-utils / login.c
1 /* This program is derived from 4.3 BSD software and is
2    subject to the copyright notice below.
3
4    The port to HP-UX has been motivated by the incapability
5    of 'rlogin'/'rlogind' as per HP-UX 6.5 (and 7.0) to transfer window sizes.
6
7    Changes:
8
9    - General HP-UX portation. Use of facilities not available
10      in HP-UX (e.g. setpriority) has been eliminated.
11      Utmp/wtmp handling has been ported.
12
13    - The program uses BSD command line options to be used
14      in connection with e.g. 'rlogind' i.e. 'new login'.
15
16    - HP features left out:          password expiry
17                                     '*' as login shell, add it if you need it
18
19    - BSD features left out:         quota checks
20                                     password expiry
21                                     analysis of terminal type (tset feature)
22
23    - BSD features thrown in:        Security logging to syslogd.
24                                     This requires you to have a (ported) syslog
25                                     system -- 7.0 comes with syslog
26
27                                     'Lastlog' feature.
28
29    - A lot of nitty gritty details have been adjusted in favour of
30      HP-UX, e.g. /etc/securetty, default paths and the environment
31      variables assigned by 'login'.
32
33    - We do *nothing* to setup/alter tty state, under HP-UX this is
34      to be done by getty/rlogind/telnetd/some one else.
35
36    Michael Glad (glad@daimi.dk)
37    Computer Science Department
38    Aarhus University
39    Denmark
40
41    1990-07-04
42
43    1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port:
44    - now explictly sets non-blocking mode on descriptors
45    - strcasecmp is now part of HP-UX
46
47    1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12
48    From 1992 till now (1997) this code for Linux has been maintained at
49    ftp.daimi.aau.dk:/pub/linux/poe/
50
51    1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
52     - added Native Language Support
53    Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
54     - fixed strerr(errno) in gettext calls
55  */
56
57 /*
58  * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
59  * All rights reserved.
60  *
61  * Redistribution and use in source and binary forms are permitted
62  * provided that the above copyright notice and this paragraph are
63  * duplicated in all such forms and that any documentation,
64  * advertising materials, and other materials related to such
65  * distribution and use acknowledge that the software was developed
66  * by the University of California, Berkeley.  The name of the
67  * University may not be used to endorse or promote products derived
68  * from this software without specific prior written permission.
69  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
70  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
71  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
72  */
73
74 /*
75  * login [ name ]
76  * login -h hostname    (for telnetd, etc.)
77  * login -f name        (for pre-authenticated login: datakit, xterm, etc.)
78  */
79
80 #include <sys/param.h>
81
82 #include <stdio.h>
83 #include <ctype.h>
84 #include <unistd.h>
85 #include <getopt.h>
86 #include <memory.h>
87 #include <time.h>
88 #include <sys/stat.h>
89 #include <sys/time.h>
90 #include <sys/resource.h>
91 #include <sys/file.h>
92 #include <termios.h>
93 #include <string.h>
94 #define index strchr
95 #define rindex strrchr
96 #include <sys/ioctl.h>
97 #include <sys/wait.h>
98 #include <signal.h>
99 #include <errno.h>
100 #include <grp.h>
101 #include <pwd.h>
102 #include <utmp.h>
103 #include <setjmp.h>
104 #include <stdlib.h>
105 #include <sys/syslog.h>
106 #include <sys/sysmacros.h>
107 #include <linux/major.h>
108 #include <netdb.h>
109 #ifdef HAVE_LIBAUDIT
110 # include <libaudit.h>
111 #endif
112
113 #include "pathnames.h"
114 #include "my_crypt.h"
115 #include "login.h"
116 #include "xstrncpy.h"
117 #include "nls.h"
118
119
120 #ifdef HAVE_SECURITY_PAM_MISC_H
121 #  include <security/pam_appl.h>
122 #  include <security/pam_misc.h>
123 #  define PAM_MAX_LOGIN_TRIES   3
124 #  define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
125        fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
126        syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \
127        pam_end(pamh, retcode); exit(1); \
128    }
129 #  define PAM_END { \
130         pam_setcred(pamh, PAM_DELETE_CRED); \
131         retcode = pam_close_session(pamh,0); \
132         pam_end(pamh,retcode); \
133 }
134 #endif
135
136 #include <lastlog.h>
137
138 #define SLEEP_EXIT_TIMEOUT 5
139
140 #include "setproctitle.h"
141
142 #ifndef HAVE_SECURITY_PAM_MISC_H
143 static void getloginname (void);
144 static void checknologin (void);
145 static int rootterm (char *ttyn);
146 #endif
147 static void timedout (int);
148 static void sigint (int);
149 static void motd (void);
150 static void dolastlog (int quiet);
151
152 #ifdef USE_TTY_GROUP
153 #  define TTY_MODE 0620
154 #else
155 #  define TTY_MODE 0600
156 #endif
157
158 #define TTYGRPNAME      "tty"           /* name of group to own ttys */
159
160 #ifndef MAXPATHLEN
161 #  define MAXPATHLEN 1024
162 #endif
163
164 /*
165  * This bounds the time given to login.  Not a define so it can
166  * be patched on machines where it's too small.
167  */
168 int     timeout = 60;
169
170 struct passwd *pwd;
171
172 #ifdef HAVE_SECURITY_PAM_MISC_H
173 static struct passwd pwdcopy;
174 #endif
175 char    hostaddress[16];        /* used in checktty.c */
176 sa_family_t hostfamily;         /* used in checktty.c */
177 char    *hostname;              /* idem */
178 static char     *username, *tty_name, *tty_number;
179 static char     thishost[100];
180 static int      failures = 1;
181 static pid_t    pid;
182
183 /* Nice and simple code provided by Linus Torvalds 16-Feb-93 */
184 /* Nonblocking stuff by Maciej W. Rozycki, macro@ds2.pg.gda.pl, 1999.
185    He writes: "Login performs open() on a tty in a blocking mode.
186    In some cases it may make login wait in open() for carrier infinitely,
187    for example if the line is a simplistic case of a three-wire serial
188    connection. I believe login should open the line in the non-blocking mode
189    leaving the decision to make a connection to getty (where it actually
190    belongs). */
191 static void
192 opentty(const char * tty) {
193         int i, fd, flags;
194
195         fd = open(tty, O_RDWR | O_NONBLOCK);
196         if (fd == -1) {
197                 syslog(LOG_ERR, _("FATAL: can't reopen tty: %s"),
198                        strerror(errno));
199                 sleep(1);
200                 exit(1);
201         }
202
203         flags = fcntl(fd, F_GETFL);
204         flags &= ~O_NONBLOCK;
205         fcntl(fd, F_SETFL, flags);
206
207         for (i = 0; i < fd; i++)
208                 close(i);
209         for (i = 0; i < 3; i++)
210                 if (fd != i)
211                         dup2(fd, i);
212         if (fd >= 3)
213                 close(fd);
214 }
215
216 /* In case login is suid it was possible to use a hardlink as stdin
217    and exploit races for a local root exploit. (Wojciech Purczynski). */
218 /* More precisely, the problem is  ttyn := ttyname(0); ...; chown(ttyn);
219    here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */
220 /* All of this is a problem only when login is suid, which it isnt. */
221 static void
222 check_ttyname(char *ttyn) {
223         struct stat statbuf;
224
225         if (lstat(ttyn, &statbuf)
226             || !S_ISCHR(statbuf.st_mode)
227             || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))
228             || (access(ttyn, R_OK | W_OK) != 0)) {
229                 syslog(LOG_ERR, _("FATAL: bad tty"));
230                 sleep(1);
231                 exit(1);
232         }
233 }
234
235 #ifdef LOGIN_CHOWN_VCS
236 /* true if the filedescriptor fd is a console tty, very Linux specific */
237 static int
238 consoletty(int fd) {
239     struct stat stb;
240
241     if ((fstat(fd, &stb) >= 0)
242         && (major(stb.st_rdev) == TTY_MAJOR)
243         && (minor(stb.st_rdev) < 64)) {
244         return 1;
245     }
246     return 0;
247 }
248 #endif
249
250 #ifdef HAVE_SECURITY_PAM_MISC_H
251 /*
252  * Log failed login attempts in _PATH_BTMP if that exists.
253  * Must be called only with username the name of an actual user.
254  * The most common login failure is to give password instead of username.
255  */
256 #define _PATH_BTMP      "/var/log/btmp"
257 static void
258 logbtmp(const char *line, const char *username, const char *hostname) {
259         struct utmp ut;
260         struct timeval tv;
261
262         memset(&ut, 0, sizeof(ut));
263
264         strncpy(ut.ut_user, username ? username : "(unknown)",
265                 sizeof(ut.ut_user));
266
267         strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
268         xstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
269
270 #if defined(_HAVE_UT_TV)            /* in <utmpbits.h> included by <utmp.h> */
271         gettimeofday(&tv, NULL);
272         ut.ut_tv.tv_sec = tv.tv_sec;
273         ut.ut_tv.tv_usec = tv.tv_usec;
274 #else
275         {
276                 time_t t;
277                 time(&t);
278                 ut.ut_time = t;     /* ut_time is not always a time_t */
279         }
280 #endif
281
282         ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */
283         ut.ut_pid = pid;
284         if (hostname) {
285                 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
286                 if (hostaddress[0])
287                         memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
288         }
289 #if HAVE_UPDWTMP                /* bad luck for ancient systems */
290         updwtmp(_PATH_BTMP, &ut);
291 #endif
292 }
293
294
295 static int child_pid = 0;
296 static volatile int got_sig = 0;
297
298 /*
299  * This handler allows to inform a shell about signals to login. If you have
300  * (root) permissions you can kill all login childrent by one signal to login
301  * process.
302  *
303  * Also, parent who is session leader is able (before setsid() in child) to
304  * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
305  */
306 static void
307 sig_handler(int signal)
308 {
309         if(child_pid)
310                 kill(-child_pid, signal);
311         else
312                 got_sig = 1;
313         if(signal == SIGTERM)
314                 kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
315 }
316
317 #endif  /* HAVE_SECURITY_PAM_MISC_H */
318
319 #ifdef HAVE_LIBAUDIT
320 static void
321 logaudit(const char *tty, const char *username, const char *hostname,
322                                         struct passwd *pwd, int status)
323 {
324         int audit_fd;
325
326         audit_fd = audit_open();
327         if (audit_fd == -1)
328                 return;
329         if (!pwd && username)
330                 pwd = getpwnam(username);
331
332         audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN,
333                 NULL, "login", username ? username : "(unknown)",
334                 pwd ? pwd->pw_uid : -1, hostname, NULL, tty, status);
335
336         close(audit_fd);
337 }
338 #else /* ! HAVE_LIBAUDIT */
339 # define logaudit(tty, username, hostname, pwd, status)
340 #endif /* HAVE_LIBAUDIT */
341
342 #ifdef HAVE_SECURITY_PAM_MISC_H
343 /* encapsulate stupid "void **" pam_get_item() API */
344 int
345 get_pam_username(pam_handle_t *pamh, char **name)
346 {
347         const void *item = (void *) *name;
348         int rc;
349         rc = pam_get_item(pamh, PAM_USER, &item);
350         *name = (char *) item;
351         return rc;
352 }
353 #endif
354
355 /*
356  * We need to check effective UID/GID. For example $HOME could be on root
357  * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID.
358  * The open(2) seems as the surest solution.
359  * -- kzak@redhat.com (10-Apr-2009)
360  */
361 int
362 effective_access(const char *path, int mode)
363 {
364         int fd = open(path, mode);
365         if (fd != -1)
366                 close(fd);
367         return fd == -1 ? -1 : 0;
368 }
369
370 int
371 main(int argc, char **argv)
372 {
373     extern int optind;
374     extern char *optarg, **environ;
375     struct group *gr;
376     register int ch;
377     register char *p;
378     int ask, fflag, hflag, pflag, cnt, errsv;
379     int quietlog, passwd_req;
380     char *domain, *ttyn;
381     char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_DEV_TTY) + 10];
382     char *termenv;
383     char *childArgv[10];
384     char *buff;
385     int childArgc = 0;
386 #ifdef HAVE_SECURITY_PAM_MISC_H
387     int retcode;
388     pam_handle_t *pamh = NULL;
389     struct pam_conv conv = { misc_conv, NULL };
390     struct sigaction sa, oldsa_hup, oldsa_term;
391 #else
392     char *salt, *pp;
393 #endif
394 #ifdef LOGIN_CHOWN_VCS
395     char vcsn[20], vcsan[20];
396 #endif
397
398     pid = getpid();
399
400     signal(SIGALRM, timedout);
401     siginterrupt(SIGALRM,1);           /* we have to interrupt syscalls like ioclt() */
402     alarm((unsigned int)timeout);
403     signal(SIGQUIT, SIG_IGN);
404     signal(SIGINT, SIG_IGN);
405
406     setlocale(LC_ALL, "");
407     bindtextdomain(PACKAGE, LOCALEDIR);
408     textdomain(PACKAGE);
409
410     setpriority(PRIO_PROCESS, 0, 0);
411     initproctitle(argc, argv);
412
413     /*
414      * -p is used by getty to tell login not to destroy the environment
415      * -f is used to skip a second login authentication
416      * -h is used by other servers to pass the name of the remote
417      *    host to login so that it may be placed in utmp and wtmp
418      */
419     gethostname(tbuf, sizeof(tbuf));
420     xstrncpy(thishost, tbuf, sizeof(thishost));
421     domain = strchr(tbuf, '.');
422
423     username = tty_name = hostname = NULL;
424     fflag = hflag = pflag = 0;
425     passwd_req = 1;
426
427     while ((ch = getopt(argc, argv, "fh:p")) != -1)
428       switch (ch) {
429         case 'f':
430           fflag = 1;
431           break;
432
433         case 'h':
434           if (getuid()) {
435               fprintf(stderr,
436                       _("login: -h for super-user only.\n"));
437               exit(1);
438           }
439           hflag = 1;
440           if (domain && (p = strchr(optarg, '.')) &&
441               strcasecmp(p, domain) == 0)
442             *p = 0;
443
444           hostname = strdup(optarg);    /* strdup: Ambrose C. Li */
445           {
446                 struct addrinfo hints, *info = NULL;
447
448                 memset(&hints, 0, sizeof(hints));
449                 hints.ai_flags = AI_ADDRCONFIG;
450
451                 hostaddress[0] = 0;
452
453                 if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) {
454                         if (info->ai_family == AF_INET) {
455                             struct sockaddr_in *sa =
456                                         (struct sockaddr_in *) info->ai_addr;
457                             memcpy(hostaddress, &(sa->sin_addr),
458                                         sizeof(sa->sin_addr));
459                         }
460                         else if (info->ai_family == AF_INET6) {
461                             struct sockaddr_in6 *sa =
462                                         (struct sockaddr_in6 *) info->ai_addr;
463                             memcpy(hostaddress, &(sa->sin6_addr),
464                                         sizeof(sa->sin6_addr));
465                         }
466                         hostfamily = info->ai_family;
467                         freeaddrinfo(info);
468                 }
469           }
470           break;
471
472         case 'p':
473           pflag = 1;
474           break;
475
476         case '?':
477         default:
478           fprintf(stderr,
479                   _("usage: login [-fp] [username]\n"));
480           exit(1);
481       }
482     argc -= optind;
483     argv += optind;
484     if (*argv) {
485         char *p = *argv;
486         username = strdup(p);
487         ask = 0;
488         /* wipe name - some people mistype their password here */
489         /* (of course we are too late, but perhaps this helps a little ..) */
490         while(*p)
491             *p++ = ' ';
492     } else
493         ask = 1;
494
495     for (cnt = getdtablesize(); cnt > 2; cnt--)
496       close(cnt);
497
498     ttyn = ttyname(0);
499
500     if (ttyn == NULL || *ttyn == '\0') {
501         /* no snprintf required - see definition of tname */
502         snprintf(tname, sizeof(tname), "%s??", _PATH_DEV_TTY);
503         ttyn = tname;
504     }
505
506     check_ttyname(ttyn);
507
508     if (strncmp(ttyn, "/dev/", 5) == 0)
509         tty_name = ttyn+5;
510     else
511         tty_name = ttyn;
512
513     if (strncmp(ttyn, "/dev/tty", 8) == 0)
514         tty_number = ttyn+8;
515     else {
516         char *p = ttyn;
517         while (*p && !isdigit(*p)) p++;
518         tty_number = p;
519     }
520
521 #ifdef LOGIN_CHOWN_VCS
522     /* find names of Virtual Console devices, for later mode change */
523     snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number);
524     snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number);
525 #endif
526
527     /* set pgid to pid */
528     setpgrp();
529     /* this means that setsid() will fail */
530
531     {
532         struct termios tt, ttt;
533
534         tcgetattr(0, &tt);
535         ttt = tt;
536         ttt.c_cflag &= ~HUPCL;
537
538         /* These can fail, e.g. with ttyn on a read-only filesystem */
539         chown(ttyn, 0, 0);
540         chmod(ttyn, TTY_MODE);
541
542         /* Kill processes left on this tty */
543         tcsetattr(0,TCSAFLUSH,&ttt);
544         signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
545         vhangup();
546         signal(SIGHUP, SIG_DFL);
547
548         /* open stdin,stdout,stderr to the tty */
549         opentty(ttyn);
550
551         /* restore tty modes */
552         tcsetattr(0,TCSAFLUSH,&tt);
553     }
554
555     openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
556
557 #if 0
558     /* other than iso-8859-1 */
559     printf("\033(K");
560     fprintf(stderr,"\033(K");
561 #endif
562
563 #ifdef HAVE_SECURITY_PAM_MISC_H
564     /*
565      * username is initialized to NULL
566      * and if specified on the command line it is set.
567      * Therefore, we are safe not setting it to anything
568      */
569
570     retcode = pam_start(hflag?"remote":"login",username, &conv, &pamh);
571     if(retcode != PAM_SUCCESS) {
572         fprintf(stderr, _("%s: PAM failure, aborting: %s\n"),
573                 "login", pam_strerror(pamh, retcode));
574         syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
575                pam_strerror(pamh, retcode));
576         exit(99);
577     }
578     /* hostname & tty are either set to NULL or their correct values,
579        depending on how much we know */
580     retcode = pam_set_item(pamh, PAM_RHOST, hostname);
581     PAM_FAIL_CHECK;
582     retcode = pam_set_item(pamh, PAM_TTY, tty_name);
583     PAM_FAIL_CHECK;
584
585     /*
586      * Andrew.Taylor@cal.montage.ca: Provide a user prompt to PAM
587      * so that the "login: " prompt gets localized. Unfortunately,
588      * PAM doesn't have an interface to specify the "Password: " string
589      * (yet).
590      */
591     retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
592     PAM_FAIL_CHECK;
593
594 #if 0
595     /*
596      * other than iso-8859-1
597      * one more time due to reset tty by PAM
598      */
599     printf("\033(K");
600     fprintf(stderr,"\033(K");
601 #endif
602
603     if (username) {
604         /* we need't the original username. We have to follow PAM. */
605         free(username);
606         username = NULL;
607     }
608
609     /* if fflag == 1, then the user has already been authenticated */
610     if (fflag && (getuid() == 0))
611         passwd_req = 0;
612     else
613         passwd_req = 1;
614
615     if(passwd_req == 1) {
616         int failcount=0;
617
618         /* if we didn't get a user on the command line, set it to NULL */
619         get_pam_username(pamh, &username);
620
621         /* there may be better ways to deal with some of these
622            conditions, but at least this way I don't think we'll
623            be giving away information... */
624         /* Perhaps someday we can trust that all PAM modules will
625            pay attention to failure count and get rid of MAX_LOGIN_TRIES? */
626
627         retcode = pam_authenticate(pamh, 0);
628         while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
629               ((retcode == PAM_AUTH_ERR) ||
630                (retcode == PAM_USER_UNKNOWN) ||
631                (retcode == PAM_CRED_INSUFFICIENT) ||
632                (retcode == PAM_AUTHINFO_UNAVAIL))) {
633             get_pam_username(pamh, &username);
634
635             syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
636                    failcount, hostname, username, pam_strerror(pamh, retcode));
637             logbtmp(tty_name, username, hostname);
638             logaudit(tty_name, username, hostname, NULL, 0);
639
640             fprintf(stderr,_("Login incorrect\n\n"));
641             pam_set_item(pamh,PAM_USER,NULL);
642             retcode = pam_authenticate(pamh, 0);
643         }
644
645         if (retcode != PAM_SUCCESS) {
646             get_pam_username(pamh, &username);
647
648             if (retcode == PAM_MAXTRIES)
649                 syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
650                         "%s, %s"), failcount, hostname, username,
651                          pam_strerror(pamh, retcode));
652             else
653                 syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
654                         hostname, username, pam_strerror(pamh, retcode));
655             logbtmp(tty_name, username, hostname);
656             logaudit(tty_name, username, hostname, NULL, 0);
657
658             fprintf(stderr,_("\nLogin incorrect\n"));
659             pam_end(pamh, retcode);
660             exit(0);
661         }
662     }
663
664     /*
665      * Authentication may be skipped (for example, during krlogin, rlogin, etc...),
666      * but it doesn't mean that we can skip other account checks. The account
667      * could be disabled or password expired (althought kerberos ticket is valid).
668      * -- kzak@redhat.com (22-Feb-2006)
669      */
670     retcode = pam_acct_mgmt(pamh, 0);
671
672     if(retcode == PAM_NEW_AUTHTOK_REQD) {
673         retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
674     }
675
676     PAM_FAIL_CHECK;
677
678     /*
679      * Grab the user information out of the password file for future usage
680      * First get the username that we are actually using, though.
681      */
682     retcode = get_pam_username(pamh, &username);
683     PAM_FAIL_CHECK;
684
685     if (!username || !*username) {
686             fprintf(stderr, _("\nSession setup problem, abort.\n"));
687             syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
688                    __FUNCTION__, __LINE__);
689             pam_end(pamh, PAM_SYSTEM_ERR);
690             exit(1);
691     }
692     if (!(pwd = getpwnam(username))) {
693             fprintf(stderr, _("\nSession setup problem, abort.\n"));
694             syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
695                    username, __FUNCTION__, __LINE__);
696             pam_end(pamh, PAM_SYSTEM_ERR);
697             exit(1);
698     }
699
700     /*
701      * Create a copy of the pwd struct - otherwise it may get
702      * clobbered by PAM
703      */
704     memcpy(&pwdcopy, pwd, sizeof(*pwd));
705     pwd = &pwdcopy;
706     pwd->pw_name   = strdup(pwd->pw_name);
707     pwd->pw_passwd = strdup(pwd->pw_passwd);
708     pwd->pw_gecos  = strdup(pwd->pw_gecos);
709     pwd->pw_dir    = strdup(pwd->pw_dir);
710     pwd->pw_shell  = strdup(pwd->pw_shell);
711     if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
712         !pwd->pw_dir || !pwd->pw_shell) {
713             fprintf(stderr, _("login: Out of memory\n"));
714             syslog(LOG_ERR, "Out of memory");
715             pam_end(pamh, PAM_SYSTEM_ERR);
716             exit(1);
717     }
718     username = pwd->pw_name;
719
720     /*
721      * Initialize the supplementary group list.
722      * This should be done before pam_setcred because
723      * the PAM modules might add groups during pam_setcred.
724      */
725     if (initgroups(username, pwd->pw_gid) < 0) {
726             syslog(LOG_ERR, "initgroups: %m");
727             fprintf(stderr, _("\nSession setup problem, abort.\n"));
728             pam_end(pamh, PAM_SYSTEM_ERR);
729             exit(1);
730     }
731
732     retcode = pam_open_session(pamh, 0);
733     PAM_FAIL_CHECK;
734
735     retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
736     if (retcode != PAM_SUCCESS)
737             pam_close_session(pamh, 0);
738     PAM_FAIL_CHECK;
739
740 #else /* ! HAVE_SECURITY_PAM_MISC_H */
741
742     for (cnt = 0;; ask = 1) {
743
744         if (ask) {
745             fflag = 0;
746             getloginname();
747         }
748
749         /* Dirty patch to fix a gigantic security hole when using
750            yellow pages. This problem should be solved by the
751            libraries, and not by programs, but this must be fixed
752            urgently! If the first char of the username is '+', we
753            avoid login success.
754            Feb 95 <alvaro@etsit.upm.es> */
755
756         if (username[0] == '+') {
757             puts(_("Illegal username"));
758             badlogin(username);
759             sleepexit(1);
760         }
761
762         /* (void)strcpy(tbuf, username); why was this here? */
763         if ((pwd = getpwnam(username))) {
764 #  ifdef SHADOW_PWD
765             struct spwd *sp;
766
767             if ((sp = getspnam(username)))
768               pwd->pw_passwd = sp->sp_pwdp;
769 #  endif
770             salt = pwd->pw_passwd;
771         } else
772           salt = "xx";
773
774         if (pwd) {
775             initgroups(username, pwd->pw_gid);
776             checktty(username, tty_name, pwd); /* in checktty.c */
777         }
778
779         /* if user not super-user, check for disabled logins */
780         if (pwd == NULL || pwd->pw_uid)
781           checknologin();
782
783         /*
784          * Disallow automatic login to root; if not invoked by
785          * root, disallow if the uid's differ.
786          */
787         if (fflag && pwd) {
788             int uid = getuid();
789
790             passwd_req = pwd->pw_uid == 0 ||
791               (uid && uid != pwd->pw_uid);
792         }
793
794         /*
795          * If trying to log in as root, but with insecure terminal,
796          * refuse the login attempt.
797          */
798         if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
799             fprintf(stderr,
800                     _("%s login refused on this terminal.\n"),
801                     pwd->pw_name);
802
803             if (hostname)
804               syslog(LOG_NOTICE,
805                      _("LOGIN %s REFUSED FROM %s ON TTY %s"),
806                      pwd->pw_name, hostname, tty_name);
807             else
808               syslog(LOG_NOTICE,
809                      _("LOGIN %s REFUSED ON TTY %s"),
810                      pwd->pw_name, tty_name);
811             logaudit(tty_name, pwd->pw_name, hostname, pwd, 0);
812             continue;
813         }
814
815         /*
816          * If no pre-authentication and a password exists
817          * for this user, prompt for one and verify it.
818          */
819         if (!passwd_req || (pwd && !*pwd->pw_passwd))
820           break;
821
822         setpriority(PRIO_PROCESS, 0, -4);
823         pp = getpass(_("Password: "));
824
825 #  ifdef CRYPTOCARD
826         if (strncmp(pp, "CRYPTO", 6) == 0) {
827             if (pwd && cryptocard()) break;
828         }
829 #  endif /* CRYPTOCARD */
830
831         p = crypt(pp, salt);
832         setpriority(PRIO_PROCESS, 0, 0);
833
834 #  ifdef KERBEROS
835         /*
836          * If not present in pw file, act as we normally would.
837          * If we aren't Kerberos-authenticated, try the normal
838          * pw file for a password.  If that's ok, log the user
839          * in without issueing any tickets.
840          */
841
842         if (pwd && !krb_get_lrealm(realm,1)) {
843             /*
844              * get TGT for local realm; be careful about uid's
845              * here for ticket file ownership
846              */
847             setreuid(geteuid(),pwd->pw_uid);
848             kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
849                                        "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
850             setuid(0);
851             if (kerror == INTK_OK) {
852                 memset(pp, 0, strlen(pp));
853                 notickets = 0;  /* user got ticket */
854                 break;
855             }
856         }
857 #  endif /* KERBEROS */
858         memset(pp, 0, strlen(pp));
859
860         if (pwd && !strcmp(p, pwd->pw_passwd))
861           break;
862
863         printf(_("Login incorrect\n"));
864         badlogin(username); /* log ALL bad logins */
865         failures++;
866
867         /* we allow 10 tries, but after 3 we start backing off */
868         if (++cnt > 3) {
869             if (cnt >= 10) {
870                 sleepexit(1);
871             }
872             sleep((unsigned int)((cnt - 3) * 5));
873         }
874     }
875 #endif /* !HAVE_SECURITY_PAM_MISC_H */
876
877     /* committed to login -- turn off timeout */
878     alarm((unsigned int)0);
879
880     endpwent();
881
882     /* This requires some explanation: As root we may not be able to
883        read the directory of the user if it is on an NFS mounted
884        filesystem. We temporarily set our effective uid to the user-uid
885        making sure that we keep root privs. in the real uid.
886
887        A portable solution would require a fork(), but we rely on Linux
888        having the BSD setreuid() */
889
890     {
891         char tmpstr[MAXPATHLEN];
892         uid_t ruid = getuid();
893         gid_t egid = getegid();
894
895         /* avoid snprintf - old systems do not have it, or worse,
896            have a libc in which snprintf is the same as sprintf */
897         if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
898                 quietlog = 0;
899         else {
900                 sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
901                 setregid(-1, pwd->pw_gid);
902                 setreuid(0, pwd->pw_uid);
903                 quietlog = (effective_access(tmpstr, O_RDONLY) == 0);
904                 setuid(0); /* setreuid doesn't do it alone! */
905                 setreuid(ruid, 0);
906                 setregid(-1, egid);
907         }
908     }
909
910     /* for linux, write entries in utmp and wtmp */
911     {
912         struct utmp ut;
913         struct utmp *utp;
914         struct timeval tv;
915
916         utmpname(_PATH_UTMP);
917         setutent();
918
919         /* Find pid in utmp.
920 login sometimes overwrites the runlevel entry in /var/run/utmp,
921 confusing sysvinit. I added a test for the entry type, and the problem
922 was gone. (In a runlevel entry, st_pid is not really a pid but some number
923 calculated from the previous and current runlevel).
924 Michael Riepe <michael@stud.uni-hannover.de>
925         */
926         while ((utp = getutent()))
927                 if (utp->ut_pid == pid
928                     && utp->ut_type >= INIT_PROCESS
929                     && utp->ut_type <= DEAD_PROCESS)
930                         break;
931
932         /* If we can't find a pre-existing entry by pid, try by line.
933            BSD network daemons may rely on this. (anonymous) */
934         if (utp == NULL) {
935              setutent();
936              ut.ut_type = LOGIN_PROCESS;
937              strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
938              utp = getutline(&ut);
939         }
940
941         if (utp) {
942             memcpy(&ut, utp, sizeof(ut));
943         } else {
944             /* some gettys/telnetds don't initialize utmp... */
945             memset(&ut, 0, sizeof(ut));
946         }
947
948         if (ut.ut_id[0] == 0)
949           strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
950
951         strncpy(ut.ut_user, username, sizeof(ut.ut_user));
952         xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
953 #ifdef _HAVE_UT_TV              /* in <utmpbits.h> included by <utmp.h> */
954         gettimeofday(&tv, NULL);
955         ut.ut_tv.tv_sec = tv.tv_sec;
956         ut.ut_tv.tv_usec = tv.tv_usec;
957 #else
958         {
959             time_t t;
960             time(&t);
961             ut.ut_time = t;     /* ut_time is not always a time_t */
962                                 /* glibc2 #defines it as ut_tv.tv_sec */
963         }
964 #endif
965         ut.ut_type = USER_PROCESS;
966         ut.ut_pid = pid;
967         if (hostname) {
968                 xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
969                 if (hostaddress[0])
970                         memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
971         }
972
973         pututline(&ut);
974         endutent();
975
976 #if HAVE_UPDWTMP
977         updwtmp(_PATH_WTMP, &ut);
978 #else
979 #if 0
980         /* The O_APPEND open() flag should be enough to guarantee
981            atomic writes at end of file. */
982         {
983             int wtmp;
984
985             if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
986                 write(wtmp, (char *)&ut, sizeof(ut));
987                 close(wtmp);
988             }
989         }
990 #else
991         /* Probably all this locking below is just nonsense,
992            and the short version is OK as well. */
993         {
994             int lf, wtmp;
995             if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
996                 flock(lf, LOCK_EX);
997                 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
998                     write(wtmp, (char *)&ut, sizeof(ut));
999                     close(wtmp);
1000                 }
1001                 flock(lf, LOCK_UN);
1002                 close(lf);
1003             }
1004         }
1005 #endif
1006 #endif
1007     }
1008
1009     logaudit(tty_name, username, hostname, pwd, 1);
1010     dolastlog(quietlog);
1011
1012     chown(ttyn, pwd->pw_uid,
1013           (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
1014     chmod(ttyn, TTY_MODE);
1015
1016 #ifdef LOGIN_CHOWN_VCS
1017     /* if tty is one of the VC's then change owner and mode of the
1018        special /dev/vcs devices as well */
1019     if (consoletty(0)) {
1020         chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
1021         chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
1022         chmod(vcsn, TTY_MODE);
1023         chmod(vcsan, TTY_MODE);
1024     }
1025 #endif
1026
1027     setgid(pwd->pw_gid);
1028
1029     if (*pwd->pw_shell == '\0')
1030       pwd->pw_shell = _PATH_BSHELL;
1031
1032     /* preserve TERM even without -p flag */
1033     {
1034         char *ep;
1035
1036         if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
1037           termenv = "dumb";
1038     }
1039
1040     /* destroy environment unless user has requested preservation */
1041     if (!pflag)
1042       {
1043           environ = (char**)malloc(sizeof(char*));
1044           memset(environ, 0, sizeof(char*));
1045       }
1046
1047     setenv("HOME", pwd->pw_dir, 0);      /* legal to override */
1048     if(pwd->pw_uid)
1049       setenv("PATH", _PATH_DEFPATH, 1);
1050     else
1051       setenv("PATH", _PATH_DEFPATH_ROOT, 1);
1052
1053     setenv("SHELL", pwd->pw_shell, 1);
1054     setenv("TERM", termenv, 1);
1055
1056     /* mailx will give a funny error msg if you forget this one */
1057     {
1058       char tmp[MAXPATHLEN];
1059       /* avoid snprintf */
1060       if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
1061               sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1062               setenv("MAIL",tmp,0);
1063       }
1064     }
1065
1066     /* LOGNAME is not documented in login(1) but
1067        HP-UX 6.5 does it. We'll not allow modifying it.
1068        */
1069     setenv("LOGNAME", pwd->pw_name, 1);
1070
1071 #ifdef HAVE_SECURITY_PAM_MISC_H
1072     {
1073         int i;
1074         char ** env = pam_getenvlist(pamh);
1075
1076         if (env != NULL) {
1077             for (i=0; env[i]; i++) {
1078                 putenv(env[i]);
1079                 /* D(("env[%d] = %s", i,env[i])); */
1080             }
1081         }
1082     }
1083 #endif
1084
1085     setproctitle("login", username);
1086
1087     if (!strncmp(tty_name, "ttyS", 4))
1088       syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
1089
1090     /* allow tracking of good logins.
1091        -steve philp (sphilp@mail.alliance.net) */
1092
1093     if (pwd->pw_uid == 0) {
1094         if (hostname)
1095           syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
1096                  tty_name, hostname);
1097         else
1098           syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
1099     } else {
1100         if (hostname)
1101           syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name,
1102                  pwd->pw_name, hostname);
1103         else
1104           syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name,
1105                  pwd->pw_name);
1106     }
1107
1108     if (!quietlog) {
1109         motd();
1110
1111 #ifdef LOGIN_STAT_MAIL
1112         /*
1113          * This turns out to be a bad idea: when the mail spool
1114          * is NFS mounted, and the NFS connection hangs, the
1115          * login hangs, even root cannot login.
1116          * Checking for mail should be done from the shell.
1117          */
1118         {
1119             struct stat st;
1120             char *mail;
1121
1122             mail = getenv("MAIL");
1123             if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
1124                 if (st.st_mtime > st.st_atime)
1125                         printf(_("You have new mail.\n"));
1126                 else
1127                         printf(_("You have mail.\n"));
1128             }
1129         }
1130 #endif
1131     }
1132
1133     signal(SIGALRM, SIG_DFL);
1134     signal(SIGQUIT, SIG_DFL);
1135     signal(SIGTSTP, SIG_IGN);
1136
1137 #ifdef HAVE_SECURITY_PAM_MISC_H
1138
1139     memset(&sa, 0, sizeof(sa));
1140     sa.sa_handler = SIG_IGN;
1141     sigaction(SIGINT, &sa, NULL);
1142
1143     sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
1144
1145     /*
1146      * detach the controlling tty
1147      * -- we needn't the tty in parent who waits for child only.
1148      *    The child calls setsid() that detach from the tty as well.
1149      */
1150     ioctl(0, TIOCNOTTY, NULL);
1151
1152     /*
1153      * We have care about SIGTERM, because leave PAM session without
1154      * pam_close_session() is pretty bad thing.
1155      */
1156     sa.sa_handler = sig_handler;
1157     sigaction(SIGHUP, &sa, NULL);
1158     sigaction(SIGTERM, &sa, &oldsa_term);
1159
1160     closelog();
1161
1162     /*
1163      * We must fork before setuid() because we need to call
1164      * pam_close_session() as root.
1165      */
1166
1167     child_pid = fork();
1168     if (child_pid < 0) {
1169        int errsv = errno;
1170        /* error in fork() */
1171        fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
1172        PAM_END;
1173        exit(0);
1174     }
1175
1176     if (child_pid) {
1177        /* parent - wait for child to finish, then cleanup session */
1178        close(0);
1179        close(1);
1180        close(2);
1181        sa.sa_handler = SIG_IGN;
1182        sigaction(SIGQUIT, &sa, NULL);
1183        sigaction(SIGINT, &sa, NULL);
1184
1185        /* wait as long as any child is there */
1186        while(wait(NULL) == -1 && errno == EINTR)
1187                ;
1188        openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
1189        PAM_END;
1190        exit(0);
1191     }
1192
1193     /* child */
1194
1195     /* restore to old state */
1196     sigaction(SIGHUP, &oldsa_hup, NULL);
1197     sigaction(SIGTERM, &oldsa_term, NULL);
1198     if(got_sig)
1199             exit(1);
1200
1201     /*
1202      * Problem: if the user's shell is a shell like ash that doesnt do
1203      * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
1204      * process in the pgrp, will kill us.
1205      */
1206
1207     /* start new session */
1208     setsid();
1209
1210     /* make sure we have a controlling tty */
1211     opentty(ttyn);
1212     openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
1213
1214     /*
1215      * TIOCSCTTY: steal tty from other process group.
1216      */
1217     if (ioctl(0, TIOCSCTTY, 1))
1218             syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
1219 #endif
1220     signal(SIGINT, SIG_DFL);
1221
1222     /* discard permissions last so can't get killed and drop core */
1223     if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
1224         syslog(LOG_ALERT, _("setuid() failed"));
1225         exit(1);
1226     }
1227
1228     /* wait until here to change directory! */
1229     if (chdir(pwd->pw_dir) < 0) {
1230         printf(_("No directory %s!\n"), pwd->pw_dir);
1231         if (chdir("/"))
1232           exit(0);
1233         pwd->pw_dir = "/";
1234         printf(_("Logging in with home = \"/\".\n"));
1235     }
1236
1237     /* if the shell field has a space: treat it like a shell script */
1238     if (strchr(pwd->pw_shell, ' ')) {
1239         buff = malloc(strlen(pwd->pw_shell) + 6);
1240
1241         if (!buff) {
1242             fprintf(stderr, _("login: no memory for shell script.\n"));
1243             exit(0);
1244         }
1245
1246         strcpy(buff, "exec ");
1247         strcat(buff, pwd->pw_shell);
1248         childArgv[childArgc++] = "/bin/sh";
1249         childArgv[childArgc++] = "-sh";
1250         childArgv[childArgc++] = "-c";
1251         childArgv[childArgc++] = buff;
1252     } else {
1253         tbuf[0] = '-';
1254         xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
1255                            p + 1 : pwd->pw_shell),
1256                 sizeof(tbuf)-1);
1257
1258         childArgv[childArgc++] = pwd->pw_shell;
1259         childArgv[childArgc++] = tbuf;
1260     }
1261
1262     childArgv[childArgc++] = NULL;
1263
1264     execvp(childArgv[0], childArgv + 1);
1265
1266     errsv = errno;
1267
1268     if (!strcmp(childArgv[0], "/bin/sh"))
1269         fprintf(stderr, _("login: couldn't exec shell script: %s.\n"),
1270                 strerror(errsv));
1271     else
1272         fprintf(stderr, _("login: no shell: %s.\n"), strerror(errsv));
1273
1274     exit(0);
1275 }
1276
1277 #ifndef HAVE_SECURITY_PAM_MISC_H
1278 static void
1279 getloginname(void) {
1280     int ch, cnt, cnt2;
1281     char *p;
1282     static char nbuf[UT_NAMESIZE + 1];
1283
1284     cnt2 = 0;
1285     for (;;) {
1286         cnt = 0;
1287         printf(_("\n%s login: "), thishost); fflush(stdout);
1288         for (p = nbuf; (ch = getchar()) != '\n'; ) {
1289             if (ch == EOF) {
1290                 badlogin("EOF");
1291                 exit(0);
1292             }
1293             if (p < nbuf + UT_NAMESIZE)
1294               *p++ = ch;
1295
1296             cnt++;
1297             if (cnt > UT_NAMESIZE + 20) {
1298                 fprintf(stderr, _("login name much too long.\n"));
1299                 badlogin(_("NAME too long"));
1300                 exit(0);
1301             }
1302         }
1303         if (p > nbuf) {
1304           if (nbuf[0] == '-')
1305             fprintf(stderr,
1306                     _("login names may not start with '-'.\n"));
1307           else {
1308               *p = '\0';
1309               username = nbuf;
1310               break;
1311           }
1312         }
1313
1314         cnt2++;
1315         if (cnt2 > 50) {
1316             fprintf(stderr, _("too many bare linefeeds.\n"));
1317             badlogin(_("EXCESSIVE linefeeds"));
1318             exit(0);
1319         }
1320     }
1321 }
1322 #endif
1323
1324 /*
1325  * Robert Ambrose writes:
1326  * A couple of my users have a problem with login processes hanging around
1327  * soaking up pts's.  What they seem to hung up on is trying to write out the
1328  * message 'Login timed out after %d seconds' when the connection has already
1329  * been dropped.
1330  * What I did was add a second timeout while trying to write the message so
1331  * the process just exits if the second timeout expires.
1332  */
1333
1334 static void
1335 timedout2(int sig) {
1336         struct termios ti;
1337
1338         /* reset echo */
1339         tcgetattr(0, &ti);
1340         ti.c_lflag |= ECHO;
1341         tcsetattr(0, TCSANOW, &ti);
1342         exit(0);                        /* %% */
1343 }
1344
1345 static void
1346 timedout(int sig) {
1347         signal(SIGALRM, timedout2);
1348         alarm(10);
1349         fprintf(stderr, _("Login timed out after %d seconds\n"), timeout);
1350         signal(SIGALRM, SIG_IGN);
1351         alarm(0);
1352         timedout2(0);
1353 }
1354
1355 #ifndef HAVE_SECURITY_PAM_MISC_H
1356 int
1357 rootterm(char * ttyn)
1358 {
1359     int fd;
1360     char buf[100],*p;
1361     int cnt, more = 0;
1362
1363     fd = open(_PATH_SECURETTY, O_RDONLY);
1364     if(fd < 0) return 1;
1365
1366     /* read each line in /etc/securetty, if a line matches our ttyline
1367        then root is allowed to login on this tty, and we should return
1368        true. */
1369     for(;;) {
1370         p = buf; cnt = 100;
1371         while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
1372         if(more && *p == '\n') {
1373             *p = '\0';
1374             if(!strcmp(buf, ttyn)) {
1375                 close(fd);
1376                 return 1;
1377             } else
1378               continue;
1379         } else {
1380             close(fd);
1381             return 0;
1382         }
1383     }
1384 }
1385 #endif /* !HAVE_SECURITY_PAM_MISC_H */
1386
1387 jmp_buf motdinterrupt;
1388
1389 void
1390 motd(void) {
1391     int fd, nchars;
1392     void (*oldint)(int);
1393     char tbuf[8192];
1394
1395     if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
1396       return;
1397     oldint = signal(SIGINT, sigint);
1398     if (setjmp(motdinterrupt) == 0)
1399       while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1400         write(fileno(stdout), tbuf, nchars);
1401     signal(SIGINT, oldint);
1402     close(fd);
1403 }
1404
1405 void
1406 sigint(int sig) {
1407     longjmp(motdinterrupt, 1);
1408 }
1409
1410 #ifndef HAVE_SECURITY_PAM_MISC_H                        /* PAM takes care of this */
1411 void
1412 checknologin(void) {
1413     int fd, nchars;
1414     char tbuf[8192];
1415
1416     if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
1417         while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1418           write(fileno(stdout), tbuf, nchars);
1419         close(fd);
1420         sleepexit(0);
1421     }
1422 }
1423 #endif
1424
1425 void
1426 dolastlog(int quiet) {
1427     struct lastlog ll;
1428     int fd;
1429
1430     if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
1431         lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1432         if (!quiet) {
1433             if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
1434                 ll.ll_time != 0) {
1435                     time_t ll_time = (time_t) ll.ll_time;
1436
1437                     printf(_("Last login: %.*s "),
1438                            24-5, ctime(&ll_time));
1439
1440                     if (*ll.ll_host != '\0')
1441                             printf(_("from %.*s\n"),
1442                                    (int)sizeof(ll.ll_host), ll.ll_host);
1443                     else
1444                             printf(_("on %.*s\n"),
1445                                    (int)sizeof(ll.ll_line), ll.ll_line);
1446             }
1447             lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET);
1448         }
1449         memset((char *)&ll, 0, sizeof(ll));
1450
1451         {
1452                 time_t t;
1453                 time(&t);
1454                 ll.ll_time = t; /* ll_time is always 32bit */
1455         }
1456
1457         xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line));
1458         if (hostname)
1459             xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
1460
1461         write(fd, (char *)&ll, sizeof(ll));
1462         close(fd);
1463     }
1464 }
1465
1466 void
1467 badlogin(const char *name) {
1468     if (failures == 1) {
1469         if (hostname)
1470           syslog(LOG_NOTICE, _("LOGIN FAILURE FROM %s, %s"),
1471                  hostname, name);
1472         else
1473           syslog(LOG_NOTICE, _("LOGIN FAILURE ON %s, %s"),
1474                  tty_name, name);
1475     } else {
1476         if (hostname)
1477           syslog(LOG_NOTICE, _("%d LOGIN FAILURES FROM %s, %s"),
1478                  failures, hostname, name);
1479         else
1480           syslog(LOG_NOTICE, _("%d LOGIN FAILURES ON %s, %s"),
1481                  failures, tty_name, name);
1482     }
1483 }
1484
1485 /* Should not be called from PAM code... */
1486 void
1487 sleepexit(int eval) {
1488     sleep(SLEEP_EXIT_TIMEOUT);
1489     exit(eval);
1490 }