1
0
Files
irix-657m-src/eoe/cmd/CMWsession/CMWdialog.c
2022-09-29 17:59:04 +03:00

893 lines
20 KiB
C

/*
* Copyright 1996, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#ident "$Revision: 1.2 $"
#include <sys/types.h>
#include <sys/mac.h>
#include <sys/mac_label.h>
#include <sys/capability.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pwd.h>
#include <shadow.h>
#include <clearance.h>
#include <capability.h>
#include <crypt.h>
#include <unistd.h>
#define CMW_ANNOUNCE "/usr/CMW/CMWannounce"
#define CMW_HOWTO "/usr/CMW/CMWhowto"
#define CMW_UID0 "/usr/CMW/CMWuid0"
#define CMW_DISABLETIME "/usr/CMW/CMWdisabletime"
#define CMW_IDLEWEEKS "/usr/CMW/CMWidleweeks"
#define CMW_PASSWD "/usr/CMW/CMWuser-passwd"
#define CMW_SITECHECK "/usr/CMW/CMWuser-sitecheck"
#define CMW_USER_NAME "/usr/CMW/CMWuser-name"
#define CMW_USER_MAC "/usr/CMW/CMWuser-mac"
#define CMW_USER_CAP "/usr/CMW/CMWuser-cap"
#define XSITECHECK "/usr/bin/X11/CMWsitecheck"
#define XPASSWD "/usr/bin/X11/CMWpasswd"
#define XCONFIRM "/usr/bin/X11/xconfirm"
#define XWSH "/usr/sbin/xwsh"
#define TERMINATORS "#;\n"
#define LOGINDEFAULTS "login"
#define CONSOLE "/dev/console"
/*
* XXX:casey These SITE_ defines ought to go into a header file
*/
#define SITE_OK 0
#define SITE_FAIL 1
#define SITE_AGAIN 2
#define SITE_CONTINUE 3
/*
* From libcmd
*/
extern FILE *defopen(char *);
extern char *defread(FILE *, char *);
#define UNKNOWN -1
#define UNSET -2
#define DFLT_MAXTRYS 3
#define MAX_TIMEOUT (15 * 60)
#define MAX_LOGFAILURES 20
#define MAX_SLEEPTIME 60
#define LOGFAILURE_BUFSIZE 100
#define LOGFAILURE_FILE "/var/adm/loginlog"
char *program;
/*
* Get an integer value from a defread string.
*/
static void
defvalue(FILE *deffp, char *name, int *valuep, int max)
{
char *cp;
if (*valuep != UNKNOWN)
return;
if ((cp = defread(deffp, name)) == NULL)
*valuep = UNSET;
else if ((*valuep = atoi(cp)) <= 0)
*valuep = UNSET;
else if (max != UNSET && *valuep > max)
*valuep = max;
}
static void
xconfirm(char *file, char *message, char *header)
{
FILE *fp;
char *option = "-t";
char *parameter;
pid_t sitepid;
int wstatus;
cap_t ocap;
cap_value_t cap_xtcb = CAP_XTCB;
switch (sitepid = fork()) {
case -1:
return;
case 0:
if (file && (fp = fopen(file, "r"))) {
fclose(fp);
option = "-file";
parameter = file;
}
else
parameter = message;
ocap = cap_acquire(1, &cap_xtcb);
execl(XCONFIRM, "xconfirm", "-c", "-exclusive",
"-header", header ? header : " ",
"-t", " ",
option, parameter,
"-t", " ",
NULL);
cap_surrender(ocap);
exit(1);
default:
break;
}
(void) waitpid(sitepid, &wstatus, 0);
}
static void
xconfirm_disabletime(void)
{
FILE *fp;
char *option = "-t";
char *parameter;
cap_t ocap;
cap_value_t cap_xtcb = CAP_XTCB;
if (fork() != 0)
return;
if (fp = fopen(CMW_DISABLETIME, "r")) {
fclose(fp);
option = "-file";
parameter = CMW_DISABLETIME;
}
else
parameter = "System is locked.";
ocap = cap_acquire(1, &cap_xtcb);
execl(XCONFIRM, "xconfirm", "-c", "-exclusive",
"-header", "System Lockout Information",
"-t", " ",
option, parameter,
"-t", " ",
NULL);
cap_surrender(ocap);
exit(2);
}
static void
do_disabletime(FILE *deffp)
{
static int disabletime = UNKNOWN;
char *cp;
defvalue(deffp, "DISABLETIME", &disabletime, UNSET);
if (disabletime == 0)
return;
xconfirm(CMW_DISABLETIME,
"System is locked.",
"System Lockout Information");
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGWINCH, SIG_IGN);
sleep(disabletime);
exit(3);
}
static int
do_maxtrys(FILE *deffp, int try, struct passwd *pwent)
{
static int maxtrys = UNKNOWN;
static int logfailures = UNKNOWN;
static char *failure[MAX_LOGFAILURES];
time_t now;
char *cp;
defvalue(deffp, "MAXTRYS", &maxtrys, UNSET);
defvalue(deffp, "LOGFAILURES", &logfailures, MAX_LOGFAILURES);
if (logfailures != UNSET) {
failure[try] = malloc(LOGFAILURE_BUFSIZE);
now = time(NULL);
sprintf(failure[try], "%s:/dev/console:%s",
pwent ? pwent->pw_name : "?", ctime(&now));
}
if (logfailures == try + 1) {
FILE *fp;
int i;
if (fp = fopen(LOGFAILURE_FILE, "r")) {
fclose(fp);
if (fp = fopen(LOGFAILURE_FILE, "a")) {
for (i = 0; i < logfailures; i++)
fprintf(fp, "%s\n", failure[i]);
fclose(fp);
}
}
do_disabletime(deffp);
}
if (maxtrys == try + 1)
do_disabletime(deffp);
return 1;
}
static void
timeout_handler(int i)
{
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
cap_t ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", "", 0, "TIMEOUT exceeded");
cap_surrender(ocap);
exit(4);
}
static int timeout = UNKNOWN;
static void
timeout_set(FILE *deffp)
{
char *cp;
defvalue(deffp, "TIMEOUT", &timeout, MAX_TIMEOUT);
if (timeout == UNSET)
return;
signal(SIGALRM, timeout_handler);
alarm(timeout);
}
static void
timeout_clear(void)
{
if (timeout == UNSET)
return;
alarm(0);
signal(SIGALRM, SIG_DFL);
}
static int
xpasswd(struct passwd *pwent)
{
FILE *fp;
pid_t sitepid;
int wstatus;
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
cap_value_t cap_xtcb = CAP_XTCB;
cap_t ocap;
switch (sitepid = fork()) {
case -1:
return 0;
case 0:
signal(SIGALRM, SIG_DFL);
signal(SIGHUP, SIG_DFL);
ocap = cap_acquire(1, &cap_xtcb);
execl(XWSH, XWSH,
"-noscrollbar", "-nomenu", "-ut",
"-display", ":0",
"-title", "Password Update Dialog",
"-icontitle", "CMWpasswd",
"-transient", "root",
"-e", XPASSWD, pwent->pw_name, NULL);
cap_surrender(ocap);
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", pwent->pw_name, 0,
"can't exec CMWpasswd program");
cap_surrender(ocap);
return 0;
}
if (waitpid(sitepid, &wstatus, 0) < 0)
return 0;
if ((fp = fopen(CMW_PASSWD, "r")) == NULL)
return 0;
unlink(CMW_PASSWD);
if (fscanf(fp, "%d", &wstatus) != 1)
return 0;
fclose(fp);
return !wstatus;
}
static int
do_passwordcheck(FILE *deffp, struct passwd *pwent)
{
time_t now;
struct spwd *spent = NULL;
char *pwdp = NULL;
char *cp;
static int mandpass = UNKNOWN;
static int passreq = UNKNOWN;
static int idleweeks = UNKNOWN;
static int lockout = UNKNOWN;
if (mandpass == UNKNOWN) {
if ((cp = defread(deffp, "MANDPASS")) && !strcmp(cp, "YES"))
mandpass = 1;
else
mandpass = UNSET;
}
if (passreq == UNKNOWN) {
if ((cp = defread(deffp, "PASSREQ")) && !strcmp(cp, "YES"))
passreq = 1;
else
passreq = UNSET;
}
defvalue(deffp, "IDLEWEEKS", &idleweeks, UNSET);
defvalue(deffp, "LOCKOUT", &lockout, 255);
if (pwent) {
spent = getspnam(pwent->pw_name);
pwdp = pwent->pw_passwd;
}
if (pwdp && *pwdp == '\0')
pwdp = NULL;
/*
* A password is required, but the account has none.
*/
if (pwent && pwdp == NULL && passreq != UNSET)
return xpasswd(pwent);
/*
* If passwords are required always prompt for one, even if we
* already know that we're not going to let'em in.
*/
if (pwent == NULL || pwdp || (mandpass != UNSET && passreq != UNSET)) {
timeout_set(deffp);
cp = getpass("Password: ");
timeout_clear();
if (cp == NULL)
return 0;
}
/*
* Return if the only purpose was to look like we recognized
* the user when in fact there was no passwd entry.
*/
if (pwent == NULL)
return 0;
/*
* User had no password. Return 1 if neither
* mandpass or passreq is set, zero if either is set.
*/
if (pwdp == NULL)
return (mandpass == UNSET && passreq == UNSET);
/*
* There is a password. Return 0 if the user's response
* is not correct.
*/
if (strcmp(pwdp, crypt(cp, pwdp)))
return 0;
/*
* The user got it right. Time to check if the
* password has ripened sufficiently to be picked.
* Aging information comes from shadow. No shadow,
* no aging.
*/
if (spent == NULL)
return 1;
/*
* XXX:casey incredibly ugly macro from shadow.h
*/
now = DAY_NOW;
/*
* Password expiration rules:
* if lastchanged is 0, the password has expired.
* if lastchanged is in the future, the password has expired.
* if maximum is set (i.e. not -1) -and-
* maximum is not smaller than minimum -and-
* lastchanged + maximum is in the past,
* the password has expired.
*
* This test is the inverse of what's in login, BTW.
* Check for an unexpired password, and return in that case.
*/
if ((spent->sp_lstchg != 0) &&
(spent->sp_lstchg <= now) &&
((spent->sp_max < 0) ||
(now <= (spent->sp_lstchg + spent->sp_max)) ||
(spent->sp_max < spent->sp_min)))
return 1;
/*
* Password has expired. Check if it's been expired
* beyond reason, as defined by IDLEWEEKS.
*/
if (spent->sp_lstchg != 0 &&
(idleweeks == 0 ||
(idleweeks > 0 && now > (spent->sp_lstchg + (7 * idleweeks))))) {
xconfirm(CMW_IDLEWEEKS,
"Your password has been expired for too long.",
"User Lockout Information");
return 0;
}
/*
* Password is stale, but not too stale. Update it.
*/
return xpasswd(pwent);
}
/*
* CMW/X environment version of the classic function from login(1).
* Use the CMWsitecheck script to squirrel away the exit status of
* the real sitecheck program because "xwsh -e cmd" does not propagate
* the exit status of cmd.
*/
static int
do_sitecheck(FILE *deffp, char *name)
{
char *path;
FILE *fp;
int wstatus;
struct stat sbuf;
mac_t mp;
mac_t mok;
pid_t sitepid;
int i = 0;
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
cap_value_t cap_xtcb = CAP_XTCB;
cap_t ocap;
if ((path = defread(deffp, "SITECHECK")) == NULL)
return SITE_CONTINUE;
/*
* Enforce security items. Check that the program is owned by root,
* and is not world-writable. Return SITE_CONTINUE if it is not,
* thus causing a traditional unix authentication.
*/
if (stat(path, &sbuf) != 0) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "cannot access sitecheck");
cap_surrender(ocap);
return SITE_CONTINUE;
}
if (sbuf.st_uid != 0) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "bad sitecheck ownership");
cap_surrender(ocap);
return SITE_CONTINUE;
}
if (sbuf.st_mode & 0022) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "bad sitecheck permissions");
cap_surrender(ocap);
return SITE_CONTINUE;
}
if ((mok = mac_from_text("msenlow/minthigh")) == NULL) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "bad system label names");
cap_surrender(ocap);
return SITE_CONTINUE;
}
if ((mp = mac_get_file(path)) == NULL) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "bad sitecheck MAC");
cap_surrender(ocap);
mac_free(mok);
return SITE_CONTINUE;
}
i = (!mac_equal(mok, mp) ||
mp->ml_msen_type == MSEN_EQUAL_LABEL ||
mp->ml_mint_type == MINT_EQUAL_LABEL);
mac_free(mp);
mac_free(mok);
if (i) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "bad sitecheck MAC");
cap_surrender(ocap);
return SITE_CONTINUE;
}
/* fork, exec, and wait for return code of siteprog */
switch (sitepid = fork()) {
case -1:
return SITE_FAIL;
case 0:
signal(SIGALRM, SIG_DFL);
signal(SIGHUP, SIG_DFL);
ocap = cap_acquire(1, &cap_xtcb);
execl(XWSH, XWSH,
"-noscrollbar", "-nomenu", "-ut",
"-display", ":0",
"-title", "Site authentication Dialog",
"-icontitle", "sitecheck",
"-transient", "root",
"-e", XSITECHECK, path, name, NULL);
cap_surrender(ocap);
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("CMWdialog", name, 0, "can't exec sitecheck program");
cap_surrender(ocap);
return SITE_CONTINUE;
}
if (waitpid(sitepid, &wstatus, 0) < 0)
return SITE_FAIL;
if ((fp = fopen(CMW_SITECHECK, "r")) == NULL)
return SITE_CONTINUE;
unlink(CMW_SITECHECK);
if (fscanf(fp, "%d", &i) != 1)
return SITE_CONTINUE;
fclose(fp);
return i;
}
/*
* Determine if root logins are allowed on only one device, and if so,
* if this is it.
*/
static int
do_console(FILE *deffp)
{
char *console;
if ((console = defread(deffp, "CONSOLE")) == NULL)
return 1;
/*
* Do the CONSOLE login default option check.
*/
if (console && strcmp(console, CONSOLE))
return 0;
return 1;
}
static void
xconfirm_root(void)
{
char message[256];
char *fmt = "Your user ID is zero (0). Please note:\n%s\n";
switch (sysconf(_SC_CAP)) {
case CAP_SYS_NO_SUPERUSER:
sprintf(message, fmt, "There is no Superuser here.");
break;
case CAP_SYS_SUPERUSER:
sprintf(message, fmt, "The Superuser is effective.");
break;
case CAP_SYS_DISABLED:
sprintf(message, fmt, "Capabilities are disabled.");
break;
default:
sprintf(message, fmt, "Capabilities are unknown.");
break;
}
xconfirm(CMW_UID0, message, "Root User Special Information");
}
static void
terminate_line(char *line)
{
char *cp;
char *tp;
for (tp = TERMINATORS ; *tp ; tp++)
if (cp = strchr(line, *tp))
*cp = '\0';
}
static int
capable(struct user_cap *capent, char *cap_requested)
{
int result = 0;
cap_t rcap = cap_from_text(cap_requested);
cap_t pcap = cap_from_text(capent->ca_allowed);
/*
* verify that requested capabilities are permitted
*/
if (pcap && rcap &&
CAP_ID_ISSET(rcap->cap_effective, pcap->cap_effective) &&
CAP_ID_ISSET(rcap->cap_permitted, pcap->cap_permitted) &&
CAP_ID_ISSET(rcap->cap_inheritable, pcap->cap_inheritable))
result = 1;
cap_free(pcap);
cap_free(rcap);
return result;
}
static void
cmw_message(char *path, char *message)
{
FILE *fp;
if ((fp = fopen(path, "w")) == NULL)
exit(5);
fprintf(fp, "%s\n", message);
fclose(fp);
}
static void
report_error(FILE *deffp, char *username, char *message)
{
static int sleeptime = UNKNOWN;
char *cp;
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
cap_t ocap;
defvalue(deffp, "SLEEPTIME", &sleeptime, MAX_SLEEPTIME);
if (sleeptime != UNSET)
sleep(sleeptime);
ocap = cap_acquire(1, &cap_audit_write);
ia_audit(program, username, 0, message);
cap_surrender(ocap);
fprintf(stdout, "Login Incorrect.\n\n");
}
static void
enable_caps()
{
cap_value_t cap_priv_port = CAP_PRIV_PORT;
cap_t oldcap;
oldcap = cap_acquire(1, &cap_priv_port);
cap_free(oldcap);
}
main(int argc, char *argv[])
{
char line[512];
char *username = NULL;
char *mac_requested = NULL;
char *cap_requested = NULL;
struct passwd *pwent = NULL;
struct user_cap *capent;
struct clearance *macent;
FILE *fp;
FILE *defaultsfp = defopen(LOGINDEFAULTS);
int trys;
int c;
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
cap_t ocap;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
program = argv[0];
enable_caps();
/*
* Print instructions regarding use of the Trusted Path.
* Allow a site specific (CMW_ANNOUNCE) message.
*/
if (fp = fopen(CMW_ANNOUNCE, "r")) {
while ((c = fgetc(fp)) != EOF)
fputc(c, stdout);
fclose(fp);
}
else {
fprintf(stdout, "\n\n");
fprintf(stdout, "\t\t>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
fprintf(stdout, "\t\t^ v\n");
fprintf(stdout, "\t\t^ Be sure the Trusted Path is on v\n");
fprintf(stdout, "\t\t^ before you attempt to log in. v\n");
fprintf(stdout, "\t\t^ v\n");
fprintf(stdout, "\t\t<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
fprintf(stdout, "\n\n");
}
/*
* Print instructions for login.
*/
if (fp = fopen(CMW_HOWTO, "r")) {
while ((c = fgetc(fp)) != EOF)
fputc(c, stdout);
fclose(fp);
}
else {
if (gethostname(line, 500) == 0)
fprintf(stdout, "Machine Name\t%s\n", line);
if (getdomainname(line, 500) == 0)
fprintf(stdout, "Domain Name\t%s\n", line);
fprintf(stdout, "\n");
fprintf(stdout, "This is the IRIX/CMW login dialog.\n");
fprintf(stdout, "You will be prompted for:\n\n");
fprintf(stdout, "\tUser Name\n");
fprintf(stdout, "\tMandatory Access Control Label\n");
fprintf(stdout, "\tCapability Set\n");
fprintf(stdout, "\tPassword (if you have one)\n\n");
fprintf(stdout, "\"Enter\" by itself will get your default\n");
fprintf(stdout, "for MAC and Capabilities.\n");
fprintf(stdout, "\n");
}
for (trys = 0; ; do_maxtrys(defaultsfp, trys++, pwent)) {
if (cap_requested) {
free(cap_requested);
cap_requested = NULL;
}
if (mac_requested) {
free(mac_requested);
mac_requested = NULL;
}
if (username) {
free(username);
username = NULL;
}
/*
* Get the User name.
*/
fprintf(stdout, "User Name: ");
if (fgets(line, 511, stdin) == NULL)
exit(6);
terminate_line(line);
if (line[0] == '\0')
continue;
username = strdup(line);
pwent = getpwnam(username);
capent = sgi_getcapabilitybyname(username);
macent = sgi_getclearancebyname(username);
/*
* Get the MAC label.
*/
fprintf(stdout, "MAC label: ");
timeout_set(defaultsfp);
if (fgets(line, 511, stdin) == NULL)
exit(7);
timeout_clear();
terminate_line(line);
if (strlen(line) > 0)
mac_requested = strdup(line);
/*
* Get the capability set.
*/
fprintf(stdout, "Capabilities: ");
timeout_set(defaultsfp);
if (fgets(line, 511, stdin) == NULL)
exit(8);
timeout_clear();
terminate_line(line);
if (strlen(line) > 0)
cap_requested = strdup(line);
/*
* The sitecheck semantics allow for an exclusive check.
*/
c = do_sitecheck(defaultsfp, pwent ? pwent->pw_name : "____");
switch (c) {
case SITE_OK:
/*
* The sitecheck program says they're OKay.
*/
break;
case SITE_FAIL:
/*
* The sitecheck program says they're baaaad.
*/
report_error(defaultsfp, username, "Sitecheck denial");
continue;
case SITE_AGAIN:
/*
* The sitecheck program wants to retry.
*/
continue;
case SITE_CONTINUE:
default:
/*
* The sitecheck program doesn't care, or wants
* the password checked anyway.
*/
if (do_passwordcheck(defaultsfp, pwent) == 0) {
report_error(defaultsfp, username,
"Bad password");
continue;
}
break;
}
/*
* If any of the *ent pointers are NULL
* then the user's account is damaged, or non-existant.
* If that's the case, fake the authentication, but
* discard the result unexamined.
*/
if (pwent == NULL || capent == NULL || macent == NULL) {
report_error(defaultsfp, username,
"Account inconsistant");
continue;
}
if (cap_requested == NULL)
cap_requested = strdup(capent->ca_default);
if (!capable(capent, cap_requested)) {
sprintf(line, "Bad CAP=%s", cap_requested);
report_error(defaultsfp, username, line);
continue;
}
if (mac_requested == NULL)
mac_requested = strdup(macent->cl_default);
if (mac_cleared(macent, mac_requested) != MAC_CLEARED) {
sprintf(line, "Bad MAC=%s", mac_requested);
report_error(defaultsfp, username, line);
continue;
}
/*
* If logging in as root (uid 0)
* special processing may be required
*/
if (pwent->pw_uid == 0 && !do_console(defaultsfp)) {
report_error(defaultsfp, username,
"root not allowed on console");
continue;
}
/*
* At this point the user is validated, and the security
* attribute information has been verified. Save it.
*/
cmw_message(CMW_USER_NAME, username);
cmw_message(CMW_USER_MAC, mac_requested);
cmw_message(CMW_USER_CAP, cap_requested);
ocap = cap_acquire(1, &cap_audit_write);
ia_audit(program, username, 1, "CMW Login");
cap_surrender(ocap);
/*
* If logging in as root (uid 0) put up a special message.
*/
if (pwent->pw_uid == 0)
xconfirm_root();
/*
* Done. Exit.
*/
exit(0);
}
}