#include "ua_server_internal.h"
#include "ua_services.h"
void
notifySession(UA_Server *server, UA_Session *session,
UA_ApplicationNotificationType type) {
if(!server->config.globalNotificationCallback &&
!server->config.sessionNotificationCallback)
return;
size_t payloadSize = 6 + session->attributes.mapSize;
UA_STACKARRAY(UA_KeyValuePair, payloadData, payloadSize);
UA_KeyValueMap payloadMap = {payloadSize, payloadData};
payloadData[0].key = UA_QUALIFIEDNAME(0, "session-id");
UA_Variant_setScalar(&payloadData[0].value, &session->sessionId,
&UA_TYPES[UA_TYPES_NODEID]);
payloadData[1].key = UA_QUALIFIEDNAME(0, "securechannel-id");
UA_UInt32 secureChannelId = 0;
if(session->channel)
secureChannelId = session->channel->securityToken.channelId;
UA_Variant_setScalar(&payloadData[1].value, &secureChannelId,
&UA_TYPES[UA_TYPES_UINT32]);
payloadData[2].key = UA_QUALIFIEDNAME(0, "session-name");
UA_Variant_setScalar(&payloadData[2].value, &session->sessionName,
&UA_TYPES[UA_TYPES_STRING]);
payloadData[3].key = UA_QUALIFIEDNAME(0, "client-description");
UA_Variant_setScalar(&payloadData[3].value, &session->clientDescription,
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
payloadData[4].key = UA_QUALIFIEDNAME(0, "client-user-id");
UA_Variant_setScalar(&payloadData[4].value, &session->clientUserIdOfSession,
&UA_TYPES[UA_TYPES_STRING]);
payloadData[5].key = UA_QUALIFIEDNAME(0, "locale-ids");
UA_Variant_setArray(&payloadData[5].value, session->localeIds,
session->localeIdsSize, &UA_TYPES[UA_TYPES_STRING]);
if(session->attributes.mapSize)
memcpy(&payloadData[6], session->attributes.map,
sizeof(UA_KeyValuePair) * session->attributes.mapSize);
if(server->config.sessionNotificationCallback)
server->config.sessionNotificationCallback(server, type, payloadMap);
if(server->config.globalNotificationCallback)
server->config.globalNotificationCallback(server, type, payloadMap);
}
static void
removeSessionCallback(UA_Server *server, session_list_entry *entry) {
lockServer(server);
UA_Session_clear(&entry->session, server);
unlockServer(server);
UA_free(entry);
}
void
UA_Session_remove(UA_Server *server, UA_Session *session,
UA_ShutdownReason shutdownReason) {
UA_LOCK_ASSERT(&server->serviceMutex);
#ifdef UA_ENABLE_SUBSCRIPTIONS
UA_Subscription *sub, *tempsub;
TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, tempsub) {
if(shutdownReason == UA_SHUTDOWNREASON_TIMEOUT) {
UA_LOG_INFO_SUBSCRIPTION(server->config.logging, sub,
"Detaching the Subscription from the timed-out Session");
UA_Session_detachSubscription(server, session, sub, true);
} else {
UA_Subscription_delete(server, sub);
}
}
UA_PublishResponseEntry *entry;
while((entry = UA_Session_dequeuePublishReq(session))) {
UA_PublishResponse_clear(&entry->response);
UA_free(entry);
}
#endif
if(server->config.accessControl.closeSession) {
server->config.accessControl.
closeSession(server, &server->config.accessControl,
&session->sessionId, session->context);
}
UA_Session_detachFromSecureChannel(server, session);
if(session->activated) {
session->activated = false;
server->activeSessionCount--;
}
session_list_entry *sentry = container_of(session, session_list_entry, session);
LIST_REMOVE(sentry, pointers);
server->sessionCount--;
switch(shutdownReason) {
case UA_SHUTDOWNREASON_CLOSE:
case UA_SHUTDOWNREASON_PURGE:
break;
case UA_SHUTDOWNREASON_TIMEOUT:
server->serverDiagnosticsSummary.sessionTimeoutCount++;
break;
case UA_SHUTDOWNREASON_REJECT:
server->serverDiagnosticsSummary.rejectedSessionCount++;
break;
case UA_SHUTDOWNREASON_SECURITYREJECT:
server->serverDiagnosticsSummary.securityRejectedSessionCount++;
break;
case UA_SHUTDOWNREASON_ABORT:
server->serverDiagnosticsSummary.sessionAbortCount++;
break;
default:
UA_assert(false);
break;
}
notifySession(server, session, UA_APPLICATIONNOTIFICATIONTYPE_SESSION_CLOSED);
sentry->cleanupCallback.callback = (UA_Callback)removeSessionCallback;
sentry->cleanupCallback.application = server;
sentry->cleanupCallback.context = sentry;
UA_EventLoop *el = server->config.eventLoop;
el->addDelayedCallback(el, &sentry->cleanupCallback);
}
void
cleanupSessions(UA_Server *server, UA_DateTime nowMonotonic) {
UA_LOCK_ASSERT(&server->serviceMutex);
session_list_entry *sentry, *temp;
LIST_FOREACH_SAFE(sentry, &server->sessions, pointers, temp) {
if(sentry->session.validTill >= nowMonotonic)
continue;
UA_LOG_INFO_SESSION(server->config.logging, &sentry->session,
"Session has timed out");
UA_Session_remove(server, &sentry->session, UA_SHUTDOWNREASON_TIMEOUT);
}
}
UA_Session *
getSessionByToken(UA_Server *server, const UA_NodeId *token) {
UA_LOCK_ASSERT(&server->serviceMutex);
session_list_entry *current = NULL;
LIST_FOREACH(current, &server->sessions, pointers) {
if(!UA_NodeId_equal(¤t->session.authenticationToken, token))
continue;
UA_EventLoop *el = server->config.eventLoop;
UA_DateTime now = el->dateTime_nowMonotonic(el);
if(now > current->session.validTill) {
UA_LOG_WARNING_SESSION(server->config.logging, ¤t->session,
"Client tries to use a session that has timed out");
return NULL;
}
return ¤t->session;
}
return NULL;
}
UA_Session *
getSessionById(UA_Server *server, const UA_NodeId *sessionId) {
UA_LOCK_ASSERT(&server->serviceMutex);
if(!sessionId)
return NULL;
session_list_entry *current = NULL;
LIST_FOREACH(current, &server->sessions, pointers) {
if(!UA_NodeId_equal(¤t->session.sessionId, sessionId))
continue;
UA_EventLoop *el = server->config.eventLoop;
UA_DateTime now = el->dateTime_nowMonotonic(el);
if(now > current->session.validTill) {
UA_LOG_WARNING_SESSION(server->config.logging, ¤t->session,
"Client tries to use a session that has timed out");
return NULL;
}
return ¤t->session;
}
if(UA_NodeId_equal(sessionId, &server->adminSession.sessionId))
return &server->adminSession;
return NULL;
}
static UA_StatusCode
signCreateSessionResponse(UA_Server *server, UA_SecureChannel *channel,
const UA_CreateSessionRequest *request,
UA_CreateSessionResponse *response) {
if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN &&
channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT)
return UA_STATUSCODE_GOOD;
const UA_SecurityPolicy *sp = channel->securityPolicy;
void *cc = channel->channelContext;
UA_SignatureData *signatureData = &response->serverSignature;
const UA_SecurityPolicySignatureAlgorithm *signAlg = &sp->asymSignatureAlgorithm;
size_t signatureSize = signAlg->getLocalSignatureSize(sp, cc);
UA_StatusCode retval = UA_STATUSCODE_GOOD;
if(!UA_SecurityPolicy_isEnhancedSecurity(sp))
retval = UA_String_copy(&signAlg->uri, &signatureData->algorithm);
retval |= UA_ByteString_allocBuffer(&signatureData->signature, signatureSize);
if(retval != UA_STATUSCODE_GOOD)
return retval;
UA_ByteString dataToSign = UA_BYTESTRING_NULL;
if(UA_SecurityPolicy_isEnhancedSecurity(sp)) {
retval = UA_SecureChannel_buildCreateSessionSignatureData(
channel, &request->clientNonce, &response->serverNonce,
&sp->localCertificate, &channel->remoteCertificate, &dataToSign);
} else {
retval = UA_ByteString_allocBuffer(&dataToSign,
request->clientCertificate.length + request->clientNonce.length);
if(retval == UA_STATUSCODE_GOOD) {
memcpy(dataToSign.data, request->clientCertificate.data,
request->clientCertificate.length);
memcpy(dataToSign.data + request->clientCertificate.length,
request->clientNonce.data, request->clientNonce.length);
}
}
if(retval != UA_STATUSCODE_GOOD)
return retval;
retval = signAlg->sign(sp, cc, &dataToSign, &signatureData->signature);
UA_ByteString_clear(&dataToSign);
return retval;
}
static UA_StatusCode
createCheckSessionAuthSecurityPolicyContext(UA_Server *server, UA_Session *session,
UA_SecurityPolicy *sp, const char *logPrefix,
const UA_ByteString remoteCertificate) {
if(session->sessionSp && session->sessionSp != sp) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"%s: Cannot instantiate SecurityPolicyContext %S for the "
"Session. A different SecurityPolicy %S is "
"already in place", logPrefix, sp->policyUri,
session->sessionSp->policyUri);
return UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
}
session->sessionSp = sp;
UA_StatusCode res = UA_STATUSCODE_GOOD;
if(session->sessionSpContext) {
res = sp->compareCertificate(sp, session->sessionSpContext, &remoteCertificate);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"%s: The client tries to use a different certificate "
"for authentication", logPrefix);
}
return res;
}
if(remoteCertificate.length > 0) {
res = sp->newChannelContext(sp, &remoteCertificate,
&session->sessionSpContext);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"%s: Could not use the supplied certificate "
"to instantiate the SecurityPolicy %S for the "
"validation of the UserIdentityToken",
logPrefix, sp->policyUri);
}
}
return res;
}
static UA_Boolean
clientRequestedEphemeralKey(const UA_ExtensionObject *additionalHeader) {
if(additionalHeader->encoding != UA_EXTENSIONOBJECT_DECODED &&
additionalHeader->encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE)
return false;
if(additionalHeader->content.decoded.type !=
&UA_TYPES[UA_TYPES_ADDITIONALPARAMETERSTYPE])
return false;
const UA_AdditionalParametersType *ap = (const UA_AdditionalParametersType*)
additionalHeader->content.decoded.data;
UA_String ecdhPolicyUri = UA_STRING("ECDHPolicyUri");
for(size_t i = 0; i < ap->parametersSize; i++) {
if(ap->parameters[i].key.namespaceIndex == 0 &&
UA_String_equal(&ap->parameters[i].key.name, &ecdhPolicyUri))
return true;
}
return false;
}
static UA_StatusCode
addEphemeralKeyAdditionalHeader(UA_Server *server, UA_Session *session,
UA_ExtensionObject *ah) {
UA_assert(session->sessionSp && session->sessionSpContext);
UA_SecurityPolicy *sp = session->sessionSp;
void *spContext = session->sessionSpContext;
UA_AdditionalParametersType *ap = UA_AdditionalParametersType_new();
if(!ap)
return UA_STATUSCODE_BADOUTOFMEMORY;
UA_ExtensionObject_setValue(ah, ap, &UA_TYPES[UA_TYPES_ADDITIONALPARAMETERSTYPE]);
UA_KeyValueMap *map = (UA_KeyValueMap*)ap;
UA_StatusCode res =
UA_KeyValueMap_setScalar(map, UA_QUALIFIEDNAME(0, "ECDHPolicyUri"),
&sp->policyUri, &UA_TYPES[UA_TYPES_STRING]);
if(res != UA_STATUSCODE_GOOD)
return res;
UA_EphemeralKeyType ephKey;
UA_EphemeralKeyType_init(&ephKey);
res = UA_ByteString_allocBuffer(&ephKey.publicKey, sp->nonceLength);
if(res != UA_STATUSCODE_GOOD)
return res;
size_t signatureSize =
sp->asymSignatureAlgorithm.getLocalSignatureSize(sp, spContext);
res = UA_ByteString_allocBuffer(&ephKey.signature, signatureSize);
if(res != UA_STATUSCODE_GOOD) {
UA_EphemeralKeyType_clear(&ephKey);
return res;
}
ephKey.publicKey.data[0] = 'e';
ephKey.publicKey.data[1] = 'p';
ephKey.publicKey.data[2] = 'h';
res |= sp->generateNonce(sp, spContext, &ephKey.publicKey);
res |= sp->asymSignatureAlgorithm.sign(sp, spContext, &ephKey.publicKey,
&ephKey.signature);
if(res == UA_STATUSCODE_GOOD)
res = UA_KeyValueMap_setScalar(map, UA_QUALIFIEDNAME(0, "ECDHKey"),
&ephKey, &UA_TYPES[UA_TYPES_EPHEMERALKEYTYPE]);
UA_EphemeralKeyType_clear(&ephKey);
return res;
}
UA_StatusCode
UA_Session_create(UA_Server *server, UA_SecureChannel *channel,
const UA_CreateSessionRequest *request, UA_Session **session) {
UA_LOCK_ASSERT(&server->serviceMutex);
if(server->sessionCount >= server->config.maxSessions) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: Could not create a Session - "
"Server limits reached");
return UA_STATUSCODE_BADTOOMANYSESSIONS;
}
session_list_entry *newentry = (session_list_entry*)
UA_malloc(sizeof(session_list_entry));
if(!newentry)
return UA_STATUSCODE_BADOUTOFMEMORY;
UA_Session_init(&newentry->session);
newentry->session.sessionId = UA_NODEID_GUID(1, UA_Guid_random());
newentry->session.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random());
newentry->session.timeout = server->config.maxSessionTimeout;
if(request->requestedSessionTimeout <= server->config.maxSessionTimeout &&
request->requestedSessionTimeout > 0)
newentry->session.timeout = request->requestedSessionTimeout;
if(channel)
UA_Session_attachToSecureChannel(server, &newentry->session, channel);
UA_EventLoop *el = server->config.eventLoop;
UA_DateTime now = el->dateTime_now(el);
UA_DateTime nowMonotonic = el->dateTime_nowMonotonic(el);
UA_Session_updateLifetime(&newentry->session, now, nowMonotonic);
LIST_INSERT_HEAD(&server->sessions, newentry, pointers);
server->sessionCount++;
notifySession(server, &newentry->session,
UA_APPLICATIONNOTIFICATIONTYPE_SESSION_CREATED);
*session = &newentry->session;
return UA_STATUSCODE_GOOD;
}
void
Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
const UA_CreateSessionRequest *request,
UA_CreateSessionResponse *response) {
UA_LOCK_ASSERT(&server->serviceMutex);
UA_LOG_DEBUG_CHANNEL(server->config.logging, channel, "CreateSession");
void *cc = channel->channelContext;
UA_SecurityPolicy *sp = channel->securityPolicy;
UA_ResponseHeader *rh = &response->responseHeader;
UA_assert(sp != NULL);
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN ||
channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
UA_StatusCode res = sp->compareCertificate(sp, cc, &request->clientCertificate);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: The client uses a different "
"certificate for SecureChannel and Session");
server->serverDiagnosticsSummary.securityRejectedSessionCount++;
server->serverDiagnosticsSummary.rejectedSessionCount++;
rh->serviceResult = UA_STATUSCODE_BADCERTIFICATEINVALID;
return;
}
}
if(channel->securityPolicy->policyType == UA_SECURITYPOLICYTYPE_NONE) {
if(request->clientCertificate.length > 0) {
UA_LOG_WARNING_CHANNEL(server->config.logging, channel,
"CreateSession: Ignoring client certificate "
"on SecurityPolicy None (Part 4, 5.6.2.2)");
}
} else if(request->clientCertificate.length > 0) {
rh->serviceResult =
validateCertificate(server, &server->config.secureChannelPKI,
channel, NULL, "CreateSession",
&request->clientDescription,
request->clientCertificate);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
server->serverDiagnosticsSummary.securityRejectedSessionCount++;
server->serverDiagnosticsSummary.rejectedSessionCount++;
return;
}
}
if(channel->securityPolicy->policyType != UA_SECURITYPOLICYTYPE_NONE &&
(request->clientNonce.length < 32 || request->clientNonce.length > 128)) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: The nonce provided by the client "
"has the wrong length");
server->serverDiagnosticsSummary.securityRejectedSessionCount++;
server->serverDiagnosticsSummary.rejectedSessionCount++;
rh->serviceResult = UA_STATUSCODE_BADNONCEINVALID;
return;
}
UA_Session *newSession = NULL;
rh->serviceResult = UA_Session_create(server, channel, request, &newSession);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
server->serverDiagnosticsSummary.rejectedSessionCount++;
return;
}
rh->serviceResult |= UA_String_copy(&request->sessionName,
&newSession->sessionName);
if(newSession->sessionName.length == 0)
rh->serviceResult |= UA_NodeId_print(&newSession->sessionId,
&newSession->sessionName);
newSession->maxResponseMessageSize = request->maxResponseMessageSize;
newSession->maxRequestMessageSize = channel->config.localMaxMessageSize;
rh->serviceResult |= UA_ApplicationDescription_copy(&request->clientDescription,
&newSession->clientDescription);
if(channel->remoteCertificate.length == 0)
rh->serviceResult |= UA_ByteString_copy(&request->clientCertificate,
&newSession->clientCertificate);
rh->serviceResult |= UA_ByteString_copy(&request->clientNonce,
&newSession->clientNonce);
#ifdef UA_ENABLE_DIAGNOSTICS
rh->serviceResult |= UA_String_copy(&request->serverUri,
&newSession->diagnostics.serverUri);
rh->serviceResult |= UA_String_copy(&request->endpointUrl,
&newSession->diagnostics.endpointUrl);
#endif
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: Could not create new session (%s)",
UA_StatusCode_name(rh->serviceResult));
UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT);
return;
}
UA_SecurityPolicy *sessionSp = NULL;
if(request->clientCertificate.length > 0) {
if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE)
sessionSp = getDefaultEncryptedSecurityPolicy(server, sp->policyType);
else
sessionSp = sp;
}
if(sessionSp) {
rh->serviceResult =
createCheckSessionAuthSecurityPolicyContext(server, newSession,
sessionSp, "CreateSession",
request->clientCertificate);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT);
return;
}
}
rh->serviceResult = UA_Session_generateNonce(newSession);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: Could not create the server nonce (%s)",
UA_StatusCode_name(rh->serviceResult));
UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT);
return;
}
if(sessionSp && UA_SecurityPolicy_isEcc(sessionSp) &&
clientRequestedEphemeralKey(&request->requestHeader.additionalHeader)) {
rh->serviceResult = addEphemeralKeyAdditionalHeader(server, newSession,
&rh->additionalHeader);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: Could not prepare the ephemeral key (%s)",
UA_StatusCode_name(rh->serviceResult));
UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT);
return;
}
}
response->sessionId = newSession->sessionId;
response->revisedSessionTimeout = (UA_Double)newSession->timeout;
response->authenticationToken = newSession->authenticationToken;
rh->serviceResult |= UA_ByteString_copy(&newSession->serverNonce,
&response->serverNonce);
if(sessionSp)
rh->serviceResult |= UA_ByteString_copy(&sessionSp->localCertificate,
&response->serverCertificate);
rh->serviceResult |= setCurrentEndpointsArray(server, request->endpointUrl,
NULL, 0,
&response->serverEndpoints,
&response->serverEndpointsSize);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: Could not prepare the response (%s)",
UA_StatusCode_name(rh->serviceResult));
UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT);
return;
}
rh->serviceResult |=
signCreateSessionResponse(server, channel, request, response);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT);
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"CreateSession: Could not sign the response (%s)",
UA_StatusCode_name(rh->serviceResult));
return;
}
#ifdef UA_ENABLE_DIAGNOSTICS
UA_EventLoop *el = server->config.eventLoop;
newSession->diagnostics.clientConnectionTime = el->dateTime_now(el);
newSession->diagnostics.clientLastContactTime =
newSession->diagnostics.clientConnectionTime;
createSessionObject(server, newSession);
#endif
UA_LOG_INFO_SESSION(server->config.logging, newSession, "Session created");
}
static UA_StatusCode
checkCertificateSignature(const UA_Server *server, const UA_SecurityPolicy *tokenSp,
void *channelContext, const UA_ByteString *serverNonce,
const UA_SignatureData *signature,
const bool isUserTokenSignature, bool allowEmptyLocalCert) {
UA_assert(tokenSp != NULL);
if(signature->signature.length == 0) {
if(isUserTokenSignature)
return UA_STATUSCODE_BADUSERSIGNATUREINVALID;
return UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID;
}
const UA_ByteString *localCertificate = &tokenSp->localCertificate;
UA_ByteString dataToVerify;
size_t dataToVerifySize = localCertificate->length + serverNonce->length;
UA_StatusCode res = UA_ByteString_allocBuffer(&dataToVerify, dataToVerifySize);
if(res != UA_STATUSCODE_GOOD)
return res;
memcpy(dataToVerify.data, localCertificate->data, localCertificate->length);
memcpy(dataToVerify.data + localCertificate->length, serverNonce->data, serverNonce->length);
check_certificate:
res = tokenSp->asymSignatureAlgorithm.
verify(tokenSp, channelContext, &dataToVerify, &signature->signature);
if(res != UA_STATUSCODE_GOOD) {
if(isUserTokenSignature)
res = UA_STATUSCODE_BADUSERSIGNATUREINVALID;
else
res = UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID;
}
if(dataToVerify.data != serverNonce->data)
UA_ByteString_clear(&dataToVerify);
if(res != UA_STATUSCODE_GOOD && localCertificate->length > 0 && allowEmptyLocalCert) {
localCertificate = &UA_BYTESTRING_NULL;
dataToVerify = *serverNonce;
goto check_certificate;
}
return res;
}
static const UA_UserTokenPolicy *
selectTokenPolicy(UA_Server *server, UA_SecureChannel *channel,
UA_Session *session, const UA_ExtensionObject *identityToken,
const UA_EndpointDescription *ed,
UA_SecurityPolicy **tokenSp) {
size_t identPoliciesSize = ed->userIdentityTokensSize;
const UA_UserTokenPolicy *identPolicies = ed->userIdentityTokens;
if(identPoliciesSize == 0) {
identPoliciesSize = server->config.accessControl.userTokenPoliciesSize;
identPolicies = server->config.accessControl.userTokenPolicies;
}
const UA_DataType *tokenDataType = identityToken->content.decoded.type;
for(size_t j = 0; j < identPoliciesSize; j++) {
const UA_UserTokenPolicy *pol = &identPolicies[j];
if(identityToken->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY &&
pol->tokenType == UA_USERTOKENTYPE_ANONYMOUS) {
*tokenSp = channel->securityPolicy;
return pol;
}
if(!tokenDataType)
continue;
switch(pol->tokenType) {
case UA_USERTOKENTYPE_ANONYMOUS:
if(tokenDataType != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN])
continue;
break;
case UA_USERTOKENTYPE_USERNAME:
if(tokenDataType != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN])
continue;
break;
case UA_USERTOKENTYPE_CERTIFICATE:
if(tokenDataType != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN])
continue;
break;
case UA_USERTOKENTYPE_ISSUEDTOKEN:
if(tokenDataType != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN])
continue;
break;
default:
continue;
}
UA_AnonymousIdentityToken *token = (UA_AnonymousIdentityToken*)
identityToken->content.decoded.data;
if(pol->policyId.length > token->policyId.length)
continue;
UA_String policyPrefix = token->policyId;
policyPrefix.length = pol->policyId.length;
if(!UA_String_equal(&policyPrefix, &pol->policyId))
continue;
UA_String utPolPostfix = securityPolicyUriPostfix(token->policyId);
UA_SecurityPolicy *candidateSp =
getSecurityPolicyByPostfix(server, utPolPostfix);
if(!candidateSp) {
UA_LOG_WARNING_SESSION(server->config.logging, session,
"ActivateSession: The UserTokenPolicy of "
"the endpoint defines an unknown "
"SecurityPolicy %S",
pol->securityPolicyUri);
continue;
}
if(pol->tokenType != UA_USERTOKENTYPE_ANONYMOUS &&
channel->securityPolicy->policyType == UA_SECURITYPOLICYTYPE_NONE &&
candidateSp->policyType == UA_SECURITYPOLICYTYPE_NONE) {
if(!server->config.allowNonePolicyPassword ||
pol->tokenType != UA_USERTOKENTYPE_USERNAME)
continue;
}
*tokenSp = candidateSp;
return pol;
}
return NULL;
}
static void
selectEndpointAndTokenPolicy(UA_Server *server, UA_SecureChannel *channel,
UA_Session *session,
const UA_ExtensionObject *identityToken,
const UA_EndpointDescription **ed,
const UA_UserTokenPolicy **utp,
UA_SecurityPolicy **tokenSp) {
UA_ServerConfig *sc = &server->config;
for(size_t i = 0; i < sc->endpointsSize; ++i) {
const UA_EndpointDescription *desc = &sc->endpoints[i];
if(desc->securityMode != channel->securityMode)
continue;
if(!UA_String_equal(&desc->securityPolicyUri,
&channel->securityPolicy->policyUri))
continue;
*utp = selectTokenPolicy(server, channel, session,
identityToken, desc, tokenSp);
if(*utp) {
*ed = desc;
return;
}
}
}
static UA_StatusCode
hideX509IdentityTokenValidationStatus(UA_StatusCode status) {
if(status == UA_STATUSCODE_GOOD)
return UA_STATUSCODE_GOOD;
return UA_STATUSCODE_BADIDENTITYTOKENREJECTED;
}
static UA_StatusCode
checkActivateSessionX509(UA_Server *server, UA_SecureChannel *channel, UA_Session *session,
const UA_SecurityPolicy *tokenSp, UA_X509IdentityToken* token,
const UA_SignatureData *tokenSignature) {
if(tokenSp->policyType == UA_SECURITYPOLICYTYPE_NONE)
return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
void *tempChannelContext;
UA_StatusCode res = tokenSp->newChannelContext(tokenSp, &token->certificateData,
&tempChannelContext);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Failed to create a context "
"for the SecurityPolicy %S", tokenSp->policyUri);
return res;
}
if(UA_SecurityPolicy_isEnhancedSecurity(channel->securityPolicy) &&
UA_SecurityPolicy_isEnhancedSecurity(tokenSp)) {
if(tokenSignature->signature.length == 0) {
res = UA_STATUSCODE_BADUSERSIGNATUREINVALID;
goto out;
}
UA_ByteString dataToVerify = UA_BYTESTRING_NULL;
res = UA_SecureChannel_buildUserTokenSignatureData(
channel, &session->serverNonce, &session->clientNonce,
&channel->securityPolicy->localCertificate,
&channel->securityPolicy->localCertificate,
&channel->remoteCertificate, &channel->remoteCertificate, &dataToVerify);
if(res == UA_STATUSCODE_GOOD) {
res = tokenSp->asymSignatureAlgorithm.verify(
tokenSp, tempChannelContext, &dataToVerify, &tokenSignature->signature);
if(res != UA_STATUSCODE_GOOD)
res = UA_STATUSCODE_BADUSERSIGNATUREINVALID;
}
UA_ByteString_clear(&dataToVerify);
} else {
res = checkCertificateSignature(server, tokenSp, tempChannelContext, &session->serverNonce,
tokenSignature, true,
(channel->securityMode == UA_MESSAGESECURITYMODE_NONE));
}
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: User token signature check "
"failed with StatusCode %s", UA_StatusCode_name(res));
goto out;
}
res = validateCertificate(server, &server->config.sessionPKI,
session->channel, session, "ActivateSession",
NULL, token->certificateData);
res = hideX509IdentityTokenValidationStatus(res);
out:
tokenSp->deleteChannelContext(tokenSp, tempChannelContext);
return res;
}
static UA_StatusCode
decryptUserToken(UA_Server *server, UA_Session *session, UA_SecureChannel *channel,
UA_SecurityPolicy *tokenSp, UA_ByteString *token,
const UA_String encryptionAlgorithm) {
if(tokenSp->policyType == UA_SECURITYPOLICYTYPE_NONE) {
if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE)
UA_LOG_WARNING_SESSION(server->config.logging, session,
"ActivateSession: Processing an unencrypted "
"UserToken. This is dangerous for the server "
"to allow.");
return UA_STATUSCODE_GOOD;
}
if(encryptionAlgorithm.length > 0 &&
!UA_String_equal(&tokenSp->asymEncryptionAlgorithm.uri,
&encryptionAlgorithm)) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Encryption algorithm used "
"for the UserIdentityToken does not match "
"the endpoint");
return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
}
UA_ByteString applicationCert = (channel->remoteCertificate.length > 0) ?
channel->remoteCertificate : session->clientCertificate;
UA_StatusCode res =
createCheckSessionAuthSecurityPolicyContext(server, session, tokenSp,
"ActivateSession", applicationCert);
if(res != UA_STATUSCODE_GOOD)
return res;
if(UA_SecurityPolicy_isEcc(tokenSp)) {
res = decryptUserTokenEcc(server->config.logging, channel,
session->sessionSp, session->sessionSpContext,
session->serverNonce, token);
} else {
res = decryptSecretLegacy(session->sessionSp, session->sessionSpContext,
session->serverNonce, token);
}
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Could not decrypt/"
"cryptographically validate the UserIdentityToken "
"with the StatusCode %s", UA_StatusCode_name(res));
}
return res;
}
#define UA_SESSION_REJECT \
do { \
server->serverDiagnosticsSummary.rejectedSessionCount++; \
return; \
} while(0)
#define UA_SECURITY_REJECT \
do { \
server->serverDiagnosticsSummary.securityRejectedSessionCount++; \
server->serverDiagnosticsSummary.rejectedSessionCount++; \
return; \
} while(0)
void
Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
const UA_ActivateSessionRequest *req,
UA_ActivateSessionResponse *resp) {
UA_LOCK_ASSERT(&server->serviceMutex);
UA_ResponseHeader *rh = &resp->responseHeader;
UA_Session *session =
getSessionByToken(server, &req->requestHeader.authenticationToken);
if(!session) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"ActivateSession: Session not found");
rh->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
UA_SESSION_REJECT;
}
if(!session->activated && session->channel != channel) {
UA_LOG_ERROR_CHANNEL(server->config.logging, channel,
"ActivateSession: The Session has to be initially "
"activated on the SecureChannel that created it");
rh->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
UA_SESSION_REJECT;
}
UA_EventLoop *el = server->config.eventLoop;
UA_DateTime nowMonotonic = el->dateTime_nowMonotonic(el);
if(session->validTill < nowMonotonic) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: The Session has timed out");
rh->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
UA_SESSION_REJECT;
}
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN ||
channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
const UA_SecurityPolicy *csp = channel->securityPolicy;
if(UA_SecurityPolicy_isEnhancedSecurity(csp)) {
if(req->clientSignature.signature.length == 0) {
rh->serviceResult = UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID;
} else {
UA_ByteString dataToVerify = UA_BYTESTRING_NULL;
rh->serviceResult = UA_SecureChannel_buildActivateSessionSignatureData(
channel, &session->serverNonce, &session->clientNonce,
&csp->localCertificate, &csp->localCertificate,
&channel->remoteCertificate, &dataToVerify);
if(rh->serviceResult == UA_STATUSCODE_GOOD) {
rh->serviceResult = csp->asymSignatureAlgorithm.verify(
csp, channel->channelContext, &dataToVerify,
&req->clientSignature.signature);
if(rh->serviceResult != UA_STATUSCODE_GOOD)
rh->serviceResult = UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID;
}
UA_ByteString_clear(&dataToVerify);
}
} else {
rh->serviceResult =
checkCertificateSignature(server, channel->securityPolicy,
channel->channelContext, &session->serverNonce,
&req->clientSignature, false, false);
}
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Client signature check failed "
"with StatusCode %s",
UA_StatusCode_name(rh->serviceResult));
UA_SECURITY_REJECT;
}
}
const UA_EndpointDescription *ed = NULL;
const UA_UserTokenPolicy *utp = NULL;
UA_SecurityPolicy *tokenSp = NULL;
selectEndpointAndTokenPolicy(server, channel, session,
&req->userIdentityToken,
&ed, &utp, &tokenSp);
if(!ed || !tokenSp) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Requested Endpoint/UserTokenPolicy "
"not available");
rh->serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
UA_SESSION_REJECT;
}
switch(utp->tokenType) {
case UA_USERTOKENTYPE_ANONYMOUS:
break;
case UA_USERTOKENTYPE_USERNAME: {
UA_UserNameIdentityToken *token = (UA_UserNameIdentityToken *)
req->userIdentityToken.content.decoded.data;
rh->serviceResult =
decryptUserToken(server, session, channel, tokenSp,
&token->password, token->encryptionAlgorithm);
break; }
case UA_USERTOKENTYPE_CERTIFICATE: {
UA_X509IdentityToken* x509token = (UA_X509IdentityToken*)
req->userIdentityToken.content.decoded.data;
rh->serviceResult =
checkActivateSessionX509(server, channel, session, tokenSp, x509token,
&req->userTokenSignature);
break; }
case UA_USERTOKENTYPE_ISSUEDTOKEN: {
UA_IssuedIdentityToken *token = (UA_IssuedIdentityToken*)
req->userIdentityToken.content.decoded.data;
rh->serviceResult =
decryptUserToken(server, session, channel, tokenSp,
&token->tokenData, token->encryptionAlgorithm);
break; }
default:
rh->serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
break;
}
if(rh->serviceResult != UA_STATUSCODE_GOOD)
UA_SECURITY_REJECT;
rh->serviceResult = server->config.accessControl.
activateSession(server, &server->config.accessControl, ed,
&channel->remoteCertificate, &session->sessionId,
&req->userIdentityToken, &session->context);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: The AccessControl plugin "
"denied the activation with the StatusCode %s",
UA_StatusCode_name(rh->serviceResult));
UA_SECURITY_REJECT;
}
if(!session->channel || session->channel != channel) {
UA_Session_attachToSecureChannel(server, session, channel);
UA_LOG_INFO_SESSION(server->config.logging, session,
"ActivateSession: Session attached to new channel");
}
rh->serviceResult = UA_Session_generateNonce(session);
rh->serviceResult |= UA_ByteString_copy(&session->serverNonce,
&resp->serverNonce);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_Session_detachFromSecureChannel(server, session);
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Could not generate the server nonce");
UA_SESSION_REJECT;
}
if(req->localeIdsSize > 0) {
UA_String *tmpLocaleIds;
rh->serviceResult |=
UA_Array_copy(req->localeIds, req->localeIdsSize,
(void**)&tmpLocaleIds, &UA_TYPES[UA_TYPES_STRING]);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_Session_detachFromSecureChannel(server, session);
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Could not store the "
"Session LocaleIds");
UA_SESSION_REJECT;
}
UA_Array_delete(session->localeIds, session->localeIdsSize,
&UA_TYPES[UA_TYPES_STRING]);
session->localeIds = tmpLocaleIds;
session->localeIdsSize = req->localeIdsSize;
}
UA_DateTime now = el->dateTime_now(el);
nowMonotonic = el->dateTime_nowMonotonic(el);
UA_Session_updateLifetime(session, now, nowMonotonic);
const UA_SecurityPolicy *sessionSp = session->sessionSp;
if(sessionSp && session->sessionSpContext &&
UA_SecurityPolicy_isEcc(sessionSp) &&
clientRequestedEphemeralKey(&req->requestHeader.additionalHeader)) {
rh->serviceResult = addEphemeralKeyAdditionalHeader(server, session,
&rh->additionalHeader);
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_SESSION(server->config.logging, session,
"ActivateSession: Could not prepare the "
"ephemeral key (%s)",
UA_StatusCode_name(rh->serviceResult));
UA_SECURITY_REJECT;
}
}
if(!session->activated) {
session->activated = true;
server->activeSessionCount++;
server->serverDiagnosticsSummary.cumulatedSessionCount++;
}
UA_String_clear(&session->clientUserIdOfSession);
const UA_DataType *tokenType = req->userIdentityToken.content.decoded.type;
if(tokenType == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
const UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken*)
req->userIdentityToken.content.decoded.data;
UA_String_copy(&userToken->userName, &session->clientUserIdOfSession);
} else if(tokenType == &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) {
UA_X509IdentityToken* userCertToken = (UA_X509IdentityToken*)
req->userIdentityToken.content.decoded.data;
UA_CertificateUtils_getSubjectName(&session->clientUserIdOfSession,
&userCertToken->certificateData);
} else {
}
#ifdef UA_ENABLE_DIAGNOSTICS
UA_SessionSecurityDiagnosticsDataType *ssd = &session->securityDiagnostics;
UA_Array_appendCopy((void**)&ssd->clientUserIdHistory,
&ssd->clientUserIdHistorySize,
&ssd->clientUserIdOfSession,
&UA_TYPES[UA_TYPES_STRING]);
UA_String_clear(&ssd->authenticationMechanism);
switch(utp->tokenType) {
case UA_USERTOKENTYPE_ANONYMOUS:
ssd->authenticationMechanism = UA_STRING_ALLOC("Anonymous"); break;
case UA_USERTOKENTYPE_USERNAME:
ssd->authenticationMechanism = UA_STRING_ALLOC("UserName"); break;
case UA_USERTOKENTYPE_CERTIFICATE:
ssd->authenticationMechanism = UA_STRING_ALLOC("Certificate"); break;
case UA_USERTOKENTYPE_ISSUEDTOKEN:
ssd->authenticationMechanism = UA_STRING_ALLOC("IssuedToken"); break;
default: break;
}
#endif
notifySession(server, session, UA_APPLICATIONNOTIFICATIONTYPE_SESSION_ACTIVATED);
UA_LOG_INFO_SESSION(server->config.logging, session,
"ActivateSession: Session activated with ClientUserId \"%S\"",
session->clientUserIdOfSession);
}
void
Service_CloseSession(UA_Server *server, UA_SecureChannel *channel,
const UA_CloseSessionRequest *request,
UA_CloseSessionResponse *response) {
UA_LOCK_ASSERT(&server->serviceMutex);
UA_ResponseHeader *rh = &response->responseHeader;
UA_Session *session = NULL;
const UA_NodeId *authToken = &request->requestHeader.authenticationToken;
rh->serviceResult = getBoundSession(server, channel, authToken, &session);
if(!session && rh->serviceResult == UA_STATUSCODE_GOOD)
rh->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
if(rh->serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING_CHANNEL(server->config.logging, channel,
"CloseSession: No Session activated to the SecureChannel");
return;
}
UA_assert(session);
UA_LOG_INFO_SESSION(server->config.logging, session, "Closing the Session");
#ifdef UA_ENABLE_SUBSCRIPTIONS
if(!request->deleteSubscriptions) {
UA_Subscription *sub, *sub_tmp;
TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, sub_tmp) {
UA_LOG_INFO_SUBSCRIPTION(server->config.logging, sub,
"Detaching the Subscription from the Session");
UA_Session_detachSubscription(server, session, sub, true);
}
}
#endif
UA_Session_remove(server, session, UA_SHUTDOWNREASON_CLOSE);
}
UA_Boolean
Service_Cancel(UA_Server *server, UA_Session *session,
const UA_CancelRequest *request, UA_CancelResponse *response) {
response->cancelCount = UA_AsyncManager_cancel(server, session,
request->requestHandle);
#ifdef UA_ENABLE_SUBSCRIPTIONS
UA_PublishResponseEntry *pre, *pre_tmp;
UA_PublishResponseEntry *prev = NULL;
SIMPLEQ_FOREACH_SAFE(pre, &session->responseQueue, listEntry, pre_tmp) {
if(pre->response.responseHeader.requestHandle != request->requestHandle) {
prev = pre;
continue;
}
if(prev)
SIMPLEQ_REMOVE_AFTER(&session->responseQueue, prev, listEntry);
else
SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
session->responseQueueSize--;
response->responseHeader.serviceResult = UA_STATUSCODE_BADREQUESTCANCELLEDBYCLIENT;
sendResponse(server, session->channel, pre->requestId, (UA_Response *)response,
&UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
UA_PublishResponse_clear(&pre->response);
UA_free(pre);
response->cancelCount++;
}
#endif
return true;
}