| We've been working on a similar problem here at the N.Y. Mercantile Exchange.
Our prblem is to pre-validate a username/password that will be sent from an
NT workstation to a VMS host as part of a transation packet.
Using $GETUIA, it is possible to retrieve and check this data, although
applying all the checks used in loginout.exe is more than a bit tedious.
Here is a sample function to show the general idea:
---------------------------------------------------------------------------
/*
**++
** FACILITY: c21_password.c
**
** MODULE DESCRIPTION:
**
** Given a username and password, the module:
** validates the username (by retrieving user data from SYSUAF)
** checks the password
** calculates the number of days until expiration
** retrieves and translates the users rights identifiers
** optionally, sets a new password.
**
** AUTHORS:
**
** David A. Bowers
** Digital Equipment Corp.
**
** CREATION DATE: 7-Mar-1996
**
** DESIGN ISSUES:
**
** SYSPRV is required for execution.
**
** Since we are using $GETUIA, all checking of SYSUAF values must be done
** explcitly within the module. We will therefore need to get operations
** to agree to use only those features that we're checking, so that any
** username/password combination that gets past us will also get past
** loginout.
** This version checks username, password and password expiration only.
**
** FORMAL PARAMETERS:
**
** usr:
** the username to be validated.
**
** pass:
** the password to be validated.
**
** npass:
** user's new password | 0 if not entered.
**
** rightsw:
** if == 0, rights lookup is bypassed.
**
** RETURN VALUES:
** password validation block containing:
** username (for sanity check)
** login_status = 1: success
** 0: invalid username/password
** -1: system error
** password_change status = 1: pasword changed
** 0: no change requested
** -1: system error
** days_to_go: number of days until password expiration
** rights_count: number of rights identifiers returned
** right[]: up to 16 rights identifier names
**
**--
*/
/***********************************************************************
** INCLUDE FILES
***********************************************************************/
#include "c21_password.h"
#include <ctype.h>
#include <descrip.h>
#include <lib$routines.h>
#include <libdtdef.h>
#include <opcdef.h>
#include <opcmsg.h>
#include <ssdef.h>
#include <starlet.h>
#include <stdlib.h>
#include <string.h>
#include <uaidef.h>
/***********************************************************************
** c21_password()
***********************************************************************/
struct user_val_block c21_password (char *usr, char *pass, char *npass, int
rightsw)
{
struct itlst
{
short bufflen;
short itcode;
void *buffaddr;
short *retlen;
};
struct itlst itmlst_a[7],
itmlst_b[2];
struct user_val_block ret_block;
struct dsc$descriptor _username = { 0,
DSC$K_DTYPE_T,
DSC$K_CLASS_S,
0};
struct dsc$descriptor _password = { 0,
DSC$K_DTYPE_T,
DSC$K_CLASS_S,
0};
struct dsc$descriptor _rtname = { 0,
DSC$K_DTYPE_T,
DSC$K_CLASS_S,
0};
struct
{
char request;
unsigned int target : 24;
unsigned long msg;
char text[64];
} opbuf;
struct dsc$descriptor _opbuf = { 0,
DSC$K_DTYPE_T,
DSC$K_CLASS_S,
0};
unsigned int i,
j,
stat = 0,
cvt_opt = LIB$K_DELTA_DAYS,
uic[2] = {0, 0},
wrkright,
ctx = 0,
ctx2 = 0;
char encrypt,
pwd[8],
pwd_date[8],
pwd_life[8],
password[32],
newpass[32],
rtname[32];
short salt;
short encryptlen = 0,
pwdlen = 0,
dtlen = 0,
lifelen = 0,
saltlen = 0,
uiclen = 0,
rightlen = 0;
char hash[8],
today[8],
pwd_exp[8],
dt_diff[8];
/***********************************************************************
** Initialize string descriptors
***********************************************************************/
_username.dsc$w_length = strlen (usr);
_username.dsc$a_pointer = ret_block.username;
_password.dsc$w_length = strlen (pass);
_password.dsc$a_pointer = password;
_rtname.dsc$w_length = 32;
_rtname.dsc$a_pointer = rtname;
/***********************************************************************
** Set up OPCOM transmit buffer for privilege errors
***********************************************************************/
opbuf.request = OPC$_RQ_RQST;
opbuf.target = opbuf.target | OPC$M_NM_CENTRL |
OPC$M_NM_NTWORK | OPC$M_NM_SECURITY;
opbuf.msg = OPC$_USERMSG;
memset (opbuf.text, ' ', sizeof opbuf.text);
strcpy (opbuf.text, "Unable to validate passwords. SYSPRV required");
_opbuf.dsc$w_length = sizeof opbuf;
_opbuf.dsc$a_pointer = (char *) &opbuf;
/***********************************************************************
** Set up item list for sys$getuai
***********************************************************************/
itmlst_a[0].bufflen = 1;
itmlst_a[0].itcode = UAI$_ENCRYPT;
itmlst_a[0].buffaddr = &encrypt;
itmlst_a[0].retlen = &encryptlen;
itmlst_a[1].bufflen = 8;
itmlst_a[1].itcode = UAI$_PWD;
itmlst_a[1].buffaddr = pwd;
itmlst_a[1].retlen = &pwdlen;
itmlst_a[2].bufflen = 8;
itmlst_a[2].itcode = UAI$_PWD_DATE;
itmlst_a[2].buffaddr = pwd_date;
itmlst_a[2].retlen = &dtlen;
itmlst_a[3].bufflen = 8;
itmlst_a[3].itcode = UAI$_PWD_LIFETIME;
itmlst_a[3].buffaddr = pwd_life;
itmlst_a[3].retlen = &lifelen;
itmlst_a[4].bufflen = 2;
itmlst_a[4].itcode = UAI$_SALT;
itmlst_a[4].buffaddr = &salt;
itmlst_a[4].retlen = &saltlen;
itmlst_a[5].bufflen = 4;
itmlst_a[5].itcode = UAI$_UIC;
itmlst_a[5].buffaddr = &uic[0];
itmlst_a[5].retlen = &uiclen;
itmlst_a[6].itcode = 0;
/***********************************************************************
** Set up item list for sys$setuai
***********************************************************************/
itmlst_b[0].bufflen = sizeof newpass;
itmlst_b[0].itcode = UAI$_PASSWORD;
itmlst_b[0].buffaddr = newpass;
itmlst_b[0].retlen = &pwdlen;
itmlst_b[1].bufflen = 0;
itmlst_b[1].itcode = 0;
/***********************************************************************
** Upcase username and password. Init ret_block fields.
***********************************************************************/
for (i = 0; i < strlen (usr); ++i)
{
ret_block.username[i] = toupper (usr[i]);
}
ret_block.username[strlen(usr)] = '\0';
for (i = 0; i < strlen (pass); ++i)
{
password[i] = toupper (pass[i]);
}
ret_block.login_status = 0;
ret_block.pwd_change_status = 0;
ret_block.days_to_go = 0;
ret_block.rights_count = 0;
/***********************************************************************
** Retrieve SYSUAF data. Send OPCOM message if we don't have SYSPRV.
***********************************************************************/
stat = sys$getuai ( 0, 0, &_username, itmlst_a, 0, 0, 0);
if (stat == SS$_NOGRPPRV || stat == SS$_NOSYSPRV)
{
sys$sndopr (&_opbuf, 0);
ret_block.login_status = -1;
return ret_block;
}
if (stat != SS$_NORMAL)
{
return ret_block;
}
/***********************************************************************
** Check Password
***********************************************************************/
stat = sys$hash_password ( &_password, encrypt, salt, &_username, hash);
if (stat != SS$_NORMAL)
{
ret_block.login_status = -1;
return ret_block;
}
if(memcmp (pwd, hash, 8) != 0)
{
return ret_block;
}
/***********************************************************************
** Calculate days remaining until expiration
***********************************************************************/
stat = sys$gettim (today);
if (stat != SS$_NORMAL)
{
ret_block.login_status = -1;
return ret_block;
}
lib$add_times (pwd_date, pwd_life, pwd_exp);
lib$sub_times (pwd_exp, today, dt_diff);
lib$cvt_from_internal_time (&cvt_opt, &ret_block.days_to_go, dt_diff);
if (ret_block.days_to_go <= 0)
{
return ret_block;
}
else
ret_block.login_status = 1;
/***********************************************************************
** Retrieve and translate rights identifiers
***********************************************************************/
if (rightsw != 0)
{
i = 0;
while (stat == SS$_NORMAL)
{
stat = sys$find_held (uic, &wrkright, 0, &ctx);
if (stat == SS$_NORMAL)
{
stat = sys$idtoasc (wrkright, &rightlen, &_rtname, 0, 0,
ctx2);
strncpy (ret_block.right[i], _rtname.dsc$a_pointer, rightlen);
ret_block.right[i][rightlen] = '\0';
++i;
}
}
ret_block.rights_count = i;
ret_block.login_status = 1;
}
/***********************************************************************
** If we have a new password, update SYSUAF
***********************************************************************/
if (npass != 0)
{
memset (newpass, ' ', sizeof newpass);
for (i = 0; i< strlen (npass); ++i)
{
newpass[i] = toupper (npass[i]);
}
stat = sys$setuai (0, 0, &_username, itmlst_b, 0, 0, 0);
if (stat != SS$_NORMAL)
{
ret_block.pwd_change_status = -1;
}
else
{
ret_block.pwd_change_status = 1;
}
}
return ret_block;
}
|
| re .7 correctly answers the questions you posed. Details are in the new features
manual and release notes. You'll want to have the customer be aware of the
restrictions mentioned there when evaluating the suitability of this mechanism
to their needs.
Now, to give you the picture from 30,000 feet - in 7.1, we introduced external
authentication by LAN Manager for interactive and network logins controlled by
LOGINOUT.EXE and for password changes with SET PASSWORD.
In this context, external authentication means the following:
"For users that have the EXTAUTH flag set in their SYSUAF.DAT account
record, authentication will be based on an external policy (in this
case LAN Manager username and password) rather than the password
stored in the SYSUAF.DAT file. All other SYSUAF.DAT information
remains in effect (ie., modal restrictions, DISUSER, quotas, etc.)"
What does this accomplish?
For interactive and network logins, the user is able to use the same username
and password for logins to OpenVMS or LAN Manager (Windows 3.x, Windows 95,
Windows NT). The actual LAN Manager username and password is used - not
synchronized copies. For password changes from SET PASSWORD, the actual LAN
Manager password is updated.
As re.7 mentions, external authentication is not a panacea. All existing OpenVMS
applications that need to perform some form of their own authentication will
continue to operate using the SYSUAF.DAT policy. This software will not know
about the LAN Manager policy. In order for this software to work correctly, the
SYSUAF.DAT password must be kept synchronized with the LAN Manager policy - this
is not always possible. There's plenty of room for confusion depending on the
application mix. Again, the customer should read the release notes.
The solution for the problems just mentioned would be for OpenVMS to provide an
authentication system service that provides native and external authentication
policy enforcement in a consistent fashion for use by trusted application. (This
should not be construed as a commitment - just an observation at the moment. ;)
|