Browse Source

HPCC-20223 Remove legacy token from WU/DFUWU, and write new signed token

Removes support for the old legacy workunit token
Adds methods to create, identify, validate, and extract from new workunit distributed access token
Creates the new DAToken when workunits are created
Allows the DAToken to be unsigned if no certificates configured
ESP Services set the DAToken as a password in the ISecUser
ESP Verifies DAToken if provided as an ISecUser password
Dali allows getPermissions requests even if no password provided, authenticates if it is

Signed-off-by: Russ Whitehead <william.whitehead@lexisnexis.com>
Russ Whitehead 6 năm trước cách đây
mục cha
commit
12f3cb5ffe

+ 1 - 0
common/workunit/CMakeLists.txt

@@ -55,6 +55,7 @@ include_directories (
          ./../../common/dllserver 
          ./../../common/dllserver 
          ./../../system/jlib
          ./../../system/jlib
          ./../../system/security/shared 
          ./../../system/security/shared 
+         ./../../system/security/cryptohelper
          ./../../common/thorhelper 
          ./../../common/thorhelper 
          ./../../rtl/eclrtl 
          ./../../rtl/eclrtl 
          ./../../rtl/nbcd
          ./../../rtl/nbcd

+ 157 - 49
common/workunit/workunit.cpp

@@ -50,6 +50,9 @@
 #include "wujobq.hpp"
 #include "wujobq.hpp"
 #include "environment.hpp"
 #include "environment.hpp"
 #include "workunit.ipp"
 #include "workunit.ipp"
+#include "digisign.hpp"
+
+using namespace cryptohelper;
 
 
 static int workUnitTraceLevel = 1;
 static int workUnitTraceLevel = 1;
 
 
@@ -3637,8 +3640,8 @@ public:
             { return c->getResults(); }
             { return c->getResults(); }
     virtual IStringVal & getScope(IStringVal & str) const
     virtual IStringVal & getScope(IStringVal & str) const
             { return c->getScope(str); }
             { return c->getScope(str); }
-    virtual IStringVal & getSecurityToken(IStringVal & str) const
-            { return c->getSecurityToken(str); }
+    virtual IStringVal & getWorkunitDistributedAccessToken(IStringVal & str) const
+            { return c->getWorkunitDistributedAccessToken(str); }
     virtual WUState getState() const
     virtual WUState getState() const
             { return c->getState(); }
             { return c->getState(); }
     virtual IStringVal & getStateEx(IStringVal & str) const
     virtual IStringVal & getStateEx(IStringVal & str) const
@@ -3783,8 +3786,6 @@ public:
             { c->setRescheduleFlag(value); }
             { c->setRescheduleFlag(value); }
     virtual void setResultLimit(unsigned value)
     virtual void setResultLimit(unsigned value)
             { c->setResultLimit(value); }
             { c->setResultLimit(value); }
-    virtual void setSecurityToken(const char *value)
-            { c->setSecurityToken(value); }
     virtual void setState(WUState state)
     virtual void setState(WUState state)
             { c->setState(state); }
             { c->setState(state); }
     virtual void setStateEx(const char * text)
     virtual void setStateEx(const char * text)
@@ -4549,6 +4550,7 @@ IWorkUnit* CWorkUnitFactory::createNamedWorkUnit(const char *wuid, const char *a
     Owned<CLocalWorkUnit> cw = _createWorkUnit(wuid, secmgr, secuser);
     Owned<CLocalWorkUnit> cw = _createWorkUnit(wuid, secmgr, secuser);
     if (scope)
     if (scope)
         cw->setWuScope(scope);  // Note - this may check access rights and throw exception. Is that correct? We might prefer to only check access once, and this will check on the lock too...
         cw->setWuScope(scope);  // Note - this may check access rights and throw exception. Is that correct? We might prefer to only check access once, and this will check on the lock too...
+    cw->setDistributedAccessToken(secuser ? secuser->getName() : "");//create and sign the workunit distributed access token
     IWorkUnit* ret = &cw->lockRemote(false);   // Note - this may throw exception if user does not have rights.
     IWorkUnit* ret = &cw->lockRemote(false);   // Note - this may throw exception if user does not have rights.
     ret->setDebugValue("CREATED_BY", app, true);
     ret->setDebugValue("CREATED_BY", app, true);
     ret->setDebugValue("CREATED_FOR", scope, true);
     ret->setDebugValue("CREATED_FOR", scope, true);
@@ -6329,17 +6331,43 @@ void CLocalWorkUnit::setDebugAgentListenerIP(const char * ip)
     p->setProp("@DebugListenerIP", ip);
     p->setProp("@DebugListenerIP", ip);
 }
 }
 
 
-IStringVal& CLocalWorkUnit::getSecurityToken(IStringVal &str) const
+IStringVal & CLocalWorkUnit::getWorkunitDistributedAccessToken(IStringVal & datoken) const
 {
 {
     CriticalBlock block(crit);
     CriticalBlock block(crit);
-    str.set(p->queryProp("@token"));
-    return str;
+    datoken.set(p->queryProp("@distributedAccessToken"));
+    return datoken;
 }
 }
 
 
-void CLocalWorkUnit::setSecurityToken(const char *value)
+bool CLocalWorkUnit::setDistributedAccessToken(const char * user)
 {
 {
     CriticalBlock block(crit);
     CriticalBlock block(crit);
-    p->setProp("@token", value);
+
+    //If this format changes, you must update the isWorkunitDAToken() and extractFromWorkunitDAToken() methods
+    VStringBuffer datoken("HPCC[u=%s,w=%s]", user, queryWuid());
+
+    IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
+    if (pDSM && pDSM->isDigiSignerConfigured())
+    {
+        datoken.append(pDSM->queryKeyName()).append(';');
+
+        StringBuffer b64Signature;
+        if (pDSM->digiSign(b64Signature, datoken))
+        {
+            datoken.append(b64Signature.str());
+        }
+        else
+        {
+            ERRLOG("Cannot create workunit Distributed Access Token, digisign failed");
+            return false;
+        }
+    }
+    else
+    {
+        WARNLOG("Cannot sign Distributed Access Token, digisign signing not configured");
+        datoken.append(";");
+    }
+    p->setProp("@distributedAccessToken", datoken);
+    return true;
 }
 }
 
 
 bool CLocalWorkUnit::getRunningGraph(IStringVal &graphName, WUGraphIDType &subId) const
 bool CLocalWorkUnit::getRunningGraph(IStringVal &graphName, WUGraphIDType &subId) const
