c c Date Written: 3-Jul-91 c c Figure 3 - GENERAL.FOR c c Author: Sri Seshadri, DCSS, Sugarland, TX 77478 c c Subroutines in this module: c c [Subroutine] Check_Decnet c [Subroutine] Get_Username (Username, Username_Length) c [Function] Confirm_Owner (Username, Username_Length) c [Subroutine] Set_Host_Zero c [Subroutine] WriteLog (Line) c [Function] Translate (Logname, LognameLength, EquivName, EquivLength) c [Subroutine] Clear_Screen c [Subroutine] Print_Status (Status) c c ************************************************************************* c c Modification History: c c Functional description: this subroutine returns if DECnet is running c and exits otherwise. You can tell if DECnet is running if the device c NET0 is mounted, a mailbox, and networked from a call to $getdvi(w) c c Calling format: c c CALL Check_Decnet c c Argument: None c Subroutine Check_Decnet Include '($dvidef)' Include '($devdef)' Structure /Getdvi_Item/ Union Map Integer * 2 Buffer_Len, Item_Code Integer * 4 Buffer_Addr, Ret_Len_Addr End Map Map Integer * 4 End_List /0/ End Map End Union End Structure Record /Getdvi_Item/ Item_list (2) Integer Bit_Vector ! bit mask of NET0 Integer Status, Lib$Stop, Sys$Getdviw integer *2 iosb(4) c set up the data structures to collect info on the DVI$_DEVCHAR Item_list(1).Item_Code = dvi$_devchar Item_list(1).Buffer_Len = 4 Item_list(1).Buffer_Addr = %loc(bit_vector) Item_list(1).Ret_Len_Addr = 0 item_list(2).end_list = 0 ! end of the list status = sys$getdviw (,,'NET0:', item_list, iosb,,,) c check the status if (.not. status) call lib$stop ( %val (status)) if (.not. iosb(1)) call lib$stop ( %val(iosb(1))) c print *, bit_vector if ((bjtest(bit_vector, dev$v_mnt)) .and. / (bjtest(bit_vector, dev$v_mbx)) .and. / (bjtest(bit_vector, dev$v_net))) then return else Print *, 'DECnet is not running...' call exit endif end c ************************************************************************* c Modification History: c c Functional description: this subroutine returns the username of the c person whose account needs to be accessed. c c Calling format: c c CALL Get_Username (Username, Username_Length) c c Argument: Username - Character string (output) c Username_Lengfth - Integer (output) c c SUBROUTINE Get_Username (Username, Username_Length) Integer Username_Length, i Character * (*) Username Print 10 10 Format (1x, 'Enter username: ', $) Read (5, 11, End=100, Err=100) Username_Length, Username 11 Format (q, a) c ... capitalize username Do 50 i = 1, username_Length If ((Username(i:i) .ge. 'a') .and. (Username(i:i) .le. 'z')) Then Username(i:i) = Char(Ichar(Username(i:i))-32) Endif 50 Continue If (Username .EQ. ' ') Call Exit Return c ... error path to exit from program 100 Call Exit End c ************************************************************************* c Modification History: c c Functional description: c this function shows the owner and account fields within the UAF record c for the username and asks if this is in fact the person whose record is c to be changed. c c Calling format: c c variable = Confirm_Owner (Username, Username_Length) c c Argument: Username - Character string (input) c Username_Lengfth - Integer (input) c c Return value: c Change it => 0 c Don't change => 1 c Integer Function Confirm_Owner (Username, Username_Length) Implicit None Include '($Uaidef)' ! for symbolic constants Include '($Syssrvnam)' Include '($Jpidef)' Include '($Ssdef)' Include '($Rmsdef)' c getuai itemlist definition Structure /Uai_Itemlist/ Union Map Integer*2 Length, Item_Code Integer*4 Buffer_Address, Return_Length_Address End Map Map Integer*4 End_Of_List End Map End Union End Structure Integer*4 Account_Length, Owner_Length, Username_Length, Status Character * 1 Charac ! temporary variable Character * (*) Username Character Account * 32, Owner * 32 Record /Uai_Itemlist/ Getuai_Itmlist(3) ! for account and owner Confirm_Owner = 1 ! assume don't change c ... get them to confirm what they are doing ... Getuai_itmlist(1).length = 32 Getuai_itmlist(1).item_code = uai$_account Getuai_itmlist(1).buffer_address = %loc(account) Getuai_itmlist(1).return_length_address = %loc(account_length) Getuai_itmlist(2).length = 32 Getuai_itmlist(2).item_code = uai$_owner Getuai_itmlist(2).buffer_address = %loc(owner) Getuai_itmlist(2).return_length_address = %loc(owner_length) Getuai_itmlist(3).end_of_list = 0 Status = sys$getuai(,,%descr(username(1:username_length)), / %ref(Getuai_itmlist),,,) If (.not. Status) Then Call Print_Status (Status) Confirm_Owner = 1 ! assume don't change Return Endif c all looks well so far Print * Print *, ' Owner field : ', Owner (2:Owner_Length) Print *, ' Account field : ', Account (1:Account_Length) Print * Print 11 11 Format (1x, 'Modify this record (Y/N) [N]? ', $) Read (5, 12, End=1000) Charac 12 Format (A) If ((Charac .eq. 'Y') .or. (Charac .eq. 'y')) Then Confirm_Owner = 0 Return Endif 1000 Confirm_Owner = 1 Return End c ****************************************************************** c Modification History: c c Functional description: c this subroutine allows you to login as the new user. You can add c the qualifier /LOG to the SET HOST command and MAIL the log file. c c Calling format: c c Call Set_Host_Zero c c Argument: None c SUBROUTINE Set_Host_Zero Integer Lib$Spawn, Completion_stat /0/, / flag /14/ ! nocli, nolognam, nokeypad c Print *, 'Before spawning ...' Call Lib$Spawn ('SET HOST 0',,,flag,,,completion_stat ) Print * c Print *, 'After spawning ...', completion_stat end c **************************************************************************** c Modification History: c c Functional description: c This subroutine appends a timestamp to the log file called c SRP$ROOT:SRP.LOG. System is the owner of this log file. The c logical SRP$ROOT must be defined in LNM$SYSTEM in EXEC mode. c c Calling format: c c Call Write_Log (Line) c c Argument: Line - Character String (input) c c Subroutine WriteLog (Line) Implicit None Include '($JPIDEF)' Integer * 4 Lib$Date_Time, Lib$Getjpi, Status, Namelen, / FNum, RmsSts, Translate, EquivLength, JunkLen Character * (*) Line ! line to be written to file Character * 23 Date_Time ! current date Character * 80 FileSpec, Junk, Name Character * 255 EquivName Status = Lib$GetJpi (JPI$_Username,,,, Name, Namelen) ! get username c get the date and time Status = Lib$Date_Time (Date_Time) c be careful how this is done. Translate SRP$ROOT in EXEC mode. Status = Translate ('SRP$ROOT', 8, EquivName, EquivLength) If (Status .eq. -1) Then Print * Print *, 'no translation for SRP$ROOT in LNM$SYSTEM' Call Exit Endif FileSpec = EquivName(1:EquivLength) // 'SRP.LOG' c append to log file (if not there, it creates it) 15 Open (Unit = 7, Status = 'Unknown', Iostat = Status, / File = Filespec, Access = 'Append', Err = 1000) Write (7, 98) 'Program accessed by: ', Name (1:NameLen), ' at ', / Date_time 98 Format (1x, 4a) Write (7, 99) Line 99 Format (1x, a) Close (7) Return 1000 Call Errsns (Fnum, RmsSts, , ,) If (RmsSts .ne. 0) Then Print * Print *, 'Error creating logfile: ', filespec Call Print_Status (RmsSts) Call Exit Endif End c ************************************************************************** c Modification History: c c Functional description: c this function translates the logical name LogName to the EquivName c in LNM$SYSTEM. c c Calling format: c c Call Translate (Logname, LognameLength, EquivName, Equiv_Length ) c c Argument: c c INPUT: LogName logical name to be translated c LognameLength Length of the logical name to be translated c c OUTPUT: Function = 0, if successful c Fucntion = -1, if not c c EquivName logical name translatation (if it exists) c EquivLength Length of the translated logical name (" " ") c Integer Function Translate (Logname, LognameLength, EquivName, / EquivLength) Implicit none Include '($lnmdef)' ! logical name table const Include '($psldef)' ! exec mode definition Include '($syssrvnam)' ! system service name Integer Status, Temp, EquivLength, LognameLength, Attr Character * (*) Logname Character * (*) Equivname Structure /itmlist/ Union Map Integer * 2 Buflen, itmcod Integer * 4 Bufadr, Retlen End Map Map Integer * 4 End_list End Map End Union End Structure record /itmlist/ trans_name(2) Translate = 0 ! assume success Trans_name (1).Buflen = 255 Trans_name (1).itmcod = lnm$_String Trans_name (1).Bufadr = %loc(EquivName) Trans_name (1).retlen = %loc(EquivLength) Trans_Name (2).End_List = 0 Attr = LNM$M_Case_Blind Status = Sys$Trnlnm (%ref(Attr), 'LNM$SYSTEM', / %descr(LogName(1:LognameLength)), %ref(PSL$C_EXEC) , Trans_Name) If (.not. Status) Then Translate = -1 ! unsuccessful Endif Return End c ************************************************************************ c clears the screen and home cursor Subroutine Clear_Screen Print *, char(27), '[1;1H' Print *, Char(27), '[2J' Return End c ************************************************************************ c Modification History: c c Functional description: c this subroutine calls sys$getmsg to print an error message based on the c Status value passed to it. c c Calling format: c c Call Print_Status (Status) c c Argument: Status - Integer (input) c c Subroutine Print_Status (Status) Integer Status, Mask /15/ Character * 132 M_Text Integer * 2 M_Len Byte Out_Array(4) Call Sys$GetMsg (%Val(Status), M_Len, M_Text, %Val(Mask), / %Ref (Out_Array)) Print *, M_Text (1:M_len) Print * Return End c c Date Written: 3-Jul-91 c c Figure 6 - RESTORE_PASSWORD.FOR c c Author: Sri Seshadri, DCSS, Sugarland, TX 77478 c c Subroutines in this module: c [Subroutine] Restore_Password (Username, Username_Length) c c ************************************************************************* c c Modification History: c c Functional description: c this SUBROUTINE restores the password stored in the user data portion c of the user record. It assumes the data is valid. The only sanity c check performed is that the first longword in the record is SANITY. c c Calling format: c c CALL Restore_Password (Username, Username_Length) c c Argument: Username - Character String (input) c Username_Length - Integer (input) c c Include files: srp_include.inc for all data definitions c Subroutine Restore_Password (Username, Username_Length) include 'srp_include.inc' If ((Username .EQ. ' ') .OR. (Username_Length .EQ. 0)) Return X = 1 Uai_ItmList(X).Length = 255 ! that's how long user data is Uai_ItmList(X).Item_Code = uai$_user_data Uai_ItmList(X).Buffer_Address = %loc(user_data) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).end_of_list = 0 Status = sys$getuai (,,%descr(username(1:username_Length)), / %ref(uai_itmlist),,,) c check the status. if valid, pick out hash value, salt, algorithm etc. If (.Not. Status) Then Call Print_Status (Status) Call Exit Else B.String = User_Data ! get the string c check if there is a match in the sanity field. if not, it's "corrupt" IF (B.Sanity .EQ. Sanity) THEN ! valid x = 1 Uai_ItmList(X).Length = 8 Uai_ItmList(X).Item_Code = uai$_pwd ! hashed value Uai_ItmList(X).Buffer_Address = %loc(b.hash) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).Length = 2 Uai_ItmList(X).Item_Code = uai$_Salt ! 2-byte salt Uai_ItmList(X).Buffer_Address = %loc(b.Salt) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).Length = 1 Uai_ItmList(X).Item_Code = uai$_encrypt ! encryption alg Uai_ItmList(X).Buffer_Address = %loc(b.Alg) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).end_of_list = 0 Status = sys$setuai(,,%descr(username(1:username_Length)), / %ref(uai_itmlist),,,) If (.Not. Status) Then Call Print_Status (Status) Call Exit Else c Print *, 'Restored password for ', Username Endif Else Print *, 'Sanity error: No saved password for ', Username Call Exit Endif Endif 100 Return End c c Date Written: 3-Jul-91 c c Figure 4 - SAVE_PASSWORD.FOR c c Author: Sri Seshadri, DCSS, Sugarland, TX 77478 c c Subroutines in this module: c [Subroutine] Save_Password (Username, Username_Length) c c ************************************************************************* c c Modification History: c c Functional description: c This subroutine saves the password of a user by first extracting the c hash value, Salt, and encryption Algorithm from the UAF record. c It then saves it in the User data portion along with the sanity value. c c Calling format: c c CALL Save_Password (Username, Username_Length) c c Argument: Username - Character String (Input) c Username_Length - Integer (Input) c c Include files: srp_include.inc for data definitions c Subroutine Save_Password (Username, Username_Length) include 'srp_include.inc' if ((Username .EQ. ' ') .or. (Username_Length .EQ. 0)) return c .... set up data structure for getting password, Salt, and Alg x = 1 Uai_ItmList(X).Length = 8 Uai_ItmList(X).Item_Code = Uai$_Pwd ! hashed value Uai_ItmList(X).Buffer_Address = %Loc(A.Hash) Uai_ItmList(X).Return_Length_Address = 0 X = X + 1 Uai_ItmList(X).Length = 2 Uai_ItmList(X).Item_Code = Uai$_Salt ! 2-byte salt Uai_ItmList(X).Buffer_Address = %Loc(A.Salt) Uai_ItmList(X).Return_Length_Address = 0 X = X + 1 Uai_ItmList(X).Length = 1 Uai_ItmList(X).Item_Code = Uai$_Encrypt ! encryption alg Uai_ItmList(X).Buffer_Address = %Loc(A.Alg) Uai_ItmList(X).Return_Length_Address = 0 X = X + 1 Uai_ItmList(X).End_Of_List = 0 Status = Sys$Getuai (,,%Descr(Username(1:Username_Length)), / %ref(Uai_Itmlist),,,) If (.Not. Status) Then Call Print_Status (Status) Call Exit Else c now save it in the user data portion for the specified user A.Sanity = Sanity User_Data = A.String X = 1 Uai_ItmList(X).Length = 255 Uai_ItmList(X).Item_Code = Uai$_User_Data Uai_ItmList(X).Buffer_Address = %Loc(User_Data) Uai_ItmList(X).Return_Length_Address = 0 X = X + 1 Uai_ItmList(X).End_of_list = 0 Status = Sys$Setuai(,,%Descr(Username(1:Username_Length)), / %Ref(Uai_Itmlist),,,) If (.Not. Status) Then Call Print_Status (Status) Call Exit Else c Print *, 'Saved password for user ', Username c Print * Endif Endif 100 Return End c Date Written: 3-Jul-91 c c Figure 5 - Set_new_password.For c c Author: Sri Seshadri, DCSS, Sugarland, TX 77478 c c Subroutines in this module: c [SUBROUTINE] Set_New_Password (Username, Username_Length, Ret_val) c c ******************************************************************* c c Modification History: c c Functional Description: c this subroutine calls the SYS$HASH_PASSWORD system service to find c the hash value of a password. You need to provide it a Salt value c (a word), a username (character string), and a password to be hashed c (character string). It uses the current Algorithm for the user (which c corressponds to UAI$C_PURDY_S) Algorithm. Other Algorithms are c UAI$C_PURDY_V, UAI$C_PURDY, UAI$C_AD_II or an Algorithm of your choice. c c Calling format: c CALL Set_New_Password (Username, Username_Length, Ret_val) c c Argument: Username - Character String (Input) c Username_Length Integer (Input) c Ret_val - Integer (Output -- 0 = OK, 1 = Error) c c Include files: SRP_INCLUDE.INC for data declarations c SUBROUTINE Set_New_Password (Username, Username_Length, Ret_val) Include 'SRP_INCLUDE.INC' Character * 32 Pwd ! password to be hashed Integer * 4 Pwd_Len ! Length of password Integer * 2 Salt ! Salt to be used Integer * 4 Hash(2) ! hashed value of password Integer I, Ret_val Byte Alg Ret_Val = 1 ! assume error c ... get the Salt and encryption Alg... x = 1 Uai_ItmList(X).Length = 2 Uai_ItmList(X).Item_Code = uai$_Salt Uai_ItmList(X).Buffer_Address = %loc(Salt) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).Length = 1 Uai_ItmList(X).Item_Code = uai$_encrypt Uai_ItmList(X).Buffer_Address = %loc(Alg) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).end_of_list = 0 Status = Sys$Getuai(,,%descr(username(1:username_Length)), / %ref(uai_itmlist),,,) If (.Not. Status) Then Call Print_Status (Status) Call Exit Endif Print * Print *, 'Enter new password for ', username(1:username_Length), / ' (in all caps)' Read (5, 10, end=100) Pwd_Len, Pwd 10 Format (q,a) Print * If (pwd .eq. ' ') Then Pwd_Len = Username_Length Pwd = Username Print *, 'Password same as username' Print * Endif DO 50 i = 1, Pwd_Len If ((Pwd(i:i) .GE. 'a') .AND. (Pwd(i:i) .LE. 'z')) Then Pwd(i:i) = Char(Ichar(Pwd(i:i))-32) Endif 50 Continue Status = Sys$Hash_Password (Pwd(1:Pwd_Len), %val(Alg), %val(Salt), / Username(1:Username_Length), %ref(Hash)) If (.Not. Status) Then Call Print_Status (Status) Call Exit Endif x = 1 Uai_ItmList(X).Length = 8 Uai_ItmList(X).Item_Code = uai$_pwd Uai_ItmList(X).Buffer_Address = %loc(hash) Uai_ItmList(X).Return_Length_Address = 0 x = x + 1 Uai_ItmList(X).end_of_list = 0 Status = Sys$Setuai(,,%descr(username(1:username_Length)), / %ref(uai_itmlist),,,) If (.Not. Status) Then Call Print_Status (Status) Call Exit Else Print *, 'Password set for Username ', Username Print * Print * Endif Ret_Val = 0 ! we're OK if we reach here... Return 100 Print *, 'Password change aborted...' Call Exit End $! Figure 1. SRP.COM $! Written by: Sridhar Seshadri, DCSS, Sugarland, TX 77478 $! Description: Command procedure to compile each of the individual $! source programs and link them together. Creates a system logical $! called SRP$ROOT (input). Also, Installs the image $! $! Requirements: VMS 5.4 and above, V5.3 FORTRAN $! 12 free global sections and 2 free global pages $! $ x = 'f$verify (0)' $ say = "write sys$output" $ on warning then exit $ set proc/priv=(sysnam,sysprv,cmkrnl) $ type/page nl: $! $ read sys$command what/End=exit_it - /prompt="Device and directory for SRP$ROOT: (default SYS$SYSDEVICE:[SRP]) " $ say " " $ if what .eqs. "" then what = "sys$sysdevice:[srp]" $ create/dir/prot=(s:rwe,o:rwe,w:e,g)/own=system/log 'what' $ Define/System/Exec SRP$Root 'what' $ say " " $ say "Add the logical name definition to Sys$manager:Systartup_V5.com" $ say "$ Define/System/Exec SRP$Root ''what'" $ say " " $! $ For = "Fortran/extend/warn=decl" $ say " " $ say "Compiling programs... (may take a few minutes)" $ for srp ! Figure 2 $ for general ! Figure 3 $ for save_password ! Figure 4 $ for set_new_password ! Figure 5 $ for restore_password ! Figure 6 $ link/notrace srp,general,save_password,set_new_password,restore_password $ copy/log SRP.EXE SRP$Root:*.*/log/Prot=(g,w) $! $ MCR Authorize Add/Id SRP_User Grant/ID SRP_User SYSTEM ! for example $! $ Set file/Acl=(Id=SRP_User,Acc=Execute) SRP$ROOT:SRP.EXE $! make sure there are enough global pages/global sections $ INSTALL Add/open/head/share/priv=(sysprv) SRP$Root:SRP $ say " " $ say "Add the lines to INSTALL the image to your SYSTARTUP_V5.COM" $ Say "$ INSTALL"" $ say "Add/open/head/share/priv=(sysprv) SRP$Root:SRP" $ say " " $ say "To run SRP, type $ RUN SRP$ROOT:SRP" $ say " " $ Exit_It: $ exit c Date written: 03-Jun-1991 c c Figure 2 - SRP.FOR (Main Program) c (includes SRP_INCLUDE.INC) c c Author: Sri Seshadri, DCSS, Sugarland, TX 77478 c c Functional description: c c This program is used to save and restore the password of a given c user. It assumes that the password is to be saved in the user c portion of the UAF record. It also assumes the record is to be c retrieved from the user portion of the UAF record. c c Subroutines in this module: c None c c Data format in the user data portion of UAF record (15 bytes): c c Sanity Check value (4 bytes --- value Sanity), c Hash value of primary password (8 bytes), c Seed (2 bytes), c Encryption Algorithm of primary password (1 byte) c c Privileges required: c SYSPRV or BYPASS to write a UAF record c Program Save_Restore_Pwd Integer Choice, Ret_Val Integer Username_Length ! Length of specifed user Integer Confirm_Owner ! Function Character * 12 Username Call Clear_Screen Call Check_Decnet Print *, 'Warning! This program is used to save and restore passwords' Print *, 'for a given user by using the user data portion of the UAF.' Print *, 'Press Control-Z at this time to exit!' Print * Call Get_Username (Username, Username_Length) If (Confirm_Owner(Username, Username_Length) .ne. 0) Call Exit Call Save_Password (Username, Username_Length) Call WriteLog ('Accessing UAF information for ' // Username) Call Set_New_Password (Username, Username_Length, Ret_Val) If (Ret_Val .EQ. 0) Then Call Set_Host_Zero ! login as new user Call Restore_Password (Username, Username_Length) Endif End c c Date Written: 3-Jun-91 c c Figure 7 - SRP_INCLUDE.INC c c Author: Sri Seshadri, DCSS, Sugarland, TX 77478 c c Subroutines in this module: c None c c This is the include file that contains the data structures for a UAF record. c It is used to set/get hashed password, encrypt algorithm, and salt. c It is also used to get the user data portion of the UAF. c c c Data format in the UAF record (15 byte): c c Sanity Check value (4 bytes --- value Sanity), c Hash value (8 bytes), c Seed (2 bytes), c Encryption Algorithm (1 byte) c implicit none include '($rmsdef)' include '($ssdef)' include '($uaidef)' include '($syssrvnam)' character username*12, / user_data*255, user_data_b*255 ! 255-byte user data structure /getuai_itemlist/ ! an UAF record union map integer*2 length, item_code integer*4 buffer_address, return_length_address end map map integer*4 end_of_list end map end union end structure Integer*4 username_length, ! actual username length / status, X, / user_data_length, / Sanity ! safeguard at retrieval Parameter (Sanity=11090323) ! some arbitrary number structure /struct/ ! 15-byte storage of user's Union ! password information map integer * 4 sanity ! safeguard integer * 4 hash(2) ! encrytped password integer * 2 salt ! random value used to encrypt byte alg ! algorithm used in encryption end map map character * 15 string ! size of the above struct end map End Union end structure record /struct/ a, b ! to map the userdata record /getuai_itemlist/ uai_itmlist(4)