From f64c34c06f7306ec692cd73c5d056183e0e2ef51 Mon Sep 17 00:00:00 2001 From: wangkx Date: Mon, 18 Sep 2023 17:02:10 -0400 Subject: [PATCH] HPCC-30037 WIP Signed-off-by: wangkx --- dali/base/dautils.cpp | 7 ++ esp/services/ws_fs/ws_fsBinding.cpp | 2 +- esp/services/ws_fs/ws_fsService.cpp | 34 +++------ esp/smc/SMCLib/TpCommon.cpp | 114 ++++++++++++++++++++-------- esp/smc/SMCLib/TpWrapper.hpp | 6 +- 5 files changed, 105 insertions(+), 58 deletions(-) diff --git a/dali/base/dautils.cpp b/dali/base/dautils.cpp index f14a8df714a..2d11a1b2950 100644 --- a/dali/base/dautils.cpp +++ b/dali/base/dautils.cpp @@ -60,6 +60,10 @@ bool isHostInPlane(IPropertyTree *plane, const char *host, bool ipMatch) Owned planeGroup = getPlaneHostGroup(plane); if (!planeGroup) return false; + StringBuffer s; + toXML(planeGroup, s); + DBGLOG("####host(%s), planeGroup(%s)", isEmptyString(host) ? "" : host, s.str()); + Owned hostsIter = planeGroup->getElements("hosts"); SocketEndpoint hostEp; if (ipMatch) @@ -123,6 +127,9 @@ bool isPathInPlane(IPropertyTree *plane, const char *path) bool validateDropZone(IPropertyTree * plane, const char * path, const char * host, bool ipMatch) { + StringBuffer s; + toXML(plane, s); + DBGLOG("####host(%s), path(%s), plane(%s)", isEmptyString(host) ? "" : host, isEmptyString(path) ? "" : path, s.str()); if (host) { if (!isHostInPlane(plane, host, ipMatch)) diff --git a/esp/services/ws_fs/ws_fsBinding.cpp b/esp/services/ws_fs/ws_fsBinding.cpp index a404e875e93..f64b89286d0 100644 --- a/esp/services/ws_fs/ws_fsBinding.cpp +++ b/esp/services/ws_fs/ws_fsBinding.cpp @@ -410,11 +410,11 @@ int CFileSpraySoapBindingEx::downloadFile(IEspContext &context, CHttpRequest* re if (!osStr.isEmpty() && (atoi(osStr.str())== OS_WINDOWS)) pathSep = '\\'; pathStr.replace(pathSep=='\\'?'/':'\\', pathSep); - addPathSepChar(pathStr); validateDropZoneReq(context, dropZoneName, netAddressStr, pathStr, SecAccess_Read); StringBuffer fullName; + addPathSepChar(pathStr); fullName.appendf("%s%s", pathStr.str(), nameStr.str()); StringBuffer headerStr("attachment;"); diff --git a/esp/services/ws_fs/ws_fsService.cpp b/esp/services/ws_fs/ws_fsService.cpp index 60df7861fd7..b1cf1cc4ecb 100644 --- a/esp/services/ws_fs/ws_fsService.cpp +++ b/esp/services/ws_fs/ws_fsService.cpp @@ -2036,7 +2036,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. - validateDZFileScopePermissions(context, sourcePlaneReq, path, sourceIPReq, SecAccess_Read); + validateDZFileScopePermsAndLegacyPhysicalPerms(context, sourcePlaneReq, path, sourceIPReq, SecAccess_Read); } if (!sourcePathReq.isEmpty()) @@ -2582,7 +2582,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); - validateDZFileScopePermissions(context, destPlane, destfileWithPath, destip, SecAccess_Write); + validateDZFileScopePermsAndLegacyPhysicalPerms(context, destPlane, destfileWithPath, destip, SecAccess_Write); } else destfileWithPath.append(destfile).trim(); @@ -3023,23 +3023,17 @@ bool CFileSprayEx::onFileList(IEspContext &context, IEspFileListRequest &req, IE } else { - StringBuffer dzName; - if (isEmptyString(dropZoneName)) - dropZoneName = findDropZonePlaneName(netaddr, sPath, dzName); - if (!isEmptyString(dropZoneName)) + Owned dropZone = getDropZoneAndValidateHostAndPath(dropZoneName, netaddr, sPath); + if (dropZone) { - SecAccessFlags permission = getDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, sPath, netaddr, SecAccess_Read); - if (permission < SecAccess_Read) - 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)); + if (isEmptyString(dropZoneName)) + dropZoneName= dropZone->queryProp("@name"); + validateDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, sPath, netaddr, SecAccess_Read); } StringArray hosts; if (isEmptyString(netaddr)) { - Owned dropZone = getDropZonePlane(dropZoneName); - if (!dropZone) - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Unknown landing zone: %s", dropZoneName); getPlaneHosts(hosts, dropZone); if (!hosts.ordinality()) hosts.append("localhost"); @@ -3049,9 +3043,10 @@ bool CFileSprayEx::onFileList(IEspContext &context, IEspFileListRequest &req, IE ForEachItemIn(i, hosts) { - const char* host = hosts.item(i); - if (validateDropZoneHostAndPath(dropZoneName, host, sPath)) - getPhysicalFiles(context, dropZoneName, host, sPath, fileNameMask, directoryOnly, files); + ///const char* host = hosts.item(i); + ///if (validateDropZoneHostAndPath(dropZoneName, host, sPath)) + /// getPhysicalFiles(context, dropZoneName, host, sPath, fileNameMask, directoryOnly, files); + getPhysicalFiles(context, dropZoneName, hosts.item(i), sPath, fileNameMask, directoryOnly, files); } } @@ -3621,12 +3616,7 @@ void CFileSprayEx::checkDropZoneFileScopeAccess(IEspContext &context, const char if (isEmptyString(dropZoneName)) dropZoneName = findDropZonePlaneName(netAddress, dropZonePath, dzName); if (!isEmptyString(dropZoneName)) - { - SecAccessFlags permission = getDZPathScopePermsAndLegacyPhysicalPerms(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); - } + validateDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, dropZonePath, netAddress, accessReq); RemoteFilename rfn; SocketEndpoint ep(netAddress); diff --git a/esp/smc/SMCLib/TpCommon.cpp b/esp/smc/SMCLib/TpCommon.cpp index 10dff4bc0f0..8c13516dc25 100644 --- a/esp/smc/SMCLib/TpCommon.cpp +++ b/esp/smc/SMCLib/TpCommon.cpp @@ -148,33 +148,33 @@ StringBuffer &findDropZonePlaneName(const char *host, const char *path, StringBu return planeName; } -extern TPWRAPPER_API bool validateDropZoneHostAndPath(const char* dropZoneName, const char* hostToCheck, const char* pathToCheck) +extern TPWRAPPER_API IPropertyTree* getDropZoneAndValidateHostAndPath(const char* dropZoneName, const char* host, const char* path) { - //Both hostToCheck and pathToCheck should not be empty. For backward compatibility, the dropZoneName may be empty. - if (isEmptyString(hostToCheck)) - throw makeStringException(ECLWATCH_INVALID_INPUT, "Host not defined."); - if (isEmptyString(pathToCheck)) - throw makeStringException(ECLWATCH_INVALID_INPUT, "Path not defined."); - if (isContainerized() && streq("localhost", hostToCheck)) - hostToCheck = nullptr; // "localhost" is a placeholder for mounted dropzones that have no hosts. - if (isEmptyString(hostToCheck) && isEmptyString(dropZoneName)) - throw makeStringException(ECLWATCH_INVALID_INPUT, "No dropzone or host provided."); + StringBuffer pathToCheck(path); + addPathSepChar(pathToCheck); - if (containsRelPaths(pathToCheck)) //Detect a path like: /home/lexis/runtime/var/lib/HPCCSystems/mydropzone/../../../ - throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Invalid path %s", pathToCheck); + const char* hostToCheck = nullptr; + if (isContainerized() && streq("localhost", host)) + hostToCheck = nullptr; // "localhost" is a placeholder for mounted dropzones that have no hosts. + else + hostToCheck = host; - StringBuffer path(pathToCheck); - addPathSepChar(path); + Owned dropZone; if (isEmptyString(dropZoneName)) { - Owned plane = findDropZonePlane(path, hostToCheck, isIPAddress(hostToCheck), false); - return nullptr != plane; + dropZone.setown(findDropZonePlane(pathToCheck, hostToCheck, isIPAddress(hostToCheck), false)); + if (!dropZone) + throwOrLogDropZoneLookUpError(ECLWATCH_INVALID_INPUT, "DropZone not found for host '%s' path '%s'.", host, path); } - - Owned plane = getDropZonePlane(dropZoneName); - if (nullptr == plane) - return false; - return validateDropZone(plane, path, hostToCheck, isIPAddress(hostToCheck)); + else + { + dropZone.setown(getDropZonePlane(dropZoneName)); + if (!dropZone) + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "DropZone '%s' not found.", dropZoneName); + if (!validateDropZone(dropZone, pathToCheck, hostToCheck, isIPAddress(hostToCheck))) + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "The host '%s' or path '%s' is not valid for dropzone %s.", host, path, dropZoneName); + } + return dropZone.getClear(); } static SecAccessFlags getDropZoneScopePermissions(IEspContext& context, const IPropertyTree* dropZone, const char* dropZonePath) @@ -207,7 +207,7 @@ static SecAccessFlags getDZPathScopePermissions(IEspContext& context, const char dropZone.setown(findDropZonePlane(dropZonePath, dropZoneHost, true, false)); if (!dropZone) { - throwOrLogDropZoneLookUpError(ECLWATCH_INVALID_INPUT, "getDZPathScopePermissions(): DropZone %s not found for host '%s' path '%s'.", + throwOrLogDropZoneLookUpError(ECLWATCH_INVALID_INPUT, "getDZPathScopePermissions(): DropZone not found for host '%s' path '%s'.", isEmptyString(dropZoneHost) ? "unspecified" : dropZoneHost, isEmptyString(dropZonePath) ? "unspecified" : dropZonePath); return SecAccess_Full; } @@ -278,22 +278,18 @@ extern TPWRAPPER_API SecAccessFlags getDZPathScopePermsAndLegacyPhysicalPerms(IE return permission; } -//Validate dropzone host and file path. Also validate dropzone file path permission. Because the file -//path does not contain a file name, the getDZPathScopePermsAndLegacyPhysicalPerms() is called to -//validate the permission. -extern TPWRAPPER_API void validateDropZoneReq(IEspContext& ctx, const char* dropZoneName, const char* host, const char* filePath, SecAccessFlags permissionReq) +extern TPWRAPPER_API void validateDZPathScopePermsAndLegacyPhysicalPerms(IEspContext &context, const char *dropZoneName, const char *path, + const char *host, SecAccessFlags permissionReq) { - if (!validateDropZoneHostAndPath(dropZoneName, host, filePath)) //The filePath should be the absolute path for the dropzone. - throw makeStringException(ECLWATCH_INVALID_INPUT, "Invalid DropZoneName, NetAddress or Path."); - - SecAccessFlags permission = getDZPathScopePermsAndLegacyPhysicalPerms(ctx, dropZoneName, filePath, host, permissionReq); + SecAccessFlags permission = getDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, path, host, permissionReq); 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, filePath, ctx.queryUserId(), getSecAccessFlagName(permission), getSecAccessFlagName(permissionReq)); + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Access DropZone %s %s not allowed for user %s (permission:%s). Read Access Required.", + dropZoneName, path, context.queryUserId(), getSecAccessFlagName(permission)); } //Validate dropzone file permission when the file path contains a file name. -extern TPWRAPPER_API void validateDZFileScopePermissions(IEspContext& context, const char* dropZoneName, const char* path, const char* host, SecAccessFlags permissionReq) +extern TPWRAPPER_API void validateDZFileScopePermsAndLegacyPhysicalPerms(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())) @@ -303,6 +299,58 @@ extern TPWRAPPER_API void validateDZFileScopePermissions(IEspContext& context, c dropZoneName, path, context.queryUserId(), getSecAccessFlagName(permission), getSecAccessFlagName(permissionReq)); } +//TODO: it would be worth expanding on the comments for the functions now in TpCommon, +//as they're quite similar and it is not obvious when one would be used over the other sometimes. + +//Validate dropzone host and file path. Also validate dropzone file path permission. Because the file +//path does not contain a file name, the getDZPathScopePermsAndLegacyPhysicalPerms() is called to +//validate the permission. +extern TPWRAPPER_API void validateDropZoneReq(IEspContext& ctx, const char* dropZoneName, const char* host, const char* filePath, SecAccessFlags permissionReq) +{ +// if (!validateDropZoneHostAndPath(dropZoneName, host, filePath)) //The filePath should be the absolute path for the dropzone. + // throw makeStringException(ECLWATCH_INVALID_INPUT, "Invalid DropZoneName, NetAddress or Path."); + + if (isEmptyString(host) || isEmptyString(filePath)) //The host and filePath must be specified for accessing a DropZone file. + throw makeStringException(ECLWATCH_INVALID_INPUT, "Host or file path not defined."); + + if (containsRelPaths(filePath)) //Detect a path like: /home/lexis/runtime/var/lib/HPCCSystems/mydropzone/../../../ + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "Invalid path %s", filePath); + + //If the dropZoneName is empty, try to find it using the host and filePath. + //If not, validate the dropZoneName, host, and filePath. + dropZoneName = ""; + Owned dropZone = getDropZoneAndValidateHostAndPath(dropZoneName, host, filePath); + if (dropZone) + dropZoneName= dropZone->queryProp("@name"); + /*StringBuffer path(filePath); + addPathSepChar(path); + const char* hostToCheck = nullptr; + if (isContainerized() && streq("localhost", path)) + hostToCheck = nullptr; // "localhost" is a placeholder for mounted dropzones that have no hosts. + else + hostToCheck = host; + if (isEmptyString(dropZoneName)) + { + Owned dropZone = findDropZonePlane(path, hostToCheck, isIPAddress(hostToCheck), false); + if (dropZone) + dropZoneName= dropZone->queryProp("@name"); + else + throwOrLogDropZoneLookUpError(ECLWATCH_INVALID_INPUT, "DropZone not found for host '%s' path '%s'.", host, filePath); + } + else + { //If the dropZoneName is specified, valid it first. And check whether the host and filePath are valid for the DropZone. + Owned dropZone = getDropZonePlane(dropZoneName); + if (!dropZone) + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "DropZone '%s' not found.", dropZoneName); + if (!validateDropZone(dropZone, path, hostToCheck, isIPAddress(hostToCheck))) + throw makeStringExceptionV(ECLWATCH_INVALID_INPUT, "The host '%s' or path '%s' is not valid for dropzone %s.", host, filePath, dropZoneName); + }*/ + + //If the dropZoneName is not empty, validate the permissions. + if (!isEmptyString(dropZoneName)) + validateDZPathScopePermsAndLegacyPhysicalPerms(ctx, dropZoneName, filePath, host, permissionReq); +} + //Validate dropzone file path and permission, Also set the dlfn. extern TPWRAPPER_API void validateDropZoneAccess(IEspContext& context, const char* targetDZNameOrHost, const char* hostReq, SecAccessFlags permissionReq, const char* fileNameWithRelPath, CDfsLogicalFileName& dlfn) diff --git a/esp/smc/SMCLib/TpWrapper.hpp b/esp/smc/SMCLib/TpWrapper.hpp index c8a882a09ed..3c68dd133c7 100644 --- a/esp/smc/SMCLib/TpWrapper.hpp +++ b/esp/smc/SMCLib/TpWrapper.hpp @@ -240,12 +240,14 @@ extern TPWRAPPER_API bool validateDataPlaneName(const char *remoteDali, const ch extern TPWRAPPER_API bool matchNetAddressRequest(const char* netAddressReg, bool ipReq, IConstTpMachine& tpMachine); extern TPWRAPPER_API StringBuffer &findDropZonePlaneName(const char* host, const char* path, StringBuffer& planeName); -extern TPWRAPPER_API bool validateDropZoneHostAndPath(const char* dropZoneName, const char* hostToCheck, const char* pathToCheck); +extern TPWRAPPER_API IPropertyTree* getDropZoneAndValidateHostAndPath(const char* dropZoneName, const char* host, const char* path); extern TPWRAPPER_API void validateDropZoneAccess(IEspContext& context, const char* targetDZNameOrHost, const char* hostReq, SecAccessFlags permissionReq, const char* fileNameWithRelPath, CDfsLogicalFileName& dlfn); extern TPWRAPPER_API SecAccessFlags getDZPathScopePermsAndLegacyPhysicalPerms(IEspContext &context, const char *dropZoneName, const char *path, const char *host, SecAccessFlags permissionReq); +extern TPWRAPPER_API void validateDZPathScopePermsAndLegacyPhysicalPerms(IEspContext &context, const char *dropZoneName, const char *path, + const char *host, SecAccessFlags permissionReq); extern TPWRAPPER_API void validateDropZoneReq(IEspContext& ctx, const char* dropZoneName, const char* host, const char* path, SecAccessFlags permissionReq); -extern TPWRAPPER_API void validateDZFileScopePermissions(IEspContext& context, const char* dropZoneName, const char* path, const char* host, SecAccessFlags permissionReq); +extern TPWRAPPER_API void validateDZFileScopePermsAndLegacyPhysicalPerms(IEspContext& context, const char* dropZoneName, const char* path, const char* host, SecAccessFlags permissionReq); #endif //_ESPWIZ_TpWrapper_HPP__