#include <sys/types.h>
#include <sys/param.h>
#include <unistd.h>
#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>
#define ISSUEFILE "/etc/issue"
#define NOLOGIN "/etc/nologin"
#define __UPDATE_ENTRY 1
#define __LOGIN 2
#ifndef SLEEPTIME
#define SLEEPTIME 4
#endif
static int Sleeptime = SLEEPTIME;
#ifndef DISABLETIME
#define DISABLETIME 20
#endif
static int Disabletime = DISABLETIME;
#define MAXTRYS 5
static int retry = MAXTRYS;
#define LOGINLOG "/var/adm/loginlog"
#define LNAME_SIZE 20
#define TTYN_SIZE 15
#define TIME_SIZE 30
#define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3)
#define L_WAITTIME 5
#define LOGTRYS 10
#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)); }
#define NMAX sizeof (((struct utmpx *)0)->ut_name)
#define HMAX sizeof (((struct utmpx *)0)->ut_host)
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define SHELL "/usr/bin/sh"
#define SHELL2 "/sbin/sh"
#define SUBLOGIN "<!sublogin>"
#define LASTLOG "/var/adm/lastlog"
#define PROG_NAME "login"
#define HUSHLOGIN ".hushlogin"
#define PBUFSIZE 8
#define MAXARGS 63
#define MAXARGSWIDTH 2
#define MAXENV 1024
#define MAXLINE 2048
#define ROOTUID 0
#define ERROR 1
#define OK 0
#define LOG_ERROR 1
#define DONT_LOG_ERROR 0
#define TRUE 1
#define FALSE 0
static int trys = 0;
static int count = 1;
static int audit_error = 0;
extern int getsecretkey();
static char user_name[NMAX];
static char minusnam[16] = "-";
static pid_t login_pid;
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 *);
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];
static char loginmsg[] = "login: ";
static char passwdmsg[] = "Password:";
static char incorrectmsg[] = "Login incorrect\n";
static struct passwd *pwd = NULL;
static char remote_host[HMAX];
static char zone_name[ZONENAME_MAX];
static struct passwd nouser = { "", "no:password", (uid_t)-1 };
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;
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
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:"
#define DEF_SUPATH "/usr/sbin:/usr/bin"
#define DEF_ATTEMPTS 3
static char *ttyprompt = NULL;
static char *ttyn = NULL;
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;
static char rusername[NMAX+1], lusername[NMAX+1];
static char terminal[MAXPATHLEN];
static int fflag;
static char ** getargs(char *);
static int login_conv(int, const struct pam_message **,
struct pam_response **, void *);
static struct pam_conv pam_conv = {login_conv, NULL};
static pam_handle_t *pamh;
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 *);
int
main(int argc, char *argv[], char **renvp)
{
int sublogin;
int pam_rc;
login_pid = getpid();
defaults();
SCPYL(progname, PROG_NAME);
if (Umask > ((mode_t)0777))
Umask = DEFUMASK;
(void) umask(Umask);
if (Def_timeout > MAX_TIMEOUT)
Def_timeout = MAX_TIMEOUT;
if (Sleeptime < 0 || Sleeptime > 5)
Sleeptime = SLEEPTIME;
(void) alarm(Def_timeout);
(void) signal(SIGQUIT, SIG_IGN);
(void) signal(SIGINT, SIG_IGN);
(void) nice(0);
sublogin = 0;
if (*renvp && strcmp(*renvp, SUBLOGIN) == 0)
sublogin = 1;
if (get_options(argc, argv) == -1) {
usage();
audit_error = ADT_FAIL_VALUE_BAD_CMD;
login_exit(1);
}
if (ttyn == NULL) {
ttyn = ttyname(0);
if (ttyn == NULL)
ttyn = "/dev/???";
}
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);
}
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);
}
turn_on_logging();
openlog("login", 0, LOG_AUTH);
if (rflag)
process_rlogin();
if (fflag) {
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);
}
} else {
login_authenticate();
}
if (process_chroot_logins() != OK)
login_exit(1);
check_for_console();
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;
}
validate_account();
if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L)
(void) printf("Could not set ULIMIT to %ld\n", Def_ulimit);
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();
setup_credentials();
update_utmpx_entry(sublogin);
if (setuid(pwd->pw_uid) == -1) {
login_exit(1);
}
chdir_to_dir_user();
establish_user_environment(renvp);
(void) pam_end(pamh, PAM_SUCCESS);
pamh = NULL;
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);
if (access(HUSHLOGIN, F_OK) != 0) {
print_banner();
display_last_login_time();
}
(void) signal(SIGXCPU, SIG_DFL);
(void) signal(SIGXFSZ, SIG_DFL);
exec_the_shell();
login_exit(1);
return (0);
}
static void
donothing(int sig)
{
if (pamh)
(void) pam_end(pamh, PAM_ABORT);
}
#ifdef notdef
static int intrupt;
static void
catch(int sig)
{
++intrupt;
}
#endif
static void
badlogin(void)
{
int retval, count1, fildes;
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);
}
}
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);
}
}
}
}
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';
}
}
}
static int
login_conv(int num_msg, const struct pam_message **msg,
struct pam_response **response, void *appdata_ptr)
{
const 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:
errno = 0;
temp = getpassphrase(m->msg);
if (temp != NULL) {
if (errno == EINTR)
return (PAM_CONV_ERR);
r->resp = strdup(temp);
if (r->resp == NULL) {
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) {
r = *response;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp)
free(r->resp);
}
free(*response);
*response = NULL;
return (PAM_BUF_ERR);
}
bzero((void *)inputline, MAXLINE);
envp = getargs(inputline);
if (inputline[0] != '\0') {
int len;
if (envp != (char **)NULL)
envp++;
(void) strncpy(r->resp, inputline,
PAM_MAX_RESP_SIZE-1);
r->resp[PAM_MAX_RESP_SIZE-1] = '\0';
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);
}
static int
verify_passwd(void)
{
int error;
const char *user;
int flag = (Passreqflag ? PAM_DISALLOW_NULL_AUTHTOK : 0);
error = pam_authenticate(pamh, flag);
(void) pam_get_item(pamh, PAM_USER, (const 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) &&
(error != PAM_USER_UNKNOWN)) {
return (PAM_SYSTEM_ERR);
}
return (error);
}
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);
}
#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;
}
if (input_line - p >= MAXLINE - 1 ||
ptr >= &envbuf[sizeof (envbuf) - 1]) {
audit_error = ADT_FAIL_VALUE_INPUT_OVERFLOW;
login_exit(1);
}
}
login_exit(0);
}
static void
get_user_name(void)
{
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 == 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;
}
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);
}
}
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);
}
#define DEFAULT_CONSOLE "/dev/console"
static void
check_for_console(void)
{
const char *consoles[] = { "/dev/console", "/dev/vt/", NULL };
int i;
if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE ||
Console == NULL)
return;
if (strcmp(Console, DEFAULT_CONSOLE) == 0) {
for (i = 0; consoles[i] != NULL; i ++) {
if (strncmp(ttyn, consoles[i],
strlen(consoles[i])) == 0)
return;
}
} else {
if (strcmp(ttyn, Console) == 0)
return;
}
(void) printf("Not on system console\n");
audit_error = ADT_FAIL_VALUE_CONSOLE;
login_exit(10);
}
static const char *const illegal[] = {
"SHELL=",
"HOME=",
"LOGNAME=",
#ifndef NO_MAIL
"MAIL=",
#endif
"CDPATH=",
"IFS=",
"PATH=",
"LD_",
"SMF_",
NULL
};
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);
}
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;
}
static void
defaults(void)
{
int flags;
char *ptr;
if (defopen(Pndefault) == 0) {
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);
}
}
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':
if (getuid() != 0 || geteuid() != 0) {
audit_error = ADT_FAIL_VALUE_DEVICE_PERM;
login_exit(1);
}
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 {
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':
if (getuid() != 0 || geteuid() != 0) {
audit_error = ADT_FAIL_VALUE_AUTH_BYPASS;
login_exit(1);
}
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':
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;
}
}
if (sflag)
SCPYL(progname, sflagname);
ttyprompt = getenv("TTYPROMPT");
if ((ttyprompt != NULL) && (*ttyprompt != '\0')) {
if ((envp = getargs(inputline)) != (char **)NULL) {
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);
}
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");
}
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", "1000000", "1152000", "1500000", "2000000", "2500000",
"3000000", "3500000", "4000000"
};
#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);
}
static void
process_rlogin(void)
{
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);
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;
}
}
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';
}
static void
validate_account(void)
{
int error;
int flag;
int tries;
(void) alarm(0);
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,
PAM_CHANGE_EXPIRED_AUTHTOK);
if (error == PAM_TRY_AGAIN) {
(void) sleep(1);
error = pam_chauthtok(pamh,
PAM_CHANGE_EXPIRED_AUTHTOK);
}
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);
}
}
}
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);
}
}
static void
chdir_to_dir_user(void)
{
if (chdir(pwd->pw_dir) < 0) {
if (chdir("/") < 0) {
(void) printf("No directory!\n");
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 = "/";
}
}
}
static void
login_authenticate(void)
{
const char *user;
int err;
int login_successful = 0;
do {
if (pam_get_item(pamh, PAM_USER, (const void **)&user) !=
PAM_SUCCESS) {
exit(1);
}
if (!user || !user[0]) {
get_user_name();
}
err = verify_passwd();
check_for_console();
switch (err) {
case PAM_SUCCESS:
case PAM_NEW_AUTHTOK_REQD:
count = 0;
login_successful = 1;
break;
case PAM_MAXTRIES:
count = retry;
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);
default:
audit_error = ADT_FAIL_PAM + err;
login_exit(1);
}
if (login_successful)
break;
if (count)
(void) sleep(Sleeptime);
(void) printf(incorrectmsg);
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 (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);
}
}
static void
setup_credentials(void)
{
int error = 0;
if (setgid(pwd->pw_gid) == -1) {
login_exit(1);
}
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, zflag ? PAM_REINITIALIZE_CRED :
PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
audit_error = ADT_FAIL_PAM + error;
login_exit(error);
}
audit_success(get_audit_id(), pwd, zone_name);
}
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);
}
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);
}
}
static void
update_utmpx_entry(int sublogin)
{
int err;
const char *user;
static char *errmsg = "No utmpx entry. "
"You must exec \"login\" from the lowest level \"shell\".";
int tmplen;
struct utmpx *u = NULL;
struct utmpx utmpx;
char *ttyntail;
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, (const void **)&user)) !=
PAM_SUCCESS) {
audit_error = ADT_FAIL_PAM + err;
login_exit(1);
}
(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) {
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);
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 == login_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) {
(void) puts(errmsg);
audit_error = ADT_FAIL_VALUE_PROGRAM;
login_exit(1);
}
} else {
updwtmpx(WTMPX_FILE, &utmpx);
}
}
static int
process_chroot_logins(void)
{
if (*pwd->pw_shell == '*') {
(void) pam_end(pamh, PAM_SUCCESS);
pamh = NULL;
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);
}
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);
}
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++)
;
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);
}
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));
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);
if (Def_tz != NULL)
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);
for (basicenv = 0; envinit[basicenv] != NULL; basicenv++)
;
if (strcmp(timez, "TZ=") != 0)
envinit[basicenv++] = timez;
if (*pwd->pw_shell == '\0') {
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
lenvp = renvp;
while (*lenvp != NULL) {
j = 0;
while (localeenv[j] != 0) {
if (locale_envmatch(localeenv[j], *lenvp) == 1) {
envinit[basicenv++] = *lenvp;
break;
}
j++;
}
lenvp++;
}
if (pflag) {
for (lenvp = renvp; *lenvp; lenvp++) {
if (!legalenvvar(*lenvp)) {
continue;
}
if ((endptr = strchr(*lenvp, '=')) == NULL) {
continue;
}
length = endptr + 1 - *lenvp;
for (j = 0; j < basicenv; j++) {
if (strncmp(envinit[j], *lenvp, length) == 0) {
envinit[j] = *lenvp;
break;
}
}
if (j == basicenv) {
envinit[basicenv++] = *lenvp;
}
}
}
if (envp == NULL)
goto switch_env;
for (j = 0, k = 0, l_index = 0;
*envp != NULL && j < (MAXARGS-1);
j++, envp++) {
if ((endptr = strchr(*envp, '=')) == NULL) {
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)) {
continue;
} else {
for (i = 0, length = endptr + 1 - *envp;
i < basicenv + k; i++) {
if (strncmp(*envp, envinit[i], length)
== 0) {
envinit[i] = *envp;
break;
}
}
if (i == basicenv+k) {
envinit[basicenv+k] = *envp;
k++;
}
}
}
}
switch_env:
environ = envinit;
}
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
#endif
}
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);
}
}
static void
exec_the_shell(void)
{
char *endptr;
int i;
(void) strlcat(minusnam, basename(pwd->pw_shell),
sizeof (minusnam));
(void) execl(pwd->pw_shell, minusnam, (char *)0);
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");
}
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);
}
static int
locale_envmatch(char *lenv, char *penv)
{
while ((*lenv == *penv) && *lenv && *penv != '=') {
lenv++;
penv++;
}
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);
}