summaryrefslogtreecommitdiff
path: root/usr/src/cmd/login
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/login
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/login')
-rw-r--r--usr/src/cmd/login/Makefile93
-rw-r--r--usr/src/cmd/login/login.c2554
-rw-r--r--usr/src/cmd/login/login.dfl98
-rw-r--r--usr/src/cmd/login/login_audit.c219
-rw-r--r--usr/src/cmd/login/login_audit.h49
-rw-r--r--usr/src/cmd/login/logindevperm.sh95
6 files changed, 3108 insertions, 0 deletions
diff --git a/usr/src/cmd/login/Makefile b/usr/src/cmd/login/Makefile
new file mode 100644
index 0000000000..50308e6f81
--- /dev/null
+++ b/usr/src/cmd/login/Makefile
@@ -0,0 +1,93 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= login
+OBJS= login.o login_audit.o
+SRCS= $(OBJS:%.o=%.c)
+ONC_SRCS=$(SRCS:%.c=%.c_onc_plus)
+
+include ../Makefile.cmd
+
+DFLTD= $(ROOTETC)/default
+DIRS= $(DFLTD)
+LOGINDEVPERM= logindevperm
+LOGINDEVPERMSRC= $(LOGINDEVPERM).sh
+ROOTLOGINDEVPERM= $(LOGINDEVPERM:%=$(ROOTETC)/%)
+
+ETCDFLTPROG = $(PROG:%=$(DFLTD)/%)
+$(ETCDFLTPROG) := FILEMODE = $(LIBFILEMODE)
+$(ETCDFLTPROG) := GROUP = sys
+$(ROOTLOGINDEVPERM) := FILEMODE = 644
+$(ROOTLOGINDEVPERM) := GROUP = sys
+FILEMODE= 4555
+OWNER= root
+
+CLOBBERFILES += $(LOGINDEVPERM) $(ONC_SRCS)
+
+CPPFLAGS += -DSYSV -DCONSOLE='"/dev/console"' -DSECURITY \
+ -D_FILE_OFFSET_BITS=64 -I$(SRC)/lib/pam_modules/krb5
+
+LDLIBS += -lcmd -lbsm -lpam -ldevinfo
+CFLAGS += $(CCVERBOSE)
+
+.KEEP_STATE:
+
+
+all: $(PROG) $(PROG).dfl $(ROOTLOGINDEVPERM)
+
+$(LOGINDEVPERM): $(LOGINDEVPERMSRC)
+ $(RM) $(LOGINDEVPERM)
+ /bin/sh $(LOGINDEVPERMSRC) > $(LOGINDEVPERM)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(DIRS) $(ROOTPROG) $(PROG).dfl $(ETCDFLTPROG) $(ROOTLOGINDEVPERM)
+
+$(DIRS):
+ $(INS.dir)
+
+$(DFLTD)/% : %
+ $(MV) $(PROG) $(PROG).save
+ $(LN) $(PROG).dfl $(PROG)
+ $(INS.file)
+ $(RM) $(PROG)
+ $(MV) $(PROG).save $(PROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../Makefile.targ
+
+# make ONC_PLUS using suffix rule
+#
+
+ONC_PLUS: $(ONC_SRCS)
diff --git a/usr/src/cmd/login/login.c b/usr/src/cmd/login/login.c
new file mode 100644
index 0000000000..ea62bf37c6
--- /dev/null
+++ b/usr/src/cmd/login/login.c
@@ -0,0 +1,2554 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* ONC_PLUS EXTRACT START */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/* Copyright (c) 1987, 1988 Microsoft Corporation */
+/* All Rights Reserved */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* ONC_PLUS EXTRACT END */
+
+/*
+ * For a complete reference to login(1), see the manual page. However,
+ * login has accreted some intentionally undocumented options, which are
+ * explained here:
+ *
+ * -a: This legacy flag appears to be unused.
+ *
+ * -f <username>: This flag was introduced by PSARC 1995/039 in support
+ * of Kerberos. But it's not used by Sun's Kerberos implementation.
+ * It is however employed by zlogin(1), since it allows one to tell
+ * login: "This user is authenticated." In the case of zlogin that's
+ * true because the zone always trusts the global zone.
+ *
+ * -z <zonename>: This flag is passed to login when zlogin(1) executes a
+ * zone login. This tells login(1) to skip it's normal CONSOLE check
+ * (i.e. that the root login must be on /dev/console) and tells us the
+ * name of the zone from which the login is occuring.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <unistd.h> /* For logfile locking */
+#include <signal.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <deflt.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <lastlog.h>
+#include <termio.h>
+#include <utmpx.h>
+#include <stdlib.h>
+#include <wait.h>
+#include <errno.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <ulimit.h>
+#include <libgen.h>
+#include <pwd.h>
+#include <security/pam_appl.h>
+#include <strings.h>
+#include <libdevinfo.h>
+#include <zone.h>
+#include "login_audit.h"
+
+#include <krb5_repository.h>
+/*
+ *
+ * *** Defines, Macros, and String Constants ***
+ *
+ *
+ */
+
+#define ISSUEFILE "/etc/issue" /* file to print before prompt */
+#define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */
+
+/*
+ * These need to be defined for UTMPX management.
+ * If we add in the utility functions later, we
+ * can remove them.
+ */
+#define __UPDATE_ENTRY 1
+#define __LOGIN 2
+
+/*
+ * Intervals to sleep after failed login
+ */
+#ifndef SLEEPTIME
+#define SLEEPTIME 4 /* sleeptime before login incorrect msg */
+#endif
+static int Sleeptime = SLEEPTIME;
+
+/*
+ * seconds login disabled after allowable number of unsuccessful attempts
+ */
+#ifndef DISABLETIME
+#define DISABLETIME 20
+#endif
+static int Disabletime = DISABLETIME;
+
+#define MAXTRYS 5
+
+static int retry = MAXTRYS;
+
+/*
+ * Login logging support
+ */
+#define LOGINLOG "/var/adm/loginlog" /* login log file */
+#define LNAME_SIZE 20 /* size of logged logname */
+#define TTYN_SIZE 15 /* size of logged tty name */
+#define TIME_SIZE 30 /* size of logged time string */
+#define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
+#define L_WAITTIME 5 /* waittime for log file to unlock */
+#define LOGTRYS 10 /* depth of 'try' logging */
+
+/*
+ * String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT
+ * SCPYL is the safer version of SCPYN
+ */
+#define SCPYL(a, b) (void) strlcpy(a, b, sizeof (a))
+#define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
+#define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0)
+#define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \
+ (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); }
+
+/*
+ * Other macros
+ */
+#define NMAX sizeof (((struct utmpx *)0)->ut_name)
+#define HMAX sizeof (((struct utmpx *)0)->ut_host)
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+
+/*
+ * Various useful files and string constants
+ */
+#define SHELL "/usr/bin/sh"
+#define SHELL2 "/sbin/sh"
+#define SUBLOGIN "<!sublogin>"
+#define LASTLOG "/var/adm/lastlog"
+#define PROG_NAME "login"
+#define HUSHLOGIN ".hushlogin"
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * Array and Buffer sizes
+ */
+#define PBUFSIZE 8 /* max significant characters in a password */
+/* ONC_PLUS EXTRACT END */
+#define MAXARGS 63 /* change value below if changing this */
+#define MAXARGSWIDTH 2 /* log10(MAXARGS) */
+#define MAXENV 1024
+#define MAXLINE 2048
+
+/*
+ * Miscellaneous constants
+ */
+#define ROOTUID 0
+#define ERROR 1
+#define OK 0
+#define LOG_ERROR 1
+#define DONT_LOG_ERROR 0
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Counters for counting the number of failed login attempts
+ */
+static int trys = 0;
+static int count = 1;
+
+/*
+ * error value for login_exit() audit output (0 == no audit record)
+ */
+static int audit_error = 0;
+
+/*
+ * Externs a plenty
+ */
+/* ONC_PLUS EXTRACT START */
+extern int getsecretkey();
+/* ONC_PLUS EXTRACT START */
+
+/*
+ * The current user name
+ */
+static char user_name[NMAX];
+static char minusnam[16] = "-";
+
+/*
+ * locale environments to be passed to shells.
+ */
+static char *localeenv[] = {
+ "LANG",
+ "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
+ "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
+static int locale_envmatch(char *, char *);
+
+/*
+ * Environment variable support
+ */
+static char shell[256] = { "SHELL=" };
+static char home[MAXPATHLEN] = { "HOME=" };
+static char term[64] = { "TERM=" };
+static char logname[30] = { "LOGNAME=" };
+static char timez[100] = { "TZ=" };
+static char hertz[10] = { "HZ=" };
+static char path[MAXPATHLEN] = { "PATH=" };
+static char *newenv[10+MAXARGS] =
+ {home, path, logname, hertz, term, 0, 0};
+static char **envinit = newenv;
+static int basicenv;
+static char *zero = (char *)0;
+static char **envp;
+#ifndef NO_MAIL
+static char mail[30] = { "MAIL=/var/mail/" };
+#endif
+extern char **environ;
+static char inputline[MAXLINE];
+
+#define MAX_ID_LEN 256
+#define MAX_REPOSITORY_LEN 256
+#define MAX_PAMSERVICE_LEN 256
+
+static char identity[MAX_ID_LEN];
+static char repository[MAX_REPOSITORY_LEN];
+static char progname[MAX_PAMSERVICE_LEN];
+
+
+/*
+ * Strings used to prompt the user.
+ */
+static char loginmsg[] = "login: ";
+static char passwdmsg[] = "Password:";
+static char incorrectmsg[] = "Login incorrect\n";
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * Password file support
+ */
+static struct passwd *pwd = NULL;
+static char remote_host[HMAX];
+static char zone_name[ZONENAME_MAX];
+
+/*
+ * Illegal passwd entries.
+ */
+static struct passwd nouser = { "", "no:password", ~ROOTUID };
+/* ONC_PLUS EXTRACT END */
+
+/*
+ * Log file support
+ */
+static char *log_entry[LOGTRYS];
+static int writelog = 0;
+static int lastlogok = 0;
+static struct lastlog ll;
+static int dosyslog = 0;
+static int flogin = MAXTRYS; /* flag for SYSLOG_FAILED_LOGINS */
+
+/*
+ * Default file toggles
+ */
+static char *Pndefault = "/etc/default/login";
+static char *Altshell = NULL;
+static char *Console = NULL;
+static int Passreqflag = 0;
+
+#define DEFUMASK 022
+static mode_t Umask = DEFUMASK;
+static char *Def_tz = NULL;
+static char *tmp_tz = NULL;
+static char *Def_hertz = NULL;
+#define SET_FSIZ 2 /* ulimit() command arg */
+static long Def_ulimit = 0;
+#define MAX_TIMEOUT (15 * 60)
+#define DEF_TIMEOUT (5 * 60)
+static unsigned Def_timeout = DEF_TIMEOUT;
+static char *Def_path = NULL;
+static char *Def_supath = NULL;
+#define DEF_PATH "/usr/bin:" /* same as PATH */
+#define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */
+
+/*
+ * Defaults for updating expired passwords
+ */
+#define DEF_ATTEMPTS 3
+
+/*
+ * ttyprompt will point to the environment variable TTYPROMPT.
+ * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt.
+ */
+static char *ttyprompt = NULL;
+static char *ttyn = NULL;
+
+/*
+ * Pass inherited environment. Used by telnetd in support of the telnet
+ * ENVIRON option.
+ */
+static boolean_t pflag = B_FALSE;
+static boolean_t uflag = B_FALSE;
+static boolean_t Rflag = B_FALSE;
+static boolean_t sflag = B_FALSE;
+static boolean_t Uflag = B_FALSE;
+static boolean_t tflag = B_FALSE;
+static boolean_t hflag = B_FALSE;
+static boolean_t rflag = B_FALSE;
+static boolean_t zflag = B_FALSE;
+
+/*
+ * Remote login support
+ */
+static char rusername[NMAX+1], lusername[NMAX+1];
+static char terminal[MAXPATHLEN];
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * Pre-authentication flag support
+ */
+static int fflag;
+
+static char ** getargs(char *);
+
+static int login_conv(int, struct pam_message **,
+ struct pam_response **, void *);
+
+static struct pam_conv pam_conv = {login_conv, NULL};
+static pam_handle_t *pamh; /* Authentication handle */
+/* ONC_PLUS EXTRACT END */
+
+/*
+ * Function declarations
+ */
+static void turn_on_logging(void);
+static void defaults(void);
+static void usage(void);
+static void process_rlogin(void);
+/* ONC_PLUS EXTRACT START */
+static void login_authenticate();
+static void setup_credentials(void);
+/* ONC_PLUS EXTRACT END */
+static void adjust_nice(void);
+static void update_utmpx_entry(int);
+static void establish_user_environment(char **);
+static void print_banner(void);
+static void display_last_login_time(void);
+static void exec_the_shell(void);
+static int process_chroot_logins(void);
+static void chdir_to_dir_user(void);
+static void check_log(void);
+static void validate_account(void);
+static void doremoteterm(char *);
+static int get_options(int, char **);
+static void getstr(char *, int, char *);
+static int legalenvvar(char *);
+static void check_for_console(void);
+static void check_for_dueling_unix(char *);
+static void get_user_name(void);
+static uint_t get_audit_id(void);
+static void login_exit(int);
+static int logins_disabled(char *);
+static void log_bad_attempts(void);
+static int is_number(char *);
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * *** main ***
+ *
+ * The primary flow of control is directed in this routine.
+ * Control moves in line from top to bottom calling subfunctions
+ * which perform the bulk of the work. Many of these calls exit
+ * when a fatal error is encountered and do not return to main.
+ *
+ *
+ */
+
+void
+main(int argc, char *argv[], char **renvp)
+{
+/* ONC_PLUS EXTRACT END */
+ int sublogin;
+ int pam_rc;
+
+ /*
+ * Set up Defaults and flags
+ */
+ defaults();
+ SCPYL(progname, PROG_NAME);
+
+ /*
+ * Set up default umask
+ */
+ if (Umask > ((mode_t)0777))
+ Umask = DEFUMASK;
+ (void) umask(Umask);
+
+ /*
+ * Set up default timeouts and delays
+ */
+ if (Def_timeout > MAX_TIMEOUT)
+ Def_timeout = MAX_TIMEOUT;
+ if (Sleeptime < 0 || Sleeptime > 5)
+ Sleeptime = SLEEPTIME;
+
+ (void) alarm(Def_timeout);
+
+ /*
+ * Ignore SIGQUIT and SIGINT and set nice to 0
+ */
+ (void) signal(SIGQUIT, SIG_IGN);
+ (void) signal(SIGINT, SIG_IGN);
+ (void) nice(0);
+
+ /*
+ * Set flag to disable the pid check if you find that you are
+ * a subsystem login.
+ */
+ sublogin = 0;
+ if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
+ sublogin = 1;
+
+ /*
+ * Parse Arguments
+ */
+ if (get_options(argc, argv) == -1) {
+ usage();
+ audit_error = ADT_FAIL_VALUE_BAD_CMD;
+ login_exit(1);
+ }
+
+ /*
+ * if devicename is not passed as argument, call ttyname(0)
+ */
+ if (ttyn == NULL) {
+ ttyn = ttyname(0);
+ if (ttyn == NULL)
+ ttyn = "/dev/???";
+ }
+
+/* ONC_PLUS EXTRACT START */
+ /*
+ * Call pam_start to initiate a PAM authentication operation
+ */
+
+ if ((pam_rc = pam_start(progname, user_name, &pam_conv, &pamh))
+ != PAM_SUCCESS) {
+ audit_error = ADT_FAIL_PAM + pam_rc;
+ login_exit(1);
+ }
+ if ((pam_rc = pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) {
+ audit_error = ADT_FAIL_PAM + pam_rc;
+ login_exit(1);
+ }
+ if ((pam_rc = pam_set_item(pamh, PAM_RHOST, remote_host)) !=
+ PAM_SUCCESS) {
+ audit_error = ADT_FAIL_PAM + pam_rc;
+ login_exit(1);
+ }
+
+ /*
+ * We currently only support special handling of the KRB5 PAM repository
+ */
+ if ((Rflag && strlen(repository)) &&
+ strcmp(repository, KRB5_REPOSITORY_NAME) == 0 &&
+ (uflag && strlen(identity))) {
+ krb5_repository_data_t krb5_data;
+ pam_repository_t pam_rep_data;
+
+ krb5_data.principal = identity;
+ krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
+
+ pam_rep_data.type = repository;
+ pam_rep_data.scope = (void *)&krb5_data;
+ pam_rep_data.scope_len = sizeof (krb5_data);
+
+ (void) pam_set_item(pamh, PAM_REPOSITORY,
+ (void *)&pam_rep_data);
+ }
+/* ONC_PLUS EXTRACT END */
+
+ /*
+ * Open the log file which contains a record of successful and failed
+ * login attempts
+ */
+ turn_on_logging();
+
+ /*
+ * say "hi" to syslogd ..
+ */
+ openlog("login", 0, LOG_AUTH);
+
+ /*
+ * Do special processing for -r (rlogin) flag
+ */
+ if (rflag)
+ process_rlogin();
+
+/* ONC_PLUS EXTRACT START */
+ /*
+ * validate user
+ */
+ /* we are already authenticated. fill in what we must, then continue */
+ if (fflag) {
+/* ONC_PLUS EXTRACT END */
+ if ((pwd = getpwnam(user_name)) == NULL) {
+ audit_error = ADT_FAIL_VALUE_USERNAME;
+
+ log_bad_attempts();
+ (void) printf("Login failed: unknown user '%s'.\n",
+ user_name);
+ login_exit(1);
+ }
+/* ONC_PLUS EXTRACT START */
+ } else {
+ /*
+ * Perform the primary login authentication activity.
+ */
+ login_authenticate();
+ }
+/* ONC_PLUS EXTRACT END */
+
+ /* change root login, then we exec another login and try again */
+ if (process_chroot_logins() != OK)
+ login_exit(1);
+
+ /*
+ * If root login and not on system console then call exit(2)
+ */
+ check_for_console();
+
+ /*
+ * Check to see if a shutdown is in progress, if it is and
+ * we are not root then throw the user off the system
+ */
+ if (logins_disabled(user_name) == TRUE) {
+ audit_error = ADT_FAIL_VALUE_LOGIN_DISABLED;
+ login_exit(1);
+ }
+
+ if (pwd->pw_uid == 0) {
+ if (Def_supath != NULL)
+ Def_path = Def_supath;
+ else
+ Def_path = DEF_SUPATH;
+ }
+
+ /*
+ * Check account expiration and passwd aging
+ */
+ validate_account();
+
+ /*
+ * We only get here if we've been authenticated.
+ */
+ /*
+ * NOTE: telnetd and rlogind rely upon this updating of utmpx
+ * to indicate that the authentication completed successfully,
+ * pam_open_session was called and therefore they are required to
+ * call pam_close_session.
+ */
+ update_utmpx_entry(sublogin);
+
+ /*
+ * Now we set up the environment for the new user, which includes
+ * the users ulimit, nice value, ownership of this tty, uid, gid,
+ * and environment variables.
+ */
+ if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
+ (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
+
+ /* di_devperm_login() sends detailed errors to syslog */
+ if (di_devperm_login((const char *)ttyn, pwd->pw_uid, pwd->pw_gid,
+ NULL) == -1) {
+ (void) fprintf(stderr, "error processing /etc/logindevperm,"
+ " see syslog for more details\n");
+ }
+
+ adjust_nice(); /* passwd file can specify nice value */
+
+/* ONC_PLUS EXTRACT START */
+ setup_credentials(); /* Set uid/gid - exits on failure */
+
+ /*
+ * Set up the basic environment for the exec. This includes
+ * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
+ */
+ chdir_to_dir_user();
+
+ establish_user_environment(renvp);
+
+ (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
+ pamh = NULL;
+/* ONC_PLUS EXTRACT END */
+
+ if (pwd->pw_uid == 0) {
+ if (dosyslog) {
+ if (remote_host[0]) {
+ syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
+ ttyn, HMAX, remote_host);
+ } else
+ syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn);
+ }
+ }
+ closelog();
+
+ (void) signal(SIGQUIT, SIG_DFL);
+ (void) signal(SIGINT, SIG_DFL);
+
+ /*
+ * Display some useful information to the new user like the banner
+ * and last login time if not a quiet login.
+ */
+
+ if (access(HUSHLOGIN, F_OK) != 0) {
+ print_banner();
+ display_last_login_time();
+ }
+
+ /*
+ * Set SIGXCPU and SIGXFSZ to default disposition.
+ * Shells inherit signal disposition from parent.
+ * And the shells should have default dispositions
+ * for the two below signals.
+ */
+ (void) signal(SIGXCPU, SIG_DFL);
+ (void) signal(SIGXFSZ, SIG_DFL);
+
+ /*
+ * Now fire off the shell of choice
+ */
+ exec_the_shell();
+
+ /*
+ * All done
+ */
+ login_exit(1);
+ /* NOTREACHED */
+}
+
+
+/*
+ * *** Utility functions ***
+ */
+
+
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * donothing & catch - Signal catching functions
+ */
+
+/*ARGSUSED*/
+static void
+donothing(int sig)
+{
+ if (pamh)
+ (void) pam_end(pamh, PAM_ABORT);
+}
+/* ONC_PLUS EXTRACT END */
+
+#ifdef notdef
+static int intrupt;
+
+/*ARGSUSED*/
+static void
+catch(int sig)
+{
+ ++intrupt;
+}
+#endif
+
+/*
+ * *** Bad login logging support ***
+ */
+
+/*
+ * badlogin() - log to the log file 'trys'
+ * unsuccessful attempts
+ */
+
+static void
+badlogin(void)
+{
+ int retval, count1, fildes;
+
+ /*
+ * Tries to open the log file. If succeed, lock it and write
+ * in the failed attempts
+ */
+ if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) {
+
+ (void) sigset(SIGALRM, donothing);
+ (void) alarm(L_WAITTIME);
+ retval = lockf(fildes, F_LOCK, 0L);
+ (void) alarm(0);
+ (void) sigset(SIGALRM, SIG_DFL);
+ if (retval == 0) {
+ for (count1 = 0; count1 < trys; count1++)
+ (void) write(fildes, log_entry[count1],
+ (unsigned)strlen(log_entry[count1]));
+ (void) lockf(fildes, F_ULOCK, 0L);
+ }
+ (void) close(fildes);
+ }
+}
+
+
+/*
+ * log_bad_attempts - log each bad login attempt - called from
+ * login_authenticate. Exits when the maximum attempt
+ * count is exceeded.
+ */
+
+static void
+log_bad_attempts(void)
+{
+ time_t timenow;
+
+ if (trys >= LOGTRYS)
+ return;
+ if (writelog) {
+ (void) time(&timenow);
+ (void) strncat(log_entry[trys], user_name, LNAME_SIZE);
+ (void) strncat(log_entry[trys], ":", (size_t)1);
+ (void) strncat(log_entry[trys], ttyn, TTYN_SIZE);
+ (void) strncat(log_entry[trys], ":", (size_t)1);
+ (void) strncat(log_entry[trys], ctime(&timenow),
+ TIME_SIZE);
+ trys++;
+ }
+ if (count > flogin) {
+ if ((pwd = getpwnam(user_name)) != NULL) {
+ if (remote_host[0]) {
+ syslog(LOG_NOTICE,
+ "Login failure on %s from %.*s, "
+ "%.*s", ttyn, HMAX, remote_host,
+ NMAX, user_name);
+ } else {
+ syslog(LOG_NOTICE,
+ "Login failure on %s, %.*s",
+ ttyn, NMAX, user_name);
+ }
+ } else {
+ if (remote_host[0]) {
+ syslog(LOG_NOTICE,
+ "Login failure on %s from %.*s",
+ ttyn, HMAX, remote_host);
+ } else {
+ syslog(LOG_NOTICE,
+ "Login failure on %s", ttyn);
+ }
+ }
+ }
+}
+
+
+/*
+ * turn_on_logging - if the logfile exist, turn on attempt logging and
+ * initialize the string storage area
+ */
+
+static void
+turn_on_logging(void)
+{
+ struct stat dbuf;
+ int i;
+
+ if (stat(LOGINLOG, &dbuf) == 0) {
+ writelog = 1;
+ for (i = 0; i < LOGTRYS; i++) {
+ if (!(log_entry[i] = malloc((size_t)ENT_SIZE))) {
+ writelog = 0;
+ break;
+ }
+ *log_entry[i] = '\0';
+ }
+ }
+}
+
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * login_conv():
+ * This is the conv (conversation) function called from
+ * a PAM authentication module to print error messages
+ * or garner information from the user.
+ */
+/*ARGSUSED*/
+static int
+login_conv(int num_msg, struct pam_message **msg,
+ struct pam_response **response, void *appdata_ptr)
+{
+ struct pam_message *m;
+ struct pam_response *r;
+ char *temp;
+ int k, i;
+
+ if (num_msg <= 0)
+ return (PAM_CONV_ERR);
+
+ *response = calloc(num_msg, sizeof (struct pam_response));
+ if (*response == NULL)
+ return (PAM_BUF_ERR);
+
+ k = num_msg;
+ m = *msg;
+ r = *response;
+ while (k--) {
+
+ switch (m->msg_style) {
+
+ case PAM_PROMPT_ECHO_OFF:
+ temp = getpassphrase(m->msg);
+ if (temp != NULL) {
+ r->resp = strdup(temp);
+ if (r->resp == NULL) {
+ /* free responses */
+ r = *response;
+ for (i = 0; i < num_msg; i++, r++) {
+ if (r->resp)
+ free(r->resp);
+ }
+ free(*response);
+ *response = NULL;
+ return (PAM_BUF_ERR);
+ }
+ }
+
+ m++;
+ r++;
+ break;
+
+ case PAM_PROMPT_ECHO_ON:
+ if (m->msg != NULL)
+ (void) fputs(m->msg, stdout);
+ r->resp = calloc(1, PAM_MAX_RESP_SIZE);
+ if (r->resp == NULL) {
+ /* free responses */
+ r = *response;
+ for (i = 0; i < num_msg; i++, r++) {
+ if (r->resp)
+ free(r->resp);
+ }
+ free(*response);
+ *response = NULL;
+ return (PAM_BUF_ERR);
+ }
+ /*
+ * The response might include environment variables
+ * information. We should store that information in
+ * envp if there is any; otherwise, envp is set to
+ * NULL.
+ */
+ bzero((void *)inputline, MAXLINE);
+
+ envp = getargs(inputline);
+
+ /* If we read in any input, process it. */
+ if (inputline[0] != '\0') {
+ int len;
+
+ if (envp != (char **)NULL)
+ /*
+ * If getargs() did not return NULL,
+ * *envp is the first string in
+ * inputline. envp++ makes envp point
+ * to environment variables information
+ * or be NULL.
+ */
+ envp++;
+
+ (void) strncpy(r->resp, inputline,
+ PAM_MAX_RESP_SIZE-1);
+ r->resp[PAM_MAX_RESP_SIZE-1] = NULL;
+ len = strlen(r->resp);
+ if (r->resp[len-1] == '\n')
+ r->resp[len-1] = '\0';
+ } else {
+ login_exit(1);
+ }
+ m++;
+ r++;
+ break;
+
+ case PAM_ERROR_MSG:
+ if (m->msg != NULL) {
+ (void) fputs(m->msg, stderr);
+ (void) fputs("\n", stderr);
+ }
+ m++;
+ r++;
+ break;
+ case PAM_TEXT_INFO:
+ if (m->msg != NULL) {
+ (void) fputs(m->msg, stdout);
+ (void) fputs("\n", stdout);
+ }
+ m++;
+ r++;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return (PAM_SUCCESS);
+}
+
+/*
+ * verify_passwd - Authenticates the user.
+ * Returns: PAM_SUCCESS if authentication successful,
+ * PAM error code if authentication fails.
+ */
+
+static int
+verify_passwd()
+{
+ int error;
+ char *user;
+ int flag = 0;
+
+ /*
+ * PAM authenticates the user for us.
+ */
+ if ((error = pam_authenticate(pamh, flag)) != PAM_SUCCESS) {
+ return (error);
+ }
+
+ /* get the user_name from the pam handle */
+ (void) pam_get_item(pamh, PAM_USER, (void**)&user);
+
+ if (user == NULL || *user == '\0')
+ return (PAM_SYSTEM_ERR);
+
+ SCPYL(user_name, user);
+ check_for_dueling_unix(user_name);
+
+ if ((pwd = getpwnam(user_name)) == NULL) {
+ return (PAM_SYSTEM_ERR);
+ }
+
+ return (error);
+}
+/* ONC_PLUS EXTRACT END */
+
+/*
+ * quotec - Called by getargs
+ */
+
+static int
+quotec(void)
+{
+ int c, i, num;
+
+ switch (c = getc(stdin)) {
+
+ case 'n':
+ c = '\n';
+ break;
+
+ case 'r':
+ c = '\r';
+ break;
+
+ case 'v':
+ c = '\013';
+ break;
+
+ case 'b':
+ c = '\b';
+ break;
+
+ case 't':
+ c = '\t';
+ break;
+
+ case 'f':
+ c = '\f';
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ for (num = 0, i = 0; i < 3; i++) {
+ num = num * 8 + (c - '0');
+ if ((c = getc(stdin)) < '0' || c > '7')
+ break;
+ }
+ (void) ungetc(c, stdin);
+ c = num & 0377;
+ break;
+
+ default:
+ break;
+ }
+ return (c);
+}
+
+/*
+ * getargs - returns an input line. Exits if EOF encountered.
+ */
+#define WHITESPACE 0
+#define ARGUMENT 1
+
+static char **
+getargs(char *input_line)
+{
+ static char envbuf[MAXLINE];
+ static char *args[MAXARGS];
+ char *ptr, **answer;
+ int c;
+ int state;
+ char *p = input_line;
+
+ ptr = envbuf;
+ answer = &args[0];
+ state = WHITESPACE;
+
+ while ((c = getc(stdin)) != EOF && answer < &args[MAXARGS-1]) {
+
+ *(input_line++) = c;
+
+ switch (c) {
+
+ case '\n':
+ if (ptr == &envbuf[0])
+ return ((char **)NULL);
+ *input_line = *ptr = '\0';
+ *answer = NULL;
+ return (&args[0]);
+
+ case ' ':
+ case '\t':
+ if (state == ARGUMENT) {
+ *ptr++ = '\0';
+ state = WHITESPACE;
+ }
+ break;
+
+ case '\\':
+ c = quotec();
+
+ default:
+ if (state == WHITESPACE) {
+ *answer++ = ptr;
+ state = ARGUMENT;
+ }
+ *ptr++ = c;
+ }
+
+ /* Attempt at overflow, exit */
+ if (input_line - p >= MAXLINE - 1 ||
+ ptr >= &envbuf[sizeof (envbuf) - 1]) {
+ audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW;
+ login_exit(1);
+ }
+ }
+
+ /*
+ * If we left loop because an EOF was received or we've overflown
+ * args[], exit immediately.
+ */
+ login_exit(0);
+ /* NOTREACHED */
+}
+
+/*
+ * get_user_name - Gets the user name either passed in, or from the
+ * login: prompt.
+ */
+
+static void
+get_user_name()
+{
+ FILE *fp;
+
+ if ((fp = fopen(ISSUEFILE, "r")) != NULL) {
+ char *ptr, buffer[BUFSIZ];
+ while ((ptr = fgets(buffer, sizeof (buffer),
+ fp)) != NULL) {
+ (void) fputs(ptr, stdout);
+ }
+ (void) fclose(fp);
+ }
+
+ /*
+ * if TTYPROMPT is not set, use our own prompt
+ * otherwise, use ttyprompt. We just set PAM_USER_PROMPT
+ * and let the module do the prompting.
+ */
+
+ if ((ttyprompt == NULL) || (*ttyprompt == '\0'))
+ (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg);
+ else
+ (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt);
+
+ envp = &zero; /* XXX: is this right? */
+}
+
+
+/*
+ * Check_for_dueling_unix - Check to see if the another login is talking
+ * to the line we've got open as a login port
+ * Exits if we're talking to another unix system
+ */
+
+static void
+check_for_dueling_unix(char *inputline)
+{
+ if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) ||
+ EQN(incorrectmsg, inputline)) {
+ (void) printf("Looking at a login line.\n");
+ login_exit(8);
+ }
+}
+
+/*
+ * logins_disabled - if the file /etc/nologin exists and the user is not
+ * root then do not permit them to login
+ */
+static int
+logins_disabled(char *user_name)
+{
+ FILE *nlfd;
+ int c;
+ if (!EQN("root", user_name) &&
+ ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) {
+ while ((c = getc(nlfd)) != EOF)
+ (void) putchar(c);
+ (void) fflush(stdout);
+ (void) sleep(5);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * check_for_console - Checks if we're getting a root login on the
+ * console, or a login from the global zone. Exits if not.
+ *
+ */
+static void
+check_for_console(void)
+{
+ if (pwd != NULL && pwd->pw_uid == 0 && zflag == B_FALSE) {
+ if ((Console != NULL) && (strcmp(ttyn, Console) != 0)) {
+ (void) printf("Not on system console\n");
+
+ audit_error = ADT_FAIL_VALUE_CONSOLE;
+ login_exit(10);
+ }
+ }
+}
+
+/*
+ * List of environment variables or environment variable prefixes that should
+ * not be propagated across logins, such as when the login -p option is used.
+ */
+static const char *const illegal[] = {
+ "SHELL=",
+ "HOME=",
+ "LOGNAME=",
+#ifndef NO_MAIL
+ "MAIL=",
+#endif
+ "CDPATH=",
+ "IFS=",
+ "PATH=",
+ "LD_",
+ "SMF_",
+ NULL
+};
+
+/*
+ * legalenvvar - Is it legal to insert this environmental variable?
+ */
+
+static int
+legalenvvar(char *s)
+{
+ const char *const *p;
+
+ for (p = &illegal[0]; *p; p++) {
+ if (strncmp(s, *p, strlen(*p)) == 0)
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * getstr - Get a string from standard input
+ * Calls exit if read(2) fails.
+ */
+
+static void
+getstr(char *buf, int cnt, char *err)
+{
+ char c;
+
+ do {
+ if (read(0, &c, 1) != 1)
+ login_exit(1);
+ *buf++ = c;
+ } while (--cnt > 1 && c != 0);
+
+ *buf = 0;
+ err = err; /* For lint */
+}
+
+
+/*
+ * defaults - read defaults
+ */
+
+static void
+defaults(void)
+{
+ int flags;
+ char *ptr;
+
+ if (defopen(Pndefault) == 0) {
+ /*
+ * ignore case
+ */
+ flags = defcntl(DC_GETFLAGS, 0);
+ TURNOFF(flags, DC_CASE);
+ (void) defcntl(DC_SETFLAGS, flags);
+
+ if ((Console = defread("CONSOLE=")) != NULL)
+ Console = strdup(Console);
+
+ if ((Altshell = defread("ALTSHELL=")) != NULL)
+ Altshell = strdup(Altshell);
+
+ if ((ptr = defread("PASSREQ=")) != NULL &&
+ strcasecmp("YES", ptr) == 0)
+ Passreqflag = 1;
+
+ if ((Def_tz = defread("TIMEZONE=")) != NULL)
+ Def_tz = strdup(Def_tz);
+
+ if ((Def_hertz = defread("HZ=")) != NULL)
+ Def_hertz = strdup(Def_hertz);
+
+ if ((Def_path = defread("PATH=")) != NULL)
+ Def_path = strdup(Def_path);
+
+ if ((Def_supath = defread("SUPATH=")) != NULL)
+ Def_supath = strdup(Def_supath);
+
+ if ((ptr = defread("ULIMIT=")) != NULL)
+ Def_ulimit = atol(ptr);
+
+ if ((ptr = defread("TIMEOUT=")) != NULL)
+ Def_timeout = (unsigned)atoi(ptr);
+
+ if ((ptr = defread("UMASK=")) != NULL)
+ if (sscanf(ptr, "%lo", &Umask) != 1)
+ Umask = DEFUMASK;
+
+ if ((ptr = defread("SLEEPTIME=")) != NULL) {
+ if (is_number(ptr))
+ Sleeptime = atoi(ptr);
+ }
+
+ if ((ptr = defread("DISABLETIME=")) != NULL) {
+ if (is_number(ptr))
+ Disabletime = atoi(ptr);
+ }
+
+ if ((ptr = defread("SYSLOG=")) != NULL)
+ dosyslog = strcmp(ptr, "YES") == 0;
+
+ if ((ptr = defread("RETRIES=")) != NULL) {
+ if (is_number(ptr))
+ retry = atoi(ptr);
+ }
+
+ if ((ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) {
+ if (is_number(ptr))
+ flogin = atoi(ptr);
+ else
+ flogin = retry;
+ } else
+ flogin = retry;
+ (void) defopen((char *)NULL);
+ }
+}
+
+
+/*
+ * get_options(argc, argv)
+ * - parse the cmd line.
+ * - return 0 if successful, -1 if failed.
+ * Calls login_exit() on misuse of -r, -h, and -z flags
+ */
+
+static int
+get_options(int argc, char *argv[])
+{
+ int c;
+ int errflg = 0;
+ char sflagname[NMAX+1];
+ const char *flags_message = "Only one of -r, -h and -z allowed\n";
+
+ while ((c = getopt(argc, argv, "u:s:R:f:h:r:pad:t:U:z:")) != -1) {
+ switch (c) {
+ case 'a':
+ break;
+
+ case 'd':
+ /*
+ * Must be root to pass in device name
+ * otherwise we exit() as punishment for trying.
+ */
+ if (getuid() != 0 || geteuid() != 0) {
+ audit_error = ADT_FAIL_VALUE_DEVICE_PERM;
+ login_exit(1); /* sigh */
+ /*NOTREACHED*/
+ }
+ ttyn = optarg;
+ break;
+
+ case 'h':
+ if (hflag || rflag || zflag) {
+ (void) fprintf(stderr, flags_message);
+ login_exit(1);
+ }
+ hflag = B_TRUE;
+ SCPYL(remote_host, optarg);
+ if (argv[optind]) {
+ if (argv[optind][0] != '-') {
+ SCPYL(terminal, argv[optind]);
+ optind++;
+ } else {
+ /*
+ * Allow "login -h hostname -" to
+ * skip setting up an username as "-".
+ */
+ if (argv[optind][1] == '\0')
+ optind++;
+ }
+
+ }
+ SCPYL(progname, "telnet");
+ break;
+
+ case 'r':
+ if (hflag || rflag || zflag) {
+ (void) fprintf(stderr, flags_message);
+ login_exit(1);
+ }
+ rflag = B_TRUE;
+ SCPYL(remote_host, optarg);
+ SCPYL(progname, "rlogin");
+ break;
+
+ case 'p':
+ pflag = B_TRUE;
+ break;
+
+ case 'f':
+ /*
+ * Must be root to bypass authentication
+ * otherwise we exit() as punishment for trying.
+ */
+ if (getuid() != 0 || geteuid() != 0) {
+ audit_error = ADT_FAIL_VALUE_AUTH_BYPASS;
+
+ login_exit(1); /* sigh */
+ /*NOTREACHED*/
+ }
+ /* save fflag user name for future use */
+ SCPYL(user_name, optarg);
+ fflag = B_TRUE;
+ break;
+ case 'u':
+ if (!strlen(optarg)) {
+ (void) fprintf(stderr,
+ "Empty string supplied with -u\n");
+ login_exit(1);
+ }
+ SCPYL(identity, optarg);
+ uflag = B_TRUE;
+ break;
+ case 's':
+ if (!strlen(optarg)) {
+ (void) fprintf(stderr,
+ "Empty string supplied with -s\n");
+ login_exit(1);
+ }
+ SCPYL(sflagname, optarg);
+ sflag = B_TRUE;
+ break;
+ case 'R':
+ if (!strlen(optarg)) {
+ (void) fprintf(stderr,
+ "Empty string supplied with -R\n");
+ login_exit(1);
+ }
+ SCPYL(repository, optarg);
+ Rflag = B_TRUE;
+ break;
+ case 't':
+ if (!strlen(optarg)) {
+ (void) fprintf(stderr,
+ "Empty string supplied with -t\n");
+ login_exit(1);
+ }
+ SCPYL(terminal, optarg);
+ tflag = B_TRUE;
+ break;
+ case 'U':
+ /*
+ * Kerberized rlogind may fork us with
+ * -U "" if the rlogin client used the "-a"
+ * option to send a NULL username. This is done
+ * to force login to prompt for a user/password.
+ * However, if Kerberos auth was used, we dont need
+ * to prompt, so we will accept the option and
+ * handle the situation later.
+ */
+ SCPYL(rusername, optarg);
+ Uflag = B_TRUE;
+ break;
+ case 'z':
+ if (hflag || rflag || zflag) {
+ (void) fprintf(stderr, flags_message);
+ login_exit(1);
+ }
+ (void) snprintf(zone_name, sizeof (zone_name),
+ "zone:%s", optarg);
+ SCPYL(progname, "zlogin");
+ zflag = B_TRUE;
+ break;
+ default:
+ errflg++;
+ break;
+ } /* end switch */
+ } /* end while */
+
+ /*
+ * If the 's svcname' flag was used, override the progname
+ * value that is to be used in the pam_start call.
+ */
+ if (sflag)
+ SCPYL(progname, sflagname);
+
+ /*
+ * get the prompt set by ttymon
+ */
+ ttyprompt = getenv("TTYPROMPT");
+
+ if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
+ /*
+ * if ttyprompt is set, there should be data on
+ * the stream already.
+ */
+ if ((envp = getargs(inputline)) != (char **)NULL) {
+ /*
+ * don't get name if name passed as argument.
+ */
+ SCPYL(user_name, *envp++);
+ }
+ } else if (optind < argc) {
+ SCPYL(user_name, argv[optind]);
+ (void) SCPYL(inputline, user_name);
+ (void) strlcat(inputline, " \n", sizeof (inputline));
+ envp = &argv[optind+1];
+
+ if (!fflag)
+ SCPYL(lusername, user_name);
+ }
+
+ if (errflg)
+ return (-1);
+ return (0);
+}
+
+/*
+ * usage - Print usage message
+ *
+ */
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage:\n"
+ " login [-p] [-d device] [-R repository] [-s service]\n"
+ "\t[-t terminal] [-u identity] [-U ruser]\n"
+ "\t[-h hostname [terminal] | -r hostname] [name [environ]...]\n");
+
+}
+
+/*
+ * doremoteterm - Sets the appropriate ioctls for a remote terminal
+ */
+static char *speeds[] = {
+ "0", "50", "75", "110", "134", "150", "200", "300",
+ "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400",
+ "57600", "76800", "115200", "153600", "230400", "307200", "460800"
+};
+
+#define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
+
+
+static void
+doremoteterm(char *term)
+{
+ struct termios tp;
+ char *cp = strchr(term, '/'), **cpp;
+ char *speed;
+
+ (void) ioctl(0, TCGETS, &tp);
+
+ if (cp) {
+ *cp++ = '\0';
+ speed = cp;
+ cp = strchr(speed, '/');
+
+ if (cp)
+ *cp++ = '\0';
+
+ for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
+ if (strcmp(*cpp, speed) == 0) {
+ (void) cfsetospeed(&tp, cpp-speeds);
+ break;
+ }
+ }
+
+ tp.c_lflag |= ECHO|ICANON;
+ tp.c_iflag |= IGNPAR|ICRNL;
+
+ (void) ioctl(0, TCSETS, &tp);
+
+}
+
+/*
+ * Process_rlogin - Does the work that rlogin and telnet
+ * need done
+ */
+static void
+process_rlogin(void)
+{
+ /*
+ * If a Kerberized rlogin was initiated, then these fields
+ * must be read by rlogin daemon itself and passed down via
+ * cmd line args.
+ */
+ if (!Uflag && !strlen(rusername))
+ getstr(rusername, sizeof (rusername), "remuser");
+ if (!strlen(lusername))
+ getstr(lusername, sizeof (lusername), "locuser");
+ if (!tflag && !strlen(terminal))
+ getstr(terminal, sizeof (terminal), "Terminal type");
+
+ if (strlen(terminal))
+ doremoteterm(terminal);
+
+ /* fflag has precedence over stuff passed by rlogind */
+ if (fflag || getuid()) {
+ pwd = &nouser;
+ return;
+ } else {
+ if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
+ login_exit(1);
+
+ pwd = getpwnam(lusername);
+ if (pwd == NULL) {
+ pwd = &nouser;
+ return;
+ }
+ }
+
+ /*
+ * Update PAM on the user name
+ */
+ if (strlen(lusername) &&
+ pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS)
+ login_exit(1);
+
+ if (strlen(rusername) &&
+ pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS)
+ login_exit(1);
+
+ SCPYL(user_name, lusername);
+ envp = &zero;
+ lusername[0] = '\0';
+}
+
+/*
+ * *** Account validation routines ***
+ *
+ */
+
+/*
+ * validate_account - This is the PAM version of validate.
+ */
+
+static void
+validate_account(void)
+{
+ int error;
+ int flag;
+ int tries; /* new password retries */
+
+ (void) alarm(0); /* give user time to come up with password */
+
+ check_log();
+
+ if (Passreqflag)
+ flag = PAM_DISALLOW_NULL_AUTHTOK;
+ else
+ flag = 0;
+
+ if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) {
+ if (error == PAM_NEW_AUTHTOK_REQD) {
+ tries = 1;
+ error = PAM_AUTHTOK_ERR;
+ while (error == PAM_AUTHTOK_ERR &&
+ tries <= DEF_ATTEMPTS) {
+ if (tries > 1)
+ (void) printf("Try again\n\n");
+
+ (void) printf("Choose a new password.\n");
+
+ error = pam_chauthtok(pamh, 0);
+ if (error == PAM_TRY_AGAIN) {
+ (void) sleep(1);
+ error = pam_chauthtok(pamh, 0);
+ }
+ tries++;
+ }
+
+ if (error != PAM_SUCCESS) {
+ if (dosyslog)
+ syslog(LOG_CRIT,
+ "change password failure: %s",
+ pam_strerror(pamh, error));
+ audit_error = ADT_FAIL_PAM + error;
+ login_exit(1);
+ } else {
+ audit_success(ADT_passwd, pwd, zone_name);
+ }
+ } else {
+ (void) printf(incorrectmsg);
+
+ if (dosyslog)
+ syslog(LOG_CRIT,
+ "login account failure: %s",
+ pam_strerror(pamh, error));
+ audit_error = ADT_FAIL_PAM + error;
+ login_exit(1);
+ }
+ }
+}
+
+/*
+ * Check_log - This is really a hack because PAM checks the log, but login
+ * wants to know if the log is okay and PAM doesn't have
+ * a module independent way of handing this info back.
+ */
+
+static void
+check_log(void)
+{
+ int fdl;
+ long long offset;
+
+ offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog);
+
+ if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) {
+ if (llseek(fdl, offset, SEEK_SET) == offset &&
+ read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) &&
+ ll.ll_time != 0)
+ lastlogok = 1;
+ (void) close(fdl);
+ }
+}
+
+/*
+ * chdir_to_dir_user - Now chdir after setuid/setgid have happened to
+ * place us in the user's home directory just in
+ * case it was protected and the first chdir failed.
+ * No chdir errors should happen at this point because
+ * all failures should have happened on the first
+ * time around.
+ */
+
+static void
+chdir_to_dir_user(void)
+{
+ if (chdir(pwd->pw_dir) < 0) {
+ if (chdir("/") < 0) {
+ (void) printf("No directory!\n");
+ /*
+ * This probably won't work since we can't get to /.
+ */
+ if (dosyslog) {
+ if (remote_host[0]) {
+ syslog(LOG_CRIT,
+ "LOGIN FAILURES ON %s FROM %.*s ",
+ " %.*s", ttyn, HMAX,
+ remote_host, NMAX, pwd->pw_name);
+ } else {
+ syslog(LOG_CRIT,
+ "LOGIN FAILURES ON %s, %.*s",
+ ttyn, NMAX, pwd->pw_name);
+ }
+ }
+ closelog();
+ (void) sleep(Disabletime);
+ exit(1);
+ } else {
+ (void) printf("No directory! Logging in with home=/\n");
+ pwd->pw_dir = "/";
+ }
+ }
+}
+
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * login_authenticate - Performs the main authentication work
+ * 1. Prints the login prompt
+ * 2. Requests and verifys the password
+ * 3. Checks the port password
+ */
+
+static void
+login_authenticate()
+{
+ char *user;
+ int err;
+ int login_successful = 0;
+
+ do {
+ /* if scheme broken, then nothing to do but quit */
+ if (pam_get_item(pamh, PAM_USER, (void **)&user)
+ != PAM_SUCCESS)
+ exit(1);
+
+ /*
+ * only get name from utility if it is not already
+ * supplied by pam_start or a pam_set_item.
+ */
+ if (!user || !user[0]) {
+ /* use call back to get user name */
+ get_user_name();
+ }
+
+ err = verify_passwd();
+
+ /*
+ * If root login and not on system console then call exit(2)
+ */
+ check_for_console();
+
+ switch (err) {
+ case PAM_SUCCESS:
+ case PAM_NEW_AUTHTOK_REQD:
+ /*
+ * Officially, pam_authenticate() shouldn't return this
+ * but it's probably the right thing to return if
+ * PAM_DISALLOW_NULL_AUTHTOK is set so the user will
+ * be forced to change password later in this code.
+ */
+ count = 0;
+ login_successful = 1;
+ break;
+ case PAM_MAXTRIES:
+ count = retry;
+ /*FALLTHROUGH*/
+ case PAM_AUTH_ERR:
+ case PAM_AUTHINFO_UNAVAIL:
+ case PAM_USER_UNKNOWN:
+ audit_failure(get_audit_id(), ADT_FAIL_PAM + err, pwd,
+ remote_host, ttyn, zone_name);
+ log_bad_attempts();
+ break;
+ case PAM_ABORT:
+ log_bad_attempts();
+ (void) sleep(Disabletime);
+ (void) printf(incorrectmsg);
+
+ audit_error = ADT_FAIL_PAM + err;
+ login_exit(1);
+ /*NOTREACHED*/
+ default: /* Some other PAM error */
+ audit_error = ADT_FAIL_PAM + err;
+ login_exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (login_successful)
+ break;
+
+ /* sleep after bad passwd */
+ if (count)
+ (void) sleep(Sleeptime);
+ (void) printf(incorrectmsg);
+ /* force name to be null in this case */
+ if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS)
+ login_exit(1);
+ if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS)
+ login_exit(1);
+ } while (count++ < retry);
+
+ if (count >= retry) {
+ audit_failure(get_audit_id(), ADT_FAIL_VALUE_MAX_TRIES, pwd,
+ remote_host, ttyn, zone_name);
+ /*
+ * If logging is turned on, output the
+ * string storage area to the log file,
+ * and sleep for Disabletime
+ * seconds before exiting.
+ */
+ if (writelog)
+ badlogin();
+ if (dosyslog) {
+ if ((pwd = getpwnam(user_name)) != NULL) {
+ if (remote_host[0]) {
+ syslog(LOG_CRIT,
+ "REPEATED LOGIN FAILURES ON %s FROM "
+ "%.*s, %.*s",
+ ttyn, HMAX, remote_host, NMAX,
+ user_name);
+ } else {
+ syslog(LOG_CRIT,
+ "REPEATED LOGIN FAILURES ON %s, %.*s",
+ ttyn, NMAX, user_name);
+ }
+ } else {
+ if (remote_host[0]) {
+ syslog(LOG_CRIT,
+ "REPEATED LOGIN FAILURES ON %s FROM "
+ "%.*s",
+ ttyn, HMAX, remote_host);
+ } else {
+ syslog(LOG_CRIT,
+ "REPEATED LOGIN FAILURES ON %s", ttyn);
+ }
+ }
+ }
+ (void) sleep(Disabletime);
+ exit(1);
+ }
+
+}
+
+/*
+ * *** Credential Related routines ***
+ *
+ */
+
+/*
+ * setup_credentials - sets the group ID, initializes the groups
+ * and sets up the secretkey.
+ * Exits if a failure occurrs.
+ */
+
+
+/*
+ * setup_credentials - PAM does all the work for us on this one.
+ */
+
+static void
+setup_credentials(void)
+{
+ int error = 0;
+
+ /* set the real (and effective) GID */
+ if (setgid(pwd->pw_gid) == -1) {
+ login_exit(1);
+ }
+
+ /*
+ * Initialize the supplementary group access list.
+ */
+ if ((user_name[0] == '\0') ||
+ (initgroups(user_name, pwd->pw_gid) == -1)) {
+ audit_error = ADT_FAIL_VALUE_PROGRAM;
+ login_exit(1);
+ }
+
+ if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+ audit_error = ADT_FAIL_PAM + error;
+ login_exit(error);
+ }
+
+ /*
+ * Record successful login and fork process that records logout.
+ * We have to do this after setting credentials because pam_setcred()
+ * loads key audit info into the cred, but before setuid() so audit
+ * system calls will work.
+ */
+ audit_success(get_audit_id(), pwd, zone_name);
+
+ /* set the real (and effective) UID */
+ if (setuid(pwd->pw_uid) == -1) {
+ login_exit(1);
+ }
+
+}
+/* ONC_PLUS EXTRACT END */
+
+static uint_t
+get_audit_id() {
+ if (rflag)
+ return (ADT_rlogin);
+ else if (hflag)
+ return (ADT_telnet);
+ else if (zflag)
+ return (ADT_zlogin);
+
+ return (ADT_login);
+}
+
+/*
+ *
+ * *** Routines to get a new user set up and running ***
+ *
+ * Things to do when starting up a new user:
+ * adjust_nice
+ * update_utmpx_entry
+ * establish_user_environment
+ * print_banner
+ * display_last_login_time
+ * exec_the_shell
+ *
+ */
+
+
+/*
+ * adjust_nice - Set the nice (process priority) value if the
+ * gecos value contains an appropriate value.
+ */
+
+static void
+adjust_nice(void)
+{
+ int pri, mflg, i;
+
+ if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
+ pri = 0;
+ mflg = 0;
+ i = 4;
+
+ if (pwd->pw_gecos[i] == '-') {
+ mflg++;
+ i++;
+ }
+
+ while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
+ pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
+
+ if (mflg)
+ pri = -pri;
+
+ (void) nice(pri);
+ }
+}
+
+/* ONC_PLUS EXTRACT START */
+/*
+ * update_utmpx_entry - Searchs for the correct utmpx entry, making an
+ * entry there if it finds one, otherwise exits.
+ */
+
+static void
+update_utmpx_entry(int sublogin)
+{
+ int err;
+ char *user;
+ static char *errmsg = "No utmpx entry. "
+ "You must exec \"login\" from the lowest level \"shell\".";
+ int tmplen;
+ struct utmpx *u = (struct utmpx *)0;
+ struct utmpx utmpx;
+ char *ttyntail;
+
+ /*
+ * If we're not a sublogin then
+ * we'll get an error back if our PID doesn't match the PID of the
+ * entry we are updating, otherwise if its a sublogin the flags
+ * field is set to 0, which means we just write a matching entry
+ * (without checking the pid), or a new entry if an entry doesn't
+ * exist.
+ */
+
+ if ((err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
+ audit_error = ADT_FAIL_PAM + err;
+ login_exit(1);
+ }
+
+ if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) !=
+ PAM_SUCCESS) {
+ audit_error = ADT_FAIL_PAM + err;
+ login_exit(1);
+ }
+/* ONC_PLUS EXTRACT END */
+
+ (void) memset((void *)&utmpx, 0, sizeof (utmpx));
+ (void) time(&utmpx.ut_tv.tv_sec);
+ utmpx.ut_pid = getpid();
+
+ if (rflag || hflag) {
+ SCPYN(utmpx.ut_host, remote_host);
+ tmplen = strlen(remote_host) + 1;
+ if (tmplen < sizeof (utmpx.ut_host))
+ utmpx.ut_syslen = tmplen;
+ else
+ utmpx.ut_syslen = sizeof (utmpx.ut_host);
+ } else if (zflag) {
+ /*
+ * If this is a login from another zone, put the
+ * zone:<zonename> string in the utmpx entry.
+ */
+ SCPYN(utmpx.ut_host, zone_name);
+ tmplen = strlen(zone_name) + 1;
+ if (tmplen < sizeof (utmpx.ut_host))
+ utmpx.ut_syslen = tmplen;
+ else
+ utmpx.ut_syslen = sizeof (utmpx.ut_host);
+ } else {
+ utmpx.ut_syslen = 0;
+ }
+
+ SCPYN(utmpx.ut_user, user);
+
+ /* skip over "/dev/" */
+ ttyntail = basename(ttyn);
+
+ while ((u = getutxent()) != NULL) {
+ if ((u->ut_type == INIT_PROCESS ||
+ u->ut_type == LOGIN_PROCESS ||
+ u->ut_type == USER_PROCESS) &&
+ ((sublogin && strncmp(u->ut_line, ttyntail,
+ sizeof (u->ut_line)) == 0) ||
+ u->ut_pid == utmpx.ut_pid)) {
+ SCPYN(utmpx.ut_line, (ttyn+sizeof ("/dev/")-1));
+ (void) memcpy(utmpx.ut_id, u->ut_id,
+ sizeof (utmpx.ut_id));
+ utmpx.ut_exit.e_exit = u->ut_exit.e_exit;
+ utmpx.ut_type = USER_PROCESS;
+ (void) pututxline(&utmpx);
+ break;
+ }
+ }
+ endutxent();
+
+ if (u == (struct utmpx *)NULL) {
+ if (!sublogin) {
+ /*
+ * no utmpx entry already setup
+ * (init or rlogind/telnetd)
+ */
+ (void) puts(errmsg);
+
+ audit_error = ADT_FAIL_VALUE_PROGRAM;
+ login_exit(1);
+ }
+ } else {
+ /* Now attempt to write out this entry to the wtmp file if */
+ /* we were successful in getting it from the utmpx file and */
+ /* the wtmp file exists. */
+ updwtmpx(WTMPX_FILE, &utmpx);
+ }
+/* ONC_PLUS EXTRACT START */
+}
+
+
+
+/*
+ * process_chroot_logins - Chroots to the specified subdirectory and
+ * re executes login.
+ */
+
+static int
+process_chroot_logins(void)
+{
+ /*
+ * If the shell field starts with a '*', do a chroot to the home
+ * directory and perform a new login.
+ */
+
+ if (*pwd->pw_shell == '*') {
+ (void) pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
+ pamh = NULL; /* really done */
+ if (chroot(pwd->pw_dir) < 0) {
+ (void) printf("No Root Directory\n");
+
+ audit_failure(get_audit_id(),
+ ADT_FAIL_VALUE_CHDIR_FAILED,
+ pwd, remote_host, ttyn, zone_name);
+
+ return (ERROR);
+ }
+ /*
+ * Set the environment flag <!sublogin> so that the next login
+ * knows that it is a sublogin.
+ */
+/* ONC_PLUS EXTRACT END */
+ envinit[0] = SUBLOGIN;
+ envinit[1] = (char *)NULL;
+ (void) printf("Subsystem root: %s\n", pwd->pw_dir);
+ (void) execle("/usr/bin/login", "login", (char *)0,
+ &envinit[0]);
+ (void) execle("/etc/login", "login", (char *)0, &envinit[0]);
+ (void) printf("No /usr/bin/login or /etc/login on root\n");
+
+ audit_error = ADT_FAIL_VALUE_PROGRAM;
+
+ login_exit(1);
+ }
+ return (OK);
+/* ONC_PLUS EXTRACT START */
+}
+
+/*
+ * establish_user_environment - Set up the new users enviornment
+ */
+
+static void
+establish_user_environment(char **renvp)
+{
+ int i, j, k, l_index, length, idx = 0;
+ char *endptr;
+ char **lenvp;
+ char **pam_env;
+
+ lenvp = environ;
+ while (*lenvp++)
+ ;
+
+ /* count the number of PAM environment variables set by modules */
+ if ((pam_env = pam_getenvlist(pamh)) != 0) {
+ for (idx = 0; pam_env[idx] != 0; idx++)
+ ;
+ }
+
+ envinit = (char **)calloc(lenvp - environ + 10
+ + MAXARGS + idx, sizeof (char *));
+ if (envinit == NULL) {
+ (void) printf("Calloc failed - out of swap space.\n");
+ login_exit(8);
+ }
+
+ /*
+ * add PAM environment variables first so they
+ * can be overwritten at login's discretion.
+ * check for illegal environment variables.
+ */
+ idx = 0; basicenv = 0;
+ if (pam_env != 0) {
+ while (pam_env[idx] != 0) {
+ if (legalenvvar(pam_env[idx])) {
+ envinit[basicenv] = pam_env[idx];
+ basicenv++;
+ }
+ idx++;
+ }
+ }
+ (void) memcpy(&envinit[basicenv], newenv, sizeof (newenv));
+/* ONC_PLUS EXTRACT END */
+
+ /* Set up environment */
+ if (rflag) {
+ ENVSTRNCAT(term, terminal);
+ } else if (hflag) {
+ if (strlen(terminal)) {
+ ENVSTRNCAT(term, terminal);
+ }
+ } else {
+ char *tp = getenv("TERM");
+
+ if ((tp != NULL) && (*tp != '\0'))
+ ENVSTRNCAT(term, tp);
+ }
+
+ ENVSTRNCAT(logname, pwd->pw_name);
+
+ /*
+ * There are three places to get timezone info. init.c sets
+ * TZ if the file /etc/TIMEZONE contains a value for TZ.
+ * login.c looks in the file /etc/default/login for a
+ * variable called TIMEZONE being set. If TIMEZONE has a
+ * value, TZ is set to that value; no environment variable
+ * TIMEZONE is set, only TZ. If neither of these methods
+ * work to set TZ, then the library routines will default
+ * to using the file /usr/lib/locale/TZ/localtime.
+ *
+ * There is a priority set up here. If /etc/TIMEZONE has
+ * a value for TZ, that value remains top priority. If the
+ * file /etc/default/login has TIMEZONE set, that has second
+ * highest priority not overriding the value of TZ in
+ * /etc/TIMEZONE. The reason for this priority is that the
+ * file /etc/TIMEZONE is supposed to be sourced by
+ * /etc/profile. We are doing the "sourcing" prematurely in
+ * init.c. Additionally, a login C shell doesn't source the
+ * file /etc/profile thus not sourcing /etc/TIMEZONE thus not
+ * allowing an adminstrator to globally set TZ for all users
+ */
+ if (Def_tz != NULL) /* Is there a TZ from defaults/login? */
+ tmp_tz = Def_tz;
+
+ if ((Def_tz = getenv("TZ")) != NULL) {
+ ENVSTRNCAT(timez, Def_tz);
+ } else if (tmp_tz != NULL) {
+ Def_tz = tmp_tz;
+ ENVSTRNCAT(timez, Def_tz);
+ }
+
+ if (Def_hertz == NULL)
+ (void) sprintf(hertz + strlen(hertz), "%lu", HZ);
+ else
+ ENVSTRNCAT(hertz, Def_hertz);
+
+ if (Def_path == NULL)
+ (void) strlcat(path, DEF_PATH, sizeof (path));
+ else
+ ENVSTRNCAT(path, Def_path);
+
+ ENVSTRNCAT(home, pwd->pw_dir);
+
+ /*
+ * Find the end of the basic environment
+ */
+ for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
+ ;
+
+ /*
+ * If TZ has a value, add it.
+ */
+ if (strcmp(timez, "TZ=") != 0)
+ envinit[basicenv++] = timez;
+
+ if (*pwd->pw_shell == '\0') {
+ /*
+ * If possible, use the primary default shell,
+ * otherwise, use the secondary one.
+ */
+ if (access(SHELL, X_OK) == 0)
+ pwd->pw_shell = SHELL;
+ else
+ pwd->pw_shell = SHELL2;
+ } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) {
+ envinit[basicenv++] = shell;
+ ENVSTRNCAT(shell, pwd->pw_shell);
+ }
+
+#ifndef NO_MAIL
+ envinit[basicenv++] = mail;
+ (void) strlcat(mail, pwd->pw_name, sizeof (mail));
+#endif
+
+ /*
+ * Pick up locale environment variables, if any.
+ */
+ lenvp = renvp;
+ while (*lenvp != NULL) {
+ j = 0;
+ while (localeenv[j] != 0) {
+ /*
+ * locale_envmatch() returns 1 if
+ * *lenvp is localenev[j] and valid.
+ */
+ if (locale_envmatch(localeenv[j], *lenvp) == 1) {
+ envinit[basicenv++] = *lenvp;
+ break;
+ }
+ j++;
+ }
+ lenvp++;
+ }
+
+ /*
+ * If '-p' flag, then try to pass on allowable environment
+ * variables. Note that by processing this first, what is
+ * passed on the final "login:" line may over-ride the invocation
+ * values. XXX is this correct?
+ */
+ if (pflag) {
+ for (lenvp = renvp; *lenvp; lenvp++) {
+ if (!legalenvvar(*lenvp)) {
+ continue;
+ }
+ /*
+ * If this isn't 'xxx=yyy', skip it. XXX
+ */
+ if ((endptr = strchr(*lenvp, '=')) == NULL) {
+ continue;
+ }
+ length = endptr + 1 - *lenvp;
+ for (j = 0; j < basicenv; j++) {
+ if (strncmp(envinit[j], *lenvp, length) == 0) {
+ /*
+ * Replace previously established value
+ */
+ envinit[j] = *lenvp;
+ break;
+ }
+ }
+ if (j == basicenv) {
+ /*
+ * It's a new definition, so add it at the end.
+ */
+ envinit[basicenv++] = *lenvp;
+ }
+ }
+ }
+
+ /*
+ * Add in all the environment variables picked up from the
+ * argument list to "login" or from the user response to the
+ * "login" request, if any.
+ */
+
+ if (envp == NULL)
+ goto switch_env; /* done */
+
+ for (j = 0, k = 0, l_index = 0;
+ *envp != NULL && j < (MAXARGS-1);
+ j++, envp++) {
+
+ /*
+ * Scan each string provided. If it doesn't have the
+ * format xxx=yyy, then add the string "Ln=" to the beginning.
+ */
+ if ((endptr = strchr(*envp, '=')) == NULL) {
+ /*
+ * This much to be malloc'd:
+ * strlen(*envp) + 1 char for 'L' +
+ * MAXARGSWIDTH + 1 char for '=' + 1 for null char;
+ *
+ * total = strlen(*envp) + MAXARGSWIDTH + 3
+ */
+ int total = strlen(*envp) + MAXARGSWIDTH + 3;
+ envinit[basicenv+k] = malloc(total);
+ if (envinit[basicenv+k] == NULL) {
+ (void) printf("%s: malloc failed\n", PROG_NAME);
+ login_exit(1);
+ }
+ (void) snprintf(envinit[basicenv+k], total, "L%d=%s",
+ l_index, *envp);
+
+ k++;
+ l_index++;
+ } else {
+ if (!legalenvvar(*envp)) { /* this env var permited? */
+ continue;
+ } else {
+
+ /*
+ * Check to see whether this string replaces
+ * any previously defined string
+ */
+ for (i = 0, length = endptr + 1 - *envp;
+ i < basicenv + k; i++) {
+ if (strncmp(*envp, envinit[i], length)
+ == 0) {
+ envinit[i] = *envp;
+ break;
+ }
+ }
+
+ /*
+ * If it doesn't, place it at the end of
+ * environment array.
+ */
+ if (i == basicenv+k) {
+ envinit[basicenv+k] = *envp;
+ k++;
+ }
+ }
+ }
+ } /* for (j = 0 ... ) */
+
+switch_env:
+ /*
+ * Switch to the new environment.
+ */
+ environ = envinit;
+}
+
+/*
+ * print_banner - Print the banner at start up
+ * Do not turn on DOBANNER ifdef. This is not
+ * relevant to SunOS.
+ */
+
+static void
+print_banner(void)
+{
+#ifdef DOBANNER
+ uname(&un);
+#if i386
+ (void) printf("UNIX System V/386 Release %s\n%s\n"
+ "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n"
+ "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n",
+ un.release, un.nodename);
+#elif sun
+ (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n"
+ "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n"
+ "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n"
+ "All Rights Reserved\n",
+ un.release, un.machine, un.nodename);
+#else
+ (void) printf("UNIX System V Release %s AT&T %s\n%s\n"
+ "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n",
+ un.release, un.machine, un.nodename);
+#endif /* i386 */
+#endif /* DOBANNER */
+}
+
+/*
+ * display_last_login_time - Advise the user the time and date
+ * that this login-id was last used.
+ */
+
+static void
+display_last_login_time(void)
+{
+ if (lastlogok) {
+ (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time));
+
+ if (*ll.ll_host != '\0')
+ (void) printf("from %.*s\n", sizeof (ll.ll_host),
+ ll.ll_host);
+ else
+ (void) printf("on %.*s\n", sizeof (ll.ll_line),
+ ll.ll_line);
+ }
+}
+
+/*
+ * exec_the_shell - invoke the specified shell or start up program
+ */
+
+static void
+exec_the_shell(void)
+{
+ char *endptr;
+ int i;
+
+ (void) strlcat(minusnam, basename(pwd->pw_shell),
+ sizeof (minusnam));
+
+ /*
+ * Exec the shell
+ */
+ (void) execl(pwd->pw_shell, minusnam, (char *)0);
+
+ /*
+ * pwd->pw_shell was not an executable object file, maybe it
+ * is a shell proceedure or a command line with arguments.
+ * If so, turn off the SHELL= environment variable.
+ */
+ for (i = 0; envinit[i] != NULL; ++i) {
+ if ((envinit[i] == shell) &&
+ ((endptr = strchr(shell, '=')) != NULL))
+ (*++endptr) = '\0';
+ }
+
+ if (access(pwd->pw_shell, R_OK|X_OK) == 0) {
+ (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0);
+ (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0);
+ }
+
+ (void) printf("No shell\n");
+}
+
+/*
+ * login_exit - Call exit() and terminate.
+ * This function is here for PAM so cleanup can
+ * be done before the process exits.
+ */
+static void
+login_exit(int exit_code)
+{
+ if (pamh)
+ (void) pam_end(pamh, PAM_ABORT);
+
+ if (audit_error)
+ audit_failure(get_audit_id(), audit_error,
+ pwd, remote_host, ttyn, zone_name);
+
+ exit(exit_code);
+ /*NOTREACHED*/
+}
+
+/*
+ * Check if lenv and penv matches or not.
+ */
+static int
+locale_envmatch(char *lenv, char *penv)
+{
+ while ((*lenv == *penv) && *lenv && *penv != '=') {
+ lenv++;
+ penv++;
+ }
+
+ /*
+ * '/' is eliminated for security reason.
+ */
+ if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')
+ return (1);
+ return (0);
+}
+
+static int
+is_number(char *ptr)
+{
+ while (*ptr != '\0') {
+ if (!isdigit(*ptr))
+ return (0);
+ ptr++;
+ }
+ return (1);
+}
diff --git a/usr/src/cmd/login/login.dfl b/usr/src/cmd/login/login.dfl
new file mode 100644
index 0000000000..03cd4d14f2
--- /dev/null
+++ b/usr/src/cmd/login/login.dfl
@@ -0,0 +1,98 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Set the TZ environment variable of the shell.
+#
+#TIMEZONE=EST5EDT
+
+# ULIMIT sets the file size limit for the login. Units are disk blocks.
+# The default of zero means no limit.
+#
+#ULIMIT=0
+
+# If CONSOLE is set, root can only login on that device.
+# Comment this line out to allow remote login by root.
+#
+CONSOLE=/dev/console
+
+# PASSREQ determines if login requires a password.
+#
+PASSREQ=YES
+
+# ALTSHELL determines if the SHELL environment variable should be set
+#
+ALTSHELL=YES
+
+# PATH sets the initial shell PATH variable
+#
+#PATH=/usr/bin:
+
+# SUPATH sets the initial shell PATH variable for root
+#
+#SUPATH=/usr/sbin:/usr/bin
+
+# TIMEOUT sets the number of seconds (between 0 and 900) to wait before
+# abandoning a login session.
+#
+#TIMEOUT=300
+
+# UMASK sets the initial shell file creation mode mask. See umask(1).
+#
+#UMASK=022
+
+# SYSLOG determines whether the syslog(3) LOG_AUTH facility should be used
+# to log all root logins at level LOG_NOTICE and multiple failed login
+# attempts at LOG_CRIT.
+#
+SYSLOG=YES
+
+# SLEEPTIME controls the number of seconds that the command should
+# wait before printing the "login incorrect" message when a
+# bad password is provided. The range is limited from
+# 0 to 5 seconds.
+#
+#SLEEPTIME=4
+
+# DISABLETIME If present, and greater than zero, the number of seconds
+# login will wait after RETRIES failed attempts or the PAM framework returns
+# PAM_ABORT. Default is 20. Minimum is 0. No maximum is imposed.
+#
+#DISABLETIME=20
+
+# RETRIES determines the number of failed logins that will be
+# allowed before login exits. Default is 5 and maximum is 15.
+# If account locking is configured (user_attr(4)/policy.conf(4))
+# for a local user's account (passwd(4)/shadow(4)), that account
+# will be locked if failed logins equals or exceeds RETRIES.
+#
+#RETRIES=5
+#
+# The SYSLOG_FAILED_LOGINS variable is used to determine how many failed
+# login attempts will be allowed by the system before a failed login
+# message is logged, using the syslog(3) LOG_NOTICE facility. For example,
+# if the variable is set to 0, login will log -all- failed login attempts.
+#
+#SYSLOG_FAILED_LOGINS=5
diff --git a/usr/src/cmd/login/login_audit.c b/usr/src/cmd/login/login_audit.c
new file mode 100644
index 0000000000..2d9592d0e7
--- /dev/null
+++ b/usr/src/cmd/login/login_audit.c
@@ -0,0 +1,219 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include "login_audit.h"
+
+
+
+/*
+ * Key assumption: login is single threaded.
+ */
+static void audit_logout(adt_session_data_t *);
+
+/*
+ * if audit is not enabled, the adt_*() functions simply return without
+ * doing anything. In the success case, the credential has already been
+ * setup with audit data by PAM.
+ */
+
+/*
+ * There is no information passed to login.c from rlogin or telnet
+ * about the terminal id. They both set the tid before they
+ * exec login; the value is picked up by adt_start_session() and is
+ * carefully *not* overwritten by adt_load_hostname().
+ */
+
+void
+audit_success(uint_t event_id, struct passwd *pwd, char *optional_text)
+{
+ adt_session_data_t *ah;
+ adt_event_data_t *event;
+ int rc;
+
+ assert(pwd != NULL);
+
+ if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
+ syslog(LOG_AUTH | LOG_ALERT, "audit: %m");
+ return;
+ }
+ if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid,
+ pwd->pw_uid, pwd->pw_gid, NULL, ADT_USER)) {
+ syslog(LOG_AUTH | LOG_ALERT, "audit: %m");
+ (void) adt_end_session(ah);
+ return;
+ }
+ event = adt_alloc_event(ah, event_id);
+
+ if (event == NULL)
+ return;
+
+ switch (event_id) {
+ case ADT_zlogin:
+ event->adt_zlogin.message = optional_text;
+ break;
+ default:
+ break;
+ }
+ rc = adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS);
+
+ (void) adt_free_event(event);
+ if (rc) {
+ (void) adt_end_session(ah);
+ syslog(LOG_AUTH | LOG_ALERT, "audit: %m");
+ return;
+ }
+ /*
+ * The code above executes whether or not audit is enabled.
+ * However audit_logout must only execute if audit is
+ * enabled so we don't fork unnecessarily.
+ */
+ if (adt_audit_enabled()) {
+ switch (event_id) {
+ case ADT_login:
+ case ADT_rlogin:
+ case ADT_telnet:
+ case ADT_zlogin:
+ audit_logout(ah); /* fork to catch logout */
+ break;
+ }
+ }
+ (void) adt_end_session(ah);
+}
+
+/*
+ * errors are ignored since there is no action to take on error
+ */
+static void
+audit_logout(adt_session_data_t *ah)
+{
+ adt_event_data_t *logout;
+ int status; /* wait status */
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ return;
+ } else if (pid == -1) {
+ syslog(LOG_AUTH | LOG_ALERT, "login: could not fork: %m");
+ exit(1);
+ } else {
+ /*
+ * When this routine is called, the current working
+ * directory is the user's home directory. Change it
+ * to root for the waiting process so that the user's
+ * home directory can be unmounted if necessary.
+ */
+ if (chdir("/") != 0) {
+ syslog(LOG_AUTH | LOG_ALERT,
+ "login: could not chdir: %m");
+ /* since we let the child finish we just bail */
+ exit(0);
+ }
+ while (pid != waitpid(pid, &status, 0))
+ continue;
+
+ logout = adt_alloc_event(ah, ADT_logout);
+ if (logout == NULL)
+ exit(0);
+
+ (void) adt_put_event(logout, ADT_SUCCESS, ADT_SUCCESS);
+
+ adt_free_event(logout);
+ exit(0);
+ }
+}
+
+/*
+ * errors are ignored since there is no action to take on error.
+ *
+ * If the user id is invalid, pwd is NULL.
+ */
+void
+audit_failure(uint_t event_id, int failure_code, struct passwd *pwd,
+ const char *hostname, const char *ttyname, char *optional_text)
+{
+ adt_session_data_t *ah;
+ adt_event_data_t *event;
+ uid_t uid;
+ gid_t gid;
+ adt_termid_t *p_tid;
+
+ if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA))
+ return;
+
+ uid = ADT_NO_ATTRIB;
+ gid = ADT_NO_ATTRIB;
+ if (pwd != NULL) {
+ uid = pwd->pw_uid;
+ gid = pwd->pw_gid;
+ }
+ /*
+ * If this is a remote login, in.rlogind or in.telnetd has
+ * already set the terminal id, in which case
+ * adt_load_hostname() will use the preset terminal id and
+ * ignore hostname. (If no remote host and ttyname is NULL,
+ * let adt_load_ttyname() figure out what to do.)
+ */
+ if (*hostname == '\0')
+ (void) adt_load_ttyname(ttyname, &p_tid);
+ else
+ (void) adt_load_hostname(hostname, &p_tid);
+
+ if (adt_set_user(ah, uid, gid, uid, gid, p_tid, ADT_NEW)) {
+ (void) adt_end_session(ah);
+ if (p_tid != NULL)
+ free(p_tid);
+ return;
+ }
+ if (p_tid != NULL)
+ free(p_tid);
+
+ event = adt_alloc_event(ah, event_id);
+ if (event == NULL) {
+ return;
+ }
+ switch (event_id) {
+ case ADT_zlogin:
+ event->adt_zlogin.message = optional_text;
+ break;
+ }
+ (void) adt_put_event(event, ADT_FAILURE, failure_code);
+
+ adt_free_event(event);
+ (void) adt_end_session(ah);
+}
diff --git a/usr/src/cmd/login/login_audit.h b/usr/src/cmd/login/login_audit.h
new file mode 100644
index 0000000000..37e3f9b810
--- /dev/null
+++ b/usr/src/cmd/login/login_audit.h
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DTAUDIT_H
+#define _DTAUDIT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bsm/adt_event.h>
+
+/* bit flags... */
+#define AUDIT_IS_TTY 1
+
+void audit_success(uint_t, struct passwd *, char *);
+void audit_failure(uint_t, int, struct passwd *, const char *,
+ const char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DTAUDIT_H */
diff --git a/usr/src/cmd/login/logindevperm.sh b/usr/src/cmd/login/logindevperm.sh
new file mode 100644
index 0000000000..bf2c48936a
--- /dev/null
+++ b/usr/src/cmd/login/logindevperm.sh
@@ -0,0 +1,95 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+#
+# This is the script that generates the logindevperm file. It is
+# architecture-aware, and dumps different stuff for x86 and sparc.
+# There is a lot of common entries, which are dumped first.
+#
+# the SID of this script, and the SID of the dumped script are
+# always the same.
+#
+
+cat <<EOM
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%logindevperm %I% %E% SMI"
+#
+# /etc/logindevperm - login-based device permissions
+#
+# If the user is logging in on a device specified in the "console" field
+# of any entry in this file, the owner/group of the devices listed in the
+# "devices" field will be set to that of the user. Similarly, the mode
+# will be set to the mode specified in the "mode" field.
+#
+# "devices" is a colon-separated list of device names. A device name
+# ending in "/*", such as "/dev/fbs/*", specifies all entries (except "."
+# and "..") in a directory. A '#' begins a comment and may appear
+# anywhere in an entry.
+# In addition, regular expressions may be used. Refer to logindevperm(4)
+# man page.
+# Note that any changes in this file should be made when logged in as
+# root as devfs provides persistence on minor node attributes.
+#
+# console mode devices
+#
+/dev/console 0600 /dev/mouse:/dev/kbd
+/dev/console 0600 /dev/sound/* # audio devices
+/dev/console 0600 /dev/fbs/* # frame buffers
+EOM
+
+case "$MACH" in
+ "i386" )
+ #
+ # These are the x86 specific entries
+ # It depends on the build machine being an x86
+ #
+ cat <<-EOM
+ EOM
+ ;;
+ "sparc" )
+ #
+ # These are the sparc specific entries
+ # It depends on the build machine being a sparc
+ #
+ cat <<-EOM
+ EOM
+ ;;
+ "ppc" )
+ #
+ # These are the ppc specific entries
+ # It depends on the build machine being a ppc
+ #
+ cat <<-EOM
+ EOM
+ ;;
+ * )
+ echo "Unknown Architecture"
+ exit 1
+ ;;
+esac