From f2e53c047f47e3ca031be9da097c66647e3c60cc 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. Revise based on review 1. Add queryDistributedFileDirectory().getFScopePermissions() and call it in the getLegacyDZPhysicalPerms(). 2. Move validateDropZoneReq(), validateDZFileScopePermission() and getDZPathScopePermissionsEx()) into TpCommon.cpp. Rename the getDZPathScopePermissionsEx() to getDZPathScopePermsAndLegacyPhysicalPerms(). Rename the validateDZFileScopePermission() to validateDZFileScopePermissions(). Also revise them. 3. The following 3 files are not called from outside. Change them from extern to static: getDZPathScopePermissions(), getDZFileScopePermissions() and getLegacyDZPhysicalPerms(). 4. Add comments. 5. Fix a bug in the message for the throwOrLogDropZoneLookUpError(). 6. Rename the validateDropZoneHostAndPath() to getDropZoneAndValidateHostAndPath() and revise it. It is called when uploading/downloading dropzone file and in WsFileSprayEx::onFileList(). There are 2 issues in the existing code: a. some code are duplicated with the code in FileSprayEx::onFileList(). b. the isDropZoneRestrictionEnabled is not used. 7. Rename the validateDZFileScopePermissions() to validateDZFileScopePermsAndLegacyPhysicalPerms() 8. Consoliate the code in FileSprayEx::onFileList() by using the getDropZoneAndValidateHostAndPath(). 9. Add validateDZPathScopePermsAndLegacyPhysicalPerms() which contains the common code for the validateDropZoneReq() and WsFileSprayEx. 10. Revise the code in validateDropZoneReq(). 11. Move the addPathSepChar() line, fix an intent bug, and fix a message bug. 12. Add more comments. Signed-off-by: wangkx --- dali/base/dadfs.cpp | 6 ++ dali/base/dadfs.hpp | 1 + dali/dfu/dfurun.cpp | 11 +- esp/services/ws_fs/ws_fsBinding.cpp | 17 +-- esp/services/ws_fs/ws_fsService.cpp | 49 +++------ esp/smc/SMCLib/TpCommon.cpp | 162 +++++++++++++++++++++++----- esp/smc/SMCLib/TpWrapper.hpp | 10 +- 7 files changed, 175 insertions(+), 81 deletions(-) diff --git a/dali/base/dadfs.cpp b/dali/base/dadfs.cpp index 713041951fe..97d45c510e0 100644 --- a/dali/base/dadfs.cpp +++ b/dali/base/dadfs.cpp @@ -1181,6 +1181,7 @@ protected: friend class CDistributedFile; IDistributedSuperFile *lookupSuperFile(const char *logicalname, IUserDescriptor *user, AccessMode accessMode, IDistributedFileTransaction *transaction, unsigned timeout=INFINITE); SecAccessFlags getFilePermissions(const char *lname,IUserDescriptor *user,unsigned auditflags); + SecAccessFlags getFScopePermissions(const char *scope,IUserDescriptor *user,unsigned auditflags); SecAccessFlags getNodePermissions(const IpAddress &ip,IUserDescriptor *user,unsigned auditflags); SecAccessFlags getFDescPermissions(IFileDescriptor *,IUserDescriptor *user,unsigned auditflags=0); SecAccessFlags getDLFNPermissions(CDfsLogicalFileName &dlfn,IUserDescriptor *user,unsigned auditflags=0); @@ -11875,6 +11876,11 @@ SecAccessFlags CDistributedFileDirectory::getDropZoneScopePermissions(const char return getScopePermissions(dlfn.get(),user,auditflags); } +SecAccessFlags CDistributedFileDirectory::getFScopePermissions(const char *scope,IUserDescriptor *user,unsigned auditflags) +{ + return getScopePermissions(scope,user,auditflags); +} + void CDistributedFileDirectory::setDefaultUser(IUserDescriptor *user) { if (user) diff --git a/dali/base/dadfs.hpp b/dali/base/dadfs.hpp index 71e29bd750a..813509ee022 100644 --- a/dali/base/dadfs.hpp +++ b/dali/base/dadfs.hpp @@ -642,6 +642,7 @@ interface IDistributedFileDirectory: extends IInterface virtual void removeSuperFile(const char *_logicalname, bool delSubs=false, IUserDescriptor *user=NULL, IDistributedFileTransaction *transaction=NULL)=0; virtual SecAccessFlags getFilePermissions(const char *lname,IUserDescriptor *user,unsigned auditflags=0)=0; // see dasess for auditflags values + virtual SecAccessFlags getFScopePermissions(const char *scope,IUserDescriptor *user,unsigned auditflags=0)=0; // see dasess for auditflags values virtual void setDefaultUser(IUserDescriptor *user)=0; virtual IUserDescriptor* queryDefaultUser()=0; virtual SecAccessFlags getNodePermissions(const IpAddress &ip,IUserDescriptor *user,unsigned auditflags=0)=0; 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..f64b89286d0 100644 --- a/esp/services/ws_fs/ws_fsBinding.cpp +++ b/esp/services/ws_fs/ws_fsBinding.cpp @@ -410,16 +410,11 @@ int CFileSpraySoapBindingEx::downloadFile(IEspContext &context, CHttpRequest* re if (!osStr.isEmpty() && (atoi(osStr.str())== OS_WINDOWS)) pathSep = '\\'; 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; + addPathSepChar(pathStr); fullName.appendf("%s%s", pathStr.str(), nameStr.str()); StringBuffer headerStr("attachment;"); @@ -463,13 +458,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..ef7cb765ac7 100644 --- a/esp/services/ws_fs/ws_fsService.cpp +++ b/esp/services/ws_fs/ws_fsService.cpp @@ -2036,10 +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. - 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)); + validateDZFileScopePermsAndLegacyPhysicalPerms(context, sourcePlaneReq, path, sourceIPReq, SecAccess_Read); } if (!sourcePathReq.isEmpty()) @@ -2585,10 +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); - 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)); + validateDZFileScopePermsAndLegacyPhysicalPerms(context, destPlane, destfileWithPath, destip, SecAccess_Write); } else destfileWithPath.append(destfile).trim(); @@ -3029,23 +3023,18 @@ 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) // In bare-metal and isDropZoneRestrictionEnabled()==false, the dropZone may be nullptr. { - SecAccessFlags permission = getDZPathScopePermissions(context, dropZoneName, sPath, 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.", - 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); + { // If the netaddr is empty, the dropZoneName will not be empty and the dropZone should be + // set by the getDropZoneAndValidateHostAndPath(). getPlaneHosts(hosts, dropZone); if (!hosts.ordinality()) hosts.append("localhost"); @@ -3055,9 +3044,7 @@ 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); + getPhysicalFiles(context, dropZoneName, hosts.item(i), sPath, fileNameMask, directoryOnly, files); } } @@ -3145,7 +3132,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 (getDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, dir, server, SecAccess_Read) < SecAccess_Read) return false; RemoteFilename rfn; @@ -3366,7 +3353,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 (getDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, fullPath, host, SecAccess_Read) < SecAccess_Read) continue; } @@ -3477,7 +3464,7 @@ 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) + if (!isEmptyString(dzName) && (getDZPathScopePermsAndLegacyPhysicalPerms(context, dzName, directoryStr, netAddress, SecAccess_Read) < SecAccess_Read)) return false; bool directoryOnly = req.getDirectoryOnly(); @@ -3627,12 +3614,7 @@ void CFileSprayEx::checkDropZoneFileScopeAccess(IEspContext &context, const char if (isEmptyString(dropZoneName)) dropZoneName = findDropZonePlaneName(netAddress, dropZonePath, dzName); if (!isEmptyString(dropZoneName)) - { - SecAccessFlags permission = getDZPathScopePermissions(context, dropZoneName, dropZonePath, nullptr); - 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); @@ -3674,7 +3656,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 + : getDZPathScopePermsAndLegacyPhysicalPerms(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..dfe5abad996 100644 --- a/esp/smc/SMCLib/TpCommon.cpp +++ b/esp/smc/SMCLib/TpCommon.cpp @@ -148,33 +148,38 @@ 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) -{ - //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."); +//If the dropZoneName is empty, find out the dropzone based on the host and path. In bare-metal and +//isDropZoneRestrictionEnabled()==false, the dropzone may not be found. +//If the dropZoneName is not empty, find out the dropzone based on the dropZoneName and validate the host and path. +extern TPWRAPPER_API IPropertyTree* getDropZoneAndValidateHostAndPath(const char* dropZoneName, const char* host, const char* path) +{ + 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() && strsame(host, "localhost")) + 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'.", + isEmptyString(host) ? "unspecified" : host, isEmptyString(path) ? "unspecified" : path); + } + 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.", + isEmptyString(host) ? "unspecified" : host, isEmptyString(path) ? "unspecified" : path, dropZoneName); } - - Owned plane = getDropZonePlane(dropZoneName); - if (nullptr == plane) - return false; - return validateDropZone(plane, path, hostToCheck, isIPAddress(hostToCheck)); + return dropZone.getClear(); } static SecAccessFlags getDropZoneScopePermissions(IEspContext& context, const IPropertyTree* dropZone, const char* dropZonePath) @@ -196,7 +201,7 @@ static SecAccessFlags getDropZoneScopePermissions(IEspContext& context, const IP return queryDistributedFileDirectory().getDropZoneScopePermissions(dropZone->queryProp("@name"), dropZonePath, userDesc); } -extern TPWRAPPER_API SecAccessFlags getDZPathScopePermissions(IEspContext& context, const char* dropZoneName, const char* dropZonePath, const char* dropZoneHost) +static SecAccessFlags getDZPathScopePermissions(IEspContext& context, const char* dropZoneName, const char* dropZonePath, const char* dropZoneHost) { if (isEmptyString(dropZonePath)) throw makeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "getDZPathScopePermissions(): DropZone path must be specified."); @@ -207,7 +212,8 @@ extern TPWRAPPER_API SecAccessFlags getDZPathScopePermissions(IEspContext& conte dropZone.setown(findDropZonePlane(dropZonePath, dropZoneHost, true, false)); if (!dropZone) { - throwOrLogDropZoneLookUpError(ECLWATCH_INVALID_INPUT, "getDZPathScopePermissions(): DropZone %s not found.", dropZoneName); + throwOrLogDropZoneLookUpError(ECLWATCH_INVALID_INPUT, "getDZPathScopePermissions(): DropZone not found for host '%s' path '%s'.", + isEmptyString(dropZoneHost) ? "unspecified" : dropZoneHost, isEmptyString(dropZonePath) ? "unspecified" : dropZonePath); return SecAccess_Full; } } @@ -221,7 +227,7 @@ extern TPWRAPPER_API SecAccessFlags getDZPathScopePermissions(IEspContext& conte return getDropZoneScopePermissions(context, dropZone, dropZonePath); } -extern TPWRAPPER_API SecAccessFlags getDZFileScopePermissions(IEspContext& context, const char* dropZoneName, const char* dropZonePath, +static SecAccessFlags getDZFileScopePermissions(IEspContext& context, const char* dropZoneName, const char* dropZonePath, const char* dropZoneHost) { StringBuffer dir, fileName; @@ -230,6 +236,104 @@ extern TPWRAPPER_API SecAccessFlags getDZFileScopePermissions(IEspContext& conte return getDZPathScopePermissions(context, dropZoneName, dropZonePath, dropZoneHost); } +static SecAccessFlags getLegacyDZPhysicalPerms(IEspContext& context, const char* dropZoneName, const char* dropZoneHost, + const char* dropZoneFile, bool isPath) +{ + 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"; + } + + CDfsLogicalFileName dlfn; + SocketEndpoint ep(dropZoneHost); + dlfn.setExternal(ep, dropZoneFile); + + StringBuffer scopes; + const char *scopesToCheck = nullptr; + if (isPath) + scopesToCheck = dlfn.get(); + else + { + dlfn.getScopes(scopes); + scopesToCheck = scopes; + } + return queryDistributedFileDirectory().getFScopePermissions(scopesToCheck, userDesc); +} + +//Get dropzone file permission for a dropzone file path (the path does NOT contain a file name). +//This function (and the validate functions below) calls the getDZPathScopePermissions(). In the +//getDZPathScopePermissions(), the isDropZoneRestrictionEnabled() will be used for bare-metal if +//a dropzone cannot be found based on host and path. +extern TPWRAPPER_API SecAccessFlags getDZPathScopePermsAndLegacyPhysicalPerms(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; +} + +//Validate dropzone file permission for a dropzone file path (the path does NOT contain a file name) +extern TPWRAPPER_API void validateDZPathScopePermsAndLegacyPhysicalPerms(IEspContext &context, const char *dropZoneName, const char *path, + const char *host, SecAccessFlags permissionReq) +{ + SecAccessFlags permission = getDZPathScopePermsAndLegacyPhysicalPerms(context, dropZoneName, path, host, permissionReq); + if (permission < 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 for a dropzone file path (the path DOES contain a file name) +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())) + 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)); +} + +//This function is called when uploading a file to a dropzone or downloading a file to a dropzone. +//1. both host and filePath should not be empty. +//2. the filePath should not contain bad file path. +//3. call the getDropZoneAndValidateHostAndPath(). +//4. if the dropzone name is available, validate dropzone file permission. Should be available unless bare-metal and isDropZoneRestrictionEnabled()==false. +extern TPWRAPPER_API void validateDropZoneReq(IEspContext& ctx, const char* dropZoneName, const char* host, const char* filePath, SecAccessFlags permissionReq) +{ + 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); + + Owned dropZone = getDropZoneAndValidateHostAndPath(dropZoneName, host, filePath); + if (dropZone) + dropZoneName = dropZone->queryProp("@name"); + + if (!isEmptyString(dropZoneName)) + validateDZPathScopePermsAndLegacyPhysicalPerms(ctx, dropZoneName, filePath, host, permissionReq); +} + +//This function is called when accessing a dropzone file from the WsFileIO service (the fileNameWithRelPath is a relative path). +//1. the targetDZNameOrHost could be a dropzone name or dropzone host. Find out the dropzone. +// If the targetDZNameOrHost is a dropzone name and the hostReq is not empty, validate the hostReq inside the dropzone. +// If the dropzone cannot be found based on the targetDZNameOrHost, throw an exception. +//2. the fileNameWithRelPath should not contain bad file path. +//3. validate dropzone file permission. +//4. set the dlfn using dropzone name and fileNameWithRelPath. extern TPWRAPPER_API void validateDropZoneAccess(IEspContext& context, const char* targetDZNameOrHost, const char* hostReq, SecAccessFlags permissionReq, const char* fileNameWithRelPath, CDfsLogicalFileName& dlfn) { @@ -246,8 +350,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..3c68dd133c7 100644 --- a/esp/smc/SMCLib/TpWrapper.hpp +++ b/esp/smc/SMCLib/TpWrapper.hpp @@ -240,10 +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 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 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 validateDZFileScopePermsAndLegacyPhysicalPerms(IEspContext& context, const char* dropZoneName, const char* path, const char* host, SecAccessFlags permissionReq); #endif //_ESPWIZ_TpWrapper_HPP__