/*****************************************************************************/ /* AuthVMS.c Licensed under the Apache License, Version 2.0 (the License); you may not use this software except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. This module provides functions related to the authentication of usernames via access to the SYSUAF and the hashed password and other entry flags, etc. It also processes VMS identifiers used for controlling access. Functions create and validate access against WASD's VMS user profile capability. Other functionality includes checking access to files, etc. via these for the server as well as users holding profiles. See AUTH.C for overall detail on the WASD authorization environment. VERSION HISTORY --------------- 27-AUG-2013 MGD bugfix; AuthVmsGetUai() interaction of logon= parameters 22-AUG-2012 MGD bugfix; AuthVmsGetUai() logon= fall through 11-OCT-2009 MGD AuthVmsGetUai() can now use [AuthSYSUAFlogonType] and/or an optional authorization rule parameter 'param="logon=.."' to specify the login mode (default is still NETWORK) a la ACME (and retrieves the access restriction days/hours associated with that mode). 18-NOV-2008 MGD bugfix; AuthVmsChangePassword() ensure that rqAuth.SysUafDataPtr is populated 30-MAY-2007 MGD AuthAccessCheck() bugfix; &ArmAccessItem 03-JUN-2005 MGD only check pwd2 pre-expiry if pwd2 is set 11-MAY-2005 MGD provide PWDMIX mixed-case plus printable char passwords in AuthVmsVerifyPassword() and AuthVmsChangePassword() 20-MAR-2005 MGD AuthVmsVerifyUser() WATCH which flag causes failure 18-MAR-2004 MGD rework $GETUAI() during ACME development, add check for pre-expired passwords 20-NOV-2003 MGD only check secondary password expiry date/time if the secondary password hash is not empty 24-JUL-2003 MGD remote username already massaged by AuthorizeRealm() 03-MAY-2003 MGD /SYSUAF=(VMS,ID) accomodated in AuthVmsVerifyUser() 30-JAN-2003 MGD authentication profile can be requested via rule, refine checks in AuthAccessReadCheck() and AuthAccessWriteCheck() 21-DEC-2002 MGD AuthVmsLoadIdentifiers() more flexible 05-OCT-2002 MGD allow wildcard specifications in AuthAccessReadCheck() 06-SEP-2002 MGD AuthAccessReadCheck() traps SS$_NOCALLPRIV returning SS$_NOPRIV to allow directory listings of DFS volumes 13-MAY-2002 MGD SYSUAF expired password redirection ([AuthSysUafPwdExpUrl]) 07-MAY-2002 MGD bugfix; account/password expiry 27-APR-2002 MGD use sys$setprv() 20-NOV-2001 MGD bugfix; /RELAXED should allow all but DISUSERed accounts to authenticate regardless of RESTRICTED or CAPTIVE flags 04-AUG-2001 MGD support module WATCHing 05-APR-2001 MGD bugfix; AuthAccessReadCheck() return SS$_NOPRIV 26-MAR-2001 MGD bugfix; sys$create_user_profile() length size from word (System Services Manual) to unsigned int (startlet.h)! 23-FEB-2001 MGD bugfix; identifier check OK should continue to load details 27-MAR-2000 MGD bugfix; AuthVmsCheckIdentifier() error return changed from AUTH_DENIED_BY_OTHER to AUTH_DENIED_BY_LOGIN 20-NOV-1999 MGD add nil-access identifier to bypass hour restrictions 02-OCT-1999 MGD VMS user profile WATCH enhancements bugfix; AuthCreateVmsUserProfile() 28-AUG-1999 MGD unbundled from AUTH.C for v6.1 */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include /* not defined VAX C 3.2 (probably unnecessary now 11-MAY-2005) */ #ifndef UAI$M_RESTRICTED #define UAI$M_RESTRICTED 0x8 #endif #ifndef UAI$C_PURDY_S #define UAI$C_PURDY_S 3 #endif /* probably not defined earlier than 7.3-2 */ #ifndef UAI$M_PWDMIX #define UAI$M_PWDMIX 0x2000000 #endif /* application related header files */ #include "wasd.h" #define WASD_MODULE "AUTHVMS" #if WATCH_MOD #define FI_NOLI WASD_MODULE, __LINE__ #else /* in production let's keep the exact line to ourselves! */ #define FI_NOLI WASD_MODULE, 0 #endif /******************/ /* global storage */ /******************/ unsigned int HttpdUserProfileLength; unsigned char *HttpdUserProfilePtr; $DESCRIPTOR (HttpdUserProfileDsc, ""); /********************/ /* external storage */ /********************/ extern BOOL AuthPolicySysUafRelaxed, AuthPolicySysUafIdentifiers, AuthPolicySysUafWasdIdentifiers, AuthPolicySysUafVms, AuthSysUafEnabled, AuthSysUafPromiscuous, AuthVmsUserProfileEnabled, AuthVmsUserProfileNoRule; extern BOOL InstanceMutexHeld[]; extern int ServerPort; extern int64 HttpdTime64; extern unsigned long AuthHttpsOnlyVmsIdentifier, AuthNilAccessVmsIdentifier, AuthPasswordChangeVmsIdentifier, AuthWasdPwdVmsIdentifier, AuthWasdHttpsVmsIdentifier, AuthWasdReadVmsIdentifier, AuthWasdWriteVmsIdentifier; extern unsigned long SysPrvMask[]; extern char ErrorSanityCheck[], SoftwareID[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern HTTPD_PROCESS HttpdProcess; extern MSG_STRUCT Msgs; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Get the specified username's flags, authorized privileges, quadword password, hash salt and encryption algorithm from SYSUAF into an allocated SYSUAF authentication data buffer. */ int AuthVmsGetUai ( REQUEST_STRUCT *rqptr, char *UserName ) { static $DESCRIPTOR (UserNameDsc, ""); static unsigned long Context = -1; int cnt, status, AccessP, AccessS, LogonType; ulong *qptr1, *qptr2; char *cptr, *sptr, *zptr; AUTH_SYSUAF *uafptr; VMS_ITEM_LIST3 *itmptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsGetUai() !&Z", UserName); /* double-check! */ if (!AuthSysUafEnabled) return (AUTH_DENIED_BY_LOGIN); uafptr = rqptr->rqAuth.SysUafDataPtr = (AUTH_SYSUAF*)VmGetHeap (rqptr, sizeof(AUTH_SYSUAF)); zptr = (sptr = uafptr->UserName) + sizeof(uafptr->UserName)-1; for (cptr = UserName; *cptr && sptr < zptr; *sptr++ = to_upper(*cptr++)); *sptr = '\0'; uafptr->UserNameLength = sptr - uafptr->UserName; itmptr = &uafptr->UaiItemList; itmptr->buf_len = sizeof(uafptr->UaiUic); itmptr->item = UAI$_UIC; itmptr->buf_addr = &uafptr->UaiUic; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiOwner)-1; itmptr->item = UAI$_OWNER; itmptr->buf_addr = &uafptr->UaiOwner; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiExpTime64); itmptr->item = UAI$_EXPIRATION; itmptr->buf_addr = &uafptr->UaiExpTime64; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiPwdDateTime64); itmptr->item = UAI$_PWD_DATE; itmptr->buf_addr = &uafptr->UaiPwdDateTime64; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiPwd2DateTime64); itmptr->item = UAI$_PWD2_DATE; itmptr->buf_addr = &uafptr->UaiPwd2DateTime64; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiPwdLifeTime64); itmptr->item = UAI$_PWD_LIFETIME; itmptr->buf_addr = &uafptr->UaiPwdLifeTime64; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiFlags); itmptr->item = UAI$_FLAGS; itmptr->buf_addr = &uafptr->UaiFlags; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiPriv); itmptr->item = UAI$_PRIV; itmptr->buf_addr = &uafptr->UaiPriv; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiPwd); itmptr->item = UAI$_PWD; itmptr->buf_addr = &uafptr->UaiPwd; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiPwd2); itmptr->item = UAI$_PWD2; itmptr->buf_addr = &uafptr->UaiPwd2; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiEncrypt); itmptr->item = UAI$_ENCRYPT; itmptr->buf_addr = &uafptr->UaiEncrypt; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(uafptr->UaiSalt); itmptr->item = UAI$_SALT; itmptr->buf_addr = &uafptr->UaiSalt; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = sizeof(unsigned long); itmptr->item = UAI$_PRIMEDAYS; itmptr->buf_addr = &uafptr->UaiPrimeDays; itmptr->ret_len = 0; itmptr++; if (!(cptr = rqptr->rqAuth.PathParameterPtr)) cptr = ""; while (*cptr && (to_lower(*cptr) != 'l' || !strsame(cptr,"logon=",6))) cptr++; if (*cptr) { /* same set of parameters as used by AuthAcmeVerifyUser() */ if (strsame (cptr, "logon=NETWORK", 13)) { LogonType = AUTH_LOGON_TYPE_NETWORK; AccessP = UAI$_NETWORK_ACCESS_P; AccessS = UAI$_NETWORK_ACCESS_S; } else if (strsame (cptr, "logon=BATCH", 11)) { LogonType = AUTH_LOGON_TYPE_BATCH; AccessP = UAI$_BATCH_ACCESS_P; AccessS = UAI$_BATCH_ACCESS_S; } else if (strsame (cptr, "logon=LOCAL", 11)) { LogonType = AUTH_LOGON_TYPE_LOCAL; AccessP = UAI$_LOCAL_ACCESS_P; AccessS = UAI$_LOCAL_ACCESS_S; } else if (strsame (cptr, "logon=DIALUP", 12)) { LogonType = AUTH_LOGON_TYPE_DIALUP; AccessP = UAI$_DIALUP_ACCESS_P; AccessS = UAI$_DIALUP_ACCESS_S; } else if (strsame (cptr, "logon=REMOTE", 12)) { LogonType = AUTH_LOGON_TYPE_REMOTE; AccessP = UAI$_REMOTE_ACCESS_P; AccessS = UAI$_REMOTE_ACCESS_S; } else if (strsame (cptr, "logon=", 6)) { AccessP = AccessS = LogonType = 0; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "unknown LOGON TYPE \"!AZ\"", cptr); } } else if (Config.cfAuth.SysUafLogonType) { switch (LogonType = Config.cfAuth.SysUafLogonType) { case AUTH_LOGON_TYPE_NETWORK : AccessP = UAI$_NETWORK_ACCESS_P; AccessS = UAI$_NETWORK_ACCESS_S; break; case AUTH_LOGON_TYPE_BATCH : AccessP = UAI$_BATCH_ACCESS_P; AccessS = UAI$_BATCH_ACCESS_S; break; case AUTH_LOGON_TYPE_LOCAL : AccessP = UAI$_LOCAL_ACCESS_P; AccessS = UAI$_LOCAL_ACCESS_S; break; case AUTH_LOGON_TYPE_DIALUP : AccessP = UAI$_DIALUP_ACCESS_P; AccessS = UAI$_DIALUP_ACCESS_S; break; case AUTH_LOGON_TYPE_REMOTE : AccessP = UAI$_REMOTE_ACCESS_P; AccessS = UAI$_REMOTE_ACCESS_S; break; default : ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } } else { LogonType = AUTH_LOGON_TYPE_NETWORK; AccessP = UAI$_NETWORK_ACCESS_P; AccessS = UAI$_NETWORK_ACCESS_S; } rqptr->rqAuth.SysUafLogonType = LogonType; itmptr->buf_len = 3; itmptr->item = AccessP; itmptr->buf_addr = &uafptr->UaiAccessP; itmptr->ret_len = 0; itmptr++; itmptr->buf_len = 3; itmptr->item = AccessS; itmptr->buf_addr = &uafptr->UaiAccessS; itmptr->ret_len = 0; itmptr++; memset (itmptr, 0, sizeof(VMS_ITEM_LIST3)); UserNameDsc.dsc$a_pointer = uafptr->UserName; UserNameDsc.dsc$w_length = uafptr->UserNameLength; /* turn on SYSPRV to allow access to SYSUAF records */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$getuai (0, &Context, &UserNameDsc, &uafptr->UaiItemList, 0, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { if (status == RMS$_RNF) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF username"); return (AUTH_DENIED_BY_LOGIN); } rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_DATABASE_VMS); ErrorVmsStatus (rqptr, status, FI_LI); return (status); } /* eliminate the counted string, strip trailing spaces */ cptr = (sptr = uafptr->UaiOwner) + 1; for (cnt = uafptr->UaiOwner[0]; cnt >= 0; cnt--) *sptr++ = *cptr++; *sptr = '\0'; while (sptr > uafptr->UaiOwner && isspace(*(sptr-1))) sptr--; *sptr = '\0'; if (WATCHING (rqptr, WATCH_AUTH)) { qptr1 = &uafptr->UaiPwd; qptr2 = &uafptr->UaiPwd2; WatchThis (WATCHITM(rqptr), WATCH_AUTH, "GETUAI \"!AZ\" !&S", uafptr->UserName, status); if (VMSok (status)) WatchDataFormatted ( "expire:!&D pwd:!&D(!8XL!8XL) pwd2:!&D(!8XL!8XL) life:!&D\n\ flags:!8XL priv:<63-32>!8XL<31-00>!8XL\n\ logon:!AZ prime:!8XL primary:!6XL secondary:!6XL\n", &uafptr->UaiExpTime64, &uafptr->UaiPwdDateTime64, qptr1[1], qptr1[0], &uafptr->UaiPwd2DateTime64, qptr2[1], qptr2[0], &uafptr->UaiPwdLifeTime64, &uafptr->UaiFlags, uafptr->UaiPriv[1], uafptr->UaiPriv[0], AuthSysUafLogonType(LogonType), uafptr->UaiPrimeDays, uafptr->UaiAccessP, uafptr->UaiAccessS); } return (status); } /*****************************************************************************/ /* Verify the request username/password hash against the SYSUAF password. Check if the primary password has expired and if it has the redirect to a configured 'change password' URL or deny access. */ int AuthVmsVerifyPassword (REQUEST_STRUCT* rqptr) { static char Password [AUTH_MAX_PASSWORD_LENGTH+1]; static $DESCRIPTOR (PasswordDsc, Password); static $DESCRIPTOR (UserNameDsc, ""); int status; char *cptr, *sptr, *zptr; AUTH_SYSUAF *uafptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsVerifyPassword() !&Z pwdmix:!&B", rqptr->rqAuth.SysUafDataPtr->UserName, rqptr->rqAuth.SysUafDataPtr->UaiFlags & UAI$M_PWDMIX); if (!(uafptr = rqptr->rqAuth.SysUafDataPtr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* flag that case-less username and password checks were performed */ rqptr->rqAuth.CaseLess = true; UserNameDsc.dsc$a_pointer = uafptr->UserName; UserNameDsc.dsc$w_length = uafptr->UserNameLength; zptr = (sptr = Password) + sizeof(Password)-1; if (uafptr->UaiFlags & UAI$M_PWDMIX) { /* 7.3-2 mixed-case plus printable char passwords */ for (cptr = rqptr->RemoteUserPassword; *cptr && sptr < zptr; *sptr++ = *cptr++); } else { /* to uppercase! */ for (cptr = rqptr->RemoteUserPassword; *cptr && sptr < zptr; *sptr++ = to_upper(*cptr++)); } *sptr = '\0'; PasswordDsc.dsc$w_length = sptr - Password; status = sys$hash_password (&PasswordDsc, uafptr->UaiEncrypt, uafptr->UaiSalt, &UserNameDsc, &uafptr->HashedPwd); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_USER); ErrorVmsStatus (rqptr, status, FI_LI); return (status); } if (uafptr->HashedPwd != uafptr->UaiPwd) { if (!AuthSysUafPromiscuous) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF password"); return (AUTH_DENIED_BY_LOGIN); } } /* assumes flag has been set by a prior call to AuthVmsVerifyUser() */ if (rqptr->rqAuth.SysUafPwdExpired) { /* password has expired */ if (!strsame (rqptr->rqHeader.RequestUriPtr, INTERNAL_PASSWORD_CHANGE, sizeof(INTERNAL_PASSWORD_CHANGE)-1)) { /* and not in the process of changing it */ if (rqptr->rqPathSet.AuthSysUafPwdExpUrlPtr) cptr = rqptr->rqPathSet.AuthSysUafPwdExpUrlPtr; else if (Config.cfAuth.SysUafPwdExpUrl[0]) cptr = Config.cfAuth.SysUafPwdExpUrl; else cptr = NULL; if (cptr) { /* expired password URL is configured */ if (!strsame (rqptr->rqHeader.RequestUriPtr, cptr, -1)) { /* request URI doesn't match it though, so redirect */ ResponseLocation (rqptr, cptr, -1); status = AUTH_DENIED_BY_REDIRECT; } } else { /* is not configured */ status = AUTH_DENIED_BY_LOGIN; } } } return (status); } /*****************************************************************************/ /* Verify the SYSUAF-authenticated request username conforms to other SYSUAF requirements, such as password not expired, rights identifier authentication control, etc. Assumes the username/password has already, or will be, validated against the SYSUAF in some manner, either directly as occurs here in AUTHVMS.C or via the ACME as occurs in AUTHACME.C. Some things checked during ACME authentication (such as password expiry, prime and secondary days) are redone here. This function additionally checks for privleged accounts and rights identifier requirements. I do not intend to further granulate this functionality and so any redundancy will be lived with. Returns a success status if user password authenticated, AUTH_DENIED_BY_LOGIN if not authenticated, or other error status if a genuine VMS error occurs (which should be reported where and when it occurs). */ int AuthVmsVerifyUser (REQUEST_STRUCT* rqptr) { /* UAI flags that disallow an identifier-controlled account access */ static unsigned long DisallowFlags = UAI$M_DISACNT | UAI$M_PWD_EXPIRED | UAI$M_PWD2_EXPIRED; /* UAI flags that disallow SYSUAF-controlled account access */ static unsigned long DisallowVmsFlags = UAI$M_DISACNT | UAI$M_PWD_EXPIRED | UAI$M_PWD2_EXPIRED | UAI$M_CAPTIVE | UAI$M_RESTRICTED; static $DESCRIPTOR (UserNameDsc, ""); static unsigned long Context = -1; BOOL AlreadyLocked; int status, LockStatus; int64 PwdExpTime64; char *cptr, *sptr, *zptr; AUTH_CREC *acrptr; AUTH_SYSUAF *uafptr; VMS_ITEM_LIST3 *itmptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsVerifyUser() !&Z", rqptr->rqAuth.SysUafDataPtr->UserName); if (!(AlreadyLocked = InstanceMutexHeld[INSTANCE_MUTEX_AUTH_CACHE])) InstanceMutexLock (INSTANCE_MUTEX_AUTH_CACHE); status = AuthCacheFindRecord (rqptr->rqAuth.CacheSearchRecordPtr, &acrptr); if (VMSnok (status)) { /* something has happened to the cache record in the meantime! */ if (AlreadyLocked) return (AUTH_DENIED_BY_OTHER); InstanceMutexUnLock (INSTANCE_MUTEX_AUTH_CACHE); return (AUTH_DENIED_BY_OTHER); } if (!AlreadyLocked) InstanceMutexUnLock (INSTANCE_MUTEX_AUTH_CACHE); if (!(uafptr = rqptr->rqAuth.SysUafDataPtr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* copy these data to the authorization cache record */ acrptr->UaiPrimeDays = uafptr->UaiPrimeDays; acrptr->UaiAccessP = uafptr->UaiAccessP; acrptr->UaiAccessS = uafptr->UaiAccessS; /*******************************/ /* check flags and expirations */ /*******************************/ /* automatically disallow if any of these flags are set! */ if ((AuthPolicySysUafIdentifiers || AuthPolicySysUafWasdIdentifiers || AuthPolicySysUafRelaxed) && (uafptr->UaiFlags & DisallowFlags)) { if (WATCHING (rqptr, WATCH_AUTH)) { if (uafptr->UaiFlags & UAI$M_DISACNT) cptr = "DISACNT"; else if (uafptr->UaiFlags & UAI$M_PWD_EXPIRED) cptr = "PWD_EXPIRED"; else if (uafptr->UaiFlags & UAI$M_PWD2_EXPIRED) cptr = "PWD2_EXPIRED"; else cptr = "?"; WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF flag:!AZ", cptr); } return (AUTH_DENIED_BY_LOGIN); } /* automatically disallow if any of these flags are set! */ if (((AuthPolicySysUafVms && !AuthPolicySysUafRelaxed) || (!AuthPolicySysUafIdentifiers && !AuthPolicySysUafRelaxed)) && (uafptr->UaiFlags & DisallowVmsFlags)) { if (WATCHING (rqptr, WATCH_AUTH)) { if (uafptr->UaiFlags & UAI$M_DISACNT) cptr = "DISACNT"; else if (uafptr->UaiFlags & UAI$M_PWD_EXPIRED) cptr = "PWD_EXPIRED"; else if (uafptr->UaiFlags & UAI$M_PWD2_EXPIRED) cptr = "PWD2_EXPIRED"; else if (uafptr->UaiFlags & UAI$M_CAPTIVE) cptr = "CAPTIVE"; else if (uafptr->UaiFlags & UAI$M_RESTRICTED) cptr = "RESTRICTED"; else cptr = "?"; WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF flag:!AZ", cptr); } return (AUTH_DENIED_BY_LOGIN); } if (uafptr->UaiExpTime64) { /* check account expiration */ if (uafptr->UaiExpTime64 < HttpdTime64) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF account expired"); return (AUTH_DENIED_BY_LOGIN); } } rqptr->rqAuth.SysUafPwdExpired = false; if (!Config.cfAuth.SysUafAcceptExpPwd && uafptr->UaiPwdDateTime64 == DELTA64_ZERO) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF pwd pre-expired"); rqptr->rqAuth.SysUafPwdExpired = true; } else if (!Config.cfAuth.SysUafAcceptExpPwd && uafptr->UaiPwdDateTime64 && uafptr->UaiPwdLifeTime64) { /* check password expiration */ PwdExpTime64 = uafptr->UaiPwdDateTime64 + uafptr->UaiPwdLifeTime64; if (PwdExpTime64 > HttpdTime64) { rqptr->rqAuth.NoCache = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF pwd expired"); /* only after the password is confirmed OK *then* do any redirect */ rqptr->rqAuth.SysUafPwdExpired = true; } } if (uafptr->UaiPwd2) { /* the secondary password has been set */ if (!Config.cfAuth.SysUafAcceptExpPwd && uafptr->UaiPwd2DateTime64 == DELTA64_ZERO) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF pwd2 pre-expired"); return (AUTH_DENIED_BY_LOGIN); } else if (!Config.cfAuth.SysUafAcceptExpPwd && uafptr->UaiPwd2DateTime64 && uafptr->UaiPwdLifeTime64) { /* check password 2 expiration */ PwdExpTime64 = uafptr->UaiPwd2DateTime64 + uafptr->UaiPwdLifeTime64; if (PwdExpTime64 > HttpdTime64) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF pwd2 expired"); return (AUTH_DENIED_BY_LOGIN); } } } /*******************************/ /* identifier/privilege checks */ /*******************************/ if (AuthPolicySysUafIdentifiers) { /* check SYSUAF-authenticated account holds the correct identifier */ if (VMSnok (status = AuthVmsLoadIdentifiers (rqptr, uafptr->UaiUic))) return (status); if (VMSnok (status = AuthVmsCheckIdentifier (rqptr))) return (status); } if (!AuthPolicySysUafRelaxed && rqptr->rqAuth.SourceRealm == AUTH_SOURCE_VMS && (uafptr->UaiPriv[0] & PRV$M_SYSPRV)) { /* not allowing all accounts, exclude those with extended privileges */ if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF privileges"); return (AUTH_DENIED_BY_LOGIN); } rqptr->rqAuth.UserDetailsLength = strlen(uafptr->UaiOwner); rqptr->rqAuth.UserDetailsPtr = cptr = VmGetHeap (rqptr, rqptr->rqAuth.UserDetailsLength+1); strzcpy (cptr, uafptr->UaiOwner, rqptr->rqAuth.UserDetailsLength+1); return (SS$_NORMAL); } /****************************************************************************/ /* SYSUAF authentication is being controlled by possession of an identifier ... got the identifier you can be SYSUAF-authenticated, haven't then you cant! See description at beginning of module. Returns normal status if account possesses suitable identifier, AUTH_DENIED_BY_LOGIN if it doesn't, or error status. */ AuthVmsCheckIdentifier (REQUEST_STRUCT *rqptr) { BOOL HoldsRealmId, HoldsWasdReadId, HoldsWasdWriteId; unsigned long ThisId; unsigned long *idptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsCheckIdentifier()"); if (!rqptr->rqAuth.VmsIdentifiersPtr) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); HoldsRealmId = HoldsWasdReadId = HoldsWasdWriteId = false; for (idptr = rqptr->rqAuth.VmsIdentifiersPtr; ThisId = *idptr; idptr++) { if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "ID %X!8XL !%I", ThisId, ThisId); if (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_ID && ThisId == rqptr->rqAuth.RealmVmsIdentifier) { HoldsRealmId = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", rqptr->rqAuth.RealmPtr); continue; } if (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_WASD_ID) { if (ThisId == AuthWasdReadVmsIdentifier) { HoldsWasdReadId = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_WASD_READ_VMS_ID); continue; } if (ThisId == AuthWasdWriteVmsIdentifier) { HoldsWasdWriteId = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_WASD_WRITE_VMS_ID); continue; } } if (ThisId == AuthHttpsOnlyVmsIdentifier) { /* account is only allowed to authenticate via SSL */ rqptr->rqAuth.HttpsOnly = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_HTTPS_ONLY_VMS_ID); continue; } if (ThisId == AuthNilAccessVmsIdentifier) { /* account may always authenticate even if hours seem to prohibit */ rqptr->rqAuth.SysUafNilAccess = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_NIL_ACCESS_VMS_ID); continue; } if (ThisId == AuthPasswordChangeVmsIdentifier) { /* account is allowed to change it's password via the server */ rqptr->rqAuth.SysUafCanChangePwd = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_PASSWORD_CHANGE_VMS_ID); continue; } if (ThisId == AuthWasdHttpsVmsIdentifier) { /* account is only allowed to authenticate via SSL */ rqptr->rqAuth.HttpsOnly = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_WASD_HTTPS_VMS_ID); continue; } if (ThisId == AuthWasdPwdVmsIdentifier) { /* account is allowed to change it's password via the server */ rqptr->rqAuth.SysUafCanChangePwd = true; if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", AUTH_WASD_PWD_VMS_ID); continue; } } /************************/ /* check authentication */ /************************/ if (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_ID && !HoldsRealmId) { /* authentication is by local identifier ... and doesn't have it */ if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF access identifier"); return (AUTH_DENIED_BY_LOGIN); } if (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_WASD_ID && !HoldsWasdWriteId && !HoldsWasdReadId) { /* authentication only with WASD identifier .. and doesn't have it */ if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FAIL SYSUAF access identifier"); return (AUTH_DENIED_BY_LOGIN); } return (SS$_NORMAL); } /****************************************************************************/ /* Allocate dynamic memory to contain a zero-terminated vector of rights identifiers held by the specified UIC. Read those identifiers. Restart if insufficient space, increasing buffer space by a factor of two, until the number of identifiers becomes rediculous. */ AuthVmsLoadIdentifiers ( REQUEST_STRUCT *rqptr, unsigned long UaiUic ) { static struct { unsigned long Uic; unsigned long Unused; } Holder = { 0, 0 }; int status, idcnt, idalloc; unsigned long Ctx, ThisId; unsigned long *idptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsLoadIdentifiers()"); /* if already loaded then just continue */ if (rqptr->rqAuth.VmsIdentifiersPtr) return (SS$_NORMAL); idalloc = AUTH_MIN_VMS_IDENTIFIERS; while (idalloc < AUTH_MAX_VMS_IDENTIFIERS) { /* plus one longword as a sentinal (will contain zero) */ rqptr->rqAuth.VmsIdentifiersPtr = idptr = VmGetHeap (rqptr, (idalloc+1)*sizeof(unsigned long)); Holder.Uic = UaiUic; rqptr->rqAuth.VmsIdentifiersCount = 0; idcnt = idalloc; /* turn on SYSPRV to allow this to be done */ sys$setprv (1, &SysPrvMask, 0, 0); Ctx = 0; while (VMSok (status = sys$find_held (&Holder, &ThisId, 0, &Ctx))) { if (--idcnt) { *idptr++ = ThisId; rqptr->rqAuth.VmsIdentifiersCount++; continue; } status = SS$_BUFFEROVF; VmFreeFromHeap (rqptr, rqptr->rqAuth.VmsIdentifiersPtr, FI_LI); idptr = NULL; idalloc *= 2; sys$finish_rdb (&Ctx); break; } /* sentinal (not really necessary, but let's be over-cautious) */ if (idptr) *idptr = 0; sys$setprv (0, &SysPrvMask, 0, 0); /* if we got to the end of the identifiers without mishap */ if (status == SS$_NOSUCHID) return (SS$_NORMAL); } /*********/ /* error */ /*********/ sys$finish_rdb (&Ctx); rqptr->rqAuth.VmsIdentifiersPtr = NULL; rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_DATABASE_VMS); ErrorVmsStatus (rqptr, status, FI_LI); return (status); } /****************************************************************************/ /* Look through the request's array of a VMS authenticated user's identifiers for the specified one. Return zero to indicate the identifier was not held, normal to indicate it was, or any other error status. */ int AuthVmsHoldsIdentifier ( REQUEST_STRUCT *rqptr, char *IdentifierName, unsigned long ThisId ) { unsigned long *idptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsHoldsIdentifier() !&Z !&X", IdentifierName, ThisId); /* if not already loaded via SYSUAF authentication then really an error */ if (!rqptr->rqAuth.VmsIdentifiersPtr) { /* has NOT been authenticated via the SYSUAF (shouldn't happen!) */ if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "IDENTIFIER request not SYSUAF authenticated"); rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_AUTH_ACCESS_DENIED), FI_NOLI); return (SS$_ABORT); } if (!ThisId) return (false); for (idptr = rqptr->rqAuth.VmsIdentifiersPtr; *idptr && *idptr != ThisId; idptr++) { if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "ID !&S !%I", *idptr, *idptr); } if (*idptr) { if (WATCHING (rqptr, WATCH_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_AUTH, "HOLDS identifier !AZ", IdentifierName); return (SS$_NORMAL); } return (0); } /****************************************************************************/ /* */ AuthVmsHoldsWasdGroupIdent ( REQUEST_STRUCT *rqptr, char *GroupName ) { static char GroupIdName [AUTH_MAX_REALM_GROUP_LENGTH+1] = AUTH_WASD__GROUP_VMS_ID; static $DESCRIPTOR (GroupIdNameDsc, GroupIdName); int status; unsigned long ThisId; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsHoldsWasdGroupIdent() !&Z", GroupName); zptr = (sptr = GroupIdName) + sizeof(GroupIdName); for (cptr = GroupName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr >= zptr) { ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); return (SS$_ABORT); } *sptr = '\0'; GroupIdNameDsc.dsc$w_length = sptr - GroupIdName; if (VMSnok (status = sys$asctoid (&GroupIdNameDsc, &ThisId, 0))) { rqptr->rqResponse.ErrorTextPtr = GroupIdName; ErrorVmsStatus (rqptr, status, FI_LI); return (status); } return (AuthVmsHoldsIdentifier (rqptr, GroupIdName, ThisId)); } /*****************************************************************************/ /* Using sys$create_user_profile() create a permanent security profile of the SYSUAF-authenticated user name. This will be stored in the cache and attached to the request structure each time the user is re-authenticated from it. This profile will be used to check file/directory (see AuthAccessReadCheck()) access permission against the authenticated user not the server account. */ int AuthVmsCreateUserProfile (REQUEST_STRUCT* rqptr) { static unsigned long Context = -1; static unsigned char *UserProfilePtr; static $DESCRIPTOR (UserNameDsc, ""); int status; unsigned int UserProfileLength; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthVmsCreateUserProfile() !&Z", rqptr->RemoteUser); if (!UserProfilePtr) UserProfilePtr = VmGet (AUTH_CREATE_USER_PROFILE_SIZE); UserProfileLength = AUTH_CREATE_USER_PROFILE_SIZE; rqptr->rqAuth.VmsUserProfilePtr = NULL; rqptr->rqAuth.VmsUserProfileLength = 0; UserNameDsc.dsc$a_pointer = rqptr->RemoteUser; UserNameDsc.dsc$w_length = rqptr->RemoteUserLength; /* turn on SYSPRV to allow this to be done */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$create_user_profile (&UserNameDsc, 0, 0, UserProfilePtr, &UserProfileLength, &Context); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = "sys$create_user_profile()"; ErrorVmsStatus (rqptr, status, FI_LI); return (status); } if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) { WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "PROFILE !&Z !UL", rqptr->RemoteUser, UserProfileLength); WatchDataDump (UserProfilePtr, UserProfileLength); } rqptr->rqAuth.VmsUserProfilePtr = VmGetHeap (rqptr, rqptr->rqAuth.VmsUserProfileLength = UserProfileLength); memcpy (rqptr->rqAuth.VmsUserProfilePtr, UserProfilePtr, UserProfileLength); return (SS$_NORMAL); } /*****************************************************************************/ /* Using sys$create_user_profile() create a permanent security profile of the HTTPd server account. Returns no status, exits server if error encountered. */ AuthVmsCreateHttpdProfile () { static unsigned long Flags = 0; int status; char HttpdUserProfile [512]; $DESCRIPTOR (HttpdUserNameDsc, ""); /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthVmsCreateHttpdProfile()"); if (HttpdUserProfileLength) return; HttpdUserNameDsc.dsc$a_pointer = HttpdProcess.UserName; HttpdUserNameDsc.dsc$w_length = strlen(HttpdProcess.UserName); status = sys$create_user_profile (&HttpdUserNameDsc, 0, Flags, HttpdUserProfile, &HttpdUserProfileLength, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "sys$create_user_profile()", FI_LI); if (WATCH_MODULE(WATCH_MOD_AUTH)) { WatchThis (WATCHALL, WATCH_MOD_AUTH, "PROFILE !&Z !UL", HttpdProcess.UserName, HttpdUserProfileLength); WatchDataDump (HttpdUserProfile, HttpdUserProfileLength); } HttpdUserProfilePtr = VmGet (HttpdUserProfileLength); memcpy (HttpdUserProfilePtr, HttpdUserProfile, HttpdUserProfileLength); HttpdUserProfileDsc.dsc$a_pointer = HttpdUserProfilePtr; HttpdUserProfileDsc.dsc$w_length = HttpdUserProfileLength; } /*****************************************************************************/ /* Change the specified username's ('rqptr->RemoteUser') password in the SYSUAF. It assumes the calling routine HTAdminChangePassword() has performed required preliminary sanity checks. This function is just to perform the change. */ AuthVmsChangePassword ( REQUEST_STRUCT *rqptr, char *PasswordNew ) { static unsigned long Context = -1; static unsigned long UaiPriv [2], HashedPwd [2], UaiPwd [2]; static unsigned short UaiSalt; static unsigned char UaiEncrypt; static char PasswordBuffer [AUTH_MAX_PASSWORD_LENGTH+1], UserNameBuffer [AUTH_MAX_USERNAME_LENGTH+1]; static VMS_ITEM_LIST3 GetItems [] = { { sizeof(UaiPwd), UAI$_PWD, &UaiPwd, 0 }, { sizeof(UaiEncrypt), UAI$_ENCRYPT, &UaiEncrypt, 0 }, { sizeof(UaiSalt), UAI$_SALT, &UaiSalt, 0 }, { 0, 0, 0, 0 } }; static VMS_ITEM_LIST3 SetItems [] = { { sizeof(HashedPwd), UAI$_PWD, &HashedPwd, 0 }, { 0, 0, 0, 0 } }; static $DESCRIPTOR (UserNameDsc, UserNameBuffer); static $DESCRIPTOR (PasswordDsc, PasswordBuffer); BOOL AccessAllowed; int status; char *cptr, *sptr, *zptr; AUTH_SYSUAF *uafptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthVmsChangePassword()"); if (WATCHING (rqptr, WATCH_RESPONSE)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "CHANGE SYSUAF password"); if (!AuthSysUafEnabled) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (!(uafptr = rqptr->rqAuth.SysUafDataPtr)) { if (VMSnok (status = AuthVmsGetUai (rqptr, rqptr->RemoteUser))) ErrorExitVmsStatus (status, ErrorSanityCheck, FI_LI); if (!(uafptr = rqptr->rqAuth.SysUafDataPtr)) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } if (AuthPolicySysUafRelaxed) AccessAllowed = true; else { if ((rqptr->rqAuth.SourceRealm == AUTH_SOURCE_VMS && !rqptr->rqAuth.SourceGroupWrite) || (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_VMS && rqptr->rqAuth.SourceGroupWrite == AUTH_SOURCE_ID) || (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_ID && rqptr->rqAuth.SourceGroupWrite == AUTH_SOURCE_ID)) AccessAllowed = true; else if (rqptr->rqAuth.SourceRealm == AUTH_SOURCE_WASD_ID && rqptr->rqAuth.SysUafCanChangePwd) AccessAllowed = true; else AccessAllowed = false; } if (!AccessAllowed) { rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_AUTH_ACCESS_DENIED), FI_LI); HTAdminChangePasswordEnd (rqptr); return; } /* yet more checking! */ zptr = (sptr = PasswordBuffer) + sizeof(PasswordBuffer)-1; cptr = PasswordNew; if (uafptr->UaiFlags & UAI$M_PWDMIX) { /* 7.3-2 mixed-case plus printable char passwords */ while (*cptr && sptr < zptr) { if (!isprint(*cptr) || *cptr == '\"') break; *sptr++ = *cptr++; } } else { /* all upper-case alphanumeric plus dollar and underscore only */ while (*cptr && sptr < zptr) { if (!(isalnum(*cptr) || *cptr == '_' || *cptr == '$')) break; *sptr++ = to_upper(*cptr++); } } if (*cptr) { /* unacceptable character present */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTADMIN_PWD_ERROR), FI_LI); HTAdminChangePasswordEnd (rqptr); return; } *sptr = '\0'; PasswordDsc.dsc$w_length = sptr - PasswordBuffer; zptr = (sptr = UserNameBuffer) + sizeof(UserNameBuffer)-1; for (cptr = rqptr->RemoteUser; *cptr && sptr < zptr; *sptr++ = to_upper(*cptr++)); *sptr = '\0'; UserNameDsc.dsc$w_length = sptr - UserNameBuffer; /* turn on SYSPRV to allow access to SYSUAF records */ sys$setprv (1, &SysPrvMask, 0, 0); UserNameDsc.dsc$w_length = rqptr->RemoteUserLength; status = sys$getuai (0, &Context, &UserNameDsc, &GetItems, 0, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_USER); ErrorVmsStatus (rqptr, status, FI_LI); HTAdminChangePasswordEnd (rqptr); return; } status = sys$hash_password (&PasswordDsc, UaiEncrypt, UaiSalt, &UserNameDsc, &HashedPwd); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_USER); ErrorVmsStatus (rqptr, status, FI_LI); HTAdminChangePasswordEnd (rqptr); return; } /* turn on SYSPRV to allow access to SYSUAF records */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$setuai (0, 0, &UserNameDsc, &SetItems, 0, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_USER); ErrorVmsStatus (rqptr, status, FI_LI); HTAdminChangePasswordEnd (rqptr); return; } HTAdminChangePasswordEnd (rqptr); } /*****************************************************************************/