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