]> git.saurik.com Git - apple/system_cmds.git/blobdiff - login.tproj/login.c
system_cmds-433.1.tar.gz
[apple/system_cmds.git] / login.tproj / login.c
index cbfd71987f5afac2b8fa17eeb9f124b7bd3587a8..76fbc6f32e77043722c9682c2300bd9ba2578b96 100644 (file)
@@ -1,23 +1,22 @@
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999, 2004, 2006 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
  * 
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
  * SUCH DAMAGE.
  */
 
-#ifndef lint
+#if 0
 static char copyright[] =
 "@(#) Copyright (c) Apple Computer, Inc. 1997\n\n";
-#endif /* not lint */
+#endif
 
 /*
  * login [ name ]
@@ -70,6 +69,7 @@ static char copyright[] =
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/file.h>
+#include <sys/wait.h>
 
 #include <err.h>
 #include <errno.h>
@@ -84,7 +84,33 @@ static char copyright[] =
 #include <ttyent.h>
 #include <tzfile.h>
 #include <unistd.h>
+#ifdef USE_PAM
+#include <utmpx.h>
+#else /* !USE_PAM */
 #include <utmp.h>
+#endif /* USE_PAM */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <TargetConditionals.h>
+
+#ifdef USE_BSM
+#include <bsm/libbsm.h>
+#include <bsm/audit_uevents.h>
+#endif
+
+#ifdef USE_PAM
+#include <pam/pam_appl.h>
+#include <pam/pam_misc.h>
+#endif
+
+#include <mach/mach_types.h>
+
+#include <servers/bootstrap.h>
 
 #include "pathnames.h"
 
@@ -101,10 +127,19 @@ void       timedout __P((int));
 #ifdef KERBEROS
 int     klogin __P((struct passwd *, char *, char *, char *));
 #endif
+void  au_success();
+void  au_fail(char *, int);
+
 
+#ifndef USE_PAM
 extern void login __P((struct utmp *));
+#endif /* !USE_PAM */
+static void bail(int, int);
+static void refused(const char *, const char *, int);
 
 #define        TTYGRPNAME      "tty"           /* name of group to own ttys */
+#define NO_SLEEP_EXIT  0
+#define SLEEP_EXIT     5
 
 /*
  * This bounds the time given to login.  Not a define so it can
@@ -121,7 +156,22 @@ int        authok;
 
 struct passwd *pwd;
 int    failures;
-char   term[64], *envinit[1], *hostname, *username, *tty;
+char   term[64], *hostname, *username = NULL, *tty;
+#ifdef USE_PAM
+static pam_handle_t *pamh = NULL;
+static struct pam_conv conv = { misc_conv, NULL };
+static int pam_err;
+static int pam_silent = PAM_SILENT;
+static int pam_cred_established;
+static int pam_session_established;
+static struct lastlogx lastlog;
+#endif /* USE_PAM */
+int hflag;
+
+#ifdef USE_BSM
+#define NA_EVENT_STR_SIZE 25
+au_tid_t tid;
+#endif
 
 int
 main(argc, argv)
@@ -131,13 +181,26 @@ main(argc, argv)
        extern char **environ;
        struct group *gr;
        struct stat st;
-       struct timeval tp;
+#ifndef USE_PAM
        struct utmp utmp;
-       int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
+#endif /* USE_PAM */
+       int ask, ch, cnt, oflag = 0, fflag, lflag, pflag;
+       int quietlog = 0, rootlogin = 0;
        uid_t uid;
-       char *domain, *p, *salt, *ttyn;
+       uid_t euid;
+       gid_t egid;
+       char *domain, *p, *ttyn;
        char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
        char localhost[MAXHOSTNAMELEN];
+#ifdef USE_PAM
+       char **pmenv;
+       pid_t pid;
+#else
+       int rval;
+       char *salt;
+#endif
+
+       char auditsuccess = 1;
 
        (void)signal(SIGALRM, timedout);
        (void)alarm(timeout);
