Files
cdesktop/cde/programs/dtlogin/bls/get_level.c
2018-04-28 12:36:44 -06:00

667 lines
17 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: get_level.c /main/4 1995/10/27 16:19:39 rswiston $ */
/*
* get_level.c
* last modified by:
* David Dolson June 7/92
* - rewrote most of B1 security routines. Much of it is based on
* parallel routines in login.
* Ron Voll July 7/92
* - rolled the xdm version of this file into dtlogin.
*/
#ifdef BLS /* Brackets entire file */
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <pwd.h>
#include <grp.h>
#include <stdio.h>
#include <termio.h>
#include <errno.h>
#ifdef SEC_NET_TTY
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
#include <sys/security.h>
#include <sys/audit.h>
#include <prot.h>
#include <protcmd.h>
#if defined(TAC3) && !defined(TPLOGIN)
#include <sys/secpolicy.h>
#include <mandatory.h>
#include <fcntl.h>
#endif
#include <stdlib.h>
#include <time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#if 0
#include <strings.h>
#endif
#include <sys/wait.h>
#include <grp.h>
/*
* Local include file for bls specific definitions.
* Also defines some of the structures from dm.h for bls usage.
*/
#include "bls.h"
/* drop those privs from the base set which are not needed by xdm */
void
drop_privs(void)
{
priv_t privs[SEC_SPRIVVEC_SIZE];
getpriv(SEC_BASE_PRIV, privs);
RMBIT(privs, SEC_ALLOWNETACCESS);
RMBIT(privs, SEC_NETPRIVSESSION);
RMBIT(privs, SEC_NETNOAUTH);
RMBIT(privs, SEC_MULTILEVELDIR);
setpriv(SEC_BASE_PRIV, privs);
return;
}
/* stuff to do at the start */
void
init_security(void)
{
/* set default file creation mode to be restrictive */
umask(~SEC_DEFAULT_MODE);
drop_privs();
}
/* check that the requested security level is valid for this user,
* return 1 = success, return 0 is fail(fatal)
*/
int
verify_user_seclevel( struct verify_info verify, char *desired_label)
{
int uid;
mand_ir_t *dl_ir, *clearance_ir;
struct pr_passwd *prpwd;
struct passwd *pwd;
prpwd = verify->prpwd;
pwd = verify->pwd;
uid = verify->uid;
/* check that desired_label falls within user's range */
dl_ir = mand_er_to_ir(desired_label);
if (dl_ir ==NULL) {
audit_login(prpwd, pwd, verify->terminal,
"Unknown clearance level", ES_LOGIN_FAILED);
Debug("unable to translate clearance\n");
return 0;
}
/* get user clearance from prpwd database */
if (prpwd->uflg.fg_clearance)
clearance_ir = &prpwd->ufld.fd_clearance;
else if (prpwd->sflg.fg_clearance)
clearance_ir = &prpwd->sfld.fd_clearance;
else
clearance_ir = mand_syslo;
/* make sure clearance dominates or equals desired_label */
switch(mand_ir_relationship(/* subject */ dl_ir,
/* object */ clearance_ir)) {
case MAND_ODOM:
case MAND_EQUAL:
/* Within range */
break;
default:
audit_login(prpwd, pwd, verify->terminal,
"Security label out of range", ES_LOGIN_FAILED);
Debug("Invalid clearance for this user\n");
mand_free_ir(dl_ir);
return 0;
}
verify->clearance_ir = clearance_ir;
verify->sec_label_ir = dl_ir;
return 1;
}
/* check the proper structures to determine if the user has a password.
* If the nullpw field is set, the user does not need one, and this
* overrides the rest of the checking.
* return 1 means that a password exists (or is not needed)
*/
int
password_exists(struct verify_info *verify)
{
struct pr_passwd *prpwd;
BOOL nocheck;
Debug("password_exists()\n");
prpwd = verify->prpwd;
if (prpwd->uflg.fg_nullpw)
nocheck=prpwd->ufld.fd_nullpw;
else if (prpwd->sflg.fg_nullpw)
nocheck=prpwd->sfld.fd_nullpw;
else
nocheck=FALSE;
if (!nocheck) { /* user needs password */
Debug("password required for user\n");
if (!prpwd->uflg.fg_encrypt ||
prpwd->ufld.fd_encrypt[0] == '\0' ) {
return 0;
}
}
return 1;
}
/* check that the requested security level can be used on this X terminal,
* and that it is not locked.
* Currently there is no support for locking xterms like there is for
* /dev/tty* terminals.
*/
int
verify_sec_xterm(struct verify_info *verify, char *desired_label)
{
return 1;
}
/* set clearance and label for the user. Audit all failures.
* return 0=fail, 1=pass
*/
int
set_sec_label(struct verify_info *verify)
{
struct pr_passwd *prpwd;
struct passwd *pwd;
/* set clearance */
prpwd = verify->prpwd;
pwd = verify->pwd;
if (setclrnce(verify->sec_label_ir)==-1) {
switch(errno) {
case EPERM:
audit_login(prpwd, pwd, verify->terminal,
"Insufficient privs to set clearance", ES_LOGIN_FAILED);
Debug ("login failed: EPERM on setclrnce()\n");
break;
case EFAULT:
/* audit:login failed: xdm memory fault */
default:
audit_login(prpwd, pwd, verify->terminal,
"Unable to set clearance", ES_LOGIN_FAILED);
Debug ("setclrnce failed: error: %d\n", errno);
break;
}
return 0;
}
/* set label */
if (setslabel(verify->sec_label_ir)==-1) {
switch(errno) {
case EPERM:
audit_login(prpwd, pwd, verify->terminal,
"Insufficient privs to set sec label", ES_LOGIN_FAILED);
Debug ("login failed: insufficient priv. to setslabel()\n");
break;
case EFAULT:
/* audit:login failed: xdm memory fault */
default:
audit_login(prpwd, pwd, verify->terminal,
"Unable to set sec label", ES_LOGIN_FAILED);
Debug ("setslabel() failed: error: %d\n", errno);
break;
}
return 0;
}
return 1;
}
/* set the effective, base, and maximum priv vectors for the
* new process, based on values from the pr_passwd entry.
* Inability to find either user priv's or default priv's
* results in failure. One or the other must be there.
* Function returns 1 for success, 0 for failure.
* A failure of this function should be considered fatal.
*/
int
set_sec_privs(struct verify_info *verify)
{
priv_t *maxprivs, *bprivs;
priv_t curr_bprivs[SEC_SPRIVVEC_SIZE];
priv_t curr_sprivs[SEC_SPRIVVEC_SIZE];
struct pr_passwd *prpwd;
struct passwd *pwd;
int bit;
prpwd = verify->prpwd;
pwd = verify->pwd;
/* kernel authorizations */
if (prpwd->uflg.fg_sprivs) {
maxprivs = &prpwd->ufld.fd_sprivs[0];
}else if(prpwd->sflg.fg_sprivs) {
maxprivs = &prpwd->sfld.fd_sprivs[0];
Debug("Using default kernel priv's\n");
}else {
audit_login(prpwd, pwd, verify->terminal,
"Unable to find kernel priv set for user",
ES_LOGIN_FAILED);
Debug("Can't find max. priv set for user-quitting\n");
return 0;
}
/* base priv's and initial effective priv's */
if (verify->prpwd->uflg.fg_bprivs) {
bprivs = &verify->prpwd->ufld.fd_bprivs[0];
}else if (verify->prpwd->sflg.fg_bprivs) { /* use system defaults */
bprivs = &verify->prpwd->sfld.fd_bprivs[0];
Debug("Using default base priv's\n");
}else{
audit_login(prpwd, pwd, verify->terminal,
"Unable to find base priv set for user",
ES_LOGIN_FAILED);
Debug("Can't find base priv set for user-quitting\n");
return 0;
}
getpriv(SEC_MAXIMUM_PRIV, curr_sprivs);
getpriv(SEC_BASE_PRIV, curr_bprivs);
/* remove those privs which the current process does not have,
* to avoid any error in the following system calls
*/
for (bit=0; bit<=SEC_MAX_SPRIV; bit++) {
if (!ISBITSET(curr_sprivs, bit))
RMBIT(maxprivs, bit);
if (!ISBITSET(curr_bprivs, bit))
RMBIT(bprivs, bit);
}
/* login removes those bits from maxprivs which the current process
* does not have. - This program assumes the system config
* utilities will enforce the rules for setpriv(3). Any failure
* of setpriv will indicate a corrupt database.
*/
if (setpriv(SEC_MAXIMUM_PRIV, maxprivs)==-1) {
switch(errno) {
case EPERM:
Debug("setpriv (max) failed: EPERM\n");
break;
case EINVAL:
Debug("setpriv (max) failed: EINVAL\n");
break;
case EFAULT:
Debug("setpriv (max) failed: EFAULT\n");
break;
default:
Debug("setpriv (max) failed for unknown error: %d\n",errno);
break;
}
audit_login(prpwd, pwd, verify->terminal,
"Unable to set Kernel privs", ES_LOGIN_FAILED);
Debug("Unable to set Kernel privs (error %d): aborting\n",errno);
return 0;
}
if (setpriv(SEC_BASE_PRIV, bprivs)==-1) {
switch(errno) {
case EPERM:
Debug("setpriv (base) failed: EPERM\n");
break;
case EINVAL:
Debug("setpriv (base) failed: EINVAL\n");
break;
case EFAULT:
Debug("setpriv (base) failed: EFAULT\n");
break;
default:
Debug("setpriv (base) failed for unknown error: %d\n",errno);
break;
}
audit_login(prpwd, pwd, verify->terminal,
"Unable to set base privs", ES_LOGIN_FAILED);
return 0;
}
if (setpriv(SEC_EFFECTIVE_PRIV, bprivs)==-1) {
switch(errno) {
case EPERM:
Debug("setpriv (effective) failed: EPERM\n");
break;
case EINVAL:
Debug("setpriv (effective) failed: EINVAL\n");
break;
case EFAULT:
Debug("setpriv (effective) failed: EFAULT\n");
break;
default:
Debug("setpriv (effective) failed for unknown error: %d\n",
errno);
break;
}
audit_login(prpwd, pwd, verify->terminal,
"Unable to set effective privs", ES_LOGIN_FAILED);
Debug("Unable to set effective privs (error %d): aborting\n",errno);
return 0;
}
return 1;
}
/* change the current process over to be owned by the user verify->uid.
* Also properly set the privs, sec label, etc.
* Also audits failures.
* return=1 for success, 0 for fail. A failure should be considered fatal.
*/
int
change_to_user(struct verify_info *verify)
{
struct pr_passwd *prpwd;
struct passwd *pwd;
int new_nice;
prpwd = verify->prpwd;
pwd = verify->pwd;
Debug("change_to_user()\n");
/* 1. set the login user id - settable only once */
if (setluid(verify->uid)==-1) {
switch(errno) {
case EPERM:
Debug("Unable to set luid - EPERM\n");
audit_login(prpwd, pwd, verify->terminal,
"Unable to set luid - insufficient privs",
ES_LOGIN_FAILED);
break;
case EINVAL:
Debug("Unable to set luid - suspicious of pwd db.\n");
audit_login(prpwd, pwd, verify->terminal,
"Unable to set luid - out of range", ES_LOGIN_FAILED);
break;
default:
Debug("Can't set luid-Unknown error %d\n",errno);
audit_login(prpwd, pwd, verify->terminal,
"Unable to set luid-unknown error", ES_LOGIN_FAILED);
break;
}
return 0;
}
/*
* Set the 'nice' priority if necessary. Since the return value
* of nice(2) can normally be -1 from the documentation, and
* -1 is the error condition, we key off of errno, not the
* return value to find if the change were successful.
* Note we must do this before the setuid(2) below.
*/
errno = 0;
prpwd = verify->prpwd;
if (prpwd->uflg.fg_nice)
new_nice = prpwd->ufld.fd_nice;
else if (prpwd->sflg.fg_nice)
new_nice = prpwd->sfld.fd_nice;
if (prpwd->uflg.fg_nice || prpwd->sflg.fg_nice) {
(void) nice(new_nice);
if (errno != 0) {
audit_login(prpwd, verify->pwd, NULL,
"bad 'nice' setting", ES_LOGIN_FAILED);
Debug("Bad priority setting.\n");
return 0;
}
}
/* 2. set the group(s) id and
* 3. set the regular user id */
#ifdef NGROUPS
/* setgroups (verify->ngroups, verify->groups);
*/
if(setgid (verify->groups[0])) {
switch(errno) {
case EPERM:
Debug("setgid EPERM\n");
break;
case EINVAL:
Debug("setgid EINVAL\n");
break;
default:
Debug("setgid unknown error: %d\n",errno);
break;
}
return 0;
}
initgroups(verify->user_name, verify->groups[0]);
#else
if(setgid (verify->gid)) {
switch(errno) {
case EPERM: Debug("setgid EPERM\n");break;
case EINVAL: Debug("setgid EINVAL\n");break;
default: Debug("setgid unknown error\n");break;
}
return 0;
}
#endif
if(setuid (verify->uid)) {
switch(errno) {
case EPERM: Debug("setgid EPERM\n");break;
case EINVAL: Debug("setgid EINVAL\n");break;
default: Debug("setgid unknown error\n");break;
}
return 0;
}
/* 4. set security clearance and label for the new process */
if (!set_sec_label(verify))
return 0;
/* 5. set audit parameters */
audit_adjust_mask(prpwd);
/* 6. set privlege levels - maximum, base, and effective */
if (!set_sec_privs(verify))
return 0;
return 1;
}
/*
* Try to read back everything, and print it. If a fatal error occurs,
* return code is 0. 1=success.
*/
int
dump_sec_debug_info(struct verify_info *verify)
{
mand_ir_t *level_ir;
priv_t privs[SEC_SPRIVVEC_SIZE];
Debug ("luid: %d, real uid: %d, effective uid:%d,\n",
getluid(),getuid(),geteuid());
Debug ("real gid:%d, effective gid: %d\n", getgid(),getegid());
level_ir = mand_alloc_ir();
if (getclrnce(level_ir)==-1) {
switch(errno) {
case EFAULT: Debug("getclrnce EFAULT\n");break;
case EINVAL: Debug("getclrnce EINVAL\n");break;
default: Debug("getclrnce unknown error:%d\n",errno);break;
}
return 0;
}else Debug ("Clearance: %s\n", mand_ir_to_er(level_ir) );
if (getslabel(level_ir)==-1) {
switch(errno) {
case EFAULT: Debug("getslabel EFAULT\n");break;
case EINVAL: Debug("getslabel EINVAL\n");break;
default: Debug("getslabel unknown error:%d\n",errno);break;
}
return 0;
}else Debug ("Level: %s\n", mand_ir_to_er(level_ir));
mand_free_ir(level_ir);
if(getpriv(SEC_MAXIMUM_PRIV, privs)==-1) {
switch(errno) {
case EFAULT: Debug("getpriv max EFAULT\n");break;
case EINVAL: Debug("getpriv max EINVAL\n");break;
default: Debug("getpriv max unknown error:%d\n",errno);
break;
}
return 0;
}else Debug ("max priv: %x.%x\n", privs[0],privs[1]);
if(getpriv(SEC_EFFECTIVE_PRIV, privs)==-1) {
switch(errno) {
case EFAULT: Debug("getpriv eff EFAULT\n");break;
case EINVAL: Debug("getpriv eff EINVAL\n");break;
default: Debug("getpriv eff unknown error:%d\n",errno);
break;
}
return 0;
}else Debug ("eff priv: %x.%x\n", privs[0],privs[1]);
if(getpriv(SEC_BASE_PRIV, privs)==-1) {
switch(errno) {
case EFAULT: Debug("getpriv base EFAULT\n");break;
case EINVAL: Debug("getpriv base EINVAL\n");break;
default: Debug("getpriv base unknown error:%d\n",errno);
break;
}
return 0;
}else Debug ("base priv: %x.%x\n", privs[0],privs[1]);
return 1;
}
/*
* writeLoginInfo
* Input: file name string (ex. $HOME/.dtlogininfo)
* verify structure with password stuff
* Write login information to a file to be displayed later, after a
* successful login.
*
* Xsession will need to be modified something like this...
* DTHELLO="$DTDIR/bin/dthello -f /etc/copyright -f $HOME/.dtlogininfo"
*/
int
writeLoginInfo( char *filename, struct verify_info *verify)
{
char *s1="Last successful login: %s";
char *s2="Last unsuccessful login: %s";
char *s3;
char s[80];
char term[15];
char *label;
char *message="Sensitivity level for process: ";
int i;
int nl;
time_t slogin, ulogin;
char *slabel;
char *uterminal, *sterminal;
FILE *fp;
Debug("Writing login info\n");
if ((fp = fopen (filename, "w")) == 0 )
return 0;
if (verify->prpwd->uflg.fg_slogin)
slogin=verify->prpwd->ufld.fd_slogin;
else
slogin=(time_t)0;
if (verify->prpwd->uflg.fg_ulogin)
ulogin=verify->prpwd->ufld.fd_ulogin;
else
ulogin=(time_t)0;
if (verify->prpwd->uflg.fg_suctty)
sterminal=verify->prpwd->ufld.fd_suctty;
else
sterminal="UNKNOWN";
if (verify->prpwd->uflg.fg_unsuctty)
uterminal=verify->prpwd->ufld.fd_unsuctty;
else
uterminal="UNKNOWN";
slabel = mand_ir_to_er(verify->sec_label_ir);
fprintf(fp,"-----------------------------------\n");
fprintf(fp,"\nPrevious login information:\n\n");
/* tricky formatting */
if (slogin != 0) {
sprintf(s, s1, ctime(&slogin));
nl=strlen(s)-1;
s[nl]='\0'; /* remove new-line */
}else{
sprintf(s,s1,"NEVER");
}
strcat(s, " from ");
strncpy(term, sterminal, 14);
term[14]='\0';
strcat(s, term);
fprintf(fp,"%s\n",s);
if (ulogin != 0) {
sprintf(s, s2, ctime(&ulogin));
nl=strlen(s)-1;
s[nl]='\0'; /* remove new-line */
}else{
sprintf(s,s2,"NEVER");
}
strcat(s, " from ");
strncpy(term, uterminal, 14);
term[14]='\0';
strcat(s, term);
fprintf(fp,"%s\n",s);
label = (char*)malloc(strlen(message)+strlen(slabel)+1);
sprintf(label, "%s%s", message, slabel);
if (strlen (label) > 77) {
for(i=75; label[i]!=',' && i>0; i--);
if (i==0) for(i=75; label[i]!=' ' && i>0; i--);
if (i==0) i=75;
strncpy(s, label, i+1);
s[i+1]='\0';
fprintf(fp,"%s\n",s);
strncpy(s, &label[i+1], 75);
s[75]='\0';
fprintf(fp,"%s\n",s);
}else{
fprintf(fp,"%s\n",label);
}
fclose(fp);
return 1;
}
#endif /* BLS */