From cf5c6e10036ef74863a5751f52adbfd21c85d7b8 Mon Sep 17 00:00:00 2001 From: wangkx Date: Thu, 17 Aug 2023 13:44:52 -0400 Subject: [PATCH] HPCC-30037 Check legacy DZ physical permission in ESP services 1. In checkPlaneFilePermissions() (dfurun.cpp), check legacy physical permission only if the plane name is set. 2. Add getLegacyDZPhysicalPerms() in ESP SMCLib. 3. Call getLegacyDZPhysicalPerms() in validateDropZoneAccess() which is used by ESP WsFileIO service. 4. In ESP FileSpray service, check legacy physical permission if plane exists. Signed-off-by: wangkx --- dali/dfu/dfurun.cpp | 11 +++---- esp/services/ws_fs/ws_fsBinding.cpp | 28 ++++++++++-------- esp/services/ws_fs/ws_fsService.cpp | 46 +++++++++++++++++++---------- esp/smc/SMCLib/TpCommon.cpp | 39 +++++++++++++++++++++++- esp/smc/SMCLib/TpWrapper.hpp | 1 + 5 files changed, 90 insertions(+), 35 deletions(-) diff --git a/dali/dfu/dfurun.cpp b/dali/dfu/dfurun.cpp index 991148ccd47..48941717df8 100644 --- a/dali/dfu/dfurun.cpp +++ b/dali/dfu/dfurun.cpp @@ -668,7 +668,6 @@ class CDFUengine: public CInterface, implements IDFUengine auditflags |= DALI_LDAP_WRITE_WANTED; SecAccessFlags perm; - bool checkLegacyPhysicalPerms = getGlobalConfigSP()->getPropBool("expert/@failOverToLegacyPhysicalPerms",!isContainerized()); IClusterInfo *iClusterInfo = fd->queryClusterNum(0); const char *planeName = iClusterInfo->queryGroupName(); if (!isEmptyString(planeName)) @@ -685,17 +684,19 @@ class CDFUengine: public CInterface, implements IDFUengine throw makeStringExceptionV(-1,"Invalid DropZone directory %s.",dir); perm = queryDistributedFileDirectory().getDropZoneScopePermissions(planeName,relativePath,user,auditflags); - if (((!write&&!HASREADPERMISSION(perm))||(write&&!HASWRITEPERMISSION(perm)))&&checkLegacyPhysicalPerms) - perm = queryDistributedFileDirectory().getFDescPermissions(fd,user,auditflags); + if (((!write&&!HASREADPERMISSION(perm))||(write&&!HASWRITEPERMISSION(perm)))) + { + if (getGlobalConfigSP()->getPropBool("expert/@failOverToLegacyPhysicalPerms",!isContainerized())) + perm = queryDistributedFileDirectory().getFDescPermissions(fd,user,auditflags); + } } else { #ifndef _CONTAINERIZED Owned factory = getEnvironmentFactory(true); Owned env = factory->openEnvironment(); - if (env->isDropZoneRestrictionEnabled()||!checkLegacyPhysicalPerms) + if (env->isDropZoneRestrictionEnabled()) throw makeStringException(-1,"Empty plane name."); - perm = queryDistributedFileDirectory().getFDescPermissions(fd,user,auditflags); #else throw makeStringException(-1,"Unexpected empty plane name."); // should never be the case in containerized setups #endif diff --git a/esp/services/ws_fs/ws_fsBinding.cpp b/esp/services/ws_fs/ws_fsBinding.cpp index 353c591f245..cac737f66cc 100644 --- a/esp/services/ws_fs/ws_fsBinding.cpp +++ b/esp/services/ws_fs/ws_fsBinding.cpp @@ -386,6 +386,19 @@ void CFileSpraySoapBindingEx::xsltTransform(const char* xml, const char* sheet, } #endif +static void validateDropZoneReq(IEspContext& ctx, const char* dropZoneName, const char* host, const char* path, SecAccessFlags permissionReq) +{ + if (!validateDropZoneHostAndPath(dropZoneName, host, path)) //The pathStr should be the absolute path for the dropzone. + throw makeStringException(ECLWATCH_INVALID_INPUT, "Invalid DropZoneName, NetAddress or Path."); + + SecAccessFlags permission = getDZPathScopePermissions(ctx, dropZoneName, path, host); + if ((permission < permissionReq) && getGlobalConfigSP()->getPropBool("expert/@failOverToLegacyPhysicalPerms", !isContainerized())) + permission = getLegacyDZPhysicalPerms(ctx, dropZoneName, host, path, true); + if (permission < permissionReq) + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone %s %s %s not allowed for user %s (permission:%s). %s permission required.", + dropZoneName, host, path, ctx.queryUserId(), getSecAccessFlagName(permission), getSecAccessFlagName(permissionReq)); +} + int CFileSpraySoapBindingEx::downloadFile(IEspContext &context, CHttpRequest* request, CHttpResponse* response) { try @@ -412,12 +425,7 @@ int CFileSpraySoapBindingEx::downloadFile(IEspContext &context, CHttpRequest* re pathStr.replace(pathSep=='\\'?'/':'\\', pathSep); addPathSepChar(pathStr); - if (!validateDropZoneHostAndPath(dropZoneName, netAddressStr, pathStr)) //The pathStr should be the absolute path for the dropzone. - throw makeStringException(ECLWATCH_INVALID_INPUT, "Invalid DropZoneName, NetAddress or Path."); - SecAccessFlags permission = getDZPathScopePermissions(context, dropZoneName, pathStr, netAddressStr); - if (permission < SecAccess_Read) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s %s not allowed for user %s (permission:%s). Read Access Required.", - dropZoneName.str(), netAddressStr.str(), pathStr.str(), context.queryUserId(), getSecAccessFlagName(permission)); + validateDropZoneReq(context, dropZoneName, netAddressStr, pathStr, SecAccess_Read); StringBuffer fullName; fullName.appendf("%s%s", pathStr.str(), nameStr.str()); @@ -463,13 +471,7 @@ int CFileSpraySoapBindingEx::onStartUpload(IEspContext& ctx, CHttpRequest* reque request->getParameter("NetAddress", netAddress); request->getParameter("Path", path); request->getParameter("DropZoneName", dropZoneName); - if (!validateDropZoneHostAndPath(dropZoneName, netAddress, path)) //The path should be the absolute path for the dropzone. - throw makeStringException(ECLWATCH_INVALID_INPUT, "Invalid DropZoneName, NetAddress or Path."); - SecAccessFlags permission = getDZPathScopePermissions(ctx, dropZoneName, path, netAddress); - if (permission < SecAccess_Full) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s %s not allowed for user %s (permission:%s). Full Access Required.", - dropZoneName.str(), netAddress.str(), path.str(), ctx.queryUserId(), getSecAccessFlagName(permission)); - + validateDropZoneReq(ctx, dropZoneName, netAddress, path, SecAccess_Full); return EspHttpBinding::onStartUpload(ctx, request, response, serv, method); } diff --git a/esp/services/ws_fs/ws_fsService.cpp b/esp/services/ws_fs/ws_fsService.cpp index 9f59f53b92a..30f6ba91906 100644 --- a/esp/services/ws_fs/ws_fsService.cpp +++ b/esp/services/ws_fs/ws_fsService.cpp @@ -1920,6 +1920,16 @@ static bool parseUNCPath(const char* sprayPath, StringBuffer& localPath, StringB return true; } +static void validateDZFileScopePermission(IEspContext& context, const char* dropZoneName, const char* path, const char* host, SecAccessFlags permissionReq) +{ + SecAccessFlags permission = getDZFileScopePermissions(context, dropZoneName, path, host); + if ((permission < permissionReq) && getGlobalConfigSP()->getPropBool("expert/@failOverToLegacyPhysicalPerms", !isContainerized())) + permission = getLegacyDZPhysicalPerms(context, dropZoneName, host, path, false); + if (permission < permissionReq) + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone %s %s not allowed for user %s (permission:%s). %s permission Required.", + dropZoneName, path, context.queryUserId(), getSecAccessFlagName(permission), getSecAccessFlagName(permissionReq)); +} + void CFileSprayEx::readAndCheckSpraySourceReq(IEspContext& context, MemoryBuffer& srcxml, const char* srcIP, const char* srcPath, const char* srcPlane, StringBuffer& sourcePlaneReq, StringBuffer& sourceIPReq, StringBuffer& sourcePathReq) { @@ -2036,10 +2046,7 @@ void CFileSprayEx::readAndCheckSpraySourceReq(IEspContext& context, MemoryBuffer { //Based on the tests, the dfuserver only supports the wildcard inside the file name, like '/path/f*'. //The dfuserver throws an error if the wildcard is inside the path, like /p*ath/file. - SecAccessFlags permission = getDZFileScopePermissions(context, sourcePlaneReq, path, nullptr); - if (permission < SecAccess_Read) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s not allowed for user %s (permission:%s). Read Access Required.", - sourcePlaneReq.str(), path, context.queryUserId(), getSecAccessFlagName(permission)); + validateDZFileScopePermission(context, sourcePlaneReq, path, sourceIPReq, SecAccess_Read); } if (!sourcePathReq.isEmpty()) @@ -2585,10 +2592,7 @@ bool CFileSprayEx::onDespray(IEspContext &context, IEspDespray &req, IEspDespray if (!isEmptyString(destPlane)) // must be true, unless bare-metal and isDropZoneRestrictionEnabled()==false { getDropZoneInfoByDestPlane(version, destPlane, destfile, destfileWithPath, umask, destip); - SecAccessFlags permission = getDZFileScopePermissions(context, destPlane, destfileWithPath, destip); - if (permission < SecAccess_Write) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s not allowed for user %s (permission:%s). Write Access Required.", - destPlane, destfileWithPath.str(), context.queryUserId(), getSecAccessFlagName(permission)); + validateDZFileScopePermission(context, destPlane, destfileWithPath, destip, SecAccess_Write); } else destfileWithPath.append(destfile).trim(); @@ -2975,6 +2979,15 @@ bool CFileSprayEx::onDFUWUFile(IEspContext &context, IEspDFUWUFileRequest &req, return true; } +static SecAccessFlags getDZPathScopePermissionsEx(IEspContext &context, const char *dropZoneName, const char *path, + const char *host, SecAccessFlags permissionReq) +{ + SecAccessFlags permission = getDZPathScopePermissions(context, dropZoneName, path, host); + if ((permission < permissionReq) && getGlobalConfigSP()->getPropBool("expert/@failOverToLegacyPhysicalPerms", !isContainerized())) + permission = getLegacyDZPhysicalPerms(context, dropZoneName, host, path, true); + return permission; +} + bool CFileSprayEx::onFileList(IEspContext &context, IEspFileListRequest &req, IEspFileListResponse &resp) { try @@ -3034,9 +3047,9 @@ bool CFileSprayEx::onFileList(IEspContext &context, IEspFileListRequest &req, IE dropZoneName = findDropZonePlaneName(netaddr, sPath, dzName); if (!isEmptyString(dropZoneName)) { - SecAccessFlags permission = getDZPathScopePermissions(context, dropZoneName, sPath, nullptr); + SecAccessFlags permission = getDZPathScopePermissionsEx(context, dropZoneName, sPath, netaddr, SecAccess_Read); if (permission < SecAccess_Read) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s not allowed for user %s (permission:%s). Read Access Required.", + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone %s %s not allowed for user %s (permission:%s). Read Access Required.", dropZoneName, sPath.str(), context.queryUserId(), getSecAccessFlagName(permission)); } @@ -3145,7 +3158,7 @@ void CFileSprayEx::addPhysicalFile(IEspContext& context, IDirectoryIterator* di, bool CFileSprayEx::searchDropZoneFiles(IEspContext& context, const char* dropZoneName, const char* server, const char* dir, const char* relDir, const char* nameFilter, IArrayOf& files, unsigned& filesFound) { - if (getDZPathScopePermissions(context, dropZoneName, dir, server) < SecAccess_Read) + if (getDZPathScopePermissionsEx(context, dropZoneName, dir, server, SecAccess_Read) < SecAccess_Read) return false; RemoteFilename rfn; @@ -3366,7 +3379,7 @@ void CFileSprayEx::getPhysicalFiles(IEspContext &context, const char *dropZoneNa if (dropZoneName && di->isDir()) { VStringBuffer fullPath("%s%s", path, fileName.str()); - if (getDZPathScopePermissions(context, dropZoneName, fullPath, nullptr) < SecAccess_Read) + if (getDZPathScopePermissionsEx(context, dropZoneName, fullPath, host, SecAccess_Read) < SecAccess_Read) continue; } @@ -3477,8 +3490,8 @@ bool CFileSprayEx::onDropZoneFiles(IEspContext &context, IEspDropZoneFilesReques StringBuffer planeName; if (isEmptyString(dzName)) dzName = findDropZonePlaneName(netAddress, directoryStr, planeName); - if (!isEmptyString(dzName) && getDZPathScopePermissions(context, dzName, directoryStr, nullptr) < SecAccess_Read) - return false; + if (!isEmptyString(dzName) && (getDZPathScopePermissionsEx(context, dzName, directoryStr, netAddress, SecAccess_Read) < SecAccess_Read)) + return false; bool directoryOnly = req.getDirectoryOnly(); IArrayOf &files = resp.getFiles(); @@ -3628,7 +3641,7 @@ void CFileSprayEx::checkDropZoneFileScopeAccess(IEspContext &context, const char dropZoneName = findDropZonePlaneName(netAddress, dropZonePath, dzName); if (!isEmptyString(dropZoneName)) { - SecAccessFlags permission = getDZPathScopePermissions(context, dropZoneName, dropZonePath, nullptr); + SecAccessFlags permission = getDZPathScopePermissionsEx(context, dropZoneName, dropZonePath, netAddress, accessReq); if (permission < accessReq) throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s not allowed for user %s (permission:%s). %s Permission Required.", dropZoneName, dropZonePath, context.queryUserId(), getSecAccessFlagName(permission), accessReqName); @@ -3674,7 +3687,8 @@ void CFileSprayEx::checkDropZoneFileScopeAccess(IEspContext &context, const char StringBuffer fullPath(dropZonePath); addPathSepChar(fullPath).append(pathToCheck); //If the dropzone name is not found, the DZPathScopePermissions cannot be validated. - SecAccessFlags permission = isEmptyString(dropZoneName) ? accessReq : getDZPathScopePermissions(context, dropZoneName, fullPath, nullptr); + SecAccessFlags permission = isEmptyString(dropZoneName) ? accessReq + : getDZPathScopePermissionsEx(context, dropZoneName, fullPath, netAddress, accessReq); if (permission < accessReq) { uniquePath.setValue(pathToCheck.str(), false); //add a path denied diff --git a/esp/smc/SMCLib/TpCommon.cpp b/esp/smc/SMCLib/TpCommon.cpp index 195c2f6e909..7903f7f95a2 100644 --- a/esp/smc/SMCLib/TpCommon.cpp +++ b/esp/smc/SMCLib/TpCommon.cpp @@ -230,6 +230,37 @@ extern TPWRAPPER_API SecAccessFlags getDZFileScopePermissions(IEspContext& conte return getDZPathScopePermissions(context, dropZoneName, dropZonePath, dropZoneHost); } +extern TPWRAPPER_API SecAccessFlags getLegacyDZPhysicalPerms(IEspContext& context, const char* dropZoneName, const char* dropZoneHost, + const char* dropZoneFile, bool pathOnly) +{ + if (isEmptyString(dropZoneHost) && isEmptyString(dropZoneName)) + throw makeStringException(ECLWATCH_INVALID_INPUT, "Neither dropzone plane or host specified."); + + Owned userDesc = createUserDescriptor(); + userDesc->set(context.queryUserId(), context.queryPassword(), context.querySignature()); + + StringBuffer host; + if (isEmptyString(dropZoneHost)) + { + Owned plane = getDropZonePlane(dropZoneName); + if (getPlaneHost(host, plane, 0)) + dropZoneHost = host.str(); + else + dropZoneHost = "localhost"; + } + + //The setExternal() requests the path with a file name. The file name is not used by the getDLFNPermissions(). + //If the dropZoneFile is a path only, the dropZoneScope + a faked file name is passed to the setExternal(). + StringBuffer pathWithFileName(dropZoneFile); + if (pathOnly) + addNonEmptyPathSepChar(pathWithFileName).append("any"); + + CDfsLogicalFileName dlfn; + SocketEndpoint ep(dropZoneHost); + dlfn.setExternal(ep, pathWithFileName); + return queryDistributedFileDirectory().getDLFNPermissions(dlfn, userDesc); +} + extern TPWRAPPER_API void validateDropZoneAccess(IEspContext& context, const char* targetDZNameOrHost, const char* hostReq, SecAccessFlags permissionReq, const char* fileNameWithRelPath, CDfsLogicalFileName& dlfn) { @@ -246,8 +277,14 @@ extern TPWRAPPER_API void validateDropZoneAccess(IEspContext& context, const cha } const char *dropZoneName = dropZone->queryProp("@name"); SecAccessFlags permission = getDZFileScopePermissions(context, dropZoneName, fileNameWithRelPath, hostReq); + if ((permission < permissionReq) && getGlobalConfigSP()->getPropBool("expert/@failOverToLegacyPhysicalPerms", !isContainerized())) + { + StringBuffer fullPath(dropZone->queryProp("@prefix")); + addNonEmptyPathSepChar(fullPath).append(fileNameWithRelPath); + permission = getLegacyDZPhysicalPerms(context, dropZoneName, hostReq, fullPath, false); + } if (permission < permissionReq) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone Scope %s %s not allowed for user %s (permission:%s). %s Access Required.", + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone %s %s not allowed for user %s (permission:%s). %s Access Required.", dropZoneName, fileNameWithRelPath, context.queryUserId(), getSecAccessFlagName(permission), getSecAccessFlagName(permissionReq)); dlfn.setPlaneExternal(dropZoneName, fileNameWithRelPath); } diff --git a/esp/smc/SMCLib/TpWrapper.hpp b/esp/smc/SMCLib/TpWrapper.hpp index bbc89ee91b0..414ad2a1972 100644 --- a/esp/smc/SMCLib/TpWrapper.hpp +++ b/esp/smc/SMCLib/TpWrapper.hpp @@ -243,6 +243,7 @@ extern TPWRAPPER_API StringBuffer &findDropZonePlaneName(const char* host, const extern TPWRAPPER_API bool validateDropZoneHostAndPath(const char* dropZoneName, const char* hostToCheck, const char* pathToCheck); extern TPWRAPPER_API SecAccessFlags getDZPathScopePermissions(IEspContext& context, const char* dropZoneName, const char* dropZonePath, const char* dropZoneHost); extern TPWRAPPER_API SecAccessFlags getDZFileScopePermissions(IEspContext& context, const char* dropZoneName, const char* dropZonePath, const char* dropZoneHost); +extern TPWRAPPER_API SecAccessFlags getLegacyDZPhysicalPerms(IEspContext& context, const char* dropZoneName, const char* dropZoneHost, const char* dropZoneFile, bool pathOnly); extern TPWRAPPER_API void validateDropZoneAccess(IEspContext& context, const char* targetDZNameOrHost, const char* hostReq, SecAccessFlags permissionReq, const char* fileNameWithRelPath, CDfsLogicalFileName& dlfn);