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