cdesktopenv/cde/programs/dtlogin/bls/validate.c

832 lines
20 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: validate.c /main/4 1995/10/27 16:19:47 rswiston $ */
/************************************<+>*************************************
****************************************************************************
**
** File: validate.c
**
** Project: HP Visual User Environment (DT)
**
** Description: Dtgreet BLS user authentication routines
**
** These routines validate the user; checking name, password,
** number of users on the system, password aging, etc.
**
**
** (c) Copyright 1987, 1988, 1989 by Hewlett-Packard Company
**
**
** Conditional compiles:
**
** OSMAJORVERSION < 8
** HP-UX 7.0/7.03 restricted license counting algorithms
** are used. Otherwise HP-UX 8.0 and beyond is used
**
** BLS HP BLS B1 simple authentication.
**
** __hpux HP-UX OS only
**
****************************************************************************
************************************<+>*************************************/
#ifdef BLS
/***************************************************************************
*
* Includes & Defines
*
***************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pwd.h>
#include "../vg.h"
/***************************************************************************
*
* HP-UX BLS authentication routines
*
***************************************************************************/
#include <sys/param.h> /* for MAXUID macro */
#include <sys/types.h>
#include <sys/utsname.h>
#include <string.h>
#include <utmp.h>
#include <time.h>
#include <grp.h>
/* BLS only headers */
# include <sys/security.h>
# include <prot.h>
# include "bls.h"
#define how_to_count ut_exit.e_exit
#ifdef __hp9000s300
static int num_users[] = { 2, 32767 };
# define MIN_VERSION 'A'
# define UNLIMITED 'B'
#else
static int num_users[] = { 2, 16, 32, 64 , 8 };
# define MIN_VERSION 'A'
# define UNLIMITED 'U'
#endif
/* Maximum number of users allowed with restricted license */
#if OSMAJORVERSION < 8
# define MAX_STRICT_USERS 2
#else
# define MAX_STRICT_USERS 8
#endif
#define NUM_VERSIONS (sizeof(num_users)/sizeof(num_users[0])) - 1
/***************************************************************************
*
* External declarations
*
***************************************************************************/
extern Widget focusWidget; /* login or password text field */
extern long groups[NGROUPS];
/***************************************************************************
*
* Procedure declarations
*
***************************************************************************/
static int CheckPassword( char *name, char *passwd );
static int CountUsers( int added_users) ;
static int CountUsersStrict( char *new_user) ;
static void WriteBtmp( char *name) ;
/***************************************************************************
*
* Global variables
*
***************************************************************************/
/* BLS only data */
struct pr_passwd *b1_pwd;
struct verify_info verify_data;
struct verify_info *verify = &verify_data;
struct greet_info greet_data;
struct greet_info *greet = &greet_data;
static int UserHasPassword = 1;
/***************************************************************************
*
* CountUsers
*
* see if new user has exceeded the maximum.
***************************************************************************/
#define NCOUNT 16
static int
CountUsers( int added_users )
{
int count[NCOUNT], nusers, i;
struct utmp *entry;
for (i=0; i<NCOUNT; i++)
count[i] = 0;
count[added_users]++;
while ( (entry = getutent()) != NULL) {
if (entry->ut_type == USER_PROCESS) {
i = entry->how_to_count;
if (i < 0 || i >= NCOUNT)
i = 1; /* if out of range, then count */
/* as ordinary user */
count[i]++;
}
}
endutent();
/*
* KEY:
* [0] does not count at all
* [1] counts as real user
* [2] logins via a pty which have not gone trough login. These
* collectively count as 1 user IF count[3] is 0, otherwise,
* they are not counted. Starting with HP-UX 8.0 they are
* no longer counted at all.
* [3] logins via a pty which have been logged through login (i.e.
* rlogin and telnet). these count as 1 "real" user per
* unique user name.
* [4-15] may be used for groups of users which collectively
* count as 1
*/
nusers = count[1];
#if OSMAJORVERSION < 8
for (i=2; i<NCOUNT; i++)
#else
for (i=3; i<NCOUNT; i++)
#endif
if (count[i] > 0)
nusers++;
return(nusers);
}
/***************************************************************************
*
* CountUsersStrict
*
* see if new user has exceeded the maximum.
***************************************************************************/
static int
CountUsersStrict( char *new_user )
{
char pty_users[MAX_STRICT_USERS][8];
int count[NCOUNT], nusers, i, cnt, pty_off = -1, uname_off;
struct utmp *entry;
/*
* Initialize count array...
*/
for (i = 0; i < NCOUNT; i++)
count[i] = 0;
/*
* Add in the new user (we know it's not a pty)...
*/
count[1]++;
while ( (entry = getutent()) != NULL ) {
if (entry->ut_type == USER_PROCESS) {
i = entry->how_to_count;
/* if out of range, then count as ordinary user logged in
via a tty */
if (i == 1 || (i < 0 || i >= NCOUNT))
count[1]++;
/* See if it is a pty login granted by login program */
else if (i == 3) {
count[3]++;
/* See if user is already logged in via login pty */
uname_off = -1;
for (cnt = 0; cnt <= pty_off; cnt++)
if (strncmp(pty_users[cnt], entry->ut_user, 8) == 0)
uname_off = cnt;
if (uname_off == -1) { /* user is not logged in via pty yet */
if (pty_off >= MAX_STRICT_USERS) /* cannot add any
more users */
return(MAX_STRICT_USERS + 1);
/* add the user name to the array of pty users */
else
strncpy(pty_users[++pty_off], entry->ut_user, 8);
}
} /* end if (i == 3) */
else
count[i]++;
} /* end if entry->ut_type == USER_PROCESS */
} /* end while (entry = getutent()) */
endutent();
/*
* KEY:
* [0] does not count at all
* [1] counts as "real" user
* [2] logins via a pty which have not gone trough login. These
* collectively count as 1 user IF count[3] is 0, otherwise,
* they are not counted. Starting with HP-UX 8.0 they are
* no longer counted at all.
* [3] logins via a pty which have been logged through login (i.e.
* rlogin and telnet). these count as 1 "real" user per
* unique user name.
* [4-15] may be used for groups of users which collectively count
* as 1
*/
nusers = pty_off + 1 + count[1]; /* Current number of users is sum of
users logged in via tty + the
number of unique users logged in
via pty which have gone through
login */
#if OSMAJORVERSION < 8
if ((count[3] == 0) && (count[2] != 0))
nusers++; /* Add 1 user for all pty logins IF
none of pty logins have been
granted by the login program */
#else
/*
* Don't count any hpterm logins (exit status of 2). We already
* counted all pty logins granted by the login program.
*/
#endif
for (i = 4; i < NCOUNT; i++)
if (count[i] > 0)
nusers++;
return(nusers);
}
/***************************************************************************
*
* CheckPassword
*
* Check validity of user password.
*
***************************************************************************/
static int
CheckPassword( char *name, char *passwd )
{
char *crypt();
struct passwd *p;
char *reason;
/*
* HP BLS B1 password authentication...
*/
if ( ISSECURE ) {
b1_pwd = getprpwnam(name);
if ( b1_pwd == NULL || strlen(name) == 0 ) {
Debug("unknown user '%s'\n", name);
audit_login((struct pr_passwd *)0, (struct passwd *)0,
dpyinfo.name, "No entry in protected password db",
ES_LOGIN_FAILED);
return(FALSE);
}
/*
* look up user's regular account information...
*/
p = getpwnam(name);
if ( p == NULL || strlen(name) == 0 ) {
Debug("unknown user '%s'\n", name);
audit_login((struct pr_passwd *)0, (struct passwd *)0,
dpyinfo.name, "No entry in password file",
ES_LOGIN_FAILED);
return(FALSE);
}
/* verify_info has become a catchall for info needed later */
verify->user_name = name;
verify->prpwd = b1_pwd;
verify->pwd = p;
strncpy(verify->terminal, dpyinfo.name, 15);
verify->terminal[15]='\0';
}
Debug("Verify %s \n",name);
/* if the password doesn't exists, we can't check it, but
* the user will be forced to change it later */
if ( (UserHasPassword = password_exists(verify)) != 0 )
if ( strcmp(bigcrypt(passwd,b1_pwd->ufld.fd_encrypt),
b1_pwd->ufld.fd_encrypt) ) {
Debug("verify failed\n");
audit_login( b1_pwd, p ,dpyinfo.name,
"Password incorrect",
ES_LOGIN_FAILED);
return(FALSE);
} else {
Debug ("username/password verify succeeded\n");
return(TRUE);
}
/*
* all password checks failed...
*/
return (FALSE);
}
/***************************************************************************
*
* BLS_Verify
*
* verify the user
*
* return codes indicate authentication results.
***************************************************************************/
#define MAXATTEMPTS 3
static struct passwd nouser = {"", "nope"}; /* invalid user password struct */
int
BLS_Verify( char *name, char *passwd )
{
static int login_attempts = 0; /* # failed authentications */
struct passwd *p; /* password structure */
struct pr_passwd *prpwd;
struct utsname utsnam;
int n;
int uid;
/*
* Desparate maneuvre to give dtgreet the privledges it needs
*/
if ( login_attempts == 0 ) {
Debug("Setting luid for dtgreet\n");
if ( getluid() == -1 )
setluid(getuid());
}
/*
* validate password...
*/
if ( CheckPassword(name, passwd) == FALSE) {
p = verify->pwd;
if ( focusWidget == passwd_text ) {
WriteBtmp(name);
if ((++login_attempts % MAXATTEMPTS) == 0 ) {
if (p->pw_name == NULL )
p = &nouser;
audit_login( b1_pwd, p ,dpyinfo.name,
"Failed login(bailout)",
ES_LOGIN_FAILED);
}
} else if ( !UserHasPassword ) {
/*
* The user has not password -- this must be the initial login for this
* user. Treat it like an expired password. This should invoke the
* password program on behalf of the user.
*/
UserHasPassword = 1;
return VF_PASSWD_AGED;
}
return(VF_INVALID);
}
prpwd = verify->prpwd;
p = verify->pwd;
/* check that the uid of both passwd and pr_passwd struct's agree */
uid = p->pw_uid;
if (uid != prpwd->ufld.fd_uid) {
audit_login(prpwd, p, verify->terminal,
"User id's inconsistent across password database\n",
ES_LOGIN_FAILED);
Debug("login failed - uid's do not match\n");
return VF_BAD_UID;
}
verify->uid = uid;
/* check if user's account is locked
* This can be by dead password (lifetime exceeded),
* fd_lock is set, or fd_max_tries is exceeded.
* locked_out is from libsec, but is poorly documented.
*/
if (locked_out(prpwd)) {
Debug("Account locked\n");
audit_login(prpwd, p, verify->terminal,
"Account locked", ES_LOGIN_FAILED);
return VF_INVALID;
}
/* can user log in at this time?
* time_lock is in libsec, but poorly documented
*/
if (time_lock(prpwd)) {
Debug("Account time-locked\n");
audit_login(prpwd, p, verify->terminal,
"Account time-locked", ES_LOGIN_FAILED);
return VF_INVALID;
}
/****************************************************
xdm checks the security level here using
verify_sec_user
We do it later from the dtgreet callback rountine
VerifySensitivityLevel()
****************************************************/
#if 0
/*
* check restricted license...
*
* Note: This only applies to local displays. Foreign displays
* (i.e. X-terminals) apparently do not count.
*/
/* Get the version info via uname. If it doesn't look right,
* assume the smallest user configuration
*/
if (getenv(LOCATION) != NULL) {
if (uname(&utsnam) < 0)
utsnam.version[0] = MIN_VERSION;
/*
* Mappings:
* 834 -> 834
* 844 -> 844
* 836 -> 635
* 846 -> 645
* 843 -> 642
* 853 -> 652
*/
if ((!strncmp(utsnam.machine, "9000/834", UTSLEN)) ||
(!strncmp(utsnam.machine, "9000/844", UTSLEN)) ||
(!strncmp(utsnam.machine, "9000/836", UTSLEN)) ||
(!strncmp(utsnam.machine, "9000/846", UTSLEN)) ||
(!strncmp(utsnam.machine, "9000/843", UTSLEN)) ||
(!strncmp(utsnam.machine, "9000/853", UTSLEN))) {
/* strict_count = 1;*/
if (CountUsersStrict(name) > MAX_STRICT_USERS) {
audit_login( b1_pwd, p ,dpyinfo.name,
"Attempted to login - too many users on the system",
ES_LOGIN_FAILED);
return(VF_MAX_USERS);
}
}
else {
if (utsnam.version[0] != UNLIMITED) {
if ((utsnam.version[0]-'A' < 0) ||
(utsnam.version[0]-'A' > NUM_VERSIONS))
utsnam.version[0] = MIN_VERSION;
n = (int) utsnam.version[0] - 'A';
if (CountUsers(1) > num_users[n]) {
audit_login( b1_pwd, p ,dpyinfo.name,
"Attempted to login - too many users on the system",
ES_LOGIN_FAILED);
return(VF_MAX_USERS);
}
}
}
}
#endif /* 0 */
/*
* check password aging...
*/
if ( passwordExpired(verify)) {
audit_login( b1_pwd, p ,dpyinfo.name,
"Password expired",
ES_LOGIN_FAILED);
return(VF_PASSWD_AGED);
}
/*
* verify home directory exists...
*/
if(chdir(p->pw_dir) < 0) {
Debug("Attempted to login -- no home directory\n");
audit_login( b1_pwd, p ,dpyinfo.name,
" Attempted to login - no home directory",
ES_LOGIN_FAILED);
return(VF_HOME);
}
/*
* validate uid and gid...
*/
#ifdef NGROUPS
getGroups(greet->name, verify, p->pw_gid);
#else
verify->gid = pwd->pw_gid;
if ((p->pw_gid < 0) ||
(p->pw_gid > MAXUID) ||
(setgid(p->pw_gid) == -1)) {
Debug("Attempted to login -- bad group id");
audit_login( b1_pwd, p ,dpyinfo.name,
"Attempted to login - bad group id",
ES_LOGIN_FAILED);
return(VF_BAD_GID);
}
#endif /* NGROUPS */
if ((p->pw_uid < 0) ||
(p->pw_uid > MAXUID) ||
(setresuid(p->pw_uid, p->pw_uid, 0) == -1)) {
Debug("Attempted to login -- bad user id\n");
audit_login( b1_pwd, p ,dpyinfo.name,
"Attempted to login - bad user id",
ES_LOGIN_FAILED);
return(VF_BAD_UID);
}
/*
* verify ok...
*/
Debug ("Successful login\n");
audit_login( b1_pwd, p ,dpyinfo.name,
"Successful login",
ES_LOGIN_REMOTE);
return(VF_OK);
}
/***************************************************************************
*
* WriteBtmp
*
* log bad login attempts
*
***************************************************************************/
static void
WriteBtmp( char *name )
{
int fd;
struct utmp utmp, *u;
Boolean found=FALSE;
bzero(&utmp, sizeof(struct utmp));
utmp.ut_pid = getppid();
while ((u = getutent()) != NULL) {
if ( (u->ut_type == INIT_PROCESS ||
u->ut_type == LOGIN_PROCESS ||
u->ut_type == USER_PROCESS) &&
u->ut_pid == utmp.ut_pid ) {
found = TRUE;
break;
}
}
/*
* if no utmp entry, this may be an X-terminal. Construct a utmp
* entry for it...
*/
if ( ! found ) {
strncpy(utmp.ut_id, "??", sizeof(utmp.ut_id));
strncpy(utmp.ut_line, dpyinfo.name, sizeof(utmp.ut_line));
utmp.ut_type = LOGIN_PROCESS;
strncpy(utmp.ut_host, dpyinfo.name, sizeof(utmp.ut_host));
u = &utmp;
}
/*
* If btmp exists, then record the bad attempt
*/
if ( (fd = open(BTMP_FILE,O_WRONLY|O_APPEND)) >= 0) {
strncpy(u->ut_user, name, sizeof(u->ut_user));
(void) time(&u->ut_time);
write(fd, (char *)u, sizeof(utmp));
(void) close(fd);
}
endutent(); /* Close utmp file */
}
/***************************************************************************
*
* VerifySensitivityLevel
*
* verify B1 Sensitivity Level
**************************************************************************/
extern char *sensitivityLevel;
int
VerifySensitivityLevel( void)
{
int i;
greet->b1security = sensitivityLevel =
(char *) XmTextFieldGetString(passwd_text);
/* new functions: (side effects: auditing, change verify) */
if (verify_user_seclevel(verify, sensitivityLevel)
&& verify_sec_xterm(verify, sensitivityLevel)) {
Debug("verify_user_seclevel succeeded.\n");
return VF_OK;
}
Debug("verify_user_seclevel failed\n");
return (VF_BAD_SEN_LEVEL);
}
#ifdef NGROUPS
groupMember ( char *name, char **members )
{
while (*members) {
if (!strcmp (name, *members))
return 1;
++members;
}
return 0;
}
getGroups ( char *name, struct verify_info *verify, int gid)
{
int ngroups;
struct group *g;
int i;
ngroups = 0;
verify->groups[ngroups++] = gid;
setgrent ();
while (g = getgrent()) {
/*
* make the list unique
*/
for (i = 0; i < ngroups; i++)
if (verify->groups[i] == g->gr_gid)
break;
if (i != ngroups)
continue;
if (groupMember (name, g->gr_mem)) {
if (ngroups >= NGROUPS)
LogError ("%s belongs to more than %d groups, %s ignored\n",
name, NGROUPS, g->gr_name);
else
verify->groups[ngroups++] = g->gr_gid;
}
}
verify->ngroups = ngroups;
endgrent ();
}
#endif
/* check whether the password has expired or not.
* return 1 means that the password has expired.
*/
int
passwordExpired( struct verify_info *verify)
{
struct pr_passwd *pr;
time_t expiration;
time_t last_change;
time_t expiration_time;
time_t now;
int passwd_status;
struct pr_passwd save_data;
struct pr_default *df;
char *ttime;
char ptime[64];
pr = verify->prpwd;
/*
* If null password, do not check expiration.
*/
if (!pr->uflg.fg_encrypt || (pr->ufld.fd_encrypt[0] == '\0'))
return 0;
now = time((long *) 0);
if (pr->uflg.fg_schange)
last_change = pr->ufld.fd_schange;
else
last_change = (time_t) 0;
if (pr->uflg.fg_expire)
expiration = pr->ufld.fd_expire;
else if (pr->sflg.fg_expire)
expiration = pr->sfld.fd_expire;
else
expiration = (time_t) 0;
df = getprdfnam(AUTH_DEFAULT);
/*
* A 0 or missing expiration field means there is no
* expiration.
*/
expiration_time = expiration ? last_change + expiration : 0;
if (expiration_time && now > expiration_time ) {
/*
* The password has expired
*/
Debug("The password is expired\n");
return 1;
}
Debug("The password is not expired\n");
return 0;
}
/***************************************************************************
*
* end HP-UX authentication routines
*
***************************************************************************/
#endif /* BLS */