John Earl Tribute - January 9, 2013 - Vol 3, Issue 21
Tracking QSECOFR with the IBM Security APIs
By John Earl
In our shop we have several people who need to use the QSECOFR user profile, but I have a problem trying to figure out who makes which changes as the security officer. Is there any way to reliably track who was signed on as the security officer when a change happened?
Yes. In fact there are many. But first, let me commend you for not giving *ALLOBJ and other special authorities to your programmers all of the time. It's simply too dangerous to allow someone to operate on a daily basis with the i/OS equivalent of a nuclear missile strapped to his hip. Let me also suggest that you not give the QSECOFR password to your programmers or anyone in your shop for that matter. It is better to create a QSECOFR clone (you can call it ZSECOFR) and give the programmers the ability to use that profile for emergency work. I like the QSECOFR clone idea because you are less likely of getting the QSECOFR profile corrupted, broken, disabled, or otherwise unusable. An alternate security officer can have all of the power of QSECOFR but still leave the QSECOFR profile available for those critical emergencies when nothing else will work. As an added precaution, you might even choose to give the alternate security officer profile slightly less authority than QSECOFR typically carries (remove *SERVICE and *AUDIT for example), yet still allow it to function successfully as an emergency profile.
More from John on Tracking Security Officers
To answer your original question about tracking who is using that powerful profile, allow me to point you towards a solution that I have used successfully for some time. The solution uses the Get Profile Handle and Set Profile APIs to perform password validation and profile swapping for a user who you want to assume the authority of a security officer. To implement this solution, create user profile ZSECOFR as a clone of QSECOFR. For safety purposes, remove *AUDIT and *SERVICE special authority, because they offer the ability to change things, such as disk configurations and object auditing, that you might not want changed as a matter of normal application recovery. Don't assign a password to ZSECOFR, and set the initial program to *NONE and the initial menu to *SIGNOFF because you don't ever want anyone to sign on directly as that profile. The create command for the ZSECOFR user profile might look like this:
CRTUSRPRF USRPRF(ZSECOFR) PASSWORD(*NONE) + USRCLS(*SECOFR) INLMNU(*SIGNOFF) + TEXT('Alternate Security Officer') + SPCAUT(*ALLOBJ *IOSYSCFG *JOBCTL + *SAVSYS *SECADM *SPLCTL)
For good measure, you can also turn on auditing for the user ZSECOFR so that everything that is done by this user is logged to the QAUDJRN security-auditing journal as well. The command to accomplish user profile auditing might look like this:
CHGUSRAUD USRPRF(ZSECOFR) + OBJAUD(*ALL) + AUDLVL(*CMD *CREATE *DELETE *OBJMGT *SAVRST *SECURITY + *SERVICE *SYSMGT)
Now you need a program that will allow your programmers to assume ZSECOFR's identity. In this case, you're going to use the profile swapping APIs rather than the usual method of adopting authority, because you want a clear audit trail whenever someone uses ZSECOFR authority. Program SWPUSRPRF shown below uses three of the user profile APIs to accomplish this. The first API, QSYGETPH, accepts the user profile name and the valid password for the user profile you want to become. After QSYGETPH validates authority, it returns a twelve-character hexadecimal profile handle that can be passed to the second API, QWTSETP. QWTSETP actually performs the profile swap and changes the authority and current user of the job, and, finally, the QSYRLSPH API does the housekeeping by releasing the profile handle.
For security purposes, the profile handle that QSYGETPH provides is limited for use only in the job that it was created in. This precaution prevents someone from creating a profile handle in one job and then distributing it to other processes or saving a profile handle for clandestine use on another day.
Is There A Security Hole Here?
You may notice that the second parameter passed to the QSYGETPH API in the program below is *NOPWD. In this example program, SWPUSRPRF is not actually passing a valid password to the API, but rather requesting to bypass traditional password processing because the current user has *USE authority to the profile that we are retrieving the profile handle for. This bypass is permitted because there is a feature in i/OS user profile security that allows you to retrieve the handle of another user if you have *USE authority to that user's profile. If you take a moment to reflect upon this, you might get really scared. And, getting scared may be a good idea, because these two APIs already exist on your system and just about anyone could write a program such as the one below. But, before you get all worked up about holes in the operating system, you should understand that this feature is a fairly useful and common feature in other operating systems, particularly UNIX, and the swapping feature has some great applications in i/OS. But, best of all, it's easy to eliminate any risk that these APIs may introduce by simply making sure that none of your users have *USE authority to anyone else's profile (more on securing user profiles later).
Now that QSYGETPH has retrieved a profile handle for you, you can pass that profile handle to QWTSETP, and it will magically change your current job to run under the authority of ZSECOFR. When you first do this, it's really quite surprising. If you issue the DSPJOB command, you'll see that nearly everything else about your job stays the same: job name, library list, job description, etc. It's just that you are now running under the authority of ZSECOFR. The only clue you have that you're running as another user is that under option 1 of the DSPJOB command, the current user profile will be listed as ZSECOFR, even though the job name still contains the name of the original, signed-on user. If you have security auditing turned on with the *SECURITY option, there will also be an audit entry in the QAUDJRN journal that will mark your movement to ZSECOFR. The last piece of work to do is to clean up after yourself by releasing the profile handle so that it isn't lying around in your job for potential retrieval by anyone else. And, just for good measure and great accountability, the program also logs a record in the QAUDJRN that records the fact that you have swapped to a security officer profile.
Just for good measure, program SWPUSRPRF works well in reverse too. If you call the program when you already swapped to ZSECOFR, it will conveniently swap you back to yourself and log that fact in the security audit journal as well.
John on - Securing Your Security Application
Any time you build a new application, it's important to build security in, and that is doubly true for a security application. This program is going to need an elevated authority to run because these APIs are shipped with *PUBLIC *EXCLUDE authority and also because you need special authority in order to Send Journal Entries (SNDJRNE) into the QAUDJRN journal. Fortunately, the security for this application is not that complicated. For starters, we'll change the program to be owned be ZSECOFR and specify that it will adopt the authority of its *OWNER. This allows any user who can run the program to access the sensitive objects that SWPUSRPRF uses. But, because you likely don't want to give everyone in your organization access to ZSECOFR, you should also remove *PUBLIC's authority to the program and specifically grant *USE authority to any individual programmer who needs temporary security officer authority. Now you've got a powerful and traceable emergency profile that you can safely give to your IT staffers without losing track of who did what.
John's Swap User Profile Program - SWPUSER
PGM DCL VAR(&CHARACTER4) TYPE(*CHAR) LEN(4) DCL VAR(&CUR_USER) TYPE(*CHAR) LEN(10) DCL VAR(&ERRORDATA) TYPE(*CHAR) LEN(96) DCL VAR(&HANDLE) TYPE(*CHAR) LEN(12) DCL VAR(&JOURNDATA) TYPE(*CHAR) LEN(336) DCL VAR(&MSGDATA) TYPE(*CHAR) LEN(80) DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) DCL VAR(&NUMBER4) TYPE(*DEC) LEN(4 0) DCL VAR(&SEC_USER) TYPE(*CHAR) LEN(10) VALUE('ZSECOFR') DCL VAR(&SIGNONUSER) TYPE(*CHAR) LEN(10) DCL VAR(&TO_USER) TYPE(*CHAR) LEN(10) CheckUser: RTVJOBA USER(&SIGNONUSER) CURUSER(&CUR_USER) IF COND(&CUR_USER *NE &SEC_USER) THEN(DO) CHGVAR VAR(&TO_USER) VALUE(&SEC_USER) CHGVAR VAR(&JOURNDATA) VALUE(&SIGNONUSER *BCAT + 'Requested authority of' *BCAT &SEC_USER) ENDDO ELSE CMD(DO) CHGVAR VAR(&TO_USER) VALUE(&SIGNONUSER) CHGVAR VAR(&JOURNDATA) VALUE(&SIGNONUSER *BCAT + 'Released authority of' *BCAT &SEC_USER) ENDDO GetHandle: CHGVAR VAR(&NUMBER4) VALUE(96) CHGVAR VAR(&CHARACTER4) VALUE(&NUMBER4) CHGVAR VAR(%BIN(&ERRORDATA 1 4)) VALUE(&CHARACTER4) CALL PGM(QSYGETPH) PARM(&TO_USER '*NOPWD' &HANDLE + &ERRORDATA) CHGVAR VAR(&CHARACTER4) VALUE(%BIN(&ERRORDATA 5 4)) CHGVAR VAR(&NUMBER4) VALUE(&CHARACTER4) IF COND(&NUMBER4 > 0) THEN(DO) CHGVAR VAR(&MSGDATA) VALUE(%SST(&ERRORDATA 17 80)) CHGVAR VAR(&MSGID) VALUE(%SST(&ERRORDATA 9 7)) GOTO CMDLBL(LOGRESULT) ENDDO SwapUser: CHGVAR VAR(&NUMBER4) VALUE(96) CHGVAR VAR(&CHARACTER4) VALUE(&NUMBER4) CHGVAR VAR(%BIN(&ERRORDATA 1 4)) VALUE(&CHARACTER4) CALL PGM(QWTSETP) PARM(&HANDLE &ERRORDATA) CHGVAR VAR(&CHARACTER4) VALUE(%BIN(&ERRORDATA 5 4)) CHGVAR VAR(&NUMBER4) VALUE(&CHARACTER4) IF COND(&NUMBER4 > 0) THEN(DO) CHGVAR VAR(&MSGDATA) VALUE(%SST(&ERRORDATA 17 80)) CHGVAR VAR(&MSGID) VALUE(%SST(&ERRORDATA 9 7)) GOTO CMDLBL(LOGRESULT) ENDDO CHGVAR VAR(&MSGDATA) VALUE(&JOURNDATA) CHGVAR VAR(&MSGID) VALUE(CPF9898) RlsHandle: CHGVAR VAR(&NUMBER4) VALUE(96) CHGVAR VAR(&CHARACTER4) VALUE(&NUMBER4) CHGVAR VAR(%BIN(&ERRORDATA 1 4)) VALUE(&CHARACTER4) CALL PGM(QSYRLSPH) PARM(&HANDLE &ERRORDATA) CHGVAR VAR(&CHARACTER4) VALUE(%BIN(&ERRORDATA 5 4)) CHGVAR VAR(&NUMBER4) VALUE(&CHARACTER4) IF COND(&NUMBER4 > 0) THEN(DO) CHGVAR VAR(&MSGDATA) VALUE(%SST(&ERRORDATA 17 80)) CHGVAR VAR(&MSGID) VALUE(%SST(&ERRORDATA 9 7)) GOTO CMDLBL(LOGRESULT) ENDDO CHGVAR VAR(&MSGDATA) VALUE(&JOURNDATA) CHGVAR VAR(&MSGID) VALUE(CPF9898) LogResult: SNDPGMMSG MSGID(&MSGID) MSGF(QCPFMSG) MSGDTA(&MSGDATA) + TOPGMQ(*PRV) SNDJRNE JRN(QSYS/QAUDJRN) TYPE('SW') ENTDTA(&JOURNDATA) ENDPGM
Securing John's SWPUSER program
CHGOBJOWN OBJ(SWPUSRPRF) OBJTYPE(*PGM) NEWOWN(ZSECOFR) CHGPGM PGM(SWPUSRPRF) USRPRF(*OWNER) GRTOBJAUT OBJ(SWPUSRPRF) OBJTYPE(*PGM) USER(*PUBLIC) AUT(*EXCLUDE) GRTOBJAUT OBJ(SWPUSRPRF) OBJTYPE(*PGM) USER(DAN) AUT(*USE)
Running John's SWPUSER program
About the Author