/*****************************************************************************/ /* MapUser.c Gets the username's home file specification for use in /~ mappings. VERSION HISTORY --------------- 07-OCT-2004 MGD unbundled from MAPURL.C */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #undef __VMS_VER #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 "wasd.h" #define WASD_MODULE "MAPUSER" /******************/ /* global storage */ /******************/ #define MAPURL_DEFAULT_USER_CACHE_SIZE 32 LIST_HEAD MapUrlUserNameCacheList; int MapUrlUserNameCacheCount, MapUrlUserNameCacheEntries; /********************/ /* external storage */ /********************/ extern BOOL AuthPolicySysUafRelaxed, OdsExtended; extern unsigned long SysPrvMask[]; extern int ToLowerCase[], ToUpperCase[]; extern CONFIG_STRUCT Config; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Get the path representing the username default device/directory. This can be retrieved from a cache, or retrieved from directly from the SYSUAF and the cache subsequently updated. The 'MAPURL_USER_RULE_FORBIDDEN_MSG' used to return a user-access-denied to the calling routine, is designed to mask the actual reason for denial. This is done to help prevent the leakage of user account information by being able to differentiate between accounts that exist and those that don't, by looking at the message. RequestExecute() checks for this message and converts it into a "directory not found" message, the same as that generated if a user does not have a [.WWW] subdirectory in the home area. */ char* MapUserName ( REQUEST_STRUCT* rqptr, char *UserNamePtr, int *PathOdsPtr ) { /* 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 unsigned long Context = -1; static unsigned long UaiFlags; static unsigned long UaiPriv [2]; static char UaiDefDev [MAPURL_USER_DEFDEV_SIZE+1], UaiDefDir [MAPURL_USER_DEFDIR_SIZE+1], UserName [MAPURL_USER_NAME_SIZE+1], UserPath [MAPURL_USER_PATH_SIZE+1]; static $DESCRIPTOR (UserNameDsc, UserName); static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } UaiItems [] = { { sizeof(UaiFlags), UAI$_FLAGS, &UaiFlags, 0 }, { sizeof(UaiPriv), UAI$_PRIV, &UaiPriv, 0 }, { sizeof(UaiDefDev), UAI$_DEFDEV, &UaiDefDev, 0 }, { sizeof(UaiDefDir), UAI$_DEFDIR, &UaiDefDir, 0 }, { 0,0,0,0 } }; int status, PathOds; char *cptr, *sptr, *zptr; char UserDefault [MAPURL_USER_DEFDEV_SIZE+MAPURL_USER_DEFDIR_SIZE+1]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUserName() !&Z", UserNamePtr); zptr = (sptr = UserName) + sizeof(UserName); for (cptr = UserNamePtr; *cptr && *cptr != '/' && sptr < zptr; *sptr++ = TOUP(*cptr++)); if (sptr >= zptr) return (MAPURL_USER_RULE_FORBIDDEN_MSG); *sptr = '\0'; UserNameDsc.dsc$w_length = sptr - UserName; /* look for it, and if found, return it from the cache */ if ((cptr = MapUserNameCache (rqptr, UserName, NULL, PathOdsPtr))) return (cptr); /***************************************/ /* get the information from the SYSUAF */ /***************************************/ /* turn on SYSPRV to allow access to SYSUAF records */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$getuai (0, &Context, &UserNameDsc, &UaiItems, 0, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { if (status == RMS$_RNF) { if (WATCHPNT(rqptr) && WATCH_CATEGORY(WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER fail !AZ unknown", UserName); return (MAPURL_USER_RULE_FORBIDDEN_MSG); } if (WATCHPNT(rqptr) && WATCH_CATEGORY(WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER sys$getuai() !&S", status); return (MAPURL_USER_RULE_FORBIDDEN_MSG); } /* automatically disallow if any of these flags are set! */ if (UaiFlags & DisallowVmsFlags) { if (WATCHPNT(rqptr) && WATCH_CATEGORY(WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER !AZ fail SYSUAF flags", UserName); return (MAPURL_USER_RULE_FORBIDDEN_MSG); } if (!AuthPolicySysUafRelaxed && (UaiPriv[0] & PRV$M_SYSPRV)) { /* not allowing all accounts, exclude those with extended privileges */ if (WATCHPNT(rqptr) && WATCH_CATEGORY(WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER !AZ fail SYSUAF privileges", UserName); return (MAPURL_USER_RULE_FORBIDDEN_MSG); } zptr = (sptr = UserDefault) + sizeof(UserDefault); for (cptr = UaiDefDev+1; UaiDefDev[0] && sptr < zptr; UaiDefDev[0]--) *sptr++ = *cptr++; for (cptr = UaiDefDir+1; UaiDefDir[0] && sptr < zptr; UaiDefDir[0]--) *sptr++ = *cptr++; if (sptr >= zptr) return (MAPURL_USER_RULE_FORBIDDEN_MSG); *sptr = '\0'; #ifdef ODS_EXTENDED if (OdsExtended) { /* on-disk-structure of user area */ PathOds = OdsVolumeStructure (UserDefault); if (PathOds && (PathOds != MAPURL_PATH_ODS_2 && PathOds != MAPURL_PATH_ODS_5)) { if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER !AZ !AZ ODS-? %X8XL %!&M", UserName, UserDefault, PathOds, PathOds); return (MAPURL_USER_RULE_FORBIDDEN_MSG); } } else #endif /* ODS_EXTENDED */ PathOds = 0; /* generate a URL-style version of the VMS specification */ MapOdsVmsToUrl (UserPath, UserDefault, sizeof(UserPath), true, PathOds); if (WATCHPNT(rqptr) && WATCH_CATEGORY(WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER !AZ !AZ !AZ ODS-!UL", UserName, UserDefault, UserPath, PathOds); /* trim trailing slash */ for (cptr = UserPath; *cptr && !SAME2(cptr,'/\0'); cptr++); *cptr = '\0'; /* update it in the cache */ MapUserNameCache (rqptr, UserName, UserPath, &PathOds); /* if required return the path's on-disk-structure */ if (PathOdsPtr) *PathOdsPtr = PathOds; return (UserPath); } /*****************************************************************************/ /* Keep a linked-list of cache entries. If 'UserName' and 'UserPath' are NULL then the list is reset (this happen on mapping rule reload). If 'UserName' is non-NULL and 'UserPath' is NULL the list is searched for a matching username and the associated path string returned. If 'UserName' and 'UserPath' are non-NULL add/update a cache entry. If the list has reached maximum size reuse the last entry, otherwise create a new entry. Move/add the entry to the head of the list. It's therefore a first-in/first-out queue. Cache contents remain current until demands on space (due to new entries) cycles through the maximum available entries. To explicitly flush the contents reload the rules. */ char* MapUserNameCache ( REQUEST_STRUCT* rqptr, char *UserName, char *UserPath, int *PathOdsPtr ) { LIST_ENTRY *leptr; MAP_URL_USER_ENTRY *ucptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUserNameCache() !&Z !&Z !&X", UserName, UserPath, PathOdsPtr); if (!UserName && !UserPath) { /***************/ /* reset cache */ /***************/ MapUrlUserNameCacheEntries = Config.cfMisc.MapUserNameCacheEntries; if (!MapUrlUserNameCacheEntries) MapUrlUserNameCacheEntries = MAPURL_DEFAULT_USER_CACHE_SIZE; MapUrlUserNameCacheCount = 0; /* empty the list */ leptr = MapUrlUserNameCacheList.HeadPtr; MapUrlUserNameCacheList.HeadPtr = MapUrlUserNameCacheList.TailPtr = NULL; MapUrlUserNameCacheList.EntryCount = 0; while (leptr) { ucptr = (MAP_URL_USER_ENTRY*)leptr; leptr = leptr->NextPtr; VmFree (ucptr, FI_LI); } return (NULL); } if (!UserPath) { /****************/ /* search cache */ /****************/ /* process the cache entry list from most to least recent */ for (leptr = MapUrlUserNameCacheList.HeadPtr; leptr; leptr = leptr->NextPtr) { ucptr = (MAP_URL_USER_ENTRY*)leptr; /* if this one has been reset there won't be any more down the list */ if (!ucptr->UserName[0]) break; /* compare the first two characters (at least one and a null) */ if (!MATCH4(ucptr->UserName, UserName)) continue; /* full string comparison */ if (strcmp (ucptr->UserName, UserName)) continue; /*************/ /* cache hit */ /*************/ if ((void*)MapUrlUserNameCacheList.HeadPtr != (void*)ucptr) { /* move it to the head of the list */ ListRemove (&MapUrlUserNameCacheList, ucptr); ListAddHead (&MapUrlUserNameCacheList, ucptr, LIST_ENTRY_TYPE_USER); } ucptr->HitCount++; ucptr->LastTime64 = rqptr->rqTime.BeginTime64; if (WATCHING (rqptr, WATCH_MAPPING)) WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "USER !AZ cache !AZ/ ODS-!UL !UL hits", ucptr->UserName, ucptr->UserPath, ucptr->PathOds, ucptr->HitCount); if (PathOdsPtr) *PathOdsPtr = ucptr->PathOds; return (ucptr->UserPath); } /* not found */ return (NULL); } /****************/ /* update cache */ /****************/ if (MapUrlUserNameCacheCount < MapUrlUserNameCacheEntries) { /* allocate memory for a new entry */ ucptr = VmGet (sizeof (MAP_URL_USER_ENTRY)); MapUrlUserNameCacheCount++; } else { /* reuse the tail entry (least recent) */ ucptr = MapUrlUserNameCacheList.TailPtr; ucptr->ReuseCount++; ListRemove (&MapUrlUserNameCacheList, ucptr); } /* add entry to the head of the user cache list (most recent) */ ListAddHead (&MapUrlUserNameCacheList, ucptr, LIST_ENTRY_TYPE_USER); strncpy (ucptr->UserName, UserName, sizeof(ucptr->UserName)); strncpy (ucptr->UserPath, UserPath, sizeof(ucptr->UserPath)); ucptr->UserName[sizeof(ucptr->UserName)-1] = ucptr->UserPath[sizeof(ucptr->UserPath)-1] = '\0'; ucptr->HitCount = 1; ucptr->LastTime64 = rqptr->rqTime.BeginTime64; #ifdef ODS_EXTENDED if (PathOdsPtr) ucptr->PathOds = *PathOdsPtr; #else /* ODS_EXTENDED */ if (PathOdsPtr) ucptr->PathOds = 0; #endif /* ODS_EXTENDED */ return (UserPath); } /*****************************************************************************/