]> granicus.if.org Git - shadow/blob - src/login.c
* src/login.c: Do not mix USE_PAM and !USE_PAM code.
[shadow] / src / login.c
1 /*
2  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2001, Marek Michałkiewicz
4  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2008, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <errno.h>
38 #include <grp.h>
39 #include <lastlog.h>
40 #ifdef UT_ADDR
41 #include <netdb.h>
42 #endif
43 #include <pwd.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include "defines.h"
49 #include "faillog.h"
50 #include "failure.h"
51 #include "getdef.h"
52 #include "prototypes.h"
53 #include "pwauth.h"
54 #include "exitcodes.h"
55 #ifdef USE_PAM
56 #include "pam_defs.h"
57
58 static pam_handle_t *pamh = NULL;
59
60 #define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
61         fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
62         SYSLOG((LOG_ERR,"%s",pam_strerror(pamh, retcode))); \
63         (void) pam_end(pamh, retcode); \
64         exit(1); \
65    }
66 #define PAM_END { retcode = pam_close_session(pamh,0); \
67                 (void) pam_end(pamh,retcode); }
68
69 #endif                          /* USE_PAM */
70
71 /*
72  * Needed for MkLinux DR1/2/2.1 - J.
73  */
74 #ifndef LASTLOG_FILE
75 #define LASTLOG_FILE "/var/log/lastlog"
76 #endif
77
78 /*
79  * Global variables
80  */
81 const char *hostname = "";
82
83 static struct passwd pwent;
84
85 #if HAVE_UTMPX_H
86 extern struct utmpx utxent;
87 struct utmpx failent;
88 #else
89 struct utmp failent;
90 #endif
91 extern struct utmp utent;
92
93 struct lastlog lastlog;
94 static bool pflg = false;
95 static bool fflg = false;
96
97 #ifdef RLOGIN
98 static bool rflg = false;
99 #else
100 #define rflg false
101 #endif
102 static bool hflg = false;
103 static bool preauth_flag = false;
104
105 /*
106  * Global variables.
107  */
108 char *Prog;
109
110 static bool amroot;
111 static int timeout;
112
113 /*
114  * External identifiers.
115  */
116
117 extern char **newenvp;
118 extern size_t newenvc;
119 extern char **environ;
120
121 #ifndef ALARM
122 #define ALARM   60
123 #endif
124
125 #ifndef RETRIES
126 #define RETRIES 3
127 #endif
128
129 /* local function prototypes */
130 static void usage (void);
131 static void setup_tty (void);
132 static void check_flags (int, char *const *);
133
134 #ifndef USE_PAM
135 static struct faillog faillog;
136
137 static void bad_time_notify (void);
138 static void check_nologin (void);
139 #endif
140
141 static void init_env (void);
142 static RETSIGTYPE alarm_handler (int);
143
144 /*
145  * usage - print login command usage and exit
146  *
147  * login [ name ]
148  * login -r hostname    (for rlogind)
149  * login -h hostname    (for telnetd, etc.)
150  * login -f name        (for pre-authenticated login: datakit, xterm, etc.)
151  */
152 static void usage (void)
153 {
154         fprintf (stderr, _("Usage: %s [-p] [name]\n"), Prog);
155         if (!amroot) {
156                 exit (1);
157         }
158         fprintf (stderr, _("       %s [-p] [-h host] [-f name]\n"), Prog);
159 #ifdef RLOGIN
160         fprintf (stderr, _("       %s [-p] -r host\n"), Prog);
161 #endif
162         exit (1);
163 }
164
165 static void setup_tty (void)
166 {
167         TERMIO termio;
168
169         GTTY (0, &termio);      /* get terminal characteristics */
170
171         /*
172          * Add your favorite terminal modes here ...
173          */
174         termio.c_lflag |= ISIG | ICANON | ECHO | ECHOE;
175         termio.c_iflag |= ICRNL;
176
177         /* leave these values unchanged if not specified in login.defs */
178         termio.c_cc[VERASE] = getdef_num ("ERASECHAR", termio.c_cc[VERASE]);
179         termio.c_cc[VKILL] = getdef_num ("KILLCHAR", termio.c_cc[VKILL]);
180
181         /*
182          * ttymon invocation prefers this, but these settings won't come into
183          * effect after the first username login 
184          */
185         STTY (0, &termio);
186 }
187
188
189 #ifndef USE_PAM
190 /*
191  * Tell the user that this is not the right time to login at this tty
192  */
193 static void bad_time_notify (void)
194 {
195         (void) puts (_("Invalid login time"));
196         (void) fflush (stdout);
197 }
198
199 static void check_nologin (void)
200 {
201         char *fname;
202
203         /*
204          * Check to see if system is turned off for non-root users.
205          * This would be useful to prevent users from logging in
206          * during system maintenance. We make sure the message comes
207          * out for root so she knows to remove the file if she's
208          * forgotten about it ...
209          */
210         fname = getdef_str ("NOLOGINS_FILE");
211         if ((NULL != fname) && (access (fname, F_OK) == 0)) {
212                 FILE *nlfp;
213                 int c;
214
215                 /*
216                  * Cat the file if it can be opened, otherwise just
217                  * print a default message
218                  */
219                 nlfp = fopen (fname, "r");
220                 if (NULL != nlfp) {
221                         while ((c = getc (nlfp)) != EOF) {
222                                 if (c == '\n') {
223                                         (void) putchar ('\r');
224                                 }
225
226                                 (void) putchar (c);
227                         }
228                         (void) fflush (stdout);
229                         (void) fclose (nlfp);
230                 } else {
231                         (void) puts (_("\nSystem closed for routine maintenance"));
232                 }
233                 /*
234                  * Non-root users must exit. Root gets the message, but
235                  * gets to login.
236                  */
237
238                 if (pwent.pw_uid != 0) {
239                         closelog ();
240                         exit (0);
241                 }
242                 puts (_("\n[Disconnect bypassed -- root login allowed.]"));
243         }
244 }
245 #endif                          /* !USE_PAM */
246
247 static void check_flags (int argc, char *const *argv)
248 {
249         int arg;
250
251         /*
252          * Check the flags for proper form. Every argument starting with
253          * "-" must be exactly two characters long. This closes all the
254          * clever rlogin, telnet, and getty holes.
255          */
256         for (arg = 1; arg < argc; arg++) {
257                 if (argv[arg][0] == '-' && strlen (argv[arg]) > 2) {
258                         usage ();
259                 }
260                 if (strcmp(argv[arg], "--") == 0) {
261                         break; /* stop checking on a "--" */
262                 }
263         }
264 }
265
266
267 static void init_env (void)
268 {
269 #ifndef USE_PAM
270         char *cp;
271 #endif
272         char *tmp;
273
274         tmp = getenv ("LANG");
275         if (NULL != tmp) {
276                 addenv ("LANG", tmp);
277         }
278
279         /*
280          * Add the timezone environmental variable so that time functions
281          * work correctly.
282          */
283         tmp = getenv ("TZ");
284         if (NULL != tmp) {
285                 addenv ("TZ", tmp);
286         }
287 #ifndef USE_PAM
288         else {
289                 cp = getdef_str ("ENV_TZ");
290                 if (NULL != cp) {
291                         addenv (('/' == *cp) ? tz (cp) : cp, NULL);
292                 }
293         }
294 #endif                          /* !USE_PAM */
295         /* 
296          * Add the clock frequency so that profiling commands work
297          * correctly.
298          */
299         tmp = getenv ("HZ");
300         if (NULL != tmp) {
301                 addenv ("HZ", tmp);
302         }
303 #ifndef USE_PAM
304         else {
305                 cp = getdef_str ("ENV_HZ");
306                 if (NULL != cp) {
307                         addenv (cp, NULL);
308                 }
309         }
310 #endif                          /* !USE_PAM */
311 }
312
313
314 static RETSIGTYPE alarm_handler (unused int sig)
315 {
316         fprintf (stderr, _("\nLogin timed out after %d seconds.\n"), timeout);
317         exit (0);
318 }
319
320
321 /*
322  * login - create a new login session for a user
323  *
324  *      login is typically called by getty as the second step of a
325  *      new user session. getty is responsible for setting the line
326  *      characteristics to a reasonable set of values and getting
327  *      the name of the user to be logged in. login may also be
328  *      called to create a new user session on a pty for a variety
329  *      of reasons, such as X servers or network logins.
330  *
331  *      the flags which login supports are
332  *      
333  *      -p - preserve the environment
334  *      -r - perform autologin protocol for rlogin
335  *      -f - do not perform authentication, user is preauthenticated
336  *      -h - the name of the remote host
337  */
338 int main (int argc, char **argv)
339 {
340         char username[32];
341         char tty[BUFSIZ];
342
343 #ifdef RLOGIN
344         char term[128] = "";
345 #endif
346 #if defined(HAVE_STRFTIME) && !defined(USE_PAM)
347         char ptime[80];
348 #endif
349         int reason = PW_LOGIN;
350         int delay;
351         int retries;
352         bool failed;
353         int flag;
354         bool subroot = false;
355 #ifndef USE_PAM
356         bool is_console;
357 #endif
358         int err;
359         const char *cp;
360         char *tmp;
361         char fromhost[512];
362         struct passwd *pwd;
363         char **envp = environ;
364         static char temp_pw[2];
365         static char temp_shell[] = "/bin/sh";
366
367 #ifdef USE_PAM
368         int retcode;
369         pid_t child;
370         char *pam_user;
371         char **ptr_pam_user = &pam_user;
372 #else
373         struct spwd *spwd = NULL;
374 #endif
375         /*
376          * Some quick initialization.
377          */
378
379         sanitize_env ();
380
381         (void) setlocale (LC_ALL, "");
382         (void) bindtextdomain (PACKAGE, LOCALEDIR);
383         (void) textdomain (PACKAGE);
384
385         initenv ();
386
387         username[0] = '\0';
388         amroot = (getuid () == 0);
389         Prog = Basename (argv[0]);
390
391         check_flags (argc, argv);
392
393         while ((flag = getopt (argc, argv, "d:f::h:pr:")) != EOF) {
394                 switch (flag) {
395                 case 'd':
396                         /* "-d device" ignored for compatibility */
397                         break;
398                 case 'f':
399                         /*
400                          * username must be a separate token
401                          * (-f root, *not* -froot).  --marekm
402                          *
403                          * if -f has an arg, use that, else use the
404                          * normal user name passed after all options
405                          * --benc
406                          */
407                         if (optarg != NULL && optarg != argv[optind - 1]) {
408                                 usage ();
409                         }
410                         fflg = true;
411                         if (optarg) {
412                                 STRFCPY (username, optarg);
413                         }
414                         break;
415                 case 'h':
416                         hflg = true;
417                         hostname = optarg;
418                         reason = PW_TELNET;
419                         break;
420 #ifdef  RLOGIN
421                 case 'r':
422                         rflg = true;
423                         hostname = optarg;
424                         reason = PW_RLOGIN;
425                         break;
426 #endif
427                 case 'p':
428                         pflg = true;
429                         break;
430                 default:
431                         usage ();
432                 }
433         }
434
435 #ifdef RLOGIN
436         /*
437          * Neither -h nor -f should be combined with -r.
438          */
439
440         if (rflg && (hflg || fflg)) {
441                 usage ();
442         }
443 #endif
444
445         /*
446          * Allow authentication bypass only if real UID is zero.
447          */
448
449         if ((rflg || fflg || hflg) && !amroot) {
450                 fprintf (stderr, _("%s: Permission denied.\n"), Prog);
451                 exit (1);
452         }
453
454         if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) {
455                 exit (1);       /* must be a terminal */
456         }
457
458         /*
459          * Be picky if run by normal users (possible if installed setuid
460          * root), but not if run by root. This way it still allows logins
461          * even if your getty is broken, or if something corrupts utmp,
462          * but users must "exec login" which will use the existing utmp
463          * entry (will not overwrite remote hostname).  --marekm
464          */
465         checkutmp (!amroot);
466         STRFCPY (tty, utent.ut_line);
467 #ifndef USE_PAM
468         is_console = console (tty);
469 #endif
470
471         if (rflg || hflg) {
472 #ifdef UT_ADDR
473                 struct hostent *he;
474
475                 /*
476                  * Fill in the ut_addr field (remote login IP address). XXX
477                  * - login from util-linux does it, but this is not the
478                  * right place to do it. The program that starts login
479                  * (telnetd, rlogind) knows the IP address, so it should
480                  * create the utmp entry and fill in ut_addr. 
481                  * gethostbyname() is not 100% reliable (the remote host may
482                  * be unknown, etc.).  --marekm
483                  */
484                 he = gethostbyname (hostname);
485                 if (NULL != he) {
486                         utent.ut_addr = *((int32_t *) (he->h_addr_list[0]));
487 #endif
488 #ifdef UT_HOST
489                         strncpy (utent.ut_host, hostname,
490                                  sizeof (utent.ut_host));
491 #endif
492 #if HAVE_UTMPX_H
493                         strncpy (utxent.ut_host, hostname,
494                                  sizeof (utxent.ut_host));
495 #endif
496                         /*
497                          * Add remote hostname to the environment. I think
498                          * (not sure) I saw it once on Irix.  --marekm
499                          */
500                         addenv ("REMOTEHOST", hostname);
501                 }
502 #ifdef __linux__
503                 /*
504                  * workaround for init/getty leaving junk in ut_host at least in
505                  * some version of RedHat.  --marekm
506                  */
507                 else if (amroot) {
508                         memzero (utent.ut_host, sizeof utent.ut_host);
509                 }
510 #endif
511                 if (fflg) {
512                         preauth_flag = true;
513                 }
514                 if (hflg) {
515                         reason = PW_RLOGIN;
516                 }
517 #ifdef RLOGIN
518                 if (   rflg
519                     && do_rlogin (hostname, username, sizeof username,
520                                   term, sizeof term)) {
521                         preauth_flag = true;
522                 }
523 #endif
524
525                 OPENLOG ("login");
526
527                 setup_tty ();
528
529 #ifndef USE_PAM
530                 umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
531
532                 {
533                         /* 
534                          * Use the ULIMIT in the login.defs file, and if
535                          * there isn't one, use the default value. The
536                          * user may have one for themselves, but otherwise,
537                          * just take what you get.
538                          */
539                         long limit = getdef_long ("ULIMIT", -1L);
540
541                         if (limit != -1) {
542                                 set_filesize_limit (limit);
543                         }
544                 }
545
546 #endif
547                 /*
548                  * The entire environment will be preserved if the -p flag
549                  * is used.
550                  */
551                 if (pflg) {
552                         while (NULL != *envp) { /* add inherited environment, */
553                                 addenv (*envp, NULL);   /* some variables change later */
554                                 envp++;
555                         }
556                 }
557
558 #ifdef RLOGIN
559                 if (term[0] != '\0') {
560                         addenv ("TERM", term);
561                 } else
562 #endif
563                 {
564                         /* preserve TERM from getty */
565                         if (!pflg) {
566                                 tmp = getenv ("TERM");
567                                 if (NULL != tmp) {
568                                         addenv ("TERM", tmp);
569                                 }
570                         }
571                 }
572
573                 init_env ();
574
575                 if (optind < argc) {    /* get the user name */
576                         if (rflg || (fflg && ('\0' != username[0]))) {
577                                 usage ();
578                         }
579
580                         STRFCPY (username, argv[optind]);
581                         strzero (argv[optind]);
582                         ++optind;
583                 }
584                 if (optind < argc) {    /* now set command line variables */
585                         set_env (argc - optind, &argv[optind]);
586                 }
587
588                 if (rflg || hflg) {
589                         cp = hostname;
590                 } else {
591                         /* FIXME: What is the priority:
592                          *        UT_HOST or HAVE_UTMPX_H? */
593 #ifdef  UT_HOST
594                         if ('\0' != utent.ut_host[0]) {
595                                 cp = utent.ut_host;
596                         } else
597 #endif
598 #if HAVE_UTMPX_H
599                         if ('\0' != utxent.ut_host[0]) {
600                                 cp = utxent.ut_host;
601                         } else
602 #endif
603                         {
604                                 cp = "";
605                         }
606                 }
607
608                 if ('\0' != *cp) {
609                         snprintf (fromhost, sizeof fromhost,
610                                   " on '%.100s' from '%.200s'", tty, cp);
611                 } else {
612                         snprintf (fromhost, sizeof fromhost,
613                                   " on '%.100s'", tty);
614                 }
615
616               top:
617                 /* only allow ALARM sec. for login */
618                 (void) signal (SIGALRM, alarm_handler);
619                 timeout = getdef_num ("LOGIN_TIMEOUT", ALARM);
620                 if (timeout > 0) {
621                         alarm (timeout);
622                 }
623
624                 environ = newenvp;      /* make new environment active */
625                 delay = getdef_num ("FAIL_DELAY", 1);
626                 retries = getdef_num ("LOGIN_RETRIES", RETRIES);
627
628 #ifdef USE_PAM
629                 retcode = pam_start ("login", username, &conv, &pamh);
630                 if (retcode != PAM_SUCCESS) {
631                         fprintf (stderr,
632                                  _("login: PAM Failure, aborting: %s\n"),
633                                  pam_strerror (pamh, retcode));
634                         SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s",
635                                  pam_strerror (pamh, retcode)));
636                         exit (99);
637                 }
638
639                 /*
640                  * hostname & tty are either set to NULL or their correct values,
641                  * depending on how much we know. We also set PAM's fail delay to
642                  * ours.
643                  */
644                 retcode = pam_set_item (pamh, PAM_RHOST, hostname);
645                 PAM_FAIL_CHECK;
646                 retcode = pam_set_item (pamh, PAM_TTY, tty);
647                 PAM_FAIL_CHECK;
648 #ifdef HAS_PAM_FAIL_DELAY
649                 retcode = pam_fail_delay (pamh, 1000000 * delay);
650                 PAM_FAIL_CHECK;
651 #endif
652                 /* if fflg, then the user has already been authenticated */
653                 if (!fflg || (getuid () != 0)) {
654                         int failcount = 0;
655                         char hostn[256];
656                         char loginprompt[256];  /* That's one hell of a prompt :) */
657
658                         /* Make the login prompt look like we want it */
659                         if (gethostname (hostn, sizeof (hostn)) == 0) {
660                                 snprintf (loginprompt,
661                                           sizeof (loginprompt),
662                                           _("%s login: "), hostn);
663                         } else {
664                                 snprintf (loginprompt,
665                                           sizeof (loginprompt), _("login: "));
666                         }
667
668                         retcode =
669                             pam_set_item (pamh, PAM_USER_PROMPT, loginprompt);
670                         PAM_FAIL_CHECK;
671
672                         /* if we didn't get a user on the command line,
673                            set it to NULL */
674                         pam_get_item (pamh, PAM_USER,
675                                       (const void **)ptr_pam_user);
676                         if (pam_user[0] == '\0') {
677                                 pam_set_item (pamh, PAM_USER, NULL);
678                         }
679
680                         /*
681                          * There may be better ways to deal with some of
682                          * these conditions, but at least this way I don't
683                          * think we'll be giving away information. Perhaps
684                          * someday we can trust that all PAM modules will
685                          * pay attention to failure count and get rid of
686                          * MAX_LOGIN_TRIES?
687                          */
688                         failcount = 0;
689                         while (true) {
690                           const char *failent_user;
691                           failed = false;
692
693                           failcount++;
694 #ifdef HAS_PAM_FAIL_DELAY
695                           if (delay > 0) {
696                             retcode = pam_fail_delay(pamh, 1000000*delay);
697                           }
698 #endif
699
700                           retcode = pam_authenticate (pamh, 0);
701
702                           pam_get_item (pamh, PAM_USER,
703                                         (const void **) ptr_pam_user);
704
705                           if ((NULL != pam_user) && ('\0' != pam_user[0])) {
706                             pwd = xgetpwnam(pam_user);
707                             if (NULL != pwd) {
708                               pwent = *pwd;
709                               failent_user = pwent.pw_name;
710                             } else {
711                               if (   getdef_bool("LOG_UNKFAIL_ENAB")
712                                   && (NULL != pam_user)) {
713                                 failent_user = pam_user;
714                               } else {
715                                 failent_user = "UNKNOWN";
716                               }
717                             }
718                           } else {
719                             pwd = NULL;
720                             failent_user = "UNKNOWN";
721                           }
722
723                           if (retcode == PAM_MAXTRIES || failcount >= retries) {
724                             SYSLOG ((LOG_NOTICE,
725                                     "TOO MANY LOGIN TRIES (%d)%s FOR '%s'",
726                                     failcount, fromhost, failent_user));
727                             fprintf(stderr,
728                                     _("Maximum number of tries exceeded (%d)\n"),
729                                     failcount);
730                             PAM_END;
731                             exit(0);
732                           } else if (retcode == PAM_ABORT) {
733                             /* Serious problems, quit now */
734                             fputs (_("login: abort requested by PAM\n"),stderr);
735                             SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()"));
736                             PAM_END;
737                             exit(99);
738                           } else if (retcode != PAM_SUCCESS) {
739                             SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%d)%s FOR '%s', %s",
740                                    failcount, fromhost, failent_user,
741                                    pam_strerror (pamh, retcode)));
742                             failed = true;
743                           }
744
745                           if (!failed) {
746                             break;
747                           }
748
749 #ifdef WITH_AUDIT
750                           audit_fd = audit_open ();
751                           audit_log_acct_message (audit_fd,
752                                                   AUDIT_USER_LOGIN,
753                                                   NULL,    /* Prog. name */
754                                                   "login",
755                                                   failent_user,
756                                                   AUDIT_NO_ID,
757                                                   hostname,
758                                                   NULL,    /* addr */
759                                                   tty,
760                                                   0);      /* result */
761                           close (audit_fd);
762 #endif                          /* WITH_AUDIT */
763
764                           fprintf (stderr, "\nLogin incorrect\n");
765
766                           /* Let's give it another go around */
767                           pam_set_item (pamh, PAM_USER, NULL);
768                         }
769
770                         /* We don't get here unless they were authenticated above */
771                         alarm (0);
772                         retcode = pam_acct_mgmt (pamh, 0);
773
774                         if (retcode == PAM_NEW_AUTHTOK_REQD) {
775                                 retcode =
776                                     pam_chauthtok (pamh,
777                                                    PAM_CHANGE_EXPIRED_AUTHTOK);
778                         }
779
780                         PAM_FAIL_CHECK;
781                 }
782
783                 /* Grab the user information out of the password file for future usage
784                    First get the username that we are actually using, though.
785                  */
786                 retcode =
787                     pam_get_item (pamh, PAM_USER, (const void **)ptr_pam_user);
788                 pwd = xgetpwnam (pam_user);
789                 if (NULL == pwd) {
790                         SYSLOG ((LOG_ERR, "xgetpwnam(%s) failed",
791                                  getdef_bool ("LOG_UNKFAIL_ENAB") ?
792                                  pam_user : "UNKNOWN"));
793                         exit (1);
794                 }
795
796                 if (fflg) {
797                         retcode = pam_acct_mgmt (pamh, 0);
798                         PAM_FAIL_CHECK;
799                 }
800
801                 if (setup_groups (pwd) != 0) {
802                         exit (1);
803                 }
804
805                 pwent = *pwd;
806
807                 retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED);
808                 PAM_FAIL_CHECK;
809
810                 retcode = pam_open_session (pamh,
811                                             hushed (&pwent) ? PAM_SILENT : 0);
812                 PAM_FAIL_CHECK;
813
814                 pwd = xgetpwnam (pam_user);
815                 if (NULL == pwd) {
816                         pwent.pw_name = pam_user;
817                         strcpy (temp_pw, "!");
818                         pwent.pw_passwd = temp_pw;
819                         pwent.pw_shell = temp_shell;
820
821                         preauth_flag = false;
822                         failed = true;
823                 } else {
824                         pwent = *pwd;
825                 }
826
827 #else                           /* ! USE_PAM */
828                 while (true) {  /* repeatedly get login/password pairs */
829                         failed = false; /* haven't failed authentication yet */
830                         if ('\0' == username[0]) {      /* need to get a login id */
831                                 if (subroot) {
832                                         closelog ();
833                                         exit (1);
834                                 }
835                                 preauth_flag = false;
836                                 login_prompt (_("\n%s login: "), username,
837                                               sizeof username);
838                                 continue;
839                         }
840
841                 pwd = xgetpwnam (username);
842                 if (NULL == pwd) {
843                         pwent.pw_name = username;
844                         strcpy (temp_pw, "!");
845                         pwent.pw_passwd = temp_pw;
846                         pwent.pw_shell = temp_shell;
847
848                         preauth_flag = false;
849                         failed = true;
850                 } else {
851                         pwent = *pwd;
852                 }
853
854                 spwd = NULL;
855                 if (   (NULL != pwd)
856                     && (strcmp (pwd->pw_passwd, SHADOW_PASSWD_STRING) == 0)) {
857                         /* !USE_PAM, no need for xgetspnam */
858                         spwd = getspnam (username);
859                         if (NULL != spwd) {
860                                 pwent.pw_passwd = spwd->sp_pwdp;
861                         } else {
862                                 SYSLOG ((LOG_WARN,
863                                          "no shadow password for '%s'%s",
864                                          username, fromhost));
865                         }
866                 }
867
868                 /*
869                  * If the encrypted password begins with a "!", the account
870                  * is locked and the user cannot login, even if they have
871                  * been "pre-authenticated."
872                  */
873                 if (   ('!' == pwent.pw_passwd[0])
874                     || ('*' == pwent.pw_passwd[0])) {
875                         failed = true;
876                 }
877
878                 /*
879                  * The -r and -f flags provide a name which has already
880                  * been authenticated by some server.
881                  */
882                 if (preauth_flag) {
883                         goto auth_ok;
884                 }
885
886                 if (pw_auth (pwent.pw_passwd, username,
887                              reason, (char *) 0) == 0) {
888                         goto auth_ok;
889                 }
890
891                 /*
892                  * Don't log unknown usernames - I mistyped the password for
893                  * username at least once. Should probably use LOG_AUTHPRIV
894                  * for those who really want to log them.  --marekm
895                  */
896                 SYSLOG ((LOG_WARN, "invalid password for '%s' %s",
897                          (   (NULL != pwd)
898                           || getdef_bool ("LOG_UNKFAIL_ENAB")) ?
899                          username : "UNKNOWN", fromhost));
900                 failed = true;
901
902               auth_ok:
903                 /*
904                  * This is the point where all authenticated users wind up.
905                  * If you reach this far, your password has been
906                  * authenticated and so on.
907                  */
908                 if (   !failed
909                     && (NULL != pwent.pw_name)
910                     && (0 == pwent.pw_uid)
911                     && !is_console) {
912                         SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost));
913                         failed = true;
914                 }
915                 if (   !failed
916                     && !login_access (username, *hostname ? hostname : tty)) {
917                         SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s",
918                                  username, fromhost));
919                         failed = true;
920                 }
921                 if (   (NULL != pwd)
922                     && getdef_bool ("FAILLOG_ENAB")
923                     && !failcheck (pwent.pw_uid, &faillog, failed)) {
924                         SYSLOG ((LOG_CRIT,
925                                  "exceeded failure limit for '%s' %s",
926                                  username, fromhost));
927                         failed = true;
928                 }
929                 if (!failed) {
930                         break;
931                 }
932
933                 /* don't log non-existent users */
934                 if ((NULL != pwd) && getdef_bool ("FAILLOG_ENAB")) {
935                         failure (pwent.pw_uid, tty, &faillog);
936                 }
937                 if (getdef_str ("FTMP_FILE") != NULL) {
938                         const char *failent_user;
939
940 #if HAVE_UTMPX_H
941                         failent = utxent;
942                         if (sizeof (failent.ut_tv) == sizeof (struct timeval)) {
943                                 gettimeofday ((struct timeval *) &failent.ut_tv,
944                                               NULL);
945                         } else {
946                                 struct timeval tv;
947
948                                 gettimeofday (&tv, NULL);
949                                 failent.ut_tv.tv_sec = tv.tv_sec;
950                                 failent.ut_tv.tv_usec = tv.tv_usec;
951                         }
952 #else
953                         failent = utent;
954                         failent.ut_time = time (NULL);
955 #endif
956                         if (NULL != pwd) {
957                                 failent_user = pwent.pw_name;
958                         } else {
959                                 if (getdef_bool ("LOG_UNKFAIL_ENAB")) {
960                                         failent_user = username;
961                                 } else {
962                                         failent_user = "UNKNOWN";
963                                 }
964                         }
965                         strncpy (failent.ut_user, failent_user,
966                                  sizeof (failent.ut_user));
967                         failent.ut_type = USER_PROCESS;
968                         failtmp (&failent);
969                 }
970                 memzero (username, sizeof username);
971
972                 retries--;
973                 if (retries <= 0) {
974                         SYSLOG ((LOG_CRIT, "REPEATED login failures%s",
975                                  fromhost));
976                 }
977                 /*
978                  * If this was a passwordless account and we get here, login
979                  * was denied (securetty, faillog, etc.). There was no
980                  * password prompt, so do it now (will always fail - the bad
981                  * guys won't see that the passwordless account exists at
982                  * all).  --marekm
983                  */
984                 if (pwent.pw_passwd[0] == '\0') {
985                         pw_auth ("!", username, reason, (char *) 0);
986                 }
987
988                 /*
989                  * Wait a while (a la SVR4 /usr/bin/login) before attempting
990                  * to login the user again. If the earlier alarm occurs
991                  * before the sleep() below completes, login will exit.
992                  */
993                 if (delay > 0) {
994                         sleep (delay);
995                 }
996
997                 puts (_("Login incorrect"));
998
999                 /* allow only one attempt with -r or -f */
1000                 if (rflg || fflg || (retries <= 0)) {
1001                         closelog ();
1002                         exit (1);
1003                 }
1004         }                       /* while (true) */
1005 #endif                          /* ! USE_PAM */
1006
1007         alarm (0);              /* turn off alarm clock */
1008
1009 #ifndef USE_PAM                 /* PAM does this */
1010         /*
1011          * porttime checks moved here, after the user has been
1012          * authenticated. now prints a message, as suggested
1013          * by Ivan Nejgebauer <ian@unsux.ns.ac.yu>.  --marekm
1014          */
1015         if (   getdef_bool ("PORTTIME_CHECKS_ENAB")
1016             && !isttytime (pwent.pw_name, tty, time ((time_t *) 0))) {
1017                 SYSLOG ((LOG_WARN, "invalid login time for '%s'%s",
1018                          username, fromhost));
1019                 closelog ();
1020                 bad_time_notify ();
1021                 exit (1);
1022         }
1023
1024         check_nologin ();
1025 #endif
1026
1027         if (getenv ("IFS")) {   /* don't export user IFS ... */
1028                 addenv ("IFS= \t\n", NULL);     /* ... instead, set a safe IFS */
1029         }
1030
1031 #ifdef USE_PAM
1032         setutmp (pam_user, tty, hostname);      /* make entry in utmp & wtmp files */
1033 #else
1034         setutmp (username, tty, hostname);      /* make entry in utmp & wtmp files */
1035 #endif
1036         if (pwent.pw_shell[0] == '*') { /* subsystem root */
1037                 pwent.pw_shell++;       /* skip the '*' */
1038                 subsystem (&pwent);     /* figure out what to execute */
1039                 subroot = true; /* say I was here again */
1040                 endpwent ();    /* close all of the file which were */
1041                 endgrent ();    /* open in the original rooted file */
1042                 endspent ();    /* system. they will be re-opened */
1043 #ifdef  SHADOWGRP
1044                 endsgent ();    /* in the new rooted file system */
1045 #endif
1046                 goto top;       /* go do all this all over again */
1047         }
1048
1049 #ifdef WITH_AUDIT
1050         audit_fd = audit_open ();
1051         audit_log_acct_message (audit_fd,
1052                                 AUDIT_USER_LOGIN,
1053                                 NULL,    /* Prog. name */
1054                                 "login",
1055                                 pwd->pw_name,
1056                                 AUDIT_NO_ID,
1057                                 hostname,
1058                                 NULL,    /* addr */
1059                                 tty,
1060                                 1);      /* result */
1061         close (audit_fd);
1062 #endif                          /* WITH_AUDIT */
1063
1064 #ifndef USE_PAM                 /* pam_lastlog handles this */
1065         if (getdef_bool ("LASTLOG_ENAB")) {     /* give last login and log this one */
1066                 dolastlog (&lastlog, &pwent, utent.ut_line, hostname);
1067         }
1068 #endif
1069
1070 #ifndef USE_PAM                 /* PAM handles this as well */
1071         /*
1072          * Have to do this while we still have root privileges, otherwise we
1073          * don't have access to /etc/shadow. expire() closes password files,
1074          * and changes to the user in the child before executing the passwd
1075          * program.  --marekm
1076          */
1077         if (spwd) {             /* check for age of password */
1078                 if (expire (&pwent, spwd)) {
1079                         /* !USE_PAM, no need for xgetpwnam */
1080                         pwd = getpwnam (username);
1081                         /* !USE_PAM, no need for xgetspnam */
1082                         spwd = getspnam (username);
1083                         if (pwd) {
1084                                 pwent = *pwd;
1085                         }
1086                 }
1087         }
1088         setup_limits (&pwent);  /* nice, ulimit etc. */
1089 #endif                          /* ! USE_PAM */
1090         chown_tty (tty, &pwent);
1091
1092 #ifdef USE_PAM
1093         /*
1094          * We must fork before setuid() because we need to call
1095          * pam_close_session() as root.
1096          */
1097         (void) signal (SIGINT, SIG_IGN);
1098         child = fork ();
1099         if (child < 0) {
1100                 /* error in fork() */
1101                 fprintf (stderr, _("%s: failure forking: %s"),
1102                          Prog, strerror (errno));
1103                 PAM_END;
1104                 exit (0);
1105         } else if (child != 0) {
1106                 /*
1107                  * parent - wait for child to finish, then cleanup
1108                  * session
1109                  */
1110                 wait (NULL);
1111                 PAM_END;
1112                 exit (0);
1113         }
1114         /* child */
1115 #endif
1116         /* If we were init, we need to start a new session */
1117         if (getppid() == 1) {
1118                 setsid();
1119                 if (ioctl(0, TIOCSCTTY, 1) != 0) {
1120                         fprintf (stderr,_("TIOCSCTTY failed on %s"),tty);
1121                 }
1122         }
1123
1124         /* We call set_groups() above because this clobbers pam_groups.so */
1125 #ifndef USE_PAM
1126         if (setup_uid_gid (&pwent, is_console))
1127 #else
1128         if (change_uid (&pwent))
1129 #endif
1130         {
1131                 exit (1);
1132         }
1133
1134         setup_env (&pwent);     /* set env vars, cd to the home dir */
1135
1136 #ifdef USE_PAM
1137         {
1138                 const char *const *env;
1139
1140                 env = (const char *const *) pam_getenvlist (pamh);
1141                 while ((NULL != env) && (NULL != *env)) {
1142                         addenv (*env, NULL);
1143                         env++;
1144                 }
1145         }
1146 #endif
1147
1148         (void) setlocale (LC_ALL, "");
1149         (void) bindtextdomain (PACKAGE, LOCALEDIR);
1150         (void) textdomain (PACKAGE);
1151
1152         if (!hushed (&pwent)) {
1153                 addenv ("HUSHLOGIN=FALSE", NULL);
1154                 /*
1155                  * pam_unix, pam_mail and pam_lastlog should take care of
1156                  * this
1157                  */
1158 #ifndef USE_PAM
1159                 motd ();        /* print the message of the day */
1160                 if (   getdef_bool ("FAILLOG_ENAB")
1161                     && (0 != faillog.fail_cnt)) {
1162                         failprint (&faillog);
1163                         /* Reset the lockout times if logged in */
1164                         if (   (0 != faillog.fail_max)
1165                             && (faillog.fail_cnt >= faillog.fail_max)) {
1166                                 puts (_
1167                                       ("Warning: login re-enabled after temporary lockout."));
1168                                 SYSLOG ((LOG_WARN,
1169                                          "login '%s' re-enabled after temporary lockout (%d failures)",
1170                                          username, (int) faillog.fail_cnt));
1171                         }
1172                 }
1173                 if (   getdef_bool ("LASTLOG_ENAB")
1174                     && (lastlog.ll_time != 0)) {
1175                         time_t ll_time = lastlog.ll_time;
1176
1177 #ifdef HAVE_STRFTIME
1178                         strftime (ptime, sizeof (ptime),
1179                                   "%a %b %e %H:%M:%S %z %Y",
1180                                   localtime (&ll_time));
1181                         printf (_("Last login: %s on %s"),
1182                                 ptime, lastlog.ll_line);
1183 #else
1184                         printf (_("Last login: %.19s on %s"),
1185                                 ctime (&ll_time), lastlog.ll_line);
1186 #endif
1187 #ifdef HAVE_LL_HOST             /* __linux__ || SUN4 */
1188                         if ('\0' != lastlog.ll_host[0]) {
1189                                 printf (_(" from %.*s"),
1190                                         (int) sizeof lastlog.
1191                                         ll_host, lastlog.ll_host);
1192                         }
1193 #endif
1194                         printf (".\n");
1195                 }
1196                 agecheck (&pwent, spwd);
1197
1198                 mailcheck ();   /* report on the status of mail */
1199 #endif                          /* !USE_PAM */
1200         } else {
1201                 addenv ("HUSHLOGIN=TRUE", NULL);
1202         }
1203
1204         if (   (NULL != getdef_str ("TTYTYPE_FILE"))
1205             && (NULL == getenv ("TERM"))) {
1206                 ttytype (tty);
1207         }
1208
1209         (void) signal (SIGQUIT, SIG_DFL);       /* default quit signal */
1210         (void) signal (SIGTERM, SIG_DFL);       /* default terminate signal */
1211         (void) signal (SIGALRM, SIG_DFL);       /* default alarm signal */
1212         (void) signal (SIGHUP, SIG_DFL);        /* added this.  --marekm */
1213         (void) signal (SIGINT, SIG_DFL);        /* default interrupt signal */
1214
1215         endpwent ();            /* stop access to password file */
1216         endgrent ();            /* stop access to group file */
1217         endspent ();            /* stop access to shadow passwd file */
1218 #ifdef  SHADOWGRP
1219         endsgent ();            /* stop access to shadow group file */
1220 #endif
1221         if (0 == pwent.pw_uid) {
1222                 SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost));
1223         } else if (getdef_bool ("LOG_OK_LOGINS")) {
1224 #ifdef USE_PAM
1225                 SYSLOG ((LOG_INFO, "'%s' logged in %s", pam_user, fromhost));
1226 #else
1227                 SYSLOG ((LOG_INFO, "'%s' logged in %s", username, fromhost));
1228 #endif
1229         }
1230         closelog ();
1231         tmp = getdef_str ("FAKE_SHELL");
1232         if (NULL != tmp) {
1233                 err = shell (tmp, pwent.pw_shell, newenvp); /* fake shell */
1234         } else {
1235                 /* exec the shell finally */
1236                 err = shell (pwent.pw_shell, (char *) 0, newenvp);
1237         }
1238         exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
1239         /* NOT REACHED */
1240         return 0;
1241 }
1242