Admin Alert: Correcting and Expanding the Program to Change User Passwords on the Fly
August 19, 2009 Joe Hertvik
Last week, I published code that allows authorized users to change user profile passwords with a simple call command. However, several readers emailed me about a mistake that will stop the code from compiling. This week, I’m publishing a correction to the code, as well as a new function for preventing the code from changing passwords for security officers, security administrators, and users with all object access.
Correction: Updated Password Program Change Code
The PSSWRDCHG code I published last issue allows selected users without security administrator authority the ability to change a user profile password or to disable a user profile. Unfortunately, there were two errors in the code that: 1) prevented the code from compiling; and 2) disabled a key security function in the code. These errors are:
1. I inadvertently left out a variable that was referenced in the code. To allow the program to compile, you will need to add the following Declare CL Variable command (DCL) to define the &USER1 variable.
DCL VAR(&USER1) TYPE(*CHAR) LEN(1) /* 'First + digit of user profile to be changed' */
2. The code to ensure that passwords can only be changed for non-IBM users is wrong. The following code in lines 27.00-34.00 was intended to prevent users from changing the passwords for any IBM-supplied user profiles (profiles that start with the letter ‘Q’).
0027.00 CHGVAR VAR(&USER1) VALUE(%SUBSTRING(&USER 1 1) 0028.00 0029.00 /* This program can only be used for non-IBM users */ 0030.00 IF COND(&USER1 *NE 'Q') THEN(DO) 0031.00 SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Invalid + 0032.00 User. Cannot changes pwds for system users') 0033.00 GOTO CMDLBL(ENDPGM) 0034.00 ENDDO
Unfortunately, this code will not stop your users from using the program to change passwords for IBM-supplied profiles. If you uploaded and tested the code from last week, lines 27.00-34.00 should be rewritten to use the following commands:
0027.00 CHGVAR VAR(&USER1) VALUE(%SUBSTRING(&USER 1 1) 0028.00 0029.00 /* This program can only be used for non-IBM users */ 0030.00 IF COND(&USER1 *EQ 'Q') THEN(DO) 0031.00 SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Invalid + 0032.00 User. Cannot changes pwds for system users') 0033.00 GOTO CMDLBL(ENDPGM) 0034.00 ENDDO
Making the second change will allow the code to work correctly in stopping your users from changing passwords for IBM-supplied user profiles.
Note: The code in the previous article has been modified online to incorporate these changes. My apologies for any problems this mistake may have caused.
Some Ideas For Extending the Password Change Code
In addition to the code corrections, reader Carl Pitcher also wrote in with a few ideas for modifying the PSSWRDCHG program. Carl suggested I should consider changing the program to do the following:
1. Don’t allow changes to profiles that have special authorities *SECADM or *ALLOBJ for obvious reasons. You can do that by dumping the user profile to an outfile and scanning the UPSPAU field.
2. Instead of changing the password, you can just reset the password to the user profile name and set the password expiration interval to expired.
The nice thing about having a program like PSSWRDCHG is that you can modify it for whatever additional features you want to include. While I have already set up the code so that it wouldn’t let you make password changes to any user profile that starts with the letter ‘Q’, I also like Carl’s first suggestion. So I decided to decouple the IBM supplied user profile protection code from the PSSWRDCHG program and make a new program that can tell you whether or not it’s OK to change the password for a target user. The name of this program is CHECKUSERA (Check User Attributes) and it has two parameters:
1. User name (10 characters)–The name of the user profile you are considering changing the password for.
2. OK (1 character)–A simple Yes or No flag (Y/N) that tells the calling user or program whether it’s OK to change that user profile’s password.
The code checks to see whether the passed-in user is an IBM-supplied user, whether the user profile’s user class is equal to Security Officer (*SECADM) or Security Administrator (*SECADM), and whether the user has all object (*ALLOBJ) or security administrator (*SECADM) special authority privileges in its user profile. If the code finds any of these situations, it passes back a ‘N’ (No) in the OK parameter. If the code doesn’t find these situations, it passes back a ‘Y’ (Yes) in the OK parameter. If you wish to use the code in last week’s PSSWRDCHG change program to detect user profiles that you shouldn’t change passwords for, add the following pieces of code to the code.
1. Declare a new variable at the top of the program called &OK with this Declare CL Variable (DCL)command:
DCL VAR(&OK) TYPE(*CHAR) LEN(1) /* This variable + tells the calling user or program whether + or not it is OK to change the password + for this user */
2. Add the following lines to the code after line 35.00 to call CHECKUSERA and exit PSSWRDCHG if the program passes back an ‘N’ (No) in the &OK parameter.
CALL PGM(CHECKUSERA) PARM(&USER &OK) IF COND(&OK *EQ 'N') THEN(GOTO CMDLBL(ENDPGM))
And that will invoke the CHECKUSERA program to determine if this user should or should not have its password changed.
Here’s the CL code for CHECKUSERA that makes it happen:
0001.00 PGM PARM(&USER &OK) /* Pass in the user name + 0002.00 that the calling program is considering making a password change for. + 0003.00 The program passes back a Y or N variable that tells the + 0004.00 calling user or program whether it is + 0005.00 okay to change the password for this user */ 0006.00 0007.00 DCLF FILE(QADSPUPB) /* Declares the QADSPUPB + 0008.00 file, which is the system-supplied output file name for the + 0009.00 Display User Profile command (DSPUSRPRF) */ 0010.00 0011.00 DCL VAR(&USER) TYPE(*CHAR) LEN(10) /* The passed + 0012.00 in user name to be examined */ 0013.00 DCL VAR(&OK) TYPE(*CHAR) LEN(1) /* This variable + 0014.00 tells the calling user or program whether or not + 0015.00 it is OK to change the password + 0016.00 for this user */ 0017.00 DCL VAR(&POSITION) TYPE(*DEC) LEN(3 0) VALUE(1) + 0018.00 /* Counter variable to scan the special + 0019.00 attributes associated with this user */ 0020.00 DCL VAR(&USER1) TYPE(*CHAR) LEN(1) VALUE(' ') /* + 0021.00 Variable to contain 1st digit of user + 0022.00 profile name */ 0023.00 0024.00 CHGDTAARA DTAARA(*LDA (1 400)) VALUE(' ') /* Clear out + 0025.00 the LDA to put in messages from the + 0026.00 program */ 0027.00 0028.00 CHGVAR VAR(&OK) VALUE(Y) /* The program assumes that it's OK to + 0029.00 change the user password unless proven otherwise */ 0030.00 0031.00 CHGVAR VAR(&USER1) VALUE(%SUBSTRING(&USER 1 1)) /* + 0032.00 Retrieve the first digit of the passed in + 0033.00 user profile */ 0034.00 0035.00 /* Check to see whether the user name is an IBM-Supplied user profile (starts with Q) */ 0036.00 IF COND(&USER1 *EQ 'Q') THEN(DO) 0037.00 CHGDTAARA DTAARA(*LDA (1 50)) VALUE('Cannot change pwd + 0038.00 for IBM supplied user profile') 0039.00 CHGVAR VAR(&OK) VALUE(N) 0040.00 GOTO CMDLBL(ENDPGM) 0041.00 ENDDO 0042.00 0043.00 DSPUSRPRF USRPRF(&USER) OUTPUT(*OUTFILE) + 0044.00 OUTFILE(QTEMP/QADSPUPB) /* Retrieve the + 0045.00 user profile attributes for this user and + 0046.00 place the results in a file named QADSPUPB file in + 0047.00 the QTEMP library */ 0048.00 MONMSG MSGID(CPF2204 CPA0701) EXEC(DO) /* no user profile exists */ 0049.00 CHGDTAARA DTAARA(*LDA (1 50)) VALUE('User profile does + 0050.00 not exist') 0051.00 CHGVAR VAR(&OK) VALUE(N) 0052.00 GOTO CMDLBL(ENDPGM) 0053.00 ENDDO 0054.00 0055.00 OVRDBF FILE(QADSPUPB) TOFILE(QTEMP/QADSPUPB) /* + 0056.00 Override all calls to QADSPUPB to the + 0057.00 newly created QADSPUPB in QTEMP, which + 0058.00 holds all the attributes for this user + 0059.00 profile */ 0060.00 0061.00 RCVF /* Read the QTEMP/QADSPUPB file, which will + 0062.00 only have one record for the user under + 0063.00 examination */ 0064.00 MONMSG MSGID(CPF0864) EXEC(DO) /* If there are no + 0065.00 records in the file, it indicates that + 0066.00 the passed in user profile does not + 0067.00 exist. Set the OK flag to N and exit the + 0068.00 program */ 0069.00 GOTO CMDLBL(ENDPGM) 0070.00 ENDDO 0071.00 0072.00 IF COND((&UPUSCL *EQ '*SECOFR') *OR (&UPUSCL + 0073.00 *EQ '*SECADM')) THEN(DO) /* If the user + 0074.00 class parameter &UPUSCL is equal to Security Officer + 0075.00 (*SECOFR) or Security Administrator + 0076.00 (*SECADM), then set the OK flag to N (No) + 0077.00 so that the user password will not be + 0078.00 changed. */ 0079.00 CHGDTAARA DTAARA(*LDA (1 25)) VALUE('Found *QSECOFR OR *SECADM') 0080.00 CHGVAR VAR(&OK) VALUE(N) 0081.00 GOTO CMDLBL(ENDPGM) 0082.00 ENDDO 0083.00 0084.00 CHGVAR VAR(&POSITION) VALUE(1) /* Set the position + 0085.00 indicator to 1 */ 0086.00 0087.00 LOOP1: IF COND(&POSITION *LE 143) THEN(DO) /* Loop + 0088.00 through the &UPSPAU field (Special + 0089.00 authorities) looking for All Object + 0090.00 authority (*ALLOBJ) in the user profile. If found, set the OK + 0091.00 flag to N, put the reason for the + 0092.00 denial in the LDA, and exit the program */ 0093.00 IF COND(%SUBSTRING(&UPSPAU &POSITION 7) = + 0094.00 '*ALLOBJ') THEN(DO) 0095.00 CHGDTAARA DTAARA(*LDA (26 20)) VALUE('Found *ALLOBJ') 0096.00 CHGVAR VAR(&OK) VALUE(N) 0097.00 GOTO CMDLBL(ENDPGM) 0098.00 ENDDO 0099.00 CHGVAR VAR(&POSITION) VALUE(&POSITION + 1) 0100.00 GOTO CMDLBL(LOOP1) 0101.00 ENDDO 0102.00 0103.00 CHGVAR VAR(&POSITION) VALUE(1) 0104.00 0105.00 0106.00 LOOP2: IF COND(&POSITION *LE 143) THEN(DO) /* Loop + 0107.00 through the &UPSPAU field (special + 0108.00 authorities) looking for security + 0109.00 administrator authority (*SECADM). If + 0110.00 found, set the OK flag to N and put the + 0111.00 reason for the denial in the LDA. */ 0112.00 IF COND(%SUBSTRING(&UPSPAU &POSITION 7) = + 0113.00 '*SECADM') THEN(DO) 0114.00 CHGDTAARA DTAARA(*LDA (46 20)) VALUE('Found *SECADM') 0115.00 CHGVAR VAR(&OK) VALUE(N) 0116.00 GOTO CMDLBL(ENDPGM) 0117.00 ENDDO 0118.00 CHGVAR VAR(&POSITION) VALUE(&POSITION + 1) 0119.00 GOTO CMDLBL(LOOP2) 0120.00 ENDDO 0121.00 0122.00 0123.00 ENDPGM: ENDPGM
And here’s what each line does.
Lines 1.00-23.00 define the variables that are passed in to the program and all other variables used in the program.
Lines 24.00-30.00 clear out the local data area (*LDA) where results are kept for each operation that succeeds or fails. These lines also set the approval parameter (&OK) to an initial value of ‘Y’ (Yes).
Lines 31.00-42.00 check to see if the passed in user profile name is an IBM supplied profile. IBM supplied profiles start with the letter ‘Q’. If the profile name starts with ‘Q’, CHECKUSERA sets the approval parameter to ‘N’ (No) and exits the program.
Lines 43.00-54.00 use the Display User Profile (DSPUSRPRF) command to input all the user profile parameters into a copy of the QADSPUPB file in the QTEMP library. QADSPUPB is a system supplied output file for the DSPUSRPRF command. The program puts this information into QTEMP, so that the file will only be on the system while the user is signed on. If the passed in user profile doesn’t exist on the system, the DSPUSRPRF command will issue a CPF2204 error (‘User profile &1 not found’) or a CPA0701 error (‘&3 received by &1 at &4). The program monitors for these errors and if it finds them, it will output a description of the problem to the LDA, it changes the &OK approval parameter to ‘N’, and exit the program.
Lines 55.00-70.00 override all references to QADSPUPB to the newly created QADSPUPB file in QTEMP. The code opens the QTEMP/QADSPUPB file and retrieves the user profile parameters for the passed-in user into the program.
Lines 72.00-83.00 check to see if the user class (user parameter &UPUSCL in QADSPUPB) for the user profile is equal to a Security Officer (*SECOFR) or a Security Administrator (*SECADM). If true, the code changes the approval flag to ‘N’ and exits the program.
Lines 84.00-101.00 check the user profile’s special authorities (as defined in the &UPSPAU field of the QADSPUPB file) to see if the user has all object authority (*ALLOBJ). If it finds *ALLOBJ authority, it turns off the approval flag and exits the program.
Lines 103.00-120.00 check the user profile’s special authorities to see if the user has security administrator special authority (*SECADM). If it finds *SECADM in the security authorities, it turns off the approval flag and exits the program.
Line 123.00 exits the program with an End Program (ENDPGM) statement.
And that’s how I extended my code to more accurately detect user profiles whose passwords shouldn’t be changed by another user. Use this code as you see fit or as a springboard for other changes to PSSWRDCHG.
Carl’s Last Suggestion
In his email, Carl also suggested ignoring the passed-in user profile password in PSSWRDCHG in favor of resetting the user profile password to the user name (its default password) and expiring the password. By expiring the password, the user would be forced to change the password himself the first time he signs on. Changing the program like this takes all password assignment responsibility out of the hands of your i5/OS administrator and makes the user totally responsible for whatever password value that he wishes to assign to his user profile. To make this change in the PSSWRDCHG code, you can make and test the following code changes in last week’s program code.
1. Replace the call to the QSYCHGPW in lines 79.00-86.00 with the following Change User Profile (CHGUSRPRF) command:
CHGUSRPRF USRPRF(&USER) PASSWORD(&USER) PWDEXP(*YES)
2. Eliminate lines 87.00-128.00 in the code. These lines of code designate and record problems that the QSYCHGPW API may have had in changing the code. Since we are replacing QSYCHGPW with an expired default user profile password, we no longer need to monitor for these conditions.
This change is fairly easy to accomplish with the existing code. Once these changes are accomplished, PSSWRDCHG will ignore whatever value is passed in to the program in the &PASSWORD parameter and instead assign an expired default user profile password to the user profile.