OpenVMS Source Code Demos

READ_SYSUAF

1000	%title "read_sysuaf_xxx.bas"						!
	%ident                      "version 102.1"				!
	declare string constant	k_version = "102.1"		,		!			&
				k_program = "read_sysuaf"			!
	!====================================================================================================
	! title  : read_sysuaf_xxx.bas
	! author : Neil Rieck (https://neilrieck.net)
	!	 : Waterloo, Ontario, Canada.
	! created: 2014-01-30
	! purpose: directly access SYSUAF for auditing purposes only
	! ver who when   what
	! --- --- ------ ------------------------------------------------------------------------------------
	! 100 NSR 140130 1. original hack
	!     NSR 140131 2. added a function to demo VMS date-time conversion (that's all for now;
	!		    My wife reminded me that I am supposed to be on vacation this week) :-)
	! 101 NSR 140808 1. a little more hacking to flesh out something for an idea I am working on
	! 102 NSR 140811 1. added code to fetch information via sys$getuai (just so we can compare)
	!====================================================================================================
	! notes  : 1) WARNING: never attempt to write to SYSUAF directly. You will corrupt something.
	!	   2) Start by analyzing the RMS structure of SYSUAF from DCL like so:
	!
	! $ analyze/rms/fdl sys$system:SYSUAF.DAT
	! $ type sysuaf.fdl (to see what analyze discovered about the data file)
	!
	!	SYS$SYSROOT:[SYSEXE]SYSUAF.FDL;1
	!
	!	IDENT   FDL_VERSION 02 "30-JAN-2014 07:26:33   OpenVMS ANALYZE/RMS_FILE Utility"
	!
	!	SYSTEM
	!	        SOURCE                  OpenVMS
	!
	!	FILE
	!	        ALLOCATION              1120
	!	        BEST_TRY_CONTIGUOUS     no
	!	        BUCKET_SIZE             3
	!	        CLUSTER_SIZE            16
	!	        CONTIGUOUS              no
	!	        EXTENSION               10
	!	        FILE_MONITORING         no
	!	        NAME                    "SYS$COMMON:[SYSEXE]SYSUAF.DAT;4437"
	!	        ORGANIZATION            indexed
	!	        OWNER                   [1,1]
	!	        PROTECTION              (system:RWED, owner:RWED, group:, world:)
	!	        GLOBAL_BUFFER_COUNT     0
	!	        GLBUFF_CNT_V83          0
	!	        GLBUFF_FLAGS_V83        none
	!
	!		RECORD
	!	        BLOCK_SPAN              yes
	!	        CARRIAGE_CONTROL        none
	!	        FORMAT                  variable
	!	        SIZE                    1412
	!
	!	AREA 0
	!	        ALLOCATION              1109
	!	        BUCKET_SIZE             3
	!	        EXTENSION               10
	!
	!	KEY 0
	!	        CHANGES                 no
	!	        DATA_KEY_COMPRESSION    yes
	!	        DATA_RECORD_COMPRESSION yes
	!	        DATA_AREA               0
	!	        DATA_FILL               100
	!	        DUPLICATES              no
	!	        INDEX_AREA              0
	!	        INDEX_COMPRESSION       yes
	!	        INDEX_FILL              100
	!	        LEVEL1_INDEX_AREA       0
	!	        NAME                    ""
	!	        NULL_KEY                no
	!	        PROLOG                  3
	!	        SEG0_LENGTH             32
	!	        SEG0_POSITION           4
	!	        TYPE                    string
	!
	!	KEY 1
	!	        CHANGES                 yes
	!	        DATA_KEY_COMPRESSION    no
	!	        DATA_AREA               0
	!	        DATA_FILL               100
	!	        DUPLICATES              yes
	!	        INDEX_AREA              0
	!	        INDEX_COMPRESSION       no
	!	        INDEX_FILL              100
	!	        LEVEL1_INDEX_AREA       0
	!	        NAME                    ""
	!	        NULL_KEY                no
	!	        SEG0_LENGTH             4
	!	        SEG0_POSITION           36
	!	        TYPE                    bin4
	!
	!	KEY 2
	!	        CHANGES                 yes
	!	        DATA_KEY_COMPRESSION    no
	!	        DATA_AREA               0
	!	        DATA_FILL               100
	!	        DUPLICATES              yes
	!	        INDEX_AREA              0
	!	        INDEX_COMPRESSION       no
	!	        INDEX_FILL              100
	!	        LEVEL1_INDEX_AREA       0
	!	        NAME                    ""
	!	        NULL_KEY                no
	!	        SEG0_LENGTH             8
	!	        SEG0_POSITION           36
	!	        TYPE                    bin8
	!
	!	KEY 3
	!	        CHANGES                 yes
	!	        DATA_KEY_COMPRESSION    no
	!	        DATA_AREA               0
	!	        DATA_FILL               100
	!	        DUPLICATES              yes
	!	        INDEX_AREA              0
	!	        INDEX_COMPRESSION       no
	!	        INDEX_FILL              100
	!	        LEVEL1_INDEX_AREA       0
	!	        NAME                    ""
	!	        NULL_KEY                no
	!	        SEG0_LENGTH             8
	!	        SEG0_POSITION           44
	!	        TYPE                    bin8
	!
	!	ANALYSIS_OF_AREA 0
	!	        RECLAIMED_SPACE         0
	!
	!	ANALYSIS_OF_KEY 0
	!	        DATA_FILL               51
	!	        DATA_KEY_COMPRESSION    69
	!	        DATA_RECORD_COMPRESSION 60
	!	        DATA_RECORD_COUNT       876
	!	        DATA_SPACE_OCCUPIED     909
	!	        DEPTH                   2
	!	        INDEX_COMPRESSION       65
	!	        INDEX_FILL              47
	!	        INDEX_SPACE_OCCUPIED    15
	!	        LEVEL1_RECORD_COUNT     303
	!	        MEAN_DATA_LENGTH        644
	!	        MEAN_INDEX_LENGTH       34
	!	        LONGEST_RECORD_LENGTH   644
	!
	!	ANALYSIS_OF_KEY 1
	!	        DATA_FILL               47
	!	        DATA_KEY_COMPRESSION    0
	!	        DATA_RECORD_COUNT       871
	!	        DATA_SPACE_OCCUPIED     42
	!	        DEPTH                   1
	!	        DUPLICATES_PER_SIDR     0
	!	        INDEX_COMPRESSION       0
	!	        INDEX_FILL              6
	!	        INDEX_SPACE_OCCUPIED    3
	!	        LEVEL1_RECORD_COUNT     14
	!	        MEAN_DATA_LENGTH        12
	!	        MEAN_INDEX_LENGTH       6
	!
	!	ANALYSIS_OF_KEY 2
	!	        DATA_FILL               47
	!	        DATA_KEY_COMPRESSION    0
	!	        DATA_RECORD_COUNT       871
	!	        DATA_SPACE_OCCUPIED     57
	!	        DEPTH                   1
	!	        DUPLICATES_PER_SIDR     0
	!	        INDEX_COMPRESSION       0
	!	        INDEX_FILL              13
	!	        INDEX_SPACE_OCCUPIED    3
	!	        LEVEL1_RECORD_COUNT     19
	!	        MEAN_DATA_LENGTH        16
	!	        MEAN_INDEX_LENGTH       10
	!
	!	ANALYSIS_OF_KEY 3
	!	        DATA_FILL               72
	!	        DATA_KEY_COMPRESSION    0
	!	        DATA_RECORD_COUNT       5
	!	        DATA_SPACE_OCCUPIED     15
	!	        DEPTH                   1
	!	        DUPLICATES_PER_SIDR     174
	!	        INDEX_COMPRESSION       0
	!	        INDEX_FILL              1
	!	        INDEX_SPACE_OCCUPIED    3
	!	        LEVEL1_RECORD_COUNT     1
	!	        MEAN_DATA_LENGTH        1104
	!	        MEAN_INDEX_LENGTH       10
	!====================================================================================================
	option type=explicit							! no kid stuff
	set no prompt								!
	!
	%include "starlet"	%from %library "sys$library:basic$starlet"	! system services (+ basic$quadword)
	%include "$ssdef"	%from %library "sys$library:basic$starlet"      ! ss$
	%include "$syidef"	%from %library "sys$library:basic$starlet"	! syi$
	%include "$uaidef"	%from %library "sys$library:basic$starlet"      ! uai$
	%include "$rmsdef"	%from %library "sys$library:basic$starlet"	! rms$
	%include "lib$routines"	%from %library "sys$library:basic$starlet"	! lib$spawn
	%include "$libdef"	%from %library "sys$library:basic$starlet"	! eg. lib$_normal
	%include "$iledef"	%from %library "sys$library:basic$starlet"	! ile3$ (Item List Entry 3 structures)
	!
	record ItemRec								! structure of item record
	    variant								!
		case    							!
		    group zero							! new code
			ILE3	myILE3						! from sys$library:basic$starlet
		    end group zero						!
		case    							!
		    group one							! legacy code
			word	BuffLen						! length
			word	ItemCode					! code
			long	BuffAddr					! address of buffer
			long	RtnLenAdr					! address of word for returned length
		    end group one						!
		case    							!
		    group two							!
			long	List_Terminator					!
			long	Junk1						!
			long	Junk2						!
		    end group two						!
	    end variant								!
	end record ItemRec							!
	!
	!	home brewed functiond
	!
	external string function bin_to_asc(basic$quadword)			!
	!
	!	constants
	!
	declare long constant	k_max_rec	= 9999				!
	!
	!	variables
	!
	declare long		read_count, handler_error			!
	declare long		x, y, offset					!
	declare string		junk$				,		&
				filter$
	!
	!	this reverse-engineered map overlays were produced by analyzing the contents of the associated FDL
	!
	!	observations:
	!	1) bin8 (8 bytes) is probably unsigned but BASIC "quad" will yield int8 (signed)
	!	2) bin4 (4 bytes) is probably unsigned but BASIC "long" will yield int4 (signed)
	!	3) notice that key-1 and key-2 overlap
	!		key-1 appears to be the lower 32-bits of UIC-member (little endian)
	!		key-2 appears to be 64-bit UIC-member
	!	4) key-3 appears to be 64-bit UIC-group
	!	5) UIC-group comes after UIC-member because we are little endian
	!	6) key-0 appears to be a segmented key consisting of USERNAME + encoded PASSWORD
	!
	map(re)	string		uaf_whole	= 1412	,		! from: SIZE in FDL			&
		string		uaf_alignZ	= 0			!
	map(re)	string		fill$		= 52	,		!					&
		string		uaf_alignM	= 0			! force a compile-time error if I'm wrong
	map(re)	string		fill$		= 4	,		!					&
		string		uaf_key0	= 32	,		! P:4  L:32 (from: key#0)		&
		long		uaf_key1		,		! P:36 L:4  (from: key#1)		&
		string		fill$		= 4	,		!	4				&
		quad		uaf_key3		,		! P:44 L:8  (from: key#3)		&
		string		uaf_alignM	= 0			! we should be at position 52
	map(re)	string		fill$		= 4	,		!					&
		string		uaf_key0	= 32	,		! P:4  L:32 (from: key#0)		&
		quad		uaf_key2		,		! P:36 L:8  (from: key#2)		&
		quad		uaf_key3		,		! P:44 L:8  (from: key#3) 		&
		string		uaf_alignM	= 0			! we should be at position 52
	!
	!	aassumptions
	!
	map(re)	string		fill$			= 4	,	!					&
		string		uaf_k0_user_pwd		= 32	,	! username (short + long)		&
		long		uaf_k1_uic_mem_L		,	! uic-member (lower word)		&
		string		fill$			= 4	,	!					&
		quad		uaf_k3_uic_grp_Q		,	!					&
		string		uaf_alignM		= 0		!
	map(re)	string		fill$			= 4	,	!					&
		string		uaf_k0_user_pwd		= 32	,	!					&
		quad		uaf_k2_uic_mem_Q		,	! uic-member				&
		quad		uaf_k3_uic_grp_Q		,	! uic_grp		 		&
		string		uaf_alignM		= 0		!
	!
	!	this map was produced by observing/hackingthe output of this program...
	!	...AS WELL AS FROM DCL LIKE SO:
	!		$dump sys$system:sysuaf.dat/page
	!								 +++--- starting offset
	map(re)	string		uaf_00		= 1	,		!  0	??			&
		string		uaf_01		= 1	,		!  1				&
		string		uaf_02		= 1	,		!  2				&
		string		uaf_03		= 1	,		!  3				&
		string		uaf_username12	= 12	,		!  4	username		&
		string		uaf_username20	= 20	,		! 16	username future growth? &
		word		uaf_member		,		! 36	uic:member		&
		word		uaf_group		,		! 38	uic:group		&
		string		fill$		= 12	,		! 40	??			&
		string		uaf_account	= 32	,		! 52				&
		byte		uaf_owner_len		,		! 84				&
		string		uaf_owner	= 31	,		! 85				&
		byte		uaf_dev_len		,		!116				&
		string		uaf_dev		= 31	,		!117				&
		byte		uaf_dir_len		,		!148				&
		string		uaf_dir    	= 63	,		!149				&
		byte		uaf_LGICMD_len		,		!212				&
		string		uaf_LGICMD	= 63	,		!213				&
		byte		uaf_cli_len		,		!276				&
		string		uaf_cli		= 31	,		!277				&
		byte		uaf_cli_tbl_len		,		!308				&
		string		uaf_cli_tbl	= 31	,		!309				&
		quad		uaf_primary_pw		,		!340	account password	&
		quad		uaf_secondary_pw	,		!348	secondary ""		&
		word		uaf_login_fails		,		!356	counter			&
		word		uaf_salt		,		!358	encryption salt		&
		byte		uaf_pri_pw_enc_type	,		!360	pw encryption type	&
		byte		uaf_sec_pw_enc_type	,		!361	secondary ""		&
		byte		uaf_min_pw_length	,		!362	minimum pw length	&
		string		fill$		= 1	,		!363	???			&
		basic$quadword	uaf_account_expiration	,		!364	64-bit timestamp	&
		basic$quadword	uaf_password_lifetime	,		!372	64-bit timestamp	&
		basic$quadword	uaf_pri_pw_changed	,		!380	64-bit timestamp	&
		basic$quadword	uaf_sec_pw_changed	,		!388	64-bit timestamp	&
		basic$quadword	uaf_last_login_inter	,		!396	64-bit timestamp	&
		basic$quadword	uaf_last_login_noninter	,		!404	64-bit timestamp	&
		basic$quadword	uaf_prv_auth		,		!412	authorized priv bits	&
		basic$quadword	uaf_prv_def		,		!420	default priv bits	&
		string		fill$		= 40	,		!428	???			&
		long		uaf_login_flags		,		!468				&
		byte		uaf_net_acc_pri_days(2)	,		!472				&
		byte		uaf_net_acc_sec_days(2)	,		!475				&
		byte		uaf_bat_acc_pri_days(2)	,		!478				&
		byte		uaf_bat_acc_sec_days(2)	,		!481				&
		byte		uaf_loc_acc_pri_days(2)	,		!484				&
		byte		uaf_loc_acc_sec_days(2)	,		!487				&
		byte		uaf_dia_acc_pri_days(2)	,		!490				&
		byte		uaf_dia_acc_sec_days(2)	,		!493				&
		byte		uaf_rem_acc_pri_days(2)	,		!496				&
		byte		uaf_rem_acc_sec_days(2)	,		!499				&
		string		fill$		= 12	,		!502				&
		byte		uaf_prime_days		,		!514				&
		string		fill$		= 1	,		!515				&
		byte		uaf_base_prior		,		!516				&
		byte		uaf_max_que_prior	,		!517				&
		word		uaf_proc_limit		,		!518				&
		word		uaf_max_jobs		,		!520				&
		word		uaf_det_lim		,		!522				&
		word		uaf_sub_lim		,		!524				&
		word		uaf_bio_lim		,		!526				&
		word		uaf_timer_lim		,		!528				&
		word		uaf_ast_lim		,		!530				&
		string		re_align	= 0			!532	(to check my math)
	map (re)string		re_buffer	= 532	,		!				&
		string		re_align	= 0			!	(to check my math)
	!
	!	stuff used in sys$getuai
	!
	declare	basic$quadword	uai_hashed_pw				,	!			&
				pass1_hash				,	!			&
				pass4_hash				,	!			&
				quad_time				,	!			&
		ItemRec		myItems(9)				,	!			&
		long		rc_bits%				,	!			&
		long		uai_flags1%				,	!			&
				uai_flags2%				,	!			&
		word		uai_salt				,	!			&
		byte		uai_encrytion_type%			,	!			&
				uai_min_pwd_length%			,	!			&
		string		user4$					,	!			&
		long		rc%					,	!			&
				junk%					,	!			&
		quad		quad_copy					!
	!
	record switcheroo							! another hacking tool
	    variant
		case
		    group zero
			basic$quadword	bquad0					! unsigned quad word (system calls)
		    end group
		case
		    group one
			string	quad$ = 8
		    end group
		case
		    group two
			quad	quad0						! signed quad word (native basic)
		    end group
		case
		    group three
			byte	byte0
			byte	byte1
			byte	byte2
			byte	byte3
			byte	byte4
			byte	byte5
			byte	byte6
			byte	byte7
		    end group
	    end variant
	end record
	declare switcheroo	quad_hack					!
	!============================================================================================
	!	main
	!============================================================================================
	main:
	print k_program +"_"+ k_version						!
	print string$(len(k_program +"_"+ k_version), asc("="))			!
	input "username filter? (blank=NO FILTERING) "; filter$			!
	junk$ = filter$								!
	junk$ = edit$(junk$,32+2)						!
	if filter$ <> junk$ then						!
	    filter$ = junk$							!
	    print "-i-filter changed to: "+ filter$				!
	    sleep 1								!
	end if									!
	!
	read_count = 0								!
	when error in								!
	    !
	    !	cavat: you need lots of privs to open "this file"
	    !
  %let %indexedopen=0								!
  %if  %indexedopen=1 %then							!
	    !
	    !	this open doesn't work just yet (we need a bin8 data type but BASIC
	    !	can only provide us with an int8 data type; so a fully functional
	    !	BASIC version of this program may not be possible)
	    !
	    print "-i-attempting indexed file open"
	    open "sys$system:SYSUAF.DAT" for input as #99			!				&
		,access read							! we only want to read		&
		,allow modify							! don't block other processes	&
		,organization indexed variable					&
		,map re								&
		,primary   key	uaf_k0_user_pwd					&
		,alternate key	uaf_k1_uic_mem_L duplicates changes		&
		,alternate key	uaf_k2_uic_mem_Q duplicates changes		&
		,alternate key	uaf_k3_uic_grp_Q duplicates changes
  %else
	    print "-i-attempting raw file open"					!
	    open "sys$system:SYSUAF.DAT" for input as #99			!				&
		,access read							! we only want to read		&
		,allow modify							! don't block other processes	&
		,organization undefined						!				&
		,recordtype any							!				&
		,map re
  %end %if
	    while 1								!
		get #99, regardless						! read record without lock
		read_count = read_count + 1					! count the reads
		gosub analyze_record						!
		cause error 11 if read_count >= k_max_rec			! only print x records in this hack
	    next								!
	use									!
	    handler_error = err							!
	    print "================================="
	    print "-i-status: "+ str$(handler_error)				!
	    print "-i-text  : "+ ert$(handler_error)				!
	    print "-i-count : "+ str$(read_count)				!
	end when								!
	!
	select handler_error							!
	    case 11								! EOF is expected
		goto fini							!
	end select								!
	!
	goto fini								!
	!---------------------------------------------------------------
	!	parse various fields in the UAF record
	!	entry:	i = record count
	!		map(re) contains valid data
	!---------------------------------------------------------------
	analyze_record:
	when error in								!
	    if filter$ <> "" then						!
		user4$ = edit$(uaf_username12,32+2)				! upcase, no white-space
		return	if pos(user4$,filter$,1) = 0				!
	    end if								!
	    !
	    print "########################################"
	    print "record: "+str$(read_count)
  %let %enablehack=0								!
  %if  %enablehack=1 %then							!
	    if uaf_username12 = "<System+Pass" then				! hmmm...
		print "encountered TEMPLATE"					!
		junk$ = uaf_whole						!
		for x = 0 to 99							!
			print str$(mod(x,10));					!
		next x								!
		print								! EOL
		for y = 0 to 13							!
		    offset = y * 100						!
		    for x = 0 to 99						!
			print mid$(junk$,offset+x+1, 1);			!
		    next x							!
		    print							! EOL
		next y								!
		print "------------------------------------"			!
		iterate								!
	    end if								!
    %end %if
		!
		!	could the first four bytesre present a layout version number?
		!
		!		0000: template account
		!		1100: normal account for OpenVMS-8.4
		!
		print " header bytes: ";asc(uaf_00);" ";asc(uaf_01);" ";asc(uaf_02);" ";asc(uaf_03);
		if asc(uaf_00)=1 and asc(uaf_01)=1 and asc(uaf_02)=0 and asc(uaf_03)=0	then
		    print " :: valid VMS-8.4 account"				!
		    goto display_record						!
		end if
		if asc(uaf_00)=0 and asc(uaf_01)=0 and asc(uaf_02)=0 and asc(uaf_03)=0	then
		    print " :: template record"					!
		    sleep 1
		    goto display_record						!
		end if								!
		print " :: unknown prefix"					!
		sleep 1
		display_record:
		!
		print " username       : "; uaf_username12
		print " group (dec)    : "; str$(uaf_group)
		print " member (dec)   : "; str$(uaf_member)
		print " account        : "; uaf_account
		print " owner          : "; left$(uaf_owner, integer(uaf_owner_len))
		print " device         : "; left$(uaf_dev, integer(uaf_dev_len))
		print " directory      : "; left$(uaf_dir, integer(uaf_dir_len))
		print " lgicmd         : "; left$(uaf_lgicmd, integer(uaf_lgicmd_len))
		print " cli            : "; left$(uaf_cli, integer(uaf_cli_len))
		print " ud cli table   : "; left$(uaf_cli_tbl, integer(uaf_cli_tbl_len))
		print " login flags    : "; str$(uaf_login_flags)
		print " min pw len     :"; uaf_min_pw_length
		print " salt           :"; uaf_salt
		print " encryption type:"; uaf_pri_pw_enc_type
		print " last intr login: "; bin_to_asc(uaf_last_login_inter)
		gosub ask_vms							!
	use									!
		print "-e-error: "+ str$(err) +" in block: ANALYZE RECORD"	!
	end when								!
	return									!
	!=======================================================================
	!	ask VMS about some details
	!	entry: user4
	!=======================================================================
	ask_vms:
	!
	!	prep for call to sys$getuai (fetch hashed password of user)
	!
	myItems(0)::BuffLen	= 4						! size of uai_encrytion_type% in bytes
	myItems(0)::ItemCode	= UAI$_ENCRYPT					!
	myItems(0)::BuffAddr	= loc(uai_encrytion_type%)			! addr of uai_encrytion_type%
	myItems(0)::RtnLenAdr	= 0						! location of bytes returned (don't care)
	!
	myItems(1)::BuffLen	= 8						! size of hashed_password in bytes (64-bit)
	myItems(1)::ItemCode	= UAI$_PWD					!
	myItems(1)::BuffAddr	= loc(uai_hashed_pw)				! addr of hashed_password
	myItems(1)::RtnLenAdr	= 0						! location of bytes returned (don't care)
	!
	myItems(2)::BuffLen	= 2						! size of uai_salt in bytes
	myItems(2)::ItemCode	= UAI$_SALT					!
	myItems(2)::BuffAddr	= loc(uai_salt)					! addr of uai_salt
	myItems(2)::RtnLenAdr	= 0						! location of bytes returned (don't care)
	!
	myItems(3)::BuffLen	= 4						! size of uai_flags1 in bytes (UAI$S_FLAGS)
	myItems(3)::ItemCode	= UAI$_FLAGS					!
	myItems(3)::BuffAddr	= loc(uai_flags1%)				! addr of uai_flags1
	myItems(3)::RtnLenAdr	= 0						! location of bytes returned (don't care)
	!
	myItems(4)::List_Terminator	= 0					! signal: no more items
	!
	!	SYS$GETUAI [nullarg] ,[contxt] ,usrnam ,itmlst ,[nullarg] ,[nullarg] ,[nullarg]
	!
	rc% = sys$getuai(,,user4$,myItems(0),,,)				!
	rc_bits% = (rc% and 7%)							!
	if rc_bits% <> 1% then							!
	    select rc%								!
		case RMS$_RNF							!
		    print "-e-user: ";user4$;" not found in SYSUAF"		!
		case else							!
		    print "-e-getuai-status:";rc%;" lookup up: ";user4$;" in SYSUAF"
	    end select								!
	else									!
	    !
	    !	see if stuff from sys$getuai matches fields on the disk
	    !
	    print "-i-SYSUAF lookup successful ----------"
	    print "-i-uai salt     :"; uai_salt					!
	    print "-i-uai enc type :"; uai_encrytion_type%			!
	    quad_hack::bquad0	= uai_hashed_pw					! xfer for next test
	    if quad_hack::quad0 = uaf_primary_pw then				!
		print "-i-passwords match"
	    else
		print "-e-passwords do not match"
	    end if
	end if									!
	return									!
	!
	!	that's all she wrote
	!
30000	fini:
	print "-i-Adios..."
	end									!
	!
	!####################################################################################################
	!	external functions
	!####################################################################################################
31000	!
	! function:	vms_time_bin_to_asc()
	!
	! notes:	17-NOV-1858 00:00:00.0 = NEVER
	!
	function string bin_to_asc(basic$quadword p1)
	option type=explicit
	declare long rc%
	%include "starlet" %from %library "sys$library:basic$starlet"	! system services (including basic$quadword)
	!
	map (LocalSysMap)	string	Sys_buf         = 22
	map (LocalSysMap)	string	Sys_day         =  2,  &
					Sys_dash1       =  1,  &
					Sys_month       =  3,  &
					Sys_dash2       =  1,  &
					Sys_year        =  4,  &
					Sys_Time        = 11
	rc% = sys$asctim(,Sys_buf,p1,)					!
	if (rc% and 7%) = 1% then
	    if Sys_year = "1858" then
		bin_to_asc = "Never"
	    else
		bin_to_asc = sys_buf
	    end if
	else
	    bin_to_asc = "-e-err:"+str$(rc%)
	end if
	end function