@@ -150,19 +213,28 @@ main(argc, argv)
        /*
         * -p is used by getty to tell login not to destroy the environment
         * -f is used to skip a second login authentication
+        *    -l is used to indicate a login session to the command that
+        *    follows username
         * -h is used by other servers to pass the name of the remote
         *    host to login so that it may be placed in utmp and wtmp
+        * -q is used to force hushlogin
         */
        domain = NULL;
        if (gethostname(localhost, sizeof(localhost)) < 0)
                syslog(LOG_ERR, "couldn't get local hostname: %m");
        else
                domain = strchr(localhost, '.');
+       
+       euid = geteuid();
+       egid = getegid();
 
-       fflag = hflag = pflag = 0;
+       fflag = hflag = lflag = pflag = 0;
        uid = getuid();
-       while ((ch = getopt(argc, argv, "fh:p")) != EOF)
+       while ((ch = getopt(argc, argv, "1fh:lpq")) != EOF)
                switch (ch) {
+               case '1':
+                       oflag = 1;
+                       break;
                case 'f':
                        fflag = 1;
                        break;
@@ -175,22 +247,30 @@ main(argc, argv)
                                *p = 0;
                        hostname = optarg;
                        break;
+               case 'l':
+                       lflag = 1;
+                       break;
                case 'p':
                        pflag = 1;
                        break;
+               case 'q':
+                       quietlog = 1;
+                       break;
                case '?':
                default:
                        if (!uid)
                                syslog(LOG_ERR, "invalid flag %c", ch);
                        (void)fprintf(stderr,
-                           "usage: login [-fp] [-h hostname] [username]\n");
+                           "usage: login [-pq] [-h hostname] [username]\n");
+                       (void)fprintf(stderr,
+                           "       login -f [-lpq] [-h hostname] [username [prog [arg ...]]]\n");
                        exit(1);
                }
        argc -= optind;
        argv += optind;
 
        if (*argv) {
-               username = *argv;
+               username = *argv++;
                ask = 0;
        } else
                ask = 1;
@@ -208,6 +288,124 @@ main(argc, argv)
        else
                tty = ttyn;
 
+#ifdef USE_BSM
+       /* Set the terminal id */
+       audit_set_terminal_id(&tid);
+       if (fstat(STDIN_FILENO, &st) < 0) {
+               fprintf(stderr, "login: Unable to stat terminal\n");
+               au_fail("Unable to stat terminal", 1);
+               exit(-1);
+       }
+       if (S_ISCHR(st.st_mode)) {
+               tid.port = st.st_rdev;
+       } else {
+               tid.port = 0;
+       }
+#endif
+
+#ifdef USE_PAM
+       pam_err = pam_start("login", username, &conv, &pamh);
+       if( pam_err != PAM_SUCCESS ) {
+               fprintf(stderr, "login: PAM Error (line %d):  %s\n", __LINE__, pam_strerror(pamh, pam_err));
+               au_fail("PAM Error", 1);
+               exit(1);
+       }
+       pam_err = pam_set_item(pamh, PAM_TTY, tty);
+       if( pam_err != PAM_SUCCESS ) {
+               fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
+               au_fail("PAM Error", 1);
+               exit(1);
+       }
+
+       pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
+       if( pam_err != PAM_SUCCESS ) {
+               fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
+               au_fail("PAM Error", 1);
+               exit(1);
+       }
+
+       pam_err = pam_set_item(pamh, PAM_USER_PROMPT, "login: ");
+       if( pam_err != PAM_SUCCESS ) {
+               fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
+               au_fail("PAM Error", 1);
+               exit(1);
+       }
+
+       if( !username )
+               getloginname();
+       pam_set_item(pamh, PAM_USER, username);
+       pwd = getpwnam(username);
+       if( (pwd != NULL) && (pwd->pw_uid == 0) )
+               rootlogin = 1;
+
+       if( (pwd != NULL) && fflag && ((uid == 0) || (uid == pwd->pw_uid)) ){
+               pam_err = 0;
+               auditsuccess = 0; /* we've simply opened a terminal window */
+       } else {
+
+               pam_err = pam_authenticate(pamh, 0);
+               while( (!oflag) && (cnt++ < 10) && ((pam_err == PAM_AUTH_ERR) ||
+                               (pam_err == PAM_USER_UNKNOWN) ||
+                               (pam_err == PAM_CRED_INSUFFICIENT) ||
+                               (pam_err == PAM_AUTHINFO_UNAVAIL))) {
+                       /* 
+                        * we are not exiting here, but this corresponds to 
+                        * a failed login event, so set exitstatus to 1 
+                        */
+                       au_fail("Login incorrect", 1);
+                       badlogin(username);
+                       printf("Login incorrect\n");
+                       rootlogin = 0;
+                       getloginname();
+                       pwd = getpwnam(username);
+                       if( (pwd != NULL) && (pwd->pw_uid == 0) )
+                               rootlogin = 1;
+                       pam_set_item(pamh, PAM_USER, username);
+                       pam_err = pam_authenticate(pamh, 0);
+               }
+
+               if( pam_err != PAM_SUCCESS ) {
+                       pam_get_item(pamh, PAM_USER, (void *)&username);
+                       badlogin(username);
+                       printf("Login incorrect\n");
+                       au_fail("Login incorrect", 1);
+                       exit(1);
+               }
+
+               pam_err = pam_acct_mgmt(pamh, 0);
+               if( pam_err == PAM_NEW_AUTHTOK_REQD ) {
+                       pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+               }
+               if( pam_err != PAM_SUCCESS ) {
+                       fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
+                       au_fail("PAM error", 1);
+                       exit(1);
+               }
+       }
+
+       pam_err = pam_get_item(pamh, PAM_USER, (void *)&username);
+       if( (pam_err == PAM_SUCCESS) && username && *username) 
+               pwd = getpwnam(username);
+
+       /* get lastlog info before PAM make a new entry */
+       if (!quietlog)
+               getlastlogxbyname(username, &lastlog);
+
+       pam_err = pam_open_session(pamh, 0);
+       if( pam_err != PAM_SUCCESS ) {
+               fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
+               au_fail("PAM error", 1);
+               exit(1);
+       }
+
+       pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+       if( pam_err != PAM_SUCCESS ) {
+               fprintf(stderr, "login: PAM Error (line %d): %s\n", __LINE__, pam_strerror(pamh, pam_err));
+               au_fail("PAM error", 1);
+               exit(1);
+       }
+
+#else /* USE_PAM */
        for (cnt = 0;; ask = 1) {
                if (ask) {
                        fflag = 0;
@@ -231,8 +429,9 @@ main(argc, argv)
                 * for nonexistent name (mistyped username).
                 */
                if (failures && strcmp(tbuf, username)) {
-                       if (failures > (pwd ? 0 : 1))
+                       if (failures > (pwd ? 0 : 1)) {
                                badlogin(tbuf);
+                       }
                        failures = 0;
                }
                (void)strcpy(tbuf, username);
@@ -295,6 +494,7 @@ main(argc, argv)
                                syslog(LOG_NOTICE,
                                    "LOGIN %s REFUSED ON TTY %s",
                                     pwd->pw_name, tty);
+                       au_fail("Login refused on terminal", 0);
                        continue;
                }
 
@@ -307,51 +507,60 @@ main(argc, argv)
                if (++cnt > 3) {
                        if (cnt >= 10) {
                                badlogin(username);
+                               au_fail("Login incorrect", 1);
                                sleepexit(1);
                        }
+                       au_fail("Login incorrect", 1);
                        sleep((u_int)((cnt - 3) * 5));
                }
        }
+#endif
 
        /* committed to login -- turn off timeout */
        (void)alarm((u_int)0);
 
        endpwent();
 
+       if (!pwd) {
+               fprintf(stderr, "login: Unable to find user: %s\n", username);
+               exit(1);
+       }
+
        /* if user not super-user, check for disabled logins */
        if (!rootlogin)
                checknologin();
 
-       if (chdir(pwd->pw_dir) < 0) {
-               (void)printf("No home directory %s!\n", pwd->pw_dir);
-               if (chdir("/"))
-                       exit(0);
-               pwd->pw_dir = "/";
-               (void)printf("Logging in with home = \"/\".\n");
-       }
-
-       quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
-
-       if (pwd->pw_change || pwd->pw_expire)
-               (void)gettimeofday(&tp, (struct timezone *)NULL);
-       if (pwd->pw_change)
-               if (tp.tv_sec >= pwd->pw_change) {
-                       (void)printf("Sorry -- your password has expired.\n");
-                       sleepexit(1);
-               } else if (pwd->pw_change - tp.tv_sec <
-                   2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
-                       (void)printf("Warning: your password expires on %s",
-                           ctime(&pwd->pw_change));
-       if (pwd->pw_expire)
-               if (tp.tv_sec >= pwd->pw_expire) {
-                       (void)printf("Sorry -- your account has expired.\n");
-                       sleepexit(1);
-               } else if (pwd->pw_expire - tp.tv_sec <
-                   2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
-                       (void)printf("Warning: your account expires on %s",
-                           ctime(&pwd->pw_expire));
+       /* Audit successful login */
+       if (auditsuccess)
+               au_success();
+
+       setegid(pwd->pw_gid);
+       seteuid(rootlogin ? 0 : pwd->pw_uid);
+
+       if (!lflag) {
+               /* First do a stat in case the homedir is automounted */
+               stat(pwd->pw_dir,&st);
+
+               if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
+                       printf("No home directory %s!\n", pwd->pw_dir);
+                       if (chdir("/")) 
+                               refused("Cannot find root directory", "HOMEDIR", 1);
+                       pwd->pw_dir = strdup("/");
+                       if (pwd->pw_dir == NULL) {
+                               syslog(LOG_NOTICE, "strdup(): %m");
+                               bail(SLEEP_EXIT, 1);
+                       }
+                       printf("Logging in with home = \"/\".\n");
+               }
+       }
+       seteuid(euid);
+       setegid(egid);
+
+       if (!quietlog)
+               quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
 
        /* Nothing else left to fail -- really log in. */
+#ifndef USE_PAM
        memset((void *)&utmp, 0, sizeof(utmp));
        (void)time(&utmp.ut_time);
        (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
@@ -359,6 +568,7 @@ main(argc, argv)
                (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
        (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
        login(&utmp);
+#endif /* USE_PAM */
 
        dolastlog(quietlog);
 
@@ -367,14 +577,31 @@ main(argc, argv)
        (void)chmod(ttyn, 0620);
        (void)setgid(pwd->pw_gid);
 
-       initgroups(username, pwd->pw_gid);
+       if (initgroups(username, pwd->pw_gid) == -1)
+               syslog(LOG_ERR, "login: initgroups() failed");
 
        if (*pwd->pw_shell == '\0')
-               pwd->pw_shell = _PATH_BSHELL;
+               pwd->pw_shell = strdup(_PATH_BSHELL);
+        if (pwd->pw_shell == NULL) {
+                syslog(LOG_NOTICE, "strdup(): %m");
+                bail(SLEEP_EXIT, 1);
+        }
+
+#if TARGET_OS_EMBEDDED
+       /* on embedded, allow a shell to live in /var/debug_mount/bin/sh */
+#define _PATH_DEBUGSHELL       "/var/debug_mount/bin/sh"
+        if (stat(pwd->pw_shell, &st) != 0) {
+               if (stat(_PATH_DEBUGSHELL, &st) == 0) {
+                       pwd->pw_shell = strdup(_PATH_DEBUGSHELL);
+               }
+        }
+#endif
 
        /* Destroy environment unless user has requested its preservation. */
-       if (!pflag)
-               environ = envinit;
+       if (!pflag) {
+               environ = malloc(sizeof(char *));
+               *environ = NULL;
+       }
        (void)setenv("HOME", pwd->pw_dir, 1);
        (void)setenv("SHELL", pwd->pw_shell, 1);
        if (term[0] == '\0')
@@ -388,6 +615,33 @@ main(argc, argv)
                (void)setenv("KRBTKFILE", krbtkfile_env, 1);
 #endif
 
+#ifdef USE_PAM
+       pmenv = pam_getenvlist(pamh);
+       for( cnt = 0; pmenv && pmenv[cnt]; cnt++ ) 
+               putenv(pmenv[cnt]);
+
+       /* Ignore SIGHUP so that the parent's call to waitpid will
+          succeed and the tty ownership can be reset. */
+       (void)signal(SIGHUP, SIG_IGN);
+
+       pid = fork();
+       if ( pid < 0 ) {
+               err(1, "fork");
+       } else if( pid != 0 ) {
+               waitpid(pid, NULL, 0);
+               pam_setcred(pamh, PAM_DELETE_CRED);
+               pam_err = pam_close_session(pamh, 0);
+               pam_end(pamh,pam_err);
+               chown(ttyn, 0, 0);
+               chmod(ttyn, 0666);
+               exit(0);
+       }
+
+       /* Restore the default SIGHUP handler for the child. */
+       (void)signal(SIGHUP, SIG_DFL);
+
+#endif
+
        if (tty[sizeof("tty")-1] == 'd')
                syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
 
@@ -418,10 +672,6 @@ main(argc, argv)
        (void)signal(SIGINT, SIG_DFL);
        (void)signal(SIGTSTP, SIG_IGN);
 
-       tbuf[0] = '-';
-       (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
-           p + 1 : pwd->pw_shell);
-
        if (setlogin(pwd->pw_name) < 0)
                syslog(LOG_ERR, "setlogin() failure: %m");
 
@@ -431,15 +681,214 @@ main(argc, argv)
        else
                (void) setuid(pwd->pw_uid);
 
-       execlp(pwd->pw_shell, tbuf, 0);
-       err(1, "%s", pwd->pw_shell);
+       /* Re-enable crash reporter */
+       do {
+               kern_return_t kr;
+               mach_port_t bp, ep, mts;
+               thread_state_flavor_t flavor = 0;
+
+#if defined(__ppc__)
+               flavor = PPC_THREAD_STATE64;
+#elif defined(__i386__)
+               flavor = x86_THREAD_STATE;
+#endif
+
+               mts = mach_task_self();
+
+               kr = task_get_bootstrap_port(mts, &bp);
+               if (kr != KERN_SUCCESS) {
+                 syslog(LOG_ERR, "task_get_bootstrap_port() failure: %s (%d)",
+                       bootstrap_strerror(kr), kr);
+                 break;
+               }
+
+               const char* bs = "com.apple.ReportCrash";
+               kr = bootstrap_look_up(bp, (char*)bs, &ep);
+               if (kr != KERN_SUCCESS) {
+                 syslog(LOG_ERR, "bootstrap_look_up(%s) failure: %s (%d)",
+                       bs, bootstrap_strerror(kr), kr);
+                 break;
+               }
+
+               kr = task_set_exception_ports(mts, EXC_MASK_CRASH, ep, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, flavor);
+               if (kr != KERN_SUCCESS) {
+                 syslog(LOG_ERR, "task_set_exception_ports() failure: %d", kr);
+                 break;
+               }
+       } while (0);
+
+       if (fflag && *argv) {
+               char *arg0 = *argv;
+               if (lflag)
+                       (void)strlcpy(tbuf, (p = strrchr(*argv, '/')) ?
+                           p + 1 : *argv, sizeof(tbuf));
+               else {
+                       tbuf[0] = '-';
+                       (void)strlcpy(tbuf + 1, (p = strrchr(*argv, '/')) ?
+                           p + 1 : *argv, sizeof(tbuf) - 1);
+               }
+               *argv = tbuf;
+               execvp(arg0, argv);
+               err(1, "%s", arg0);
+       } else {
+               if (lflag)
+                       (void)strlcpy(tbuf, (p = strrchr(pwd->pw_shell, '/')) ?
+                           p + 1 : pwd->pw_shell, sizeof(tbuf));
+               else {
+                       tbuf[0] = '-';
+                       (void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
+                           p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
+               }
+               execlp(pwd->pw_shell, tbuf, (char *)NULL);
+               err(1, "%s", pwd->pw_shell);
+       }
 }
 
 #ifdef KERBEROS
-#define        NBUFSIZ         (UT_NAMESIZE + 1 + 5)   /* .root suffix */
+#define        NBUFSIZ         (MAXLOGNAME + 1 + 5)    /* .root suffix */
 #else
-#define        NBUFSIZ         (UT_NAMESIZE + 1)
+#define        NBUFSIZ         (MAXLOGNAME + 1)
+#endif
+
+/*
+ * The following tokens are included in the audit record for successful login attempts
+ * header
+ * subject
+ * return
+ */ 
+void au_success()
+{
+#ifdef USE_BSM
+       token_t *tok;
+       int aufd;
+       au_mask_t aumask;
+       auditinfo_t auinfo;
+       uid_t uid = pwd->pw_uid;
+       gid_t gid = pwd->pw_gid;
+       pid_t pid = getpid();
+       long au_cond;
+
+       /* If we are not auditing, don't cut an audit record; just return */
+       if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
+               fprintf(stderr, "login: Could not determine audit condition\n");
+               exit(1);
+       }
+       if (au_cond == AUC_NOAUDIT)
+               return;
+
+       /* Compute and Set the user's preselection mask */ 
+       if(au_user_mask(pwd->pw_name, &aumask) == -1) {
+               fprintf(stderr, "login: Could not set audit mask\n");
+               exit(1);
+       }
+
+       /* Set the audit info for the user */
+       auinfo.ai_auid = uid;
+       auinfo.ai_asid = pid;
+       bcopy(&tid, &auinfo.ai_termid, sizeof(auinfo.ai_termid));
+       bcopy(&aumask, &auinfo.ai_mask, sizeof(auinfo.ai_mask));
+       if(setaudit(&auinfo) != 0) {
+               fprintf(stderr, "login: setaudit failed:  %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if((aufd = au_open()) == -1) {
+               fprintf(stderr, "login: Audit Error: au_open() failed\n");
+               exit(1);
+       }
+
+       /* The subject that is created (euid, egid of the current process) */
+       if((tok = au_to_subject32(uid, geteuid(), getegid(), 
+                       uid, gid, pid, pid, &tid)) == NULL) {
+               fprintf(stderr, "login: Audit Error: au_to_subject32() failed\n");
+               exit(1);
+       }
+       au_write(aufd, tok);
+
+       if((tok = au_to_return32(0, 0)) == NULL) {
+               fprintf(stderr, "login: Audit Error: au_to_return32() failed\n");
+               exit(1);
+       }
+       au_write(aufd, tok);
+
+       if(au_close(aufd, 1, AUE_login) == -1) {
+               fprintf(stderr, "login: Audit Record was not committed.\n");
+               exit(1);
+       }
+#endif
+}
+
+/*
+ * The following tokens are included in the audit record for successful login attempts
+ * header
+ * subject
+ * text
+ * return
+ */ 
+void au_fail(char *errmsg, int na)
+{
+#ifdef USE_BSM
+       token_t *tok;
+       int aufd;
+       long au_cond;
+       uid_t uid;
+       gid_t gid;
+       pid_t pid = getpid();
+
+       /* If we are not auditing, don't cut an audit record; just return */
+       if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
+               fprintf(stderr, "login: Could not determine audit condition\n");
+               exit(1);
+       }
+       if (au_cond == AUC_NOAUDIT)
+               return;
+
+       if((aufd = au_open()) == -1) {
+               fprintf(stderr, "login: Audit Error: au_open() failed\n");
+               exit(1);
+       }
+
+       if(na) {
+               /* Non attributable event */
+               /* Assuming that login is not called within a users' session => auid,asid == -1 */
+               if((tok = au_to_subject32(-1, geteuid(), getegid(), -1, -1, 
+                               pid, -1, &tid)) == NULL) {
+
+                       fprintf(stderr, "login: Audit Error: au_to_subject32() failed\n");
+                       exit(1);
+               }
+       }
+       else {
+               /* we know the subject -- so use its value instead */
+               uid = pwd->pw_uid;
+               gid = pwd->pw_gid;
+               if((tok = au_to_subject32(uid, geteuid(), getegid(), 
+                               uid, gid, pid, pid, &tid)) == NULL) {
+                       fprintf(stderr, "login: Audit Error: au_to_subject32() failed\n");
+                       exit(1);
+               }
+       }
+       au_write(aufd, tok);
+
+       /* Include the error message */
+       if((tok = au_to_text(errmsg)) == NULL) {
+               fprintf(stderr, "login: Audit Error: au_to_text() failed\n");
+               exit(1);
+       }
+       au_write(aufd, tok);
+
+       if((tok = au_to_return32(1, errno)) == NULL) {
+               fprintf(stderr, "login: Audit Error: au_to_return32() failed\n");
+               exit(1);
+       }
+       au_write(aufd, tok);
+
+       if(au_close(aufd, 1, AUE_login) == -1) {
+               fprintf(stderr, "login: Audit Error: au_close()  was not committed\n");
+               exit(1);
+       }
 #endif
+}
 
 void
 getloginname()
@@ -458,7 +907,7 @@ getloginname()
                        if (p < nbuf + (NBUFSIZ - 1))
                                *p++ = ch;
                }
-               if (p > nbuf)
+               if (p > nbuf) {
                        if (nbuf[0] == '-')
                                (void)fprintf(stderr,
                                    "login names may not start with '-'.\n");
@@ -467,6 +916,7 @@ getloginname()
                                username = nbuf;
                                break;
                        }
+               }
        }
 }
 
@@ -526,6 +976,7 @@ checknologin()
        if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
                while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
                        (void)write(fileno(stdout), tbuf, nchars);
+               au_fail("No login", 0);
                sleepexit(0);
        }
 }
@@ -534,9 +985,35 @@ void
 dolastlog(quiet)
        int quiet;
 {
+#ifdef USE_PAM
+       if (quiet)
+               return;
+       if (*lastlog.ll_line) {
+               (void)printf("Last login: %.*s ",
+                   24-5, (char *)ctime(&lastlog.ll_tv.tv_sec));
+               if (*lastlog.ll_host != '\0')
+                       (void)printf("from %.*s\n",
+                           (int)sizeof(lastlog.ll_host),
+                           lastlog.ll_host);
+               else
+                       (void)printf("on %.*s\n",
+                           (int)sizeof(lastlog.ll_line),
+                           lastlog.ll_line);
+       }
+#else /* !USE_PAM */
        struct lastlog ll;
        int fd;
 
+       /* HACK HACK HACK: This is because HFS doesn't support sparse files
+        * and seeking into the file too far is too slow.  The "solution"
+        * is to just bail if the seek time for a large uid would be too
+        * slow.
+        */
+       if(pwd->pw_uid > 100000) {
+               syslog(LOG_NOTICE, "User login %s (%d) not logged in lastlog.  UID too large.", pwd->pw_name, pwd->pw_uid);
+               return;
+       }
+
        if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
                (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
                if (!quiet) {
@@ -563,6 +1040,7 @@ dolastlog(quiet)
                (void)write(fd, (char *)&ll, sizeof(ll));
                (void)close(fd);
        }
+#endif /* USE_PAM */
 }
 
 void
@@ -607,3 +1085,67 @@ sleepexit(eval)
        (void)sleep(5);
        exit(eval);
 }
+
+static void
+refused(const char *msg, const char *rtype, int lout)
+{
+
+        if (msg != NULL)
+               printf("%s.\n", msg);
+        if (hflag)
+                syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
+                      pwd->pw_name, rtype, hostname, tty);
+        else
+                syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
+                      pwd->pw_name, rtype, tty);
+        if (lout)
+                bail(SLEEP_EXIT, 1);
+}
+
+#ifdef USE_PAM
+/*
+ * Log a PAM error
+ */
+static void
+pam_syslog(const char *msg)
+{
+        syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
+}
+
+/*
+ * Shut down PAM
+ */
+static void
+pam_cleanup()
+{
+
+        if (pamh != NULL) {
+                if (pam_session_established) {
+                        pam_err = pam_close_session(pamh, 0);
+                        if (pam_err != PAM_SUCCESS)
+                                pam_syslog("pam_close_session()");
+                }
+                pam_session_established = 0;
+                if (pam_cred_established) {
+                        pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
+                        if (pam_err != PAM_SUCCESS)
+                                pam_syslog("pam_setcred()");
+                }
+                pam_cred_established = 0;
+                pam_end(pamh, pam_err);
+                pamh = NULL;
+        }
+}
+#endif /* USE_PAM */
+/*
+ * Exit, optionally after sleeping a few seconds
+ */
+void
+bail(int sec, int eval)
+{
+#ifdef USE_PAM
+        pam_cleanup();
+#endif
+        (void)sleep(sec);
+        exit(eval);
+}