]> granicus.if.org Git - shadow/blob - src/login.c
Ensure that getpwent() is used in setpwent(), getpwent(),
[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                 pwd = xgetpwnam (pam_user);
742                 if (!pwd) {
743                         SYSLOG ((LOG_ERR, "xgetpwnam(%s) failed",
744                                  getdef_bool ("LOG_UNKFAIL_ENAB") ?
745                                  pam_user : "UNKNOWN"));
746                         exit (1);
747                 }
748
749                 if (fflg) {
750                         retcode = pam_acct_mgmt (pamh, 0);
751                         PAM_FAIL_CHECK;
752                 }
753
754                 if (setup_groups (pwd))
755                         exit (1);
756
757                 pwent = *pwd;
758
759                 retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED);
760                 PAM_FAIL_CHECK;
761
762                 retcode = pam_open_session (pamh,
763                                             hushed (&pwent) ? PAM_SILENT : 0);
764                 PAM_FAIL_CHECK;
765
766 #else                           /* ! USE_PAM */
767                 while (1) {     /* repeatedly get login/password pairs */
768                         failed = 0;     /* haven't failed authentication yet */
769                         if (!username[0]) {     /* need to get a login id */
770                                 if (subroot) {
771                                         closelog ();
772                                         exit (1);
773                                 }
774                                 preauth_flag = 0;
775                                 login_prompt (_("\n%s login: "), username,
776                                               sizeof username);
777                                 continue;
778                         }
779 #endif                          /* ! USE_PAM */
780
781 #ifdef USE_PAM
782                 if (!(pwd = xgetpwnam (pam_user))) {
783                         pwent.pw_name = pam_user;
784 #else
785                 if (!(pwd = xgetpwnam (username))) {
786                         pwent.pw_name = username;
787 #endif
788                         strcpy (temp_pw, "!");
789                         pwent.pw_passwd = temp_pw;
790                         pwent.pw_shell = temp_shell;
791
792                         preauth_flag = 0;
793                         failed = 1;
794                 } else {
795                         pwent = *pwd;
796                 }
797 #ifndef USE_PAM
798                 spwd = NULL;
799                 if (pwd && strcmp (pwd->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
800                         /* !USE_PAM, no need for xgetspnam */
801                         spwd = getspnam (username);
802                         if (spwd)
803                                 pwent.pw_passwd = spwd->sp_pwdp;
804                         else
805                                 SYSLOG ((LOG_WARN,
806                                          "no shadow password for `%s'%s",
807                                          username, fromhost));
808                 }
809
810                 /*
811                  * If the encrypted password begins with a "!", the account
812                  * is locked and the user cannot login, even if they have
813                  * been "pre-authenticated."
814                  */
815                 if (pwent.pw_passwd[0] == '!' || pwent.pw_passwd[0] == '*')
816                         failed = 1;
817
818                 /*
819                  * The -r and -f flags provide a name which has already
820                  * been authenticated by some server.
821                  */
822                 if (preauth_flag)
823                         goto auth_ok;
824
825                 if (pw_auth
826                     (pwent.pw_passwd, username, reason, (char *) 0) == 0)
827                         goto auth_ok;
828
829                 /*
830                  * Don't log unknown usernames - I mistyped the password for
831                  * username at least once. Should probably use LOG_AUTHPRIV
832                  * for those who really want to log them.  --marekm
833                  */
834                 SYSLOG ((LOG_WARN, "invalid password for `%s' %s",
835                          (pwd
836                           || getdef_bool ("LOG_UNKFAIL_ENAB")) ?
837                          username : "UNKNOWN", fromhost));
838                 failed = 1;
839
840               auth_ok:
841                 /*
842                  * This is the point where all authenticated users wind up.
843                  * If you reach this far, your password has been
844                  * authenticated and so on.
845                  */
846                 if (!failed && pwent.pw_name && pwent.pw_uid == 0
847                     && !is_console) {
848                         SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost));
849                         failed = 1;
850                 }
851                 if (!failed
852                     && !login_access (username, *hostname ? hostname : tty)) {
853                         SYSLOG ((LOG_WARN, "LOGIN `%s' REFUSED %s",
854                                  username, fromhost));
855                         failed = 1;
856                 }
857                 if (pwd && getdef_bool ("FAILLOG_ENAB") &&
858                     !failcheck (pwent.pw_uid, &faillog, failed)) {
859                         SYSLOG ((LOG_CRIT,
860                                  "exceeded failure limit for `%s' %s",
861                                  username, fromhost));
862                         failed = 1;
863                 }
864                 if (!failed)
865                         break;
866
867                 /* don't log non-existent users */
868                 if (pwd && getdef_bool ("FAILLOG_ENAB"))
869                         failure (pwent.pw_uid, tty, &faillog);
870                 if (getdef_str ("FTMP_FILE") != NULL) {
871                         const char *failent_user;
872
873 #if HAVE_UTMPX_H
874                         failent = utxent;
875                         if (sizeof (failent.ut_tv) == sizeof (struct timeval))
876                                 gettimeofday ((struct timeval *)
877                                               &failent.ut_tv, NULL);
878                         else {
879                                 struct timeval tv;
880
881                                 gettimeofday (&tv, NULL);
882                                 failent.ut_tv.tv_sec = tv.tv_sec;
883                                 failent.ut_tv.tv_usec = tv.tv_usec;
884                         }
885 #else
886                         failent = utent;
887                         failent.ut_time = time (NULL);
888 #endif
889                         if (pwd) {
890                                 failent_user = pwent.pw_name;
891                         } else {
892                                 if (getdef_bool ("LOG_UNKFAIL_ENAB"))
893                                         failent_user = username;
894                                 else
895                                         failent_user = "UNKNOWN";
896                         }
897                         strncpy (failent.ut_user, failent_user,
898                                  sizeof (failent.ut_user));
899                         failent.ut_type = USER_PROCESS;
900                         failtmp (&failent);
901                 }
902                 memzero (username, sizeof username);
903
904                 if (--retries <= 0)
905                         SYSLOG ((LOG_CRIT, "REPEATED login failures%s",
906                                  fromhost));
907                 /*
908                  * If this was a passwordless account and we get here, login
909                  * was denied (securetty, faillog, etc.). There was no
910                  * password prompt, so do it now (will always fail - the bad
911                  * guys won't see that the passwordless account exists at
912                  * all).  --marekm
913                  */
914                 if (pwent.pw_passwd[0] == '\0')
915                         pw_auth ("!", username, reason, (char *) 0);
916
917                 /*
918                  * Wait a while (a la SVR4 /usr/bin/login) before attempting
919                  * to login the user again. If the earlier alarm occurs
920                  * before the sleep() below completes, login will exit.
921                  */
922                 if (delay > 0)
923                         sleep (delay);
924
925                 puts (_("Login incorrect"));
926
927                 /* allow only one attempt with -r or -f */
928                 if (rflg || fflg || retries <= 0) {
929                         closelog ();
930                         exit (1);
931                 }
932         }                       /* while (1) */
933 #endif                          /* ! USE_PAM */
934         alarm (0);              /* turn off alarm clock */
935 #ifndef USE_PAM                 /* PAM does this */
936         /*
937          * porttime checks moved here, after the user has been
938          * authenticated. now prints a message, as suggested
939          * by Ivan Nejgebauer <ian@unsux.ns.ac.yu>.  --marekm
940          */
941         if (getdef_bool ("PORTTIME_CHECKS_ENAB") &&
942             !isttytime (pwent.pw_name, tty, time ((time_t *) 0))) {
943                 SYSLOG ((LOG_WARN, "invalid login time for `%s'%s",
944                          username, fromhost));
945                 closelog ();
946                 bad_time_notify ();
947                 exit (1);
948         }
949
950         check_nologin ();
951 #endif
952
953         if (getenv ("IFS"))     /* don't export user IFS ... */
954                 addenv ("IFS= \t\n", NULL);     /* ... instead, set a safe IFS */
955
956 #ifdef USE_PAM
957         setutmp (pam_user, tty, hostname);      /* make entry in utmp & wtmp files */
958 #else
959         setutmp (username, tty, hostname);      /* make entry in utmp & wtmp files */
960 #endif
961         if (pwent.pw_shell[0] == '*') { /* subsystem root */
962                 pwent.pw_shell++;       /* skip the '*' */
963                 subsystem (&pwent);     /* figure out what to execute */
964                 subroot++;      /* say I was here again */
965                 endpwent ();    /* close all of the file which were */
966                 endgrent ();    /* open in the original rooted file */
967                 endspent ();    /* system. they will be re-opened */
968 #ifdef  SHADOWGRP
969                 endsgent ();    /* in the new rooted file system */
970 #endif
971                 goto top;       /* go do all this all over again */
972         }
973
974 #ifdef WITH_AUDIT
975         {
976                 char buf[32];
977
978                 audit_fd = audit_open ();
979                 snprintf (buf, sizeof (buf), "uid=%d", pwd->pw_uid);
980                 audit_log_user_message (audit_fd, AUDIT_USER_LOGIN,
981                                         buf, hostname, NULL, tty, 1);
982                 close (audit_fd);
983         }
984 #endif                          /* WITH_AUDIT */
985
986 #ifndef USE_PAM                 /* pam_lastlog handles this */
987         if (getdef_bool ("LASTLOG_ENAB"))       /* give last login and log this one */
988                 dolastlog (&lastlog, &pwent, utent.ut_line, hostname);
989 #endif
990
991 #ifndef USE_PAM                 /* PAM handles this as well */
992         /*
993          * Have to do this while we still have root privileges, otherwise we
994          * don't have access to /etc/shadow. expire() closes password files,
995          * and changes to the user in the child before executing the passwd
996          * program.  --marekm
997          */
998         if (spwd) {             /* check for age of password */
999                 if (expire (&pwent, spwd)) {
1000                         /* !USE_PAM, no need for xgetpwnam */
1001                         pwd = getpwnam (username);
1002                         /* !USE_PAM, no need for xgetspnam */
1003                         spwd = getspnam (username);
1004                         if (pwd)
1005                                 pwent = *pwd;
1006                 }
1007         }
1008         setup_limits (&pwent);  /* nice, ulimit etc. */
1009 #endif                          /* ! USE_PAM */
1010         chown_tty (tty, &pwent);
1011
1012 #ifdef USE_PAM
1013         /*
1014          * We must fork before setuid() because we need to call
1015          * pam_close_session() as root.
1016          */
1017         signal (SIGINT, SIG_IGN);
1018         child = fork ();
1019         if (child < 0) {
1020                 /* error in fork() */
1021                 fprintf (stderr, _("%s: failure forking: %s"),
1022                          Prog, strerror (errno));
1023                 PAM_END;
1024                 exit (0);
1025         } else if (child) {
1026                 /*
1027                  * parent - wait for child to finish, then cleanup
1028                  * session
1029                  */
1030                 wait (NULL);
1031                 PAM_END;
1032                 exit (0);
1033         }
1034         /* child */
1035 #endif
1036         /* If we were init, we need to start a new session */
1037         if (getppid() == 1) {
1038                 setsid();
1039                 if (ioctl(0, TIOCSCTTY, 1))
1040                         fprintf(stderr,_("TIOCSCTTY failed on %s"),tty);
1041         }
1042
1043         /* We call set_groups() above because this clobbers pam_groups.so */
1044 #ifndef USE_PAM
1045         if (setup_uid_gid (&pwent, is_console))
1046 #else
1047         if (change_uid (&pwent))
1048 #endif
1049                 exit (1);
1050
1051         setup_env (&pwent);     /* set env vars, cd to the home dir */
1052
1053 #ifdef USE_PAM
1054         {
1055                 const char *const *env;
1056
1057                 env = (const char *const *) pam_getenvlist (pamh);
1058                 while (env && *env) {
1059                         addenv (*env, NULL);
1060                         env++;
1061                 }
1062         }
1063 #endif
1064
1065         setlocale (LC_ALL, "");
1066         bindtextdomain (PACKAGE, LOCALEDIR);
1067         textdomain (PACKAGE);
1068
1069         if (!hushed (&pwent)) {
1070                 addenv ("HUSHLOGIN=FALSE", NULL);
1071                 /*
1072                  * pam_unix, pam_mail and pam_lastlog should take care of
1073                  * this
1074                  */
1075 #ifndef USE_PAM
1076                 motd ();        /* print the message of the day */
1077                 if (getdef_bool ("FAILLOG_ENAB")
1078                     && faillog.fail_cnt != 0) {
1079                         failprint (&faillog);
1080                         /* Reset the lockout times if logged in */
1081                         if (faillog.fail_max &&
1082                             faillog.fail_cnt >= faillog.fail_max) {
1083                                 puts (_
1084                                       ("Warning: login re-enabled after temporary lockout."));
1085                                 SYSLOG ((LOG_WARN,
1086                                          "login `%s' re-enabled after temporary lockout (%d failures)",
1087                                          username, (int) faillog.fail_cnt));
1088                         }
1089                 }
1090                 if (getdef_bool ("LASTLOG_ENAB")
1091                     && lastlog.ll_time != 0) {
1092                         time_t ll_time = lastlog.ll_time;
1093
1094 #ifdef HAVE_STRFTIME
1095                         strftime (ptime, sizeof (ptime),
1096                                   "%a %b %e %H:%M:%S %z %Y",
1097                                   localtime (&ll_time));
1098                         printf (_("Last login: %s on %s"),
1099                                 ptime, lastlog.ll_line);
1100 #else
1101                         printf (_("Last login: %.19s on %s"),
1102                                 ctime (&ll_time), lastlog.ll_line);
1103 #endif
1104 #ifdef HAVE_LL_HOST             /* __linux__ || SUN4 */
1105                         if (lastlog.ll_host[0])
1106                                 printf (_(" from %.*s"),
1107                                         (int) sizeof lastlog.
1108                                         ll_host, lastlog.ll_host);
1109 #endif
1110                         printf (".\n");
1111                 }
1112                 agecheck (&pwent, spwd);
1113
1114                 mailcheck ();   /* report on the status of mail */
1115 #endif                          /* !USE_PAM */
1116         } else
1117                 addenv ("HUSHLOGIN=TRUE", NULL);
1118
1119         if (getdef_str ("TTYTYPE_FILE") != NULL && getenv ("TERM") == NULL)
1120                 ttytype (tty);
1121
1122         signal (SIGQUIT, SIG_DFL);      /* default quit signal */
1123         signal (SIGTERM, SIG_DFL);      /* default terminate signal */
1124         signal (SIGALRM, SIG_DFL);      /* default alarm signal */
1125         signal (SIGHUP, SIG_DFL);       /* added this.  --marekm */
1126         signal (SIGINT, SIG_DFL);       /* default interrupt signal */
1127
1128         endpwent ();            /* stop access to password file */
1129         endgrent ();            /* stop access to group file */
1130         endspent ();            /* stop access to shadow passwd file */
1131 #ifdef  SHADOWGRP
1132         endsgent ();            /* stop access to shadow group file */
1133 #endif
1134         if (pwent.pw_uid == 0)
1135                 SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost));
1136         else if (getdef_bool ("LOG_OK_LOGINS"))
1137 #ifdef USE_PAM
1138                 SYSLOG ((LOG_INFO, "`%s' logged in %s", pam_user, fromhost));
1139 #else
1140                 SYSLOG ((LOG_INFO, "`%s' logged in %s", username, fromhost));
1141 #endif
1142         closelog ();
1143         if ((tmp = getdef_str ("FAKE_SHELL")) != NULL)
1144                 err = shell (tmp, pwent.pw_shell, newenvp); /* fake shell */
1145         else
1146                 /* exec the shell finally */
1147                 err = shell (pwent.pw_shell, (char *) 0, newenvp);
1148         exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
1149         /* NOT REACHED */
1150         return 0;
1151 }