login.c revision 052519c2d30736afb1861979b73d5a889cf7fba8
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* 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 */
/*
* 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
* name of the zone from which the login is occurring.
*/
#include <unistd.h> /* For logfile locking */
#include <signal.h>
#include <stdio.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 ***
*
*
*/
/*
* 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
#endif
/*
* seconds login disabled after allowable number of unsuccessful attempts
*/
#ifndef DISABLETIME
#define DISABLETIME 20
#endif
static int Disabletime = DISABLETIME;
#define MAXTRYS 5
/*
* Login logging support
*/
/*
* String manipulation macros: SCPYN, SCPYL, EQN and ENVSTRNCAT
* SCPYL is the safer version of SCPYN
*/
/*
* Other macros
*/
#define min(a, b) (((a) < (b)) ? (a) : (b))
/*
* Various useful files and string constants
*/
#define SUBLOGIN "<!sublogin>"
#define PROG_NAME "login"
#define HUSHLOGIN ".hushlogin"
/*
* Array and Buffer sizes
*/
#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
*/
extern int getsecretkey();
/*
* The current user name
*/
/*
* login_pid, used to find utmpx entry to update.
*/
/*
* 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 int basicenv;
static char *zero = (char *)0;
static char **envp;
#ifndef NO_MAIL
#endif
extern char **environ;
#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";
/*
* Password file support
*/
static char remote_host[HMAX];
static char zone_name[ZONENAME_MAX];
/*
* Illegal passwd entries.
*/
/*
* Log file support
*/
static int writelog = 0;
static int lastlogok = 0;
static int dosyslog = 0;
/*
* Default file toggles
*/
static int Passreqflag = 0;
#define DEFUMASK 022
static long Def_ulimit = 0;
static unsigned Def_timeout = DEF_TIMEOUT;
static char *Def_supath = NULL;
/*
* 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.
*/
/*
* Pass inherited environment. Used by telnetd in support of the telnet
* ENVIRON option.
*/
/*
* Remote login support
*/
static char terminal[MAXPATHLEN];
/*
* Pre-authentication flag support
*/
static int fflag;
static char ** getargs(char *);
static int login_conv(int, struct pam_message **,
struct pam_response **, void *);
/*
* Function declarations
*/
static void turn_on_logging(void);
static void defaults(void);
static void usage(void);
static void process_rlogin(void);
static void login_authenticate();
static void setup_credentials(void);
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)__NORETURN;
static int logins_disabled(char *);
static void log_bad_attempts(void);
static int is_number(char *);
/*
* *** 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.
*
*
*/
int
{
int sublogin;
int pam_rc;
/*
* Set up Defaults and flags
*/
defaults();
/*
* Set up default umask
*/
/*
* Set up default timeouts and delays
*/
if (Def_timeout > MAX_TIMEOUT)
(void) alarm(Def_timeout);
/*
* Ignore SIGQUIT and SIGINT and set nice to 0
*/
(void) nice(0);
/*
* Set flag to disable the pid check if you find that you are
* a subsystem login.
*/
sublogin = 0;
sublogin = 1;
/*
* Parse Arguments
*/
usage();
login_exit(1);
}
/*
* if devicename is not passed as argument, call ttyname(0)
*/
ttyn = "/dev/???";
}
/*
* Call pam_start to initiate a PAM authentication operation
*/
!= PAM_SUCCESS) {
login_exit(1);
}
login_exit(1);
}
PAM_SUCCESS) {
login_exit(1);
}
/*
* We currently only support special handling of the KRB5 PAM repository
*/
(void *)&pam_rep_data);
}
/*
* Open the log file which contains a record of successful and failed
* login attempts
*/
/*
* say "hi" to syslogd ..
*/
/*
* Do special processing for -r (rlogin) flag
*/
if (rflag)
/*
* validate user
*/
/* we are already authenticated. fill in what we must, then continue */
if (fflag) {
(void) printf("Login failed: unknown user '%s'.\n",
login_exit(1);
}
} else {
/*
* Perform the primary login authentication activity.
*/
}
/* 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 to see if a shutdown is in progress, if it is and
* we are not root then throw the user off the system
*/
login_exit(1);
}
if (Def_supath != NULL)
else
}
/*
* Check account expiration and passwd aging
*/
/*
* We only get here if we've been authenticated.
*/
/*
* 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.
*/
/* di_devperm_login() sends detailed errors to syslog */
NULL) == -1) {
" see syslog for more details\n");
}
adjust_nice(); /* passwd file can specify nice value */
setup_credentials(); /* Set user credentials - exits on failure */
/*
* 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.
*/
/* set the real (and effective) UID */
login_exit(1);
}
/*
* Set up the basic environment for the exec. This includes
* HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL.
*/
if (dosyslog) {
if (remote_host[0]) {
} else
}
}
closelog();
/*
* Display some useful information to the new user like the banner
* and last login time if not a quiet login.
*/
print_banner();
}
/*
* 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.
*/
/*
* Now fire off the shell of choice
*/
/*
* All done
*/
login_exit(1);
return (0);
}
/*
* *** Utility functions ***
*/
/*
* donothing & catch - Signal catching functions
*/
/*ARGSUSED*/
static void
{
if (pamh)
}
#ifdef notdef
static int intrupt;
/*ARGSUSED*/
static void
{
++intrupt;
}
#endif
/*
* *** Bad login logging support ***
*/
/*
* badlogin() - log to the log file 'trys'
* unsuccessful attempts
*/
static void
badlogin(void)
{
/*
* Tries to open the log file. If succeed, lock it and write
* in the failed attempts
*/
(void) alarm(L_WAITTIME);
(void) alarm(0);
if (retval == 0) {
}
}
}
/*
* 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)
{
return;
if (writelog) {
trys++;
}
if (remote_host[0]) {
"Login failure on %s from %.*s, "
} else {
"Login failure on %s, %.*s",
}
} else {
if (remote_host[0]) {
"Login failure on %s from %.*s",
} else {
"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)
{
int i;
writelog = 1;
for (i = 0; i < LOGTRYS; i++) {
writelog = 0;
break;
}
*log_entry[i] = '\0';
}
}
}
/*
* 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
{
struct pam_message *m;
struct pam_response *r;
char *temp;
int k, i;
if (num_msg <= 0)
return (PAM_CONV_ERR);
return (PAM_BUF_ERR);
k = num_msg;
m = *msg;
r = *response;
while (k--) {
switch (m->msg_style) {
case PAM_PROMPT_ECHO_OFF:
errno = 0;
return (PAM_CONV_ERR);
/* free responses */
r = *response;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp)
}
return (PAM_BUF_ERR);
}
}
m++;
r++;
break;
case PAM_PROMPT_ECHO_ON:
/* free responses */
r = *response;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp)
}
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.
*/
/* If we read in any input, process it. */
if (inputline[0] != '\0') {
int len;
/*
* 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++;
} else {
login_exit(1);
}
m++;
r++;
break;
case PAM_ERROR_MSG:
}
m++;
r++;
break;
case PAM_TEXT_INFO:
}
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(void)
{
int error;
char *user;
/*
* PAM authenticates the user for us.
*/
/* get the user_name from the pam handle */
return (PAM_SYSTEM_ERR);
(error != PAM_USER_UNKNOWN)) {
return (PAM_SYSTEM_ERR);
}
return (error);
}
/*
* quotec - Called by getargs
*/
static int
quotec(void)
{
int c, i, num;
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++) {
break;
}
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)
{
int c;
int state;
char *p = input_line;
state = WHITESPACE;
*(input_line++) = c;
switch (c) {
case '\n':
return ((char **)NULL);
return (&args[0]);
case ' ':
case '\t':
*ptr++ = '\0';
state = WHITESPACE;
}
break;
case '\\':
c = quotec();
default:
if (state == WHITESPACE) {
}
*ptr++ = c;
}
/* Attempt at overflow, exit */
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(void)
{
}
}
/*
* 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.
*/
else
}
/*
* 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)
{
(void) printf("Looking at a login line.\n");
login_exit(8);
}
}
/*
* root then do not permit them to login
*/
static int
logins_disabled(char *user_name)
{
int c;
(void) putchar(c);
(void) sleep(5);
return (TRUE);
}
return (FALSE);
}
#define DEFAULT_CONSOLE "/dev/console"
/*
* check_for_console - Checks if we're getting a root login on the
* console, or a login from the global zone. Exits if not.
*
* zones, but checking them does no harm.
*/
static void
check_for_console(void)
{
int i;
return;
return;
}
} else {
return;
}
(void) printf("Not on system console\n");
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_",
};
/*
* legalenvvar - Is it legal to insert this environmental variable?
*/
static int
legalenvvar(char *s)
{
const char *const *p;
for (p = &illegal[0]; *p; p++) {
return (0);
}
return (1);
}
/*
* getstr - Get a string from standard input
* Calls exit if read(2) fails.
*/
static void
{
char c;
do {
login_exit(1);
*buf++ = c;
} while (--cnt > 1 && c != 0);
*buf = 0;
}
/*
* defaults - read defaults
*/
static void
defaults(void)
{
int flags;
char *ptr;
/*
* ignore case
*/
Passreqflag = 1;
}
}
}
else
} else
}
}
/*
* 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
{
int c;
int errflg = 0;
const char *flags_message = "Only one of -r, -h and -z allowed\n";
switch (c) {
case 'a':
break;
case 'd':
/*
* Must be root to pass in device name
* otherwise we exit() as punishment for trying.
*/
/*NOTREACHED*/
}
break;
case 'h':
login_exit(1);
}
optind++;
} else {
/*
* Allow "login -h hostname -" to
* skip setting up an username as "-".
*/
optind++;
}
}
break;
case 'r':
login_exit(1);
}
break;
case 'p':
break;
case 'f':
/*
* Must be root to bypass authentication
* otherwise we exit() as punishment for trying.
*/
/*NOTREACHED*/
}
/* save fflag user name for future use */
break;
case 'u':
"Empty string supplied with -u\n");
login_exit(1);
}
break;
case 's':
"Empty string supplied with -s\n");
login_exit(1);
}
break;
case 'R':
"Empty string supplied with -R\n");
login_exit(1);
}
break;
case 't':
"Empty string supplied with -t\n");
login_exit(1);
}
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
* However, if Kerberos auth was used, we dont need
* to prompt, so we will accept the option and
* handle the situation later.
*/
break;
case 'z':
login_exit(1);
}
"zone:%s", optarg);
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)
/*
* get the prompt set by ttymon
*/
/*
* if ttyprompt is set, there should be data on
* the stream already.
*/
/*
* don't get name if name passed as argument.
*/
}
if (!fflag)
}
if (errflg)
return (-1);
return (0);
}
/*
* usage - Print usage message
*
*/
static void
usage(void)
{
"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",
"921600"
};
static void
doremoteterm(char *term)
{
char *speed;
if (cp) {
*cp++ = '\0';
if (cp)
*cp++ = '\0';
break;
}
}
}
/*
* 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.
*/
/* fflag has precedence over stuff passed by rlogind */
return;
} else {
login_exit(1);
return;
}
}
/*
* Update PAM on the user name
*/
login_exit(1);
login_exit(1);
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)
else
flag = 0;
if (error == PAM_NEW_AUTHTOK_REQD) {
tries = 1;
while (error == PAM_AUTHTOK_ERR &&
tries <= DEF_ATTEMPTS) {
if (tries > 1)
(void) printf("Try again\n\n");
(void) printf("Choose a new password.\n");
if (error == PAM_TRY_AGAIN) {
(void) sleep(1);
}
tries++;
}
if (error != PAM_SUCCESS) {
if (dosyslog)
"change password failure: %s",
login_exit(1);
} else {
}
} else {
(void) printf(incorrectmsg);
if (dosyslog)
"login account failure: %s",
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;
lastlogok = 1;
}
}
/*
* 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("/") < 0) {
(void) printf("No directory!\n");
/*
* This probably won't work since we can't get to /.
*/
if (dosyslog) {
if (remote_host[0]) {
"LOGIN FAILURES ON %s FROM %.*s ",
} else {
"LOGIN FAILURES ON %s, %.*s",
}
}
closelog();
(void) sleep(Disabletime);
exit(1);
} else {
(void) printf("No directory! Logging in with home=/\n");
}
}
}
/*
* 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(void)
{
char *user;
int err;
int login_successful = 0;
do {
/* if scheme broken, then nothing to do but quit */
exit(1);
/*
* only get name from utility if it is not already
* supplied by pam_start or a pam_set_item.
*/
/* use call back to get user name */
}
err = verify_passwd();
/*
* If root login and not on system console then call exit(2)
*/
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:
/*FALLTHROUGH*/
case PAM_AUTH_ERR:
case PAM_AUTHINFO_UNAVAIL:
case PAM_USER_UNKNOWN:
break;
case PAM_ABORT:
(void) sleep(Disabletime);
(void) printf(incorrectmsg);
login_exit(1);
/*NOTREACHED*/
default: /* Some other PAM error */
login_exit(1);
/*NOTREACHED*/
}
if (login_successful)
break;
/* sleep after bad passwd */
if (count)
(void) printf(incorrectmsg);
/* force name to be null in this case */
login_exit(1);
login_exit(1);
/*
* 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 (remote_host[0]) {
"REPEATED LOGIN FAILURES ON %s "
"FROM %.*s, %.*s",
} else {
"REPEATED LOGIN FAILURES ON "
"%s, %.*s",
}
} else {
if (remote_host[0]) {
"REPEATED LOGIN FAILURES ON %s "
"FROM %.*s",
} else {
"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 */
login_exit(1);
}
/*
* Initialize the supplementary group access list.
*/
if ((user_name[0] == '\0') ||
login_exit(1);
}
PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
}
/*
* 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.
*/
}
static uint_t
get_audit_id(void)
{
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)
{
pri = 0;
mflg = 0;
i = 4;
mflg++;
i++;
}
if (mflg)
}
}
/*
* update_utmpx_entry - Searchs for the correct utmpx entry, making an
* entry there if it finds one, otherwise exits.
*/
static void
{
int err;
char *user;
static char *errmsg = "No utmpx entry. "
"You must exec \"login\" from the lowest level \"shell\".";
int tmplen;
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.
*/
login_exit(1);
}
PAM_SUCCESS) {
login_exit(1);
}
else
} else if (zflag) {
/*
* If this is a login from another zone, put the
* zone:<zonename> string in the utmpx entry.
*/
else
} else {
}
/* skip over "/dev/" */
if ((u->ut_type == INIT_PROCESS ||
u->ut_type == LOGIN_PROCESS ||
u->ut_type == USER_PROCESS) &&
sizeof (u->ut_line)) == 0) ||
(void) pututxline(&utmpx);
break;
}
}
endutxent();
if (!sublogin) {
/*
* no utmpx entry already setup
*/
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. */
}
}
/*
* 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.
*/
(void) printf("No Root Directory\n");
return (ERROR);
}
/*
* Set the environment flag <!sublogin> so that the next login
* knows that it is a sublogin.
*/
&envinit[0]);
login_exit(1);
}
return (OK);
}
/*
* establish_user_environment - Set up the new users enviornment
*/
static void
establish_user_environment(char **renvp)
{
char *endptr;
char **lenvp;
char **pam_env;
while (*lenvp++)
;
/* count the number of PAM environment variables set by modules */
;
}
sizeof (char *));
(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.
*/
if (pam_env != 0) {
basicenv++;
}
idx++;
}
}
/* Set up environment */
if (rflag) {
} else if (hflag) {
}
} else {
}
/*
* There are three places to get timezone info. init.c sets
* 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
*
* a value for TZ, that value remains top priority. If the
* highest priority not overriding the value of TZ in
* init.c. Additionally, a login C shell doesn't source the
* allowing an adminstrator to globally set TZ for all users
*/
}
else
else
/*
* Find the end of the basic environment
*/
;
/*
* If TZ has a value, add it.
*/
/*
* If possible, use the primary default shell,
* otherwise, use the secondary one.
*/
else
}
#ifndef NO_MAIL
#endif
/*
* Pick up locale environment variables, if any.
*/
j = 0;
while (localeenv[j] != 0) {
/*
* locale_envmatch() returns 1 if
* *lenvp is localenev[j] and valid.
*/
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) {
if (!legalenvvar(*lenvp)) {
continue;
}
/*
* If this isn't 'xxx=yyy', skip it. XXX
*/
continue;
}
for (j = 0; j < basicenv; j++) {
/*
* Replace previously established value
*/
break;
}
}
if (j == basicenv) {
/*
* It's a new definition, so add it at the end.
*/
}
}
}
/*
* 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.
*/
goto switch_env; /* done */
for (j = 0, k = 0, l_index = 0;
j++, envp++) {
/*
* Scan each string provided. If it doesn't have the
* format xxx=yyy, then add the string "Ln=" to the beginning.
*/
/*
* 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
*/
login_exit(1);
}
k++;
l_index++;
} else {
continue;
} else {
/*
* Check to see whether this string replaces
* any previously defined string
*/
i < basicenv + k; i++) {
== 0) {
break;
}
}
/*
* If it doesn't, place it at the end of
* environment array.
*/
if (i == basicenv+k) {
k++;
}
}
}
} /* for (j = 0 ... ) */
/*
* Switch to the new environment.
*/
}
/*
* 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
#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",
(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",
#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",
#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) {
else
}
}
/*
* exec_the_shell - invoke the specified shell or start up program
*/
static void
exec_the_shell(void)
{
char *endptr;
int i;
sizeof (minusnam));
/*
* Exec the shell
*/
/*
* 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.
*/
(*++endptr) = '\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)
if (audit_error)
/*NOTREACHED*/
}
/*
* Check if lenv and penv matches or not.
*/
static int
{
lenv++;
penv++;
}
/*
* '/' is eliminated for security reason.
*/
return (1);
return (0);
}
static int
{
while (*ptr != '\0') {
return (0);
ptr++;
}
return (1);
}