@@ -6805,6 +6833,119 @@ const char *clusterTypeString(ClusterType clusterType, bool lcrSensitive)
     throwUnexpected();
     throwUnexpected();
 }
 }
 
 
+bool extractFromWorkunitDAToken(const char * distributedAccessToken, StringBuffer * wuid, StringBuffer * user, StringBuffer * privKey)
+{
+    if (!isWorkunitDAToken(distributedAccessToken))
+    {
+        DBGLOG("Not a valid workunit distributed access token");
+        return false;
+    }
+
+    //Extract the string between [ and ]
+    const char * pEndBracket = strchr(distributedAccessToken + 5, ']');
+    StringBuffer tokenValues;
+    tokenValues.append(pEndBracket - distributedAccessToken - 5, distributedAccessToken + 5);
+
+    StringArray nameValues;
+    nameValues.appendList(tokenValues.str(), ",",true);
+
+    if (user || wuid)
+    {
+        for (int x = 0; x < nameValues.ordinality(); x++)
+        {
+            if (user && 0==strncmp(nameValues[x],"u=",2))//Extract user
+                user->append(nameValues[x] + 2);
+            if (wuid && 0==strncmp(nameValues[x],"w=",2))//Extract wuid
+                wuid->append(nameValues[x] + 2);
+        }
+    }
+
+    if (privKey)
+    {
+        const char * finger = pEndBracket;
+        ++finger;
+        while (*finger && *finger != ';')
+            privKey->append(1, finger++);
+    }
+    return true;
+}
+
+//Verifies the given signed workunit distributed access token was created
+//and signed by the given wuid and user, and that the workunit is still active
+//Returns:
+//   0 : Success, token is valid and workunit is active
+//   1 : Signature does not verify (wuid/username don't match, or signature does not verify)
+//   2 : Workunit not active
+//   Throws if unable to open workunit
+wuTokenStates verifyWorkunitDAToken(const char * distributedAccessToken)
+{
+    StringBuffer tokWuid;
+    StringBuffer tokUser;
+    if (!extractFromWorkunitDAToken(distributedAccessToken, &tokWuid, &tokUser, nullptr))//get the wuid and user
+    {
+        //Not a valid workunit distributed access token
+        return wuTokenInvalid;
+    }
+
+    //Validate signature
+    IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
+    if (pDSM && pDSM->isDigiVerifierConfigured())
+    {
+        const char * finger;
+        StringBuffer token;//receives copy of everything up until signature
+        for (finger = distributedAccessToken; *finger && *finger != ';'; finger++)
+            token.append(1, finger);
+        token.append(1, finger);//append ;
+
+        StringBuffer sig(++finger);
+        if (!pDSM->digiVerify(sig, token))
+        {
+            ERRLOG("verifyWorkunitDAToken : workunit distributed access token does not verify");
+            return wuTokenInvalid;
+        }
+    }
+
+    Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
+    Owned<IConstWorkUnit> cw = factory->openWorkUnit(tokWuid.str());
+    if(!cw)
+    {
+        throw MakeStringException(WUERR_WorkunitAccessDenied,"verifyWorkunitDAToken : Cannot open workunit %s",tokWuid.str());
+    }
+
+    //Verify user matches
+    if (!isEmptyString(cw->queryUser()) || !isEmptyString(tokUser.str()))
+    {
+        if (!streq(cw->queryUser(), tokUser.str()))
+        {
+            ERRLOG("verifyWorkunitDAToken : token user does not match workunit");
+            return wuTokenInvalid;
+        }
+    }
+
+    // no need to compare tokWuid with workunit wuid, because it will always match
+
+    bool wuActive;
+    switch (cw->getState())
+    {
+    case WUStateRunning:
+    case WUStateDebugRunning:
+    case WUStateBlocked:
+    case WUStateAborting:
+    case WUStateUploadingFiles:
+#ifdef _DEBUG
+        DBGLOG("verifyWorkunitDAToken : Workunit token validated for %s %s, state is '%s'", cw->queryWuid(), cw->queryUser(), getWorkunitStateStr(cw->getState()));
+#endif
+        wuActive = true;
+        break;
+    default:
+        ERRLOG("verifyWorkunitDAToken : Workunit %s not active, state is '%s'", cw->queryWuid(), getWorkunitStateStr(cw->getState()));
+        wuActive = false;
+        break;
+    }
+
+    return wuActive ? wuTokenValid : wuTokenWorkunitInactive;
+}
+
 IPropertyTree *queryRoxieProcessTree(IPropertyTree *environment, const char *process)
 IPropertyTree *queryRoxieProcessTree(IPropertyTree *environment, const char *process)
 {
 {
     if (!process || !*process)
     if (!process || !*process)
@@ -7460,11 +7601,10 @@ IUserDescriptor *CLocalWorkUnit::queryUserDescriptor() const
     CriticalBlock block(crit);
     CriticalBlock block(crit);
     if (!userDesc)
     if (!userDesc)
     {
     {
-        SCMStringBuffer token, user, password;
-        getSecurityToken(token);
-        extractToken(token.str(), queryWuid(), user, password);
         userDesc.setown(createUserDescriptor());
         userDesc.setown(createUserDescriptor());
-        userDesc->set(user.str(), password.str());
+        SCMStringBuffer token;
+        getWorkunitDistributedAccessToken(token);
+        userDesc->set(queryUser(), token.str());//use token as password
     }
     }
     return userDesc;
     return userDesc;
 }
 }
@@ -9239,6 +9379,10 @@ StringBuffer &appendPTreeOpenTag(StringBuffer &s, IPropertyTree *tree, const cha
         {
         {
             if (hidePasswords && streq(attrs->queryName(), "@token"))
             if (hidePasswords && streq(attrs->queryName(), "@token"))
                 continue;
                 continue;
+#ifndef _DEBUG
+            if (hidePasswords && streq(attrs->queryName(), "@distributedAccessToken"))
+                continue;
+#endif
             if (doindent)
             if (doindent)
                 s.append('\n').appendN(attributeindent, ' ');
                 s.append('\n').appendN(attributeindent, ' ');
             else if (count > 3)
             else if (count > 3)
@@ -11412,10 +11556,6 @@ extern WORKUNIT_API void submitWorkUnit(const char *wuid, const char *username,
     Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
     Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
     Owned<IWorkUnit> workunit = factory->updateWorkUnit(wuid);
     Owned<IWorkUnit> workunit = factory->updateWorkUnit(wuid);
     assertex(workunit);
     assertex(workunit);
-
-    SCMStringBuffer token;
-    createToken(wuid, username, password, token);
-    workunit->setSecurityToken(token.str());
     StringAttr clusterName(workunit->queryClusterName());
     StringAttr clusterName(workunit->queryClusterName());
     if (!clusterName.length()) 
     if (!clusterName.length()) 
         throw MakeStringException(WUERR_InvalidCluster, "No target cluster specified");
         throw MakeStringException(WUERR_InvalidCluster, "No target cluster specified");
@@ -12130,38 +12270,6 @@ extern WORKUNIT_API bool getWorkUnitCreateTime(const char *wuid,CDateTime &time)
     return false;
     return false;
 }
 }
 
 
-extern WORKUNIT_API IStringVal& createToken(const char *wuid, const char *user, const char *password, IStringVal &str)
-{
-    StringBuffer wu, token("X");
-    wu.append(wuid).append(';').append(user).append(';').append(password);
-    encrypt(token,wu.str());
-    str.set(token.str());
-    return str;
-}
-
-// This will be replaced by something more secure!
-extern WORKUNIT_API void extractToken(const char *token, const char *wuid, IStringVal &user, IStringVal &password)
-{
-    if (token && *token)
-    {
-        StringBuffer wu;
-        decrypt(wu, token+1);
-        const char *finger = strchr(wu.str(),';');
-        if (finger && strnicmp(wuid, wu.str(), finger-wu.str())==0)
-        {
-            const char *finger1 = strchr(++finger,';');
-            if(finger1)
-            {
-                user.setLen(finger, (size32_t)(finger1-finger));
-                finger1++;
-                password.setLen(finger1, (size32_t)(wu.str() + wu.length() - finger1));
-                return;
-            }
-        }
-        throw MakeStringException(WUERR_InvalidSecurityToken, "Invalid call to extractToken");
-    }
-}
-
 extern WORKUNIT_API WUState getWorkUnitState(const char* state)
 extern WORKUNIT_API WUState getWorkUnitState(const char* state)
 {
 {
     return (WUState) getEnum(state, states);
     return (WUState) getEnum(state, states);

+ 18 - 5
common/workunit/workunit.hpp

@@ -1217,7 +1217,7 @@ interface IConstWorkUnit : extends IConstWorkUnitInfo
     virtual unsigned getResultLimit() const = 0;
     virtual unsigned getResultLimit() const = 0;
     virtual IConstWUResultIterator & getResults() const = 0;
     virtual IConstWUResultIterator & getResults() const = 0;
     virtual IStringVal & getScope(IStringVal & str) const = 0;
     virtual IStringVal & getScope(IStringVal & str) const = 0;
-    virtual IStringVal & getSecurityToken(IStringVal & str) const = 0;
+    virtual IStringVal & getWorkunitDistributedAccessToken(IStringVal & datoken) const = 0;
     virtual IStringVal & getStateEx(IStringVal & str) const = 0;
     virtual IStringVal & getStateEx(IStringVal & str) const = 0;
     virtual __int64 getAgentSession() const = 0;
     virtual __int64 getAgentSession() const = 0;
     virtual unsigned getAgentPID() const = 0;
     virtual unsigned getAgentPID() const = 0;
@@ -1302,7 +1302,6 @@ interface IWorkUnit : extends IConstWorkUnit
     virtual void setPriorityLevel(int level) = 0;
     virtual void setPriorityLevel(int level) = 0;
     virtual void setRescheduleFlag(bool value) = 0;
     virtual void setRescheduleFlag(bool value) = 0;
     virtual void setResultLimit(unsigned value) = 0;
     virtual void setResultLimit(unsigned value) = 0;
-    virtual void setSecurityToken(const char * value) = 0;
     virtual void setState(WUState state) = 0;
     virtual void setState(WUState state) = 0;
     virtual void setStateEx(const char * text) = 0;  // Indicates why blocked
     virtual void setStateEx(const char * text) = 0;  // Indicates why blocked
     virtual void setAgentSession(__int64 sessionId) = 0;
     virtual void setAgentSession(__int64 sessionId) = 0;
@@ -1597,15 +1596,29 @@ extern WORKUNIT_API IWUResult * updateWorkUnitResult(IWorkUnit * w, const char *
 extern WORKUNIT_API IConstWUResult * getWorkUnitResult(IConstWorkUnit * w, const char *name, unsigned sequence);
 extern WORKUNIT_API IConstWUResult * getWorkUnitResult(IConstWorkUnit * w, const char *name, unsigned sequence);
 extern WORKUNIT_API void updateSuppliedXmlParams(IWorkUnit * w);
 extern WORKUNIT_API void updateSuppliedXmlParams(IWorkUnit * w);
 
 
+//workunit distributed access token support
+enum wuTokenStates { wuTokenValid=0, wuTokenInvalid, wuTokenWorkunitInactive };
+extern WORKUNIT_API wuTokenStates verifyWorkunitDAToken(const char * distributedAccessToken);
+extern WORKUNIT_API bool extractFromWorkunitDAToken(const char * token, StringBuffer * wuid, StringBuffer * user, StringBuffer * privKey);
+inline bool isWorkunitDAToken(const char * distributedAccessToken)
+{
+	//Does given token appear to be in the right format. KeyFile and signature are optional
+	//    HPCC[u=user,w=wuid]keyFile;signature
+    const char * finger = distributedAccessToken;
+    if (finger && 0 == strncmp(finger,"HPCC[u=",7))
+        if ((finger = strstr(finger+7, ",w=")))
+            if ((finger = strchr(finger+3, ']')))
+                if ((finger = strchr(finger+1, ';')))
+                    return true;
+    return false;
+}
+
 //returns a state code.  WUStateUnknown == timeout
 //returns a state code.  WUStateUnknown == timeout
 extern WORKUNIT_API WUState waitForWorkUnitToComplete(const char * wuid, int timeout = -1, bool returnOnWaitState = false);
 extern WORKUNIT_API WUState waitForWorkUnitToComplete(const char * wuid, int timeout = -1, bool returnOnWaitState = false);
 extern WORKUNIT_API bool waitForWorkUnitToCompile(const char * wuid, int timeout = -1);
 extern WORKUNIT_API bool waitForWorkUnitToCompile(const char * wuid, int timeout = -1);
 extern WORKUNIT_API WUState secWaitForWorkUnitToComplete(const char * wuid, ISecManager &secmgr, ISecUser &secuser, int timeout = -1, bool returnOnWaitState = false);
 extern WORKUNIT_API WUState secWaitForWorkUnitToComplete(const char * wuid, ISecManager &secmgr, ISecUser &secuser, int timeout = -1, bool returnOnWaitState = false);
 extern WORKUNIT_API bool secWaitForWorkUnitToCompile(const char * wuid, ISecManager &secmgr, ISecUser &secuser, int timeout = -1);
 extern WORKUNIT_API bool secWaitForWorkUnitToCompile(const char * wuid, ISecManager &secmgr, ISecUser &secuser, int timeout = -1);
 extern WORKUNIT_API bool secDebugWorkunit(const char * wuid, ISecManager &secmgr, ISecUser &secuser, const char *command, StringBuffer &response);
 extern WORKUNIT_API bool secDebugWorkunit(const char * wuid, ISecManager &secmgr, ISecUser &secuser, const char *command, StringBuffer &response);
-extern WORKUNIT_API IStringVal& createToken(const char *wuid, const char *user, const char *password, IStringVal &str);
-// This latter is temporary - tokens will be replaced by something more secure
-extern WORKUNIT_API void extractToken(const char *token, const char *wuid, IStringVal &user, IStringVal &password);
 extern WORKUNIT_API WUState getWorkUnitState(const char* state);
 extern WORKUNIT_API WUState getWorkUnitState(const char* state);
 extern WORKUNIT_API IWorkflowScheduleConnection * getWorkflowScheduleConnection(char const * wuid);
 extern WORKUNIT_API IWorkflowScheduleConnection * getWorkflowScheduleConnection(char const * wuid);
 extern WORKUNIT_API const char *skipLeadingXml(const char *text);
 extern WORKUNIT_API const char *skipLeadingXml(const char *text);

+ 2 - 2
common/workunit/workunit.ipp

@@ -293,7 +293,7 @@ public:
     virtual unsigned getResultLimit() const;
     virtual unsigned getResultLimit() const;
     virtual IConstWUResultIterator & getResults() const;
     virtual IConstWUResultIterator & getResults() const;
     virtual IStringVal & getScope(IStringVal & str) const;
     virtual IStringVal & getScope(IStringVal & str) const;
-    virtual IStringVal & getSecurityToken(IStringVal & str) const;
+    virtual IStringVal & getWorkunitDistributedAccessToken(IStringVal & tok) const;
     virtual WUState getState() const;
     virtual WUState getState() const;
     virtual IStringVal & getStateEx(IStringVal & str) const;
     virtual IStringVal & getStateEx(IStringVal & str) const;
     virtual __int64 getAgentSession() const;
     virtual __int64 getAgentSession() const;
@@ -371,7 +371,7 @@ public:
     void setState(WUState state);
     void setState(WUState state);
     void setStateEx(const char * text);
     void setStateEx(const char * text);
     void setAgentSession(__int64 sessionId);
     void setAgentSession(__int64 sessionId);
-    void setSecurityToken(const char *value);
+    bool setDistributedAccessToken(const char * user);
     void setStatistic(StatisticCreatorType creatorType, const char * creator, StatisticScopeType scopeType, const char * scope, StatisticKind kind, const char * optDescription, unsigned __int64 value, unsigned __int64 count, unsigned __int64 maxValue, StatsMergeAction mergeAction);
     void setStatistic(StatisticCreatorType creatorType, const char * creator, StatisticScopeType scopeType, const char * scope, StatisticKind kind, const char * optDescription, unsigned __int64 value, unsigned __int64 count, unsigned __int64 maxValue, StatsMergeAction mergeAction);
     void setTracingValue(const char * propname, const char * value);
     void setTracingValue(const char * propname, const char * value);
     void setTracingValueInt(const char * propname, int value);
     void setTracingValueInt(const char * propname, int value);

+ 12 - 3
dali/server/daldap.cpp

@@ -144,6 +144,7 @@ public:
             username.append(filesdefaultuser);
             username.append(filesdefaultuser);
             decrypt(password, filesdefaultpassword);
             decrypt(password, filesdefaultpassword);
             WARNLOG("Missing credentials, injecting deprecated filesdefaultuser");
             WARNLOG("Missing credentials, injecting deprecated filesdefaultuser");
+            reqSignature = nullptr;
         }
         }
 
 
         Owned<ISecUser> user = ldapsecurity->createUser(username);
         Owned<ISecUser> user = ldapsecurity->createUser(username);
@@ -195,11 +196,19 @@ public:
                 ERRLOG("LDAP: getPermissions(%s) scope=%s user=%s digital signature support not available",key?key:"NULL",obj?obj:"NULL",username.str());
                 ERRLOG("LDAP: getPermissions(%s) scope=%s user=%s digital signature support not available",key?key:"NULL",obj?obj:"NULL",username.str());
         }
         }
 
 
-        if (!ldapsecurity->authenticateUser(*user, NULL))
+        if (!isEmptyString(user->credentials().getPassword()))
         {
         {
-            ERRLOG("LDAP: getPermissions(%s) scope=%s user=%s fails LDAP authentication",key?key:"NULL",obj?obj:"NULL",username.str());
-            return SecAccess_None;//deny
+            if (!ldapsecurity->authenticateUser(*user, NULL))
+            {
+                const char * extra = "";
+                if (isEmptyString(reqSignature))
+                    extra = " (Password or Dali Signature not provided)";
+                ERRLOG("LDAP: getPermissions(%s) scope=%s user=%s fails LDAP authentication%s",key?key:"NULL",obj?obj:"NULL",username.str(), extra);
+                return SecAccess_None;//deny
+            }
         }
         }
+        else
+            user->setAuthenticateStatus(AS_AUTHENTICATED);
 
 
         bool filescope = stricmp(key,"Scope")==0;
         bool filescope = stricmp(key,"Scope")==0;
         bool wuscope = stricmp(key,"workunit")==0;
         bool wuscope = stricmp(key,"workunit")==0;

+ 4 - 1
ecl/eclcc/eclcc.cpp

@@ -2462,7 +2462,10 @@ int EclCC::parseCommandLineOptions(int argc, const char* argv[])
         else if (iter.matchOption(tempArg, "-token"))
         else if (iter.matchOption(tempArg, "-token"))
         {
         {
             // For use by eclccserver - not documented in usage()
             // For use by eclccserver - not documented in usage()
-            extractToken(tempArg, optWUID, StringAttrAdaptor(optUser), StringAttrAdaptor(optPassword));
+            StringBuffer wuid,user;
+            extractFromWorkunitDAToken(tempArg, &wuid, &user,nullptr);
+            optWUID.set(wuid.str());
+            optUser.set(user.str());
         }
         }
         else if (iter.matchOption(optWUID, "-wuid"))
         else if (iter.matchOption(optWUID, "-wuid"))
         {
         {

+ 1 - 1
ecl/eclccserver/eclccserver.cpp

@@ -327,7 +327,7 @@ class EclccCompileThread : implements IPooledThread, implements IErrorReporter,
                 eclccCmd.appendf(" -scope=%s", wuScope);
                 eclccCmd.appendf(" -scope=%s", wuScope);
             eclccCmd.appendf(" -cluster=%s", targetCluster);
             eclccCmd.appendf(" -cluster=%s", targetCluster);
             SCMStringBuffer token;
             SCMStringBuffer token;
-            workunit->getSecurityToken(token);
+            workunit->getWorkunitDistributedAccessToken(token);
             if (token.length())
             if (token.length())
                 eclccCmd.appendf(" -wuid=%s -token=%s", workunit->queryWuid(), token.str());
                 eclccCmd.appendf(" -wuid=%s -token=%s", workunit->queryWuid(), token.str());
         }
         }

+ 22 - 0
esp/bindings/http/platform/httpbinding.cpp

@@ -48,6 +48,7 @@
 #include "jsonhelpers.hpp"
 #include "jsonhelpers.hpp"
 #include "dasds.hpp"
 #include "dasds.hpp"
 #include "daclient.hpp"
 #include "daclient.hpp"
+#include "workunit.hpp"
 
 
 #define FILE_UPLOAD     "FileUploadAccess"
 #define FILE_UPLOAD     "FileUploadAccess"
 #define DEFAULT_HTTP_PORT 80
 #define DEFAULT_HTTP_PORT 80
@@ -701,6 +702,27 @@ bool EspHttpBinding::basicAuth(IEspContext* ctx)
         return false;
         return false;
     }
     }
 
 
+    //Check if the password is a "real" password, or a workunit distributed access token
+    const char * pwd = user->credentials().getPassword();
+    if (isWorkunitDAToken(pwd))
+    {
+        wuTokenStates state = verifyWorkunitDAToken(pwd);//throws if cannot open workunit
+        if (state == wuTokenValid)
+        {
+            user->setAuthenticateStatus(AS_AUTHENTICATED);
+        }
+        else
+        {
+            user->setAuthenticateStatus(AS_INVALID_CREDENTIALS);
+            const char * reason = state == wuTokenInvalid ? "WUToken Workunit Token invalid" : "WUToken Workunit Inactive";
+            ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", reason);
+            ctx->setAuthError(EspAuthErrorNotAuthenticated);
+            ctx->setRespMsg(reason);
+            user->credentials().setPassword(nullptr);
+            return false;
+        }
+    }
+
     if(m_secmgr.get() == NULL)
     if(m_secmgr.get() == NULL)
     {
     {
         WARNLOG("No mechanism established for authentication");
         WARNLOG("No mechanism established for authentication");

+ 1 - 0
esp/protocols/http/CMakeLists.txt

@@ -66,6 +66,7 @@ include_directories(
     ./../../../system/security/LdapSecurity
     ./../../../system/security/LdapSecurity
     ./../../../system/mp 
     ./../../../system/mp 
     ./../../../dali/base
     ./../../../dali/base
+    ./../../../common/workunit
     )
     )
 
 
 add_definitions(-DESPHTTP_EXPORTS -DESP_TIMING -D_USRDLL -DESP_PLUGIN)
 add_definitions(-DESPHTTP_EXPORTS -DESP_TIMING -D_USRDLL -DESP_PLUGIN)

+ 0 - 3
esp/services/ws_ecl/ws_ecl_service.cpp

@@ -1907,9 +1907,6 @@ int CWsEclBinding::submitWsEclWorkunit(IEspContext & context, WsEclWuInfo &wsinf
         }
         }
     }
     }
 
 
-    SCMStringBuffer token;
-    createToken(wuid.str(), context.queryUserId(), context.queryPassword(), token);
-    workunit->setSecurityToken(token.str());
     workunit->setState(WUStateSubmitted);
     workunit->setState(WUStateSubmitted);
     workunit->commit();
     workunit->commit();
 
 

+ 0 - 2
esp/services/ws_workunits/ws_workunitsHelpers.cpp

@@ -3278,8 +3278,6 @@ void WsWuHelpers::copyWsWorkunit(IEspContext &context, IWorkUnit &wu, const char
 
 
     queryExtendedWU(&wu)->copyWorkUnit(src, false, false);
     queryExtendedWU(&wu)->copyWorkUnit(src, false, false);
 
 
-    SCMStringBuffer token;
-    wu.setSecurityToken(createToken(wu.queryWuid(), context.queryUserId(), context.queryPassword(), token).str());
     wu.commit();
     wu.commit();
 }
 }
 
 

+ 0 - 3
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -1670,9 +1670,6 @@ void copyWorkunitForRecompile(IEspContext &context, IWorkUnitFactory *factory, c
 
 
     wu->setAction(WUActionCompile);
     wu->setAction(WUActionCompile);
 
 
-    SCMStringBuffer token;
-    wu->setSecurityToken(createToken(wuid.str(), context.queryUserId(), context.queryPassword(), token).str());
-
     jobname.set(src->queryJobName());
     jobname.set(src->queryJobName());
     if (jobname.length())
     if (jobname.length())
         wu->setJobName(jobname);
         wu->setJobName(jobname);

+ 0 - 8
esp/services/ws_workunits/ws_workunitsService.cpp

@@ -832,9 +832,6 @@ bool CWsWorkunitsEx::onWUResubmit(IEspContext &context, IEspWUResubmitRequest &r
                     NewWsWorkunit wu(factory, context);
                     NewWsWorkunit wu(factory, context);
                     wuid.set(wu->queryWuid());
                     wuid.set(wu->queryWuid());
                     queryExtendedWU(wu)->copyWorkUnit(src, false, false);
                     queryExtendedWU(wu)->copyWorkUnit(src, false, false);
-
-                    SCMStringBuffer token;
-                    wu->setSecurityToken(createToken(wuid.str(), context.queryUserId(), context.queryPassword(), token).str());
                 }
                 }
 
 
                 wuids.append(wuid.str());
                 wuids.append(wuid.str());
@@ -963,9 +960,6 @@ bool CWsWorkunitsEx::onWUSchedule(IEspContext &context, IEspWUScheduleRequest &r
         if (req.getMaxRunTime())
         if (req.getMaxRunTime())
             wu->setDebugValueInt("maxRunTime", req.getMaxRunTime(), true);
             wu->setDebugValueInt("maxRunTime", req.getMaxRunTime(), true);
 
 
-        SCMStringBuffer token;
-        wu->setSecurityToken(createToken(wuid.str(), context.queryUserId(), context.queryPassword(), token).str());
-
         AuditSystemAccess(context.queryUserId(), true, "Scheduled %s", wuid.str());
         AuditSystemAccess(context.queryUserId(), true, "Scheduled %s", wuid.str());
     }
     }
     catch(IException* e)
     catch(IException* e)
@@ -4636,8 +4630,6 @@ void deploySharedObject(IEspContext &context, StringBuffer &wuid, const char *fi
     {
     {
         if (srcxml->hasProp("@jobName"))
         if (srcxml->hasProp("@jobName"))
             wu->setJobName(srcxml->queryProp("@jobName"));
             wu->setJobName(srcxml->queryProp("@jobName"));
-        if (srcxml->hasProp("@token"))
-            wu->setSecurityToken(srcxml->queryProp("@token"));
         if (srcxml->hasProp("Query/Text"))
         if (srcxml->hasProp("Query/Text"))
             wu.setQueryText(srcxml->queryProp("Query/Text"));
             wu.setQueryText(srcxml->queryProp("Query/Text"));
     }
     }

+ 3 - 4
plugins/fileservices/fileservices.cpp

@@ -202,10 +202,9 @@ static IPropertyTree *getEnvironmentTree(IConstEnvironment * daliEnv)
 
 
 static void setServerAccess(CClientFileSpray &server, IConstWorkUnit * wu)
 static void setServerAccess(CClientFileSpray &server, IConstWorkUnit * wu)
 {
 {
-    StringBuffer user, password, token;
-    wu->getSecurityToken(StringBufferAdaptor(token));
-    extractToken(token.str(), wu->queryWuid(), StringBufferAdaptor(user), StringBufferAdaptor(password));
-    server.setUsernameToken(user.str(), password.str(), "");
+    SCMStringBuffer token;
+    wu->getWorkunitDistributedAccessToken(token);
+    server.setUsernameToken(wu->queryUser(), token.str(), "");//use workunit token as password
 }
 }
 
 
 static StringArray availableWsFS;
 static StringArray availableWsFS;

+ 2 - 0
system/security/LdapSecurity/CMakeLists.txt

@@ -46,6 +46,7 @@ include_directories (
          ./../../../esp/platform 
          ./../../../esp/platform 
 	     ./../../../dali/base
 	     ./../../../dali/base
 	     ./../../../system/mp
 	     ./../../../system/mp
+	     ./../../../common/workunit
          ${OPENLDAP_INCLUDE_DIR}
          ${OPENLDAP_INCLUDE_DIR}
     )
     )
 
 
@@ -56,6 +57,7 @@ install ( TARGETS LdapSecurity RUNTIME DESTINATION ${EXEC_DIR} LIBRARY DESTINATI
 target_link_libraries ( LdapSecurity
 target_link_libraries ( LdapSecurity
          jlib
          jlib
          dalibase
          dalibase
+         workunit
          ${OPENLDAP_LIBRARIES}
          ${OPENLDAP_LIBRARIES}
     )
     )
 
 

+ 38 - 13
system/security/LdapSecurity/ldapsecurity.cpp

@@ -21,9 +21,9 @@
 #include "ldapsecurity.hpp"
 #include "ldapsecurity.hpp"
 #include "authmap.ipp"
 #include "authmap.ipp"
 #include "digisign.hpp"
 #include "digisign.hpp"
-
 using namespace cryptohelper;
 using namespace cryptohelper;
 
 
+#include "workunit.hpp"
 
 
 /**********************************************************
 /**********************************************************
  *     CLdapSecUser                                       *
  *     CLdapSecUser                                       *
@@ -666,23 +666,46 @@ bool CLdapSecManager::authenticate(ISecUser* user)
         return true;
         return true;
     }
     }
 
 
-    //Verify provided signature if present
-    IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
-    if (pDSM && pDSM->isDigiVerifierConfigured() && !isEmptyString(user->credentials().getSignature()))
+    //Verify provided user signature if present.
+    //User signatures are calculated and saved when a user is first authenticated,
+    //and are meant to help eliminate the need to call LDAP to authenticate
+    //the same user when there is no caching enabled
+    IDigitalSignatureManager * pDSM = nullptr;
+    if (!isEmptyString(user->credentials().getSignature()))
     {
     {
-        StringBuffer b64Signature(user->credentials().getSignature());
-        if (!pDSM->digiVerify(b64Signature, user->getName()))//digital signature valid?
+        if (isUserCached)
         {
         {
-            user->setAuthenticateStatus(AS_INVALID_CREDENTIALS);
-            WARNLOG("Invalid digital signature for user %s", user->getName());
-            return false;
+            if (streq(cachedUser->credentials().getSignature(), user->credentials().getSignature()))
+            {
+                user->setAuthenticateStatus(AS_AUTHENTICATED);
+                return true;
+            }
+            else
+            {
+                WARNLOG("Digital signature for %s does not match cached signature", user->getName());
+                user->setAuthenticateStatus(AS_INVALID_CREDENTIALS);
+                return false;
+            }
         }
         }
         else
         else
         {
         {
-            user->setAuthenticateStatus(AS_AUTHENTICATED);
-            if(isCaching && !isUserCached)
-                m_permissionsCache->add(*user);
-            return true;
+            pDSM = queryDigitalSignatureManagerInstanceFromEnv();
+            if (pDSM && pDSM->isDigiVerifierConfigured() && !isEmptyString(user->credentials().getSignature()))
+            {
+                if (!pDSM->digiVerify(user->credentials().getSignature(), user->getName()))//digital signature valid?
+                {
+                    user->setAuthenticateStatus(AS_INVALID_CREDENTIALS);
+                    WARNLOG("Invalid digital signature for user %s", user->getName());
+                    return false;
+                }
+                else
+                {
+                    user->setAuthenticateStatus(AS_AUTHENTICATED);
+                    if(isCaching && !isUserCached)
+                        m_permissionsCache->add(*user);
+                    return true;
+                }
+            }
         }
         }
     }
     }
 
 
@@ -707,6 +730,8 @@ bool CLdapSecManager::authenticate(ISecUser* user)
         else if (isEmptyString(user->credentials().getPassword()) && (0 == user->credentials().getSessionToken()) && isEmptyString(user->credentials().getSignature()))
         else if (isEmptyString(user->credentials().getPassword()) && (0 == user->credentials().getSessionToken()) && isEmptyString(user->credentials().getSignature()))
         {
         {
             //No need to sign if password or authenticated session based user
             //No need to sign if password or authenticated session based user
+            if (!pDSM)
+                pDSM = queryDigitalSignatureManagerInstanceFromEnv();
             if (pDSM && pDSM->isDigiSignerConfigured())
             if (pDSM && pDSM->isDigiSignerConfigured())
             {
             {
                //Set user digital signature
                //Set user digital signature

+ 15 - 1
system/security/cryptohelper/digisign.cpp

@@ -136,6 +136,11 @@ public:
     {
     {
         return digiVerify(b64Signature, strlen(text), text);
         return digiVerify(b64Signature, strlen(text), text);
     }
     }
+
+    virtual const char * queryKeyName() const override
+    {
+        return pubKey->queryKeyName();
+    }
 };
 };
 
 
 #else
 #else
@@ -190,6 +195,16 @@ public:
     {
     {
         throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
         throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
     }
     }
+
+    virtual const char * queryPublicKeyFile() const override
+    {
+        throwStringExceptionV(-1, "digiVerify: unavailable without openssl");
+    }
+
+    virtual const char * queryPrivateKeyFile() const override
+    {
+        throwStringExceptionV(-1, "digiSign: unavailable without openssl");
+    }
 };
 };
 
 
 #endif
 #endif
@@ -238,7 +253,6 @@ IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const
 {
 {
 #if defined(_USE_OPENSSL) && !defined(_WIN32)
 #if defined(_USE_OPENSSL) && !defined(_WIN32)
     Owned<CLoadedKey> pubKey, privKey;
     Owned<CLoadedKey> pubKey, privKey;
-
     Owned<IMultiException> exceptions;
     Owned<IMultiException> exceptions;
     if (!isEmptyString(pubKeyFileName))
     if (!isEmptyString(pubKeyFileName))
     {
     {

+ 1 - 0
system/security/cryptohelper/digisign.hpp

@@ -41,6 +41,7 @@ public:
     virtual bool digiSign(StringBuffer & b64Signature, const char *text) const = 0;
     virtual bool digiSign(StringBuffer & b64Signature, const char *text) const = 0;
     virtual bool digiVerify(const char *b64Signature, size32_t dataSz, const void *data) const = 0;//verifies, using public key
     virtual bool digiVerify(const char *b64Signature, size32_t dataSz, const void *data) const = 0;//verifies, using public key
     virtual bool digiVerify(const char *b64Signature, const char *text) const = 0;
     virtual bool digiVerify(const char *b64Signature, const char *text) const = 0;
+    virtual const char * queryKeyName() const = 0;
 };
 };
 
 
 //Uses the HPCCPublicKey/HPCCPrivateKey key files specified in environment.conf
 //Uses the HPCCPublicKey/HPCCPrivateKey key files specified in environment.conf

+ 3 - 3
thorlcr/graph/thgraph.cpp

@@ -2674,10 +2674,10 @@ void CJobBase::init()
     tmp.append(graphName);
     tmp.append(graphName);
     key.set(tmp.str());
     key.set(tmp.str());
 
 
-    SCMStringBuffer tokenUser, password;
-    extractToken(token.str(), wuid.str(), tokenUser, password);
+    StringBuffer user;
+    extractFromWorkunitDAToken(token.str(), nullptr, &user, nullptr);
     userDesc = createUserDescriptor();
     userDesc = createUserDescriptor();
-    userDesc->set(user.str(), password.str());
+    userDesc->set(user.str(), token.str());//use workunit token as password
 
 
     forceLogGraphIdMin = (graph_id)getWorkUnitValueInt("forceLogGraphIdMin", 0);
     forceLogGraphIdMin = (graph_id)getWorkUnitValueInt("forceLogGraphIdMin", 0);
     forceLogGraphIdMax = (graph_id)getWorkUnitValueInt("forceLogGraphIdMax", 0);
     forceLogGraphIdMax = (graph_id)getWorkUnitValueInt("forceLogGraphIdMax", 0);

+ 1 - 1
thorlcr/graph/thgraphmaster.cpp

@@ -1291,7 +1291,7 @@ CJobMaster::CJobMaster(IConstWorkUnit &_workunit, const char *graphName, ILoaded
 {
 {
     SCMStringBuffer _token, _scope;
     SCMStringBuffer _token, _scope;
     workunit->getScope(_scope);
     workunit->getScope(_scope);
-    workunit->getSecurityToken(_token);
+    workunit->getWorkunitDistributedAccessToken(_token);
     wuid.set(workunit->queryWuid());
     wuid.set(workunit->queryWuid());
     user.set(workunit->queryUser());
     user.set(workunit->queryUser());
     token.append(_token.str());
     token.append(_token.str());

+ 5 - 6
tools/swapnode/swapnodelib.cpp

@@ -84,16 +84,15 @@ bool WuResubmit(const char *wuid)
         ERRLOG("WuResubmit(%s): could not resubmit as workunit state is '%s'", wuid, wu->queryStateDesc());
         ERRLOG("WuResubmit(%s): could not resubmit as workunit state is '%s'", wuid, wu->queryStateDesc());
         return false;
         return false;
     }
     }
-    SCMStringBuffer token;
-    wu->getSecurityToken(token);
-    SCMStringBuffer user;
-    SCMStringBuffer password;
-    extractToken(token.str(), wuid, user, password);
+    SCMStringBuffer daToken;
+    StringBuffer user;
+    wu->getWorkunitDistributedAccessToken(daToken);//TODO Can we use wu->queryUser() instead?
+    extractFromWorkunitDAToken(daToken.str(), nullptr, &user, nullptr);//get user from token
     wu->resetWorkflow();
     wu->resetWorkflow();
     wu->setState(WUStateSubmitted);
     wu->setState(WUStateSubmitted);
     wu->commit();
     wu->commit();
     wu.clear();
     wu.clear();
-    submitWorkUnit(wuid,user.str(),password.str());
+    submitWorkUnit(wuid,user.str(),daToken.str());//use workunit token as password
 
 
     PROGLOG("WuResubmit(%s): resubmitted",wuid);
     PROGLOG("WuResubmit(%s): resubmitted",wuid);
     return true;
     return true;

+ 1 - 2
tools/wutool/wutool.cpp

@@ -998,6 +998,7 @@ protected:
         p2->removeProp("Debug/created_by");
         p2->removeProp("Debug/created_by");
         p2->removeProp("@isClone");
         p2->removeProp("@isClone");
         p2->removeProp("@wuidVersion");
         p2->removeProp("@wuidVersion");
+        p2->removeProp("@distributedAccessToken");
         ASSERT(streq(p2->queryProp("Variables/Variable[@name='one']/@status"), "undefined"));
         ASSERT(streq(p2->queryProp("Variables/Variable[@name='one']/@status"), "undefined"));
         p2->setProp("Variables/Variable[@name='one']/@status", "calculated");
         p2->setProp("Variables/Variable[@name='one']/@status", "calculated");
         p2->renameProp("/", "W_LOCAL");
         p2->renameProp("/", "W_LOCAL");
@@ -1329,7 +1330,6 @@ protected:
             wu->setPriorityLevel(2) ;
             wu->setPriorityLevel(2) ;
             wu->setRescheduleFlag(true);
             wu->setRescheduleFlag(true);
             wu->setResultLimit(101);
             wu->setResultLimit(101);
-            wu->setSecurityToken("secret");
             wu->setState(WUStateAborted);
             wu->setState(WUStateAborted);
             wu->setStateEx("stateEx");
             wu->setStateEx("stateEx");
             wu->setAgentSession(1234567890123);
             wu->setAgentSession(1234567890123);
@@ -1390,7 +1390,6 @@ protected:
             ASSERT(wu->getPriorityLevel()==2);
             ASSERT(wu->getPriorityLevel()==2);
             ASSERT(wu->getRescheduleFlag());
             ASSERT(wu->getRescheduleFlag());
             ASSERT(wu->getResultLimit()==101);
             ASSERT(wu->getResultLimit()==101);
-            ASSERT(streq(wu->getSecurityToken(s).str(), "secret"));
             ASSERT(wu->getState()==WUStateAborted);
             ASSERT(wu->getState()==WUStateAborted);
             ASSERT(streq(wu->getStateEx(s).str(), "stateEx"));
             ASSERT(streq(wu->getStateEx(s).str(), "stateEx"));
             ASSERT(wu->getAgentSession()==1234567890123);
             ASSERT(wu->getAgentSession()==1234567890123);