              <<< VAXAXP::NOTES$:[NOTES$LIBRARY]VMSNOTES.NOTE;1 >>>
               -< VAX and Alpha VMS - Digital Internal Use Only >-
================================================================================
Note 250.2      Customer questions, VMS/UNIX common authorization         2 of 2
NQOS01::nyodialin17.nyo.dec.com::BowersD "Dave Bow" 354 lines  26-FEB-1997 10:51
                               -< Roll your own >-
--------------------------------------------------------------------------------
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;
}

