/*****************************************************************************/ /* AuthCache.c THE GNU GENERAL PUBLIC LICENSE APPLIES DOUBLY TO ANYTHING TO DO WITH AUTHENTICATION AND AUTHORIZATION! This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License, or any later version. > This package is distributed in the hope that it will be useful, > but WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. INSTANCE SUPPORT ---------------- Note that within the global section no self-relative addresses can be used (i.e. a linked list cannot be used). All references into the section must be made relative to the starting address. This is due to the starting address not being fixed in per-process virtual memory. With the advent of multiple integrated "instances" executing on a single system and sharing incoming service requests it became highly desirable to provide a single global instance of the especially significant authentication cache resource. This has been done by making it work within the confines of globally shared memory (i.e. a global section and collection of global pages). This necessarily puts some constraints on the design and operation. Most notably normal memory allocation routines are impossible and the quantity of memory available for the required structures becomes constrained. Hence a fixed (but configurable) sized authentication cache, with records being reused as required. A typical message when there are too few cache entries for the concurrent number of requests requiring authorization is SS$_ITEMNOTFOUND (or "%SYSTEM-W-ITEMNOTFOUND, requested item cannot be returned"). This indicates the authorization cache is "thrashing" for space (i.e. too many concurrent requests attempting to use it). Increase the value of [AuthCacheEntriesMax], shut down all servers (to cause the global section to be deleted) and then restart. Typical message for insufficient storage space for an authorization cache entry id SS$_RESULTOVF or "%SYSTEM-F-RESULTOVF, resultant string overflow". This might occur if /PROFILE was in use and an account generated a particularly large one! Increase the value of [AuthCacheEntrySize], shut down all servers (to cause the global section to be deleted) and then restart. See AUTH.C for overall detail on the WASD authorization environment. VERSION HISTORY --------------- 04-JAN-2015 MGD bugfix; AuthCacheNeedsReval() AlreadyLocked (per JPP) 07-NOV-2013 MGD AuthCacheNeedsReval() so multiple cache entries for the same credentials do not trigger multiple revalidations 25-MAY-2013 MGD [AuthRevalidateLoginCookie] obsolete 09-SEP-2012 MGD TOKEN authentication 18-JUL-2010 MGD Uni Malaga pushes report items from !3ZL to !4ZL :-) 22-MAY-2007 JPP bugfix; AuthCacheGblSecInit() 16-MAY-2007 MGD AuthCacheReport() include authorization accounting 15-MAR-2007 MGD bugfix; agent mappings using VMS-USER: not being cached 08-SEP-2003 MGD allow for config-directory in cache 06-AUG-2003 MGD sanity checking for locked cache bugfix; AuthCacheAddRecord() 04-FEB-2003 MGD AuthCacheReset() minor refinements 06-AUG-2002 MGD enhance global section creation, bugfix; set AuthCacheRecordSize from HTTPD$CONFIG value 02-MAR-2002 MGD bugfix; read group cache offset assignment 20-OCT-2001 MGD significant cache rework (for instance sharing) 04-AUG-2001 MGD support module WATCHing 07-MAY-2000 MGD login cookie 08-APR-2000 MGD bugfix; AuthCacheFree() 04-MAR-2000 MGD use FaolToNet(), et.al. 24-DEC-1999 MGD break-in evasion period in report 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 /* application related header files */ #include "wasd.h" #define WASD_MODULE "AUTHCACHE" #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 */ /******************/ int AuthCacheRecordMax, AuthCacheRecordSize; AUTH_GBLSEC *AuthGblSecPtr; int AuthGblSecPages, AuthGblSecSize; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern BOOL AuthVmsUserProfileEnabled, InstanceNodeSupervisor; extern BOOL InstanceMutexHeld[]; extern int AuthGblSecVersion, GblPageCount, GblSectionCount, HttpdTickSecond, InstanceNodeConfig, InstanceEnvNumber; extern unsigned long GblSecPrvMask []; extern char ErrorSanityCheck[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern MSG_STRUCT Msgs; extern SYS_INFO SysInfo; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Initialize the authentication cache. */ AuthCacheInit (META_CONFIG *mcptr) { static char ProblemRecordSize [] = "Cache record size could be too small (adjusted upwards)"; int cnt, status; AUTH_CREC *acrptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheInit()"); if (AuthGblSecPtr) { /* cache has previously been initialized, just reset it */ AuthCachePurge (true); return; } AuthCacheRecordMax = Config.cfAuth.CacheEntriesMax; AuthCacheRecordSize = Config.cfAuth.CacheEntrySize; if (AuthCacheRecordMax < AUTH_DEFAULT_CACHE_RECORD_MAX) AuthCacheRecordMax = AUTH_DEFAULT_CACHE_RECORD_MAX; if (!AuthCacheRecordSize) if (AuthVmsUserProfileEnabled) AuthCacheRecordSize = AUTH_DEFAULT_CACHE_RECORD_PROFILE_SIZE; else AuthCacheRecordSize = AUTH_DEFAULT_CACHE_RECORD_SIZE; /* let's round it to a 64 byte chunk */ if (AuthCacheRecordSize % 64) AuthCacheRecordSize = ((AuthCacheRecordSize / 64) + 1) * 64; if (AuthVmsUserProfileEnabled && AuthCacheRecordSize < AUTH_DEFAULT_CACHE_RECORD_PROFILE_SIZE) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemRecordSize); AuthCacheRecordSize = AUTH_DEFAULT_CACHE_RECORD_PROFILE_SIZE; } else if (AuthCacheRecordSize < AUTH_DEFAULT_CACHE_RECORD_SIZE) { MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemRecordSize); AuthCacheRecordSize = AUTH_DEFAULT_CACHE_RECORD_PROFILE_SIZE; } AuthCacheGblSecInit (mcptr); } /*****************************************************************************/ /* If only one instance can execute (from configuration) then allocate a block of process-local dynamic memory and point to that as the cache. If multiple instances create and map a global section and point to that. */ int AuthCacheGblSecInit (META_CONFIG *mcptr) { static char ReportCacheRecords [] = "Cache for !UL records of !UL bytes in !AZ of !UL page(let)s"; /* global, allocate space, system, in page file, writable */ static int CreFlags = SEC$M_GBL | SEC$M_EXPREG | SEC$M_SYSGBL | SEC$M_PAGFIL | SEC$M_WRT; static int DelFlags = SEC$M_SYSGBL; /* system & owner full access, group and world no access */ static unsigned long ProtectionMask = 0xff00; /* it is recommended to map into any virtual address in the region (P0) */ static unsigned long InAddr [2] = { 0x200, 0x200 }; int attempt, status, BaseGblSecPages, CacheRecordPoolSize, PageCount, SetPrvStatus; short ShortLength; unsigned long RetAddr [2]; char GblSecName [32]; $DESCRIPTOR (GblSecNameDsc, GblSecName); AUTH_GBLSEC *gsptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheGblSecInit()"); CacheRecordPoolSize = AuthCacheRecordSize * AuthCacheRecordMax; AuthGblSecSize = sizeof(AUTH_GBLSEC) + CacheRecordPoolSize; AuthGblSecPages = AuthGblSecSize / 512; if (AuthGblSecSize & 0x1ff) AuthGblSecPages++; if (InstanceNodeConfig <= 1) { /* no need for a global section, just use process-local storage */ AuthGblSecPtr = (AUTH_GBLSEC*)VmGet (AuthGblSecPages * 512); sys$gettim (&AuthGblSecPtr->SinceTime64); MetaConReport (mcptr, METACON_REPORT_INFORM, ReportCacheRecords, AuthCacheRecordMax, AuthCacheRecordSize, "local storage", AuthGblSecPages); return (SS$_CREATED); } FaoToBuffer (GblSecName, sizeof(GblSecName), &ShortLength, GBLSEC_NAME_FAO, HTTPD_NAME, AUTH_GBLSEC_VERSION_NUMBER, InstanceEnvNumber, "AUTH"); GblSecNameDsc.dsc$w_length = ShortLength; if VMSnok ((SetPrvStatus = sys$setprv (1, &GblSecPrvMask, 0, 0))) ErrorExitVmsStatus (SetPrvStatus, "sys$setprv()", FI_LI); for (attempt = 1; attempt <= 2; attempt++) { /* create and/or map the specified global section */ sys$setprv (1, &GblSecPrvMask, 0, 0); status = sys$crmpsc (&InAddr, &RetAddr, 0, CreFlags, &GblSecNameDsc, 0, 0, 0, AuthGblSecPages, 0, ProtectionMask, AuthGblSecPages); sys$setprv (0, &GblSecPrvMask, 0, 0); if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "sys$crmpsc() !&S begin:!UL end:!UL", status, RetAddr[0], RetAddr[1]); PageCount = (RetAddr[1]+1) - RetAddr[0] >> 9; AuthGblSecPtr = gsptr = (AUTH_GBLSEC*)RetAddr[0]; AuthGblSecPages = PageCount; if (VMSnok (status) || status == SS$_CREATED) break; /* section already exists, break if 'same size' and version! */ if (gsptr->GblSecVersion && gsptr->GblSecVersion == AuthGblSecVersion && gsptr->GblSecLength == AuthGblSecSize) break; /* delete the current global section, have one more attempt */ sys$setprv (1, &GblSecPrvMask, 0, 0); status = sys$dgblsc (DelFlags, &GblSecNameDsc, 0); sys$setprv (0, &GblSecPrvMask, 0, 0); status = SS$_IDMISMATCH; } if (VMSnok (status)) { /* must have this global section! */ char String [256]; FaoToBuffer (String, sizeof(String), NULL, "1 global section, !UL global pages", AuthGblSecPages); ErrorExitVmsStatus (status, String, FI_LI); } if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "GBLSEC \"!AZ\" page(let)s:!UL !&S", GblSecName, PageCount, status); MetaConReport (mcptr, METACON_REPORT_INFORM, ReportCacheRecords, AuthCacheRecordMax, AuthCacheRecordSize, status == SS$_CREATED ? "a new global section" : "an existing global section", AuthGblSecPages); if (status == SS$_CREATED) { /* first time it's been mapped */ memset (gsptr, 0, PageCount * 512); gsptr->GblSecVersion = AuthGblSecVersion; gsptr->GblSecLength = AuthGblSecSize; sys$gettim (&AuthGblSecPtr->SinceTime64); } GblSectionCount++; GblPageCount += PageCount; return (status); } /*****************************************************************************/ /* Create a request-local record of authorization information. */ void AuthCacheRequestRecord ( REQUEST_STRUCT *rqptr, AUTH_CREC **RecordPtrPtr ) { char *cptr, *sptr; AUTH_CREC *acrptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheRequestRecord()"); acrptr = (AUTH_CREC*)VmGetHeap (rqptr, AuthCacheRecordSize); *RecordPtrPtr = acrptr; PUT_QUAD_QUAD (rqptr->rqTime.BeginTime64, acrptr->LastAccessTime64); acrptr->SourceRealm = rqptr->rqAuth.SourceRealm; /* "fixed" record fields */ strcpy (acrptr->Realm, rqptr->rqAuth.RealmPtr); acrptr->RealmLength = rqptr->rqAuth.RealmLength; acrptr->UserNameLength = rqptr->RemoteUserLength; strcpy (acrptr->UserName, rqptr->RemoteUser); strcpy (acrptr->Password, rqptr->RemoteUserPassword); /* "dynamic" record fields */ cptr = sptr = (char*)acrptr->Storage; memcpy (acrptr->ConfigDirectory = cptr, rqptr->ConfigDirectory, rqptr->ConfigDirectoryLength + 1); acrptr->ConfigDirectoryLength = rqptr->ConfigDirectoryLength; acrptr->ConfigDirectoryOffset = cptr - sptr; cptr += acrptr->ConfigDirectoryLength + 1; memcpy (acrptr->PathParameter = cptr, rqptr->rqAuth.PathParameterPtr, rqptr->rqAuth.PathParameterLength + 1); acrptr->PathParameterLength = rqptr->rqAuth.PathParameterLength; acrptr->PathParameterOffset = cptr - sptr; cptr += acrptr->PathParameterLength + 1; acrptr->SourceGroupWrite = rqptr->rqAuth.SourceGroupWrite; memcpy (acrptr->GroupWrite = cptr, rqptr->rqAuth.GroupWritePtr, rqptr->rqAuth.GroupWriteLength + 1); acrptr->GroupWriteLength = rqptr->rqAuth.GroupWriteLength; acrptr->GroupWriteOffset = cptr - sptr; cptr += acrptr->GroupWriteLength + 1; acrptr->SourceGroupRead = rqptr->rqAuth.SourceGroupRead; memcpy (acrptr->GroupRead = cptr, rqptr->rqAuth.GroupReadPtr, rqptr->rqAuth.GroupReadLength + 1); acrptr->GroupReadLength = rqptr->rqAuth.GroupReadLength; acrptr->GroupReadOffset = cptr - sptr; cptr += acrptr->GroupReadLength + 1; /* note where we can start storing user details, VMS profile, etc. */ acrptr->StorageNextOffset = cptr - sptr; /* small sanity check */ if (AuthCacheRecordSize - (cptr - (char*)acrptr) <= 0) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } /*****************************************************************************/ /* Add the supplied authorization information to the (global section) cache. */ int AuthCacheAddRecord ( AUTH_CREC *aptr, AUTH_CREC **RecordPtrPtr ) { int cnt, OldestSecond, RecordCount; char *cptr; unsigned char *RecordPoolPtr; AUTH_CREC *acrptr, *acr2ptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheAddRecord() !UL", AuthGblSecPtr->CacheRecordCount); if (InstanceNodeConfig > 1 && !InstanceMutexHeld[INSTANCE_MUTEX_AUTH_CACHE]) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); RecordCount = AuthGblSecPtr->CacheRecordCount; RecordPoolPtr = AuthGblSecPtr->CacheRecordPool; if (RecordCount < AuthCacheRecordMax) { /* fresh record */ acrptr = RecordPoolPtr + (AuthCacheRecordSize * RecordCount); AuthGblSecPtr->CacheRecordCount++; } else { /* all entries in list in use, reuse the least recently accessed */ OldestSecond = HttpdTickSecond + 999999; acrptr = acr2ptr = RecordPoolPtr; for (cnt = 0; cnt < RecordCount; cnt++) { acrptr = RecordPoolPtr + (AuthCacheRecordSize * cnt); if (acrptr->LastAccessTickSecond < OldestSecond) { acr2ptr = acrptr; OldestSecond = acrptr->LastAccessTickSecond; } if (acrptr->SourceRealm) continue; break; } AuthGblSecPtr->CacheReuseCount++; acrptr = acr2ptr; memset (acrptr, 0, AuthCacheRecordSize); } /* copy contents of source record */ memcpy (acrptr, aptr, AuthCacheRecordSize); /* adjust variable length field pointers using the field offsets */ cptr = (char*)acrptr->Storage; acrptr->ConfigDirectory = cptr + acrptr->ConfigDirectoryOffset; acrptr->PathParameter = cptr + acrptr->PathParameterOffset; acrptr->GroupWrite = cptr + acrptr->GroupWriteOffset; acrptr->GroupRead = cptr + acrptr->GroupReadOffset; sys$gettim (&acrptr->LastAccessTime64); acrptr->LastAccessTickSecond = HttpdTickSecond; acrptr->LastAccessMinutesAgo = acrptr->LastAccessSecondsAgo = 0; *RecordPtrPtr = acrptr; return (SS$_NORMAL); } /*****************************************************************************/ /* Add additional details to the cached record. */ int AuthCacheAddRecordDetails ( REQUEST_STRUCT *rqptr, AUTH_CREC *acrptr ) { int StorageRemaining; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheAddRecordDetails()"); if (InstanceNodeConfig > 1 && !InstanceMutexHeld[INSTANCE_MUTEX_AUTH_CACHE]) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); acrptr->DetailsUpdated = true; sptr = (char*)acrptr->Storage; cptr = sptr + acrptr->StorageNextOffset; StorageRemaining = AuthCacheRecordSize - (cptr - (char*)acrptr); StorageRemaining -= rqptr->rqAuth.UserDetailsLength + 1; if (StorageRemaining <= 0) { ErrorNoticed (rqptr, SS$_RESULTOVF, NULL, FI_LI); return (SS$_RESULTOVF); } memcpy (acrptr->UserDetails = cptr, rqptr->rqAuth.UserDetailsPtr ? rqptr->rqAuth.UserDetailsPtr : "", rqptr->rqAuth.UserDetailsLength + 1); acrptr->UserDetailsLength = rqptr->rqAuth.UserDetailsLength; acrptr->UserDetailsOffset = acrptr->UserDetails - sptr; cptr += acrptr->UserDetailsLength + 1; StorageRemaining -= rqptr->rqAuth.VmsUserProfileLength; if (StorageRemaining <= 0) { ErrorNoticed (rqptr, SS$_RESULTOVF, NULL, FI_LI); return (SS$_RESULTOVF); } if (acrptr->VmsUserProfileLength = rqptr->rqAuth.VmsUserProfileLength) { memcpy (acrptr->VmsUserProfilePtr = cptr, rqptr->rqAuth.VmsUserProfilePtr, rqptr->rqAuth.VmsUserProfileLength); acrptr->VmsUserProfileLength = rqptr->rqAuth.VmsUserProfileLength; acrptr->VmsUserProfileOffset = acrptr->VmsUserProfilePtr - sptr; cptr += acrptr->VmsUserProfileLength; } /* note where we could if we needed to start storing more detail */ acrptr->StorageNextOffset = cptr - sptr; return (SS$_NORMAL); } /*****************************************************************************/ /* Search all records in the cache for those with the same realm and username. Check any entries found for revalidation timeout. If one is found that does *not* require revalidation (i.e. has recently enough been revalidated) then return false. If none are found or all require revalidation return true. */ BOOL AuthCacheNeedsReval ( REQUEST_STRUCT *rqptr, AUTH_CREC *acr1ptr ) { BOOL AlreadyLocked; int cnt, HitCount, RecordCount; unsigned long LastAccessMinutesAgo; unsigned char *RecordPoolPtr; AUTH_CREC *acr2ptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthCacheNeedsReval() !AZ !AZ !UL", acr1ptr->Realm, acr1ptr->UserName, rqptr->rqAuth.RevalidateTimeout); if (rqptr->AuthRevalidateCount) return (false); if (!(AlreadyLocked = InstanceMutexHeld[INSTANCE_MUTEX_AUTH_CACHE])) InstanceMutexLock (INSTANCE_MUTEX_AUTH_CACHE); RecordCount = AuthGblSecPtr->CacheRecordCount; RecordPoolPtr = AuthGblSecPtr->CacheRecordPool; for (cnt = HitCount = 0; cnt < RecordCount; cnt++) { acr2ptr = RecordPoolPtr + (AuthCacheRecordSize * cnt); if (!acr2ptr->SourceRealm) continue; if (WATCH_MODULE(WATCH_MOD_AUTH) && WATCH_MODULE(WATCH_MOD__DETAIL)) WatchDataFormatted ("!UL !UL\n!&Z !&Z\n!&Z !&Z\n!&Z !&Z\n", acr1ptr->SourceRealm, acr2ptr->SourceRealm, acr1ptr->Realm, acr2ptr->Realm, acr1ptr->ConfigDirectory, acr2ptr->ConfigDirectory, acr1ptr->UserName, acr2ptr->UserName); /* compare the records */ if (acr1ptr->SourceRealm != acr2ptr->SourceRealm) continue; if (acr1ptr->UserNameLength != acr2ptr->UserNameLength) continue; if (acr1ptr->RealmLength != acr2ptr->RealmLength) continue; if (acr1ptr->ConfigDirectoryLength != acr2ptr->ConfigDirectoryLength) continue; if (strcmp (acr1ptr->Realm, acr2ptr->Realm)) continue; if (strcmp (acr1ptr->UserName, acr2ptr->UserName)) continue; if (acr1ptr->ConfigDirectoryLength && strcmp (acr1ptr->ConfigDirectory, acr2ptr->ConfigDirectory)) continue; /* got this far, must have matched */ LastAccessMinutesAgo = (HttpdTickSecond - acr2ptr->LastAccessTickSecond) / 60; if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchDataFormatted ("!UL < !UL !UL\n", LastAccessMinutesAgo, rqptr->rqAuth.RevalidateTimeout, LastAccessMinutesAgo < rqptr->rqAuth.RevalidateTimeout ? HitCount+1 : HitCount); /* there will always be one (itself) so we need a second */ if (LastAccessMinutesAgo < rqptr->rqAuth.RevalidateTimeout) if (HitCount++) break; } if (!AlreadyLocked) InstanceMutexUnLock (INSTANCE_MUTEX_AUTH_CACHE); if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "!&?TRUE\rFALSE\r", HitCount <= 1); return (HitCount <= 1); } /*****************************************************************************/ /* */ int AuthCacheFindRecord ( AUTH_CREC *acr1ptr, AUTH_CREC **RecordPtrPtr ) { int cnt, MinutesAgo, RecordCount; unsigned char *RecordPoolPtr; AUTH_CREC *acr2ptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheFindRecord() !UL !UL", AuthGblSecPtr->CacheRecordCount, acr1ptr->FindRecordCount); if (InstanceNodeConfig > 1 && !InstanceMutexHeld[INSTANCE_MUTEX_AUTH_CACHE]) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); RecordCount = AuthGblSecPtr->CacheRecordCount; RecordPoolPtr = AuthGblSecPtr->CacheRecordPool; for (cnt = 0; cnt < RecordCount; cnt++) { acr2ptr = RecordPoolPtr + (AuthCacheRecordSize * cnt); if (!acr2ptr->SourceRealm) continue; if (WATCH_MODULE(WATCH_MOD_AUTH) && WATCH_MODULE(WATCH_MOD__DETAIL)) WatchDataFormatted ( "!UL !UL\n!UL !UL\n!UL !UL\n!&Z !&Z\n!&Z !&Z\n\ !&Z !&Z\n!&Z !&Z\n!&Z !&Z\n!&Z !&Z\n!UL !UL !&B\n", acr1ptr->SourceRealm, acr2ptr->SourceRealm, acr1ptr->SourceGroupWrite, acr2ptr->SourceGroupWrite, acr1ptr->SourceGroupRead, acr2ptr->SourceGroupRead, acr1ptr->Realm, acr2ptr->Realm, acr1ptr->ConfigDirectory, acr2ptr->ConfigDirectory, acr1ptr->PathParameter, acr2ptr->PathParameter, acr1ptr->GroupWrite, acr2ptr->GroupWrite, acr1ptr->GroupRead, acr2ptr->GroupRead, acr1ptr->UserName, acr2ptr->UserName, strlen(acr1ptr->Password), strlen(acr2ptr->Password), !strcmp(acr1ptr->Password, acr2ptr->Password)); /* compare the records */ if (acr1ptr->SourceRealm != acr2ptr->SourceRealm) continue; if (acr1ptr->UserNameLength != acr2ptr->UserNameLength) continue; if (acr1ptr->RealmLength != acr2ptr->RealmLength) continue; if (acr1ptr->ConfigDirectoryLength != acr2ptr->ConfigDirectoryLength) continue; if (acr1ptr->PathParameterLength != acr2ptr->PathParameterLength) continue; if (acr1ptr->SourceGroupWrite != acr2ptr->SourceGroupWrite) continue; if (acr1ptr->GroupWriteLength != acr2ptr->GroupWriteLength) continue; if (acr1ptr->SourceGroupRead != acr2ptr->SourceGroupRead) continue; if (acr1ptr->GroupReadLength != acr2ptr->GroupReadLength) continue; if (strcmp (acr1ptr->UserName, acr2ptr->UserName)) continue; if (strcmp (acr1ptr->Realm, acr2ptr->Realm)) continue; if (acr1ptr->ConfigDirectoryLength && strcmp (acr1ptr->ConfigDirectory, acr2ptr->ConfigDirectory)) continue; if (acr1ptr->PathParameterLength && strcmp (acr1ptr->PathParameter, acr2ptr->PathParameter)) continue; if (acr1ptr->GroupWriteLength && strcmp (acr1ptr->GroupWrite, acr2ptr->GroupWrite)) continue; if (acr1ptr->GroupReadLength && strcmp (acr1ptr->GroupRead, acr2ptr->GroupRead)) continue; /* got this far, must have matched */ *RecordPtrPtr = acr2ptr; if (!acr1ptr->FindRecordCount++) { /* first time this request that the record has been searched for */ acr2ptr->LastAccessSecondsAgo = HttpdTickSecond - acr2ptr->LastAccessTickSecond; acr2ptr->LastAccessMinutesAgo = acr2ptr->LastAccessSecondsAgo / 60; if (acr2ptr->LastAccessMinutesAgo < Config.cfAuth.CacheMinutes) AuthGblSecPtr->CacheHitCount++; else AuthGblSecPtr->CacheTimeoutCount++; sys$gettim (&acr2ptr->LastAccessTime64); acr2ptr->LastAccessTickSecond = HttpdTickSecond; } if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "FOUND"); return (SS$_NORMAL); } if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "NOT-FOUND"); AuthGblSecPtr->CacheMissCount++; return (SS$_ITEMNOTFOUND); } /*****************************************************************************/ /* Clear all of the authentication records. Will result in all subsequent requests being re-authenticated from their respective on-disk databases. Called from the Admin.c and Control.c modules. 'ControlFlush' indicates its was initiated through a /DO= command and has therefore been seen by all of multiple instances. */ int AuthCachePurge (BOOL ControlFlush) { int cnt, status, RecordCount; unsigned char *RecordPoolPtr; AUTH_CREC *acrptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCachePurge() !&B !UL !&B", ControlFlush, InstanceNodeConfig, InstanceNodeSupervisor); /* in a multi-instance config, shared data only by the supervisor */ if (ControlFlush && InstanceNodeConfig > 1 && !InstanceNodeSupervisor) return (SS$_NORMAL); InstanceMutexLock (INSTANCE_MUTEX_AUTH_CACHE); RecordCount = AuthGblSecPtr->CacheRecordCount; RecordPoolPtr = AuthGblSecPtr->CacheRecordPool; for (cnt = 0; cnt < RecordCount; cnt++) { acrptr = RecordPoolPtr + (AuthCacheRecordSize * cnt); memset (acrptr, 0, AuthCacheRecordSize); } AuthGblSecPtr->CacheRecordCount = 0; sys$gettim (&AuthGblSecPtr->SinceTime64); InstanceMutexUnLock (INSTANCE_MUTEX_AUTH_CACHE); return (SS$_NORMAL); } /*****************************************************************************/ /* Reset the entry for all records with a matching realm and user name. This effectively causes the username to be revalidated next authorized path access. */ int AuthCacheReset ( REQUEST_STRUCT *rqptr, char *Realm, char *UserName ) { int cnt, status, RecordCount; unsigned char *RecordPoolPtr; AUTH_CREC *acrptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "AuthCacheReset() !&Z !&Z", Realm, UserName); InstanceMutexLock (INSTANCE_MUTEX_AUTH_CACHE); RecordCount = AuthGblSecPtr->CacheRecordCount; RecordPoolPtr = AuthGblSecPtr->CacheRecordPool; for (cnt = 0; cnt < RecordCount; cnt++) { acrptr = RecordPoolPtr + (AuthCacheRecordSize * cnt); if (!acrptr->SourceRealm) continue; if (Realm && Realm[0] && strcmp (Realm, acrptr->Realm)) continue; if (UserName && UserName[0] && strcmp (UserName, acrptr->UserName)) continue; if (WATCH_MODULE(WATCH_MOD_AUTH)) WatchThis (WATCHALL, WATCH_MOD_AUTH, "!&Z !&Z", acrptr->Realm, acrptr->UserName); memset (acrptr, 0, AuthCacheRecordSize); } InstanceMutexUnLock (INSTANCE_MUTEX_AUTH_CACHE); return (SS$_NORMAL); } /*****************************************************************************/ /* Display all records in the authentication cache. */ AuthCacheReport (REQUEST_STRUCT *rqptr) { static char BeginPageFao [] = "

\n\ \ \n\
\n\ \n\ \ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\
Source
ACME:!UL
Agent:!UL
HTA:!UL
List:!UL
RFC1413:!UL
Skeleton-Key:!UL!AZ
SYSUAF:!UL
Token:!UL
X509:!UL
\n\
\n\ \ \n\ \n\ \n\ \n\ \n\
Scheme
Basic:!UL
Digest:!UL
Other:!UL
\n\
\n\ \ \n\ \n\ \n\ \n\ \ \n\ \n\ \n\ \n\ \n\
Cache
Since:!20&W
Maximum:!UL x !UL bytes
Current:!UL
Reused:!UL
Hits:!UL
Misses:!UL
Timeouts:!UL
\n\ \
\n\ \

\n\\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \n"; static char RecordFao [] = "\ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \ \ \ \n"; static char EmptyCacheFao [] = "\ \n"; static char EndPageFao [] = "
RealmGroup-R+WGroup-RConfig-Dir / Path-Param
UserAccessBasicDigestMost RecentCacheSourceFailBreak-in
!4ZL!AZ!AZ!&@!&@!AZ !AZ
!AZ!AZ!AZ!AZ!UL!UL!20%D!UL!UL!UL!AZ!UL!AZ
0000empty
\n\ \n\ \n\ \n"; int cnt, status, RecordCount; unsigned long FaoVector [48]; unsigned long *vecptr; unsigned char *RecordPoolPtr; char *cptr; AUTH_CREC *acrptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_AUTH)) WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH, "AuthCacheReport()"); AdminPageTitle (rqptr, "User Authentication"); vecptr = FaoVector; InstanceMutexLock (INSTANCE_MUTEX_HTTPD); *vecptr++ = AccountingPtr->AuthAcmeCount; *vecptr++ = AccountingPtr->AuthAgentCount; *vecptr++ = AccountingPtr->AuthHtDatabaseCount; *vecptr++ = AccountingPtr->AuthSimpleListCount; *vecptr++ = AccountingPtr->AuthRFC1413Count; *vecptr++ = AccountingPtr->AuthSkelKeyCount; if (HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond) *vecptr++ = "(Active)"; else *vecptr++ = "(Inactive)"; *vecptr++ = AccountingPtr->AuthVmsCount; *vecptr++ = AccountingPtr->AuthTokenCount; *vecptr++ = AccountingPtr->AuthX509Count; *vecptr++ = AccountingPtr->AuthBasicCount; *vecptr++ = AccountingPtr->AuthDigestCount; *vecptr++ = AccountingPtr->AuthOtherCount; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); InstanceMutexLock (INSTANCE_MUTEX_AUTH_CACHE); *vecptr++ = &AuthGblSecPtr->SinceTime64; *vecptr++ = AuthCacheRecordMax; *vecptr++ = AuthCacheRecordSize; *vecptr++ = AuthGblSecPtr->CacheRecordCount; *vecptr++ = AuthGblSecPtr->CacheReuseCount; *vecptr++ = AuthGblSecPtr->CacheHitCount; *vecptr++ = AuthGblSecPtr->CacheMissCount; *vecptr++ = AuthGblSecPtr->CacheTimeoutCount; status = FaolToNet (rqptr, BeginPageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); RecordCount = AuthGblSecPtr->CacheRecordCount; RecordPoolPtr = AuthGblSecPtr->CacheRecordPool; for (cnt = 0; cnt < RecordCount; cnt++) { acrptr = RecordPoolPtr + (AuthCacheRecordSize * cnt); if (!acrptr->SourceRealm) continue; cptr = AuthCanString (acrptr->AuthUserCan, AUTH_CAN_FORMAT_HTML); if (!cptr) cptr = "*ERROR*"; vecptr = FaoVector; *vecptr++ = cnt+1; *vecptr++ = acrptr->Realm; *vecptr++ = AuthSourceString (acrptr->Realm, acrptr->SourceRealm); if (acrptr->GroupWrite[0]) { *vecptr++ = "!AZ!AZ"; *vecptr++ = acrptr->GroupWrite; *vecptr++ = AuthSourceString (acrptr->GroupWrite, acrptr->SourceGroupWrite); } else *vecptr++ = "none"; if (acrptr->GroupRead[0]) { *vecptr++ = "!AZ!AZ"; *vecptr++ = acrptr->GroupRead; *vecptr++ = AuthSourceString (acrptr->GroupRead, acrptr->SourceGroupRead); } else *vecptr++ = "none"; if (acrptr->ConfigDirectory[0]) *vecptr++ = acrptr->ConfigDirectory; else if (!acrptr->PathParameter[0]) *vecptr++ = "neither"; else *vecptr++ = ""; if (acrptr->PathParameter[0]) *vecptr++ = acrptr->PathParameter; else *vecptr++ = ""; *vecptr++ = acrptr->UserName; *vecptr++ = cptr; if (acrptr->VmsUserProfileLength) *vecptr++ = " (profile)"; else *vecptr++ = ""; if (acrptr->HttpsOnly) *vecptr++ = " ("https:" only)"; else *vecptr++ = ""; *vecptr++ = acrptr->BasicCount; *vecptr++ = acrptr->DigestCount; *vecptr++ = &acrptr->LastAccessTime64; *vecptr++ = acrptr->AccessCount; *vecptr++ = acrptr->DataBaseCount; if (acrptr->FailureCount < Config.cfAuth.FailureLimit) { *vecptr++ = acrptr->FailureCount; *vecptr++ = ""; *vecptr++ = 0; *vecptr++ = ""; } else { *vecptr++ = 0; *vecptr++ = ""; *vecptr++ = acrptr->FailureCount; *vecptr++ = ""; } status = FaolToNet (rqptr, RecordFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } if (!RecordCount) { status = FaolToNet (rqptr, EmptyCacheFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } status = FaolToNet (rqptr, EndPageFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); InstanceMutexUnLock (INSTANCE_MUTEX_AUTH_CACHE); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); AdminEnd (rqptr); } /*****************************************************************************/