.TITLE VMS$PASSWORD_POLICY ; ; Sample site password policy module. ; ; Analyzes plaintext password, counting character types. ; Resultant variables: ; ; CountSpecial, CountUpper, CountLower, CountNumeric ; ; countain counts of each type of character. ; ; PresSpecial, PresUpper, CountPres, CountPres ; ; are presence flags, 0 if there are none of that type ; of character in the string, 1 if there are. ; ; There are also counts and flags for Control characters and ; Invalid characters. These should not be found within the ; proposed password string as the normal OpenVMS processing ; should detect and reject them before this module is invoked. ; ; Results can be used to test password against a complexity ; policy. In this example, there is commented code which ; checks that one of each type of character is presence. The ; working path checks that at least 3 of the 4 possible types ; are present. ; ; The default password processing used on OpenVMS assumes ; uppercase and lowercase characters are interchangeable ; within a password, though this difference can be preserved ; for use with add-on and external authentication schemes. ; ; Starting with OpenVMS Alpha V7.3-2 and later, enable the ; password case preservation mechanism with PWDMIX flag ; on the associated usernames; this flag must be set on each ; username intended for mixed-case passwords. For details, ; see the documentation of the OpenVMS AUTHORIZE (UAF) ; utility and the AUTHORIZE ADD /FLAG command. ; ; The password policy interface is first available in (as it was ; then known) VAX/VMS V5.4. ; ; Case-preservation of passwords and the PWDMIX flag is ; first available in V7.3-2. Prior to this release, the input ; password input is always case-blind. ; ; Author: John Gillings ; ; Updates: ; 30-Nov-2007, Stephen Hoffman, HoffmanLabs LLC ; Very minor changes for clarification, verification ; 22-Jan-2009, Stephen Hoffman, HoffmanLabs LLC ; Small updates to comments; no functional changes ; 03-Feb-2009, Stephen Hoffman, HoffmanLabs LLC ; Additional comments; specifically flag that PWDMIX ; exists (only!) on OpenVMS Alpha V7.3-2 and later. ; No code changes. ; ; ; Usage: ; ; $ MACRO VMS$PASSWORD_POLICY ; $ LINK/SHAREABLE/SYSEXE VMS$PASSWORD_POLICY+SYS$INPUT:/OPTIONS ;SYMBOL_VECTOR = ( - ;POLICY_PLAINTEXT=PROCEDURE,- ;POLICY_HASH=PROCEDURE) ; ; $ COPY VMS$PASSWORD_POLICY.EXE SYS$COMMON:[SYSLIB]/PROT=(W:RE) ; $ INSTALL ADD SYS$LIBRARY:VMS$PASSWORD_POLICY/OPEN/HEAD/SHARE ; $ MCR SYSMAN ;SYSMAN> PARAMETER USE ACTIVE ;SYSMAN> PARAMETER SET LOAD_PWD_POLICY 1 ;SYSMAN> PARAMETER WRITE ACTIVE ; ; Note: consider leaving the CURRENT parameter for LOAD_PWD_POLICY set ; to 0 when using this module. The USE ACTIVE, SET, WRITE ACTIVE sequence ; should then be included in the startup procedure after the INSTALL ADD ; is confirmed to have worked. Leaving it all to happen automatically may ; disable changing password altogether, should the INSTALL fail to operate ; or should the startup not reach the INSTALL command due to some unrelated ; failure. The LOAD_PWD_POLICY system parameter is a dynamic parameter; it ; can be altered within the context of the running system. ; ; As part of the site-specific startup DCL mentioned above, add the INSTALL ; command into the same procedure. And invoke this procedure from within ; SYS$MANAGER:SYSTARTUP_VMS.COM so that the policy is (re)loaded as part ; of the OpenVMS bootstrap. (The startup runs with most privileges enabled; ; that context is assumed here.) That startup procedure would look like this: ; ; $ Set NoOn ; $! install and configure the site-local password policy module ; $ INSTALL ADD SYS$LIBRARY:VMS$PASSWORD_POLICY/OPEN/HEAD/SHARE ; $ RUN SYS$SYSTEM:SYSMAN ;SYSMAN> PARAMETER USE ACTIVE ;SYSMAN> PARAMETER SET LOAD_PWD_POLICY 1 ;SYSMAN> PARAMETER WRITE ACTIVE ;SYSMAN> EXIT ; $ EXIT ; ; $SSDEF .PSECT $RWDATA,RD,WRT,NOEXE,NOSHR ; The counter table; receives the count of each character type found CountTab: .LONG CountControl: .LONG CountSpecial: .LONG CountNumeric: .LONG CountUpper: .LONG CountLower: .LONG CountInvalid: .LONG ; The presence table; flags here set to 1 if present PresTab: .LONG PresControl: .LONG PresSpecial: .LONG PresNumeric: .LONG PresUpper: .LONG PresLower: .LONG PresInvalid: .LONG .PSECT $RODATA,RD,NOWRT,NOEXE,SHR CharTab: ; ASCII table, defines character types ; Confirm that the following table of ASCII characters matches ; your local requirements before deployment of this module. ; ; 1 = Control ; 2 = Special ; 3 = Numeric ; 4 = Uppercase ; 5 = Lowercase ; 6 = Invalid ; ; NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI .BYTE 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; ; DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US .BYTE 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; ; SP ! " # $ % & ' ( ) * + , - . / .BYTE 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; ; 0 1 2 3 4 5 6 7 8 9 : ; < = > ? .BYTE 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 ; ; @ A B C D E F G H I J K L M N O .BYTE 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; ; P Q R S T U V W X Y Z [ \ ] ^ _ .BYTE 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3 ; ; ` a b c d e f g h i j k l m n o .BYTE 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ; ; p q r s t u v w x y z { | } ~ DEL .BYTE 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 6 ; .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 ; all 128-255 invalid ASCII .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 .BYTE 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 ; Message to output if password string fails policy check badpwd: .ASCID - /%SYSTEM-F-PWDPOLICY, Password does not conform to security policy/ .PSECT $CODE,RD,EXE,NOWRT,SHR ; POLICY_HASH is called with two parameters; the first is the ; quadword hash and the second is the username. ; ; The use of this call is not needed here; return success. .ENTRY POLICY_HASH,^M<> ; no need to mess with the hash value MOVL #SS$_NORMAL,R0 ; always success RET ; POLICY_HASH is called with two parameters; the first is the ; plaintext password string descriptor, and the second is the ; username string descriptor. .ENTRY POLICY_PLAINTEXT,^M MOVAQ @4(AP),R3 ; Get address of descriptor for plaintext password MOVZWL (R3),R7 ; Get password length MOVL 4(R3),R8 ; Get pointer to start of string ; Clear character type counters and presence flags CLRL CountControl CLRL CountSpecial CLRL CountNumeric CLRL CountUpper CLRL CountLower CLRL CountInvalid CLRL PresControl CLRL PresSpecial CLRL PresNumeric CLRL PresUpper CLRL PresLower CLRL PresInvalid ; Now step through the password counting character types nloop: MOVZBL (R8)+,R4 ; Get next character MOVB CharTab[R4],R4 ; Get character type INCL CountTab[R4] ; Increment type counter MOVL #1,PresTab[R4] ; Set presence flag SOBGTR R7,nloop ; countdown loop ; uncomment and/or alter the following code to make decisions ; based on the required password policy around the characters ; present in the password. ; ; TSTL CountSpecial ; BEQL bad ; TSTL CountNumeric ; BEQL bad ; TSTL CountUpper ; BEQL bad ; TSTL CountLower ; BEQL bad MOVL PresSpecial,R4 ; take sum of presence flags ADDL PresNumeric,R4 ADDL PresUpper,R4 ADDL PresLower,R4 CMPL R4,#3 ; Require at least 3 different types BLSS bad ; You can insert additional verification, and can call the ; sys$getuai system service should you require additional ; information on the specified username. ; ok: MOVL #SS$_NORMAL,R0 ; return OK RET bad: PUSHAB badpwd ; issue message CALLS #1,G^LIB$PUT_OUTPUT MOVL #SS$_PWDWEAK,R0 ; return "password weak" RET .END