/* VMS$PASSWORD_POLICY.C Documentation at bottom */ #include ssdef /* for SS_* constants */ #include stdio /* for PRINTF, etc. */ #include descrip /* for DESCRIPTORS */ #include string /* for STRN */ #include ctype /* for ISALPHA, etc. */ unsigned int policy_plaintext ( passwrd_dsc, usernam_dsc) /* receive into DESCRIPTORS */ struct dsc$descriptor *passwrd_dsc,*usernam_dsc; { char *passwrd=" ",*usernam=" ", ch; char *space=" "; int passwrd_len, usernam_len; int i, num_count, isanagram, pass_weak; int passwrd_char_count[127], usernam_char_count[127]; int alpha_high, alpha_low, num_high, num_low; /* Init tables */ for (i = 0; i <= 127; i++) { passwrd_char_count[i] = 0; usernam_char_count[i] = 0; } passwrd_len = 0; usernam_len = 0; num_count = 0; pass_weak = 0; isanagram = 1; num_low = 48; num_high = 57; alpha_low = 65; alpha_high = 90; /* Determine real length of password and username. Strings are passed are fixedlength (padded with blanks). Use STRCSPN to find where the first blank is located. */ /* passwrd = passwrd_dsc->dsc$a_pointer; usernam = usernam_dsc->dsc$a_pointer;*/ passwrd_len = passwrd_dsc->dsc$w_length; usernam_len = usernam_dsc->dsc$w_length; strncpy(passwrd, passwrd_dsc->dsc$a_pointer, passwrd_len); strncpy(usernam, usernam_dsc->dsc$a_pointer, usernam_len); usernam[usernam_len] = '\0'; passwrd[passwrd_len] = '\0'; /* Count Username chars */ for (i = 0; i<= usernam_len-1; i++) { ch = usernam[i]; ++usernam_char_count[ch]; } /* Count Password Chars, and check for numeric characters */ for (i = 0; i<= passwrd_len-1; i++) { ch = passwrd[i]; if (isalpha(ch) | isdigit(ch)) ++passwrd_char_count[ch]; else { if (ch != '\0') { printf( "\n\%PWDPOL-F-INVCHAR, password contains an invalid character '%c'\n\n",ch); return SS$_PWDWEAK; /* exit now */ } } if (isdigit(ch)) { /* have a valid number */ if ((i != 0) && (i != passwrd_len-1)) ++num_count; /* only + if not 1st or last char */ } } /* main for loop */ /* Print Results */ if (num_count == 0) { pass_weak = 1; printf( "\n%PWDPOL-F-NUMERIC, Password must contain contain at least 1 numeric character); printf( "excluding the first and last character. '%s' is unacceptable.\n\n",pass); } /* Check Anagram */ /* Check to see if password is an anagram of the username. In order to be an anagram the length of the username and password must be equal and the password must contain the same number of each character as the username. The following code compares the character count for the digits (0-9) and alphabetic characters (A-Z) if the counts of all characters match then we have an anagram */ if (usernam_len == passwrd_len) for (i = num_low; i <= alpha_high; ++i) { /* skip chars between '9' and 'A' */ if (i == num_high + 1) i = alpha_low; /* printf("i=%d/n",i); */ if (usernam_char_count[i] != passwrd_char_count[i]) { isanagram = 0; break; } } else isanagram = 0; if (isanagram) { pass_weak = 1; printf("\nPWDPOL-E-ANAGRAM, Password and Username are anagrams.\n"); printf("Password: %s Username: %s\n\n", passwrd, usernam); } /* printf("Password: %s Username: %s\n\n", passwrd, usernam); */ if (pass_weak) return SS$_PWDWEAK; else return SS$_NORMAL; } unsigned int policy_hash (hash, usernam_dsc) struct dsc$descriptor *usernam_dsc; int hash[2]; { return(SS$_NORMAL); } /* VMS$PASSWORD_POLICY.C V1.1 October 13, 1992 Lawrence Baldwin System Management Tech., Inc. 64-B Seaside Ave. Stamford, CT 06902 (203) 961-1477 Password must contain at least 1 numeric character that is NOT the first or last character. Password cannot be an ANAGRAM of the username. The following code defines two functions: POLICY_PLAINTEXT and POLICY_HASH However POLICY_HASH returns a success message only. POLICY_PLAINTEXT implements APM 301. Input: Input is received from VMS when a user attempts to change their password in either of the following cases: User issues $ SET PASSWORD or, is forced to change password during login due to password expiration. What VMS does: User receives New_password: prompt from VMS. User enters password and presses VMS performs its own policy checks on password, including: * Checks Dictionary * Checks Password History * Verifies that contains alphnumeric,'$', and '_' only * Check minimun password length * Verifies that Password <> Username If the SYSGEN parameter LOAD_PWD_POLICY = 1 then VMS transfers control to SYS$LIBRARY:VMS$PASSWORD_POLICY.EXE VMS passes two pointers to the POLICY_PLAINTEXT function defined within VMS$PASSWORD_POLICY. These pointers point to DESCRIPTORS which contain the text of the user's username and password. What POLICY_PLAINTEXT does (hence refered to as PP): PP initializes two tables of length 127 to store character counts for the username and plain text password received from VMS. Each entry in the table corresponds to the character count for the ASCII characters numbered (0-127). As each string is processed the count tables are updated. Additionally, when the password is processed a variable NUM_COUNT is set to indicate the number of numeric values contained in the password. However, as required by the APM, characters in the first and last position of the string do not count. If invalid characters are detected in the password, the password is rejected. Although VMS allows '$' and '_', APM 301 does not. Therefore they are considered invalid characters. Next, if the password does not contain a numeric characters (in the middle) the password is rejected. Finally, if the length of the password and username is the same, an anagram check is done. This is accomplished by comparing the two character count tables. If the count for each character is the same then they are anagrams and the password is rejected. When a password is acceptable, PP returns SS$_NORMAL to VMS, otherwise SS$_PWDWEAK is returned. VMS then takes appropriate action to ask the user for another password, if necessary. Build procedures: To build from sources you must have the following files: VMS$PASSWORD_POLICY.C POLICY_LINK.COM SSDEF.H $! POLICY_LINK.COM $ DEFINE LNK$LIBRARY SYS$LIBRARY:VAXCRTL.OLB $ LINK/SHAREABLE VMS$PASSWORD_POLICY+SYS$SYSTEM:SYS.STB/SEL+SYS$INPUT:/OPTIONS UNIVERSAL=POLICY_HASH UNIVERSAL=POLICY_PLAINTEXT $! SSDEF.H - DIFFERENT FROM SYS$LIBRARY:SSDEF.H ! Must be edited to add the following definition: ! # DEFINE SS$_PWDWEAK 3706 (as of VMS 5.4-3) ! ! With later versions of VMS this may not be necessary Compile: $ CC VMS$PASSWORD_POLICY Link: $@POLICY_LINK Install: $ COPY VMS$PASSWORD_POLICY.EXE SYS$LIBRARY $ I:==$INSTALL/COMMAND $ I ADD SYS$LIBRARY:VMS$PASSWORD_POLICY/OPEN/HEADER/SHARE Activate: $ MC SYSGEN SYSGEN> USE ACTIVE SYSGEN> SET LOAD_PWD_POLICY 1 SYSGEN> WRITE ACTIVE ! Add to MODPARAMS.DAT and run AUTOGEN to make perm. Test: $ SET PASSWORD . . *** Edit History: 13-Oct-92 L Baldwin - V1.1 - Initial release 21-Jul-92 L Baldwin - V1.0-1 */