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