Skip to content

Commit

Permalink
Merge pull request open62541#6065 from jpfr/merge_14_master_6
Browse files Browse the repository at this point in the history
Merge 1.4 to master
  • Loading branch information
jpfr authored Oct 16, 2023
2 parents 744ea7e + 82d55d2 commit 77e703a
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 140 deletions.
2 changes: 2 additions & 0 deletions plugins/crypto/openssl/ua_pki_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "ua_openssl_version_abstraction.h"
#include "libc_time.h"

#include <limits.h>

/* Find binary substring. Taken and adjusted from
* http://tungchingkai.blogspot.com/2011/07/binary-strstr.html */

Expand Down
8 changes: 8 additions & 0 deletions src/server/ua_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,14 @@ UA_Server_run_startup(UA_Server *server) {
UA_AsyncManager_start(&server->asyncManager, server);
#endif

/* Are there enough SecureChannels possible for the max number of sessions? */
if(config->maxSecureChannels != 0 &&
(config->maxSessions == 0 || config->maxSessions <= config->maxSecureChannels)) {
UA_LOG_WARNING(&config->logger, UA_LOGCATEGORY_SERVER,
"Maximum SecureChannels count not enough for the "
"maximum Sessions count");
}

/* Add a regular callback for housekeeping tasks. With a 1s interval. */
retVal = addRepeatedCallback(server, serverHouseKeeping,
NULL, 1000.0, &server->houseKeepingCallbackId);
Expand Down
22 changes: 10 additions & 12 deletions src/server/ua_server_binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -938,17 +938,16 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,

/* Check timestamp in the request header */
UA_RequestHeader *requestHeader = &request.requestHeader;
if(requestHeader->timestamp == 0) {
if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_WARN) {
UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
"The server sends no timestamp in the request header. "
"See the 'verifyRequestTimestamp' setting.");
if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_ABORT) {
retval = sendServiceFault(channel, requestId, requestHeader->requestHandle,
UA_STATUSCODE_BADINVALIDTIMESTAMP);
UA_clear(&request, requestType);
return retval;
}
if(requestHeader->timestamp == 0 &&
server->config.verifyRequestTimestamp <= UA_RULEHANDLING_WARN) {
UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
"The server sends no timestamp in the request header. "
"See the 'verifyRequestTimestamp' setting.");
if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_ABORT) {
retval = sendServiceFault(channel, requestId, requestHeader->requestHandle,
UA_STATUSCODE_BADINVALIDTIMESTAMP);
UA_clear(&request, requestType);
return retval;
}
}

Expand Down Expand Up @@ -1135,7 +1134,6 @@ createServerSecureChannel(UA_BinaryProtocolManager *bpm, UA_ConnectionManager *c
* alternative security token, we don't touch this value during the token
* rollover. */
entry->channel.securityToken.channelId = bpm->lastChannelId++;
entry->channel.altSecurityToken.channelId = entry->channel.securityToken.channelId;

/* Set an initial timeout before the negotiation handshake. So the channel
* is caught if the client is unresponsive.
Expand Down
1 change: 0 additions & 1 deletion src/server/ua_server_discovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ register_server_with_discovery_server(UA_Server *server,
* as the members are stack-allocated or point into the server config. */
UA_RegisterServer2Request request;
UA_RegisterServer2Request_init(&request);
request.requestHeader.timestamp = UA_DateTime_now();
request.requestHeader.timeoutHint = 10000;

request.server.isOnline = !isUnregister;
Expand Down
47 changes: 45 additions & 2 deletions src/server/ua_server_ns0.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,43 @@ readMinSamplingInterval(UA_Server *server, const UA_NodeId *sessionId, void *ses

#if defined(UA_GENERATED_NAMESPACE_ZERO) && defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
static UA_StatusCode
resendData(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId,
void *objectContext, size_t inputSize, const UA_Variant *input,
size_t outputSize, UA_Variant *output) {
/* Get the input argument */
if(inputSize != 1 ||
!UA_Variant_hasScalarType(input, &UA_TYPES[UA_TYPES_UINT32]))
return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));

/* Get the Session */
UA_LOCK(&server->serviceMutex);
UA_Session *session = getSessionById(server, sessionId);
if(!session) {
UA_UNLOCK(&server->serviceMutex);
return UA_STATUSCODE_BADINTERNALERROR;
}

/* Get the Subscription */
UA_Subscription *subscription = getSubscriptionById(server, subscriptionId);
if(!subscription) {
UA_UNLOCK(&server->serviceMutex);
return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
}

/* The Subscription is not attached to this Session */
if(subscription->session != session) {
UA_UNLOCK(&server->serviceMutex);
return UA_STATUSCODE_BADUSERACCESSDENIED;
}

UA_Subscription_resendData(server, subscription);

UA_UNLOCK(&server->serviceMutex);
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId,
void *objectContext, size_t inputSize, const UA_Variant *input,
Expand Down Expand Up @@ -724,13 +761,15 @@ writeNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v,
return writeValueAttribute(server, UA_NODEID_NUMERIC(0, id), &var);
}

#ifdef UA_GENERATED_NAMESPACE_ZERO
static UA_StatusCode
writeNs0Variable(UA_Server *server, UA_UInt32 id, void *v, const UA_DataType *type) {
UA_Variant var;
UA_Variant_init(&var);
UA_Variant_setScalar(&var, v, type);
return writeValueAttribute(server, UA_NODEID_NUMERIC(0, id), &var);
}
#endif

#ifndef UA_GENERATED_NAMESPACE_ZERO
static UA_StatusCode
Expand Down Expand Up @@ -1096,7 +1135,6 @@ initNS0(UA_Server *server) {
deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_ESTIMATEDRETURNTIME), true);
deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_LOCALTIME), true);
deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_REQUESTSERVERSTATECHANGE), true);
deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_RESENDDATA), true);
deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION), true);
deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SETSUBSCRIPTIONDURABLE), true);

Expand Down Expand Up @@ -1250,8 +1288,13 @@ initNS0(UA_Server *server) {
#endif

#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
retVal |= setMethodNode_callback(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
retVal |= setMethodNode_callback(server,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
readMonitoredItems);

retVal |= setMethodNode_callback(server,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_RESENDDATA),
resendData);
#endif

/* The HasComponent references to the ModellingRules are not part of the
Expand Down
2 changes: 1 addition & 1 deletion src/server/ua_services.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ void Service_RegisterServer2(UA_Server *server, UA_Session *session,
* Open or renew a SecureChannel that can be used to ensure Confidentiality and
* Integrity for Message exchange during a Session. */
void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel* channel,
const UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelResponse *response);

/**
Expand Down
195 changes: 78 additions & 117 deletions src/server/ua_services_securechannel.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,155 +16,116 @@
* decoding of the message. The main SecureChannel logic is handled in
* /src/ua_securechannel.* and /src/server/ua_server_binary.c. */

static UA_StatusCode
UA_SecureChannelManager_open(UA_Server *server, UA_SecureChannel *channel,
const UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelResponse *response) {
if(channel->state != UA_SECURECHANNELSTATE_ACK_SENT) {
UA_LOG_ERROR_CHANNEL(&server->config.logger, channel,
"Called open on already open or closed channel");
return UA_STATUSCODE_BADINTERNALERROR;
}

/* Set the SecurityMode */
void
Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel *channel,
UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelResponse *response) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
if(request->securityMode != UA_MESSAGESECURITYMODE_NONE &&
UA_ByteString_equal(&sp->policyUri, &UA_SECURITY_POLICY_NONE_URI))
return UA_STATUSCODE_BADSECURITYMODEREJECTED;
channel->securityMode = request->securityMode;

/* Set the initial SecurityToken. Set the alternative token that is moved to
* the primary token when the first symmetric message triggers a token
* revolve. Lifetime 0 -> set the maximum possible lifetime */
UA_EventLoop *el = server->config.eventLoop;
channel->renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER;
channel->altSecurityToken.tokenId = generateSecureChannelTokenId(server);
channel->altSecurityToken.createdAt = el->dateTime_nowMonotonic(el);
channel->altSecurityToken.revisedLifetime =
(request->requestedLifetime > server->config.maxSecurityTokenLifetime) ?
server->config.maxSecurityTokenLifetime : request->requestedLifetime;
if(channel->altSecurityToken.revisedLifetime == 0)
channel->altSecurityToken.revisedLifetime = server->config.maxSecurityTokenLifetime;

/* Store the received client nonce and generate the server nonce. The
* syymmetric encryption keys are generated from the nonces when the first
* symmetric message is received that triggers revolving of the token. */
UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
UA_CHECK_STATUS(retval, return retval);
switch(request->requestType) {
/* Open the channel */
case UA_SECURITYTOKENREQUESTTYPE_ISSUE:
/* We must expect an OPN handshake */
if(channel->state != UA_SECURECHANNELSTATE_ACK_SENT) {
UA_LOG_ERROR_CHANNEL(&server->config.logger, channel,
"Called open on already open or closed channel");
response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
goto error;
}

retval = UA_SecureChannel_generateLocalNonce(channel);
UA_CHECK_STATUS(retval, return retval);
/* Set the SecurityMode */
if(request->securityMode != UA_MESSAGESECURITYMODE_NONE &&
UA_ByteString_equal(&sp->policyUri, &UA_SECURITY_POLICY_NONE_URI)) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
goto error;
}
channel->securityMode = request->securityMode;
break;

/* Set the response. The token will be revolved to the new token when the
* first symmetric messages is received. */
response->securityToken = channel->altSecurityToken;
response->securityToken.createdAt = UA_DateTime_now(); /* Only for sending */
response->responseHeader.timestamp = response->securityToken.createdAt;
response->responseHeader.requestHandle = request->requestHeader.requestHandle;
retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
UA_CHECK_STATUS(retval, return retval);
/* Renew the channel */
case UA_SECURITYTOKENREQUESTTYPE_RENEW:
/* The channel must be open to be renewed */
if(channel->state != UA_SECURECHANNELSTATE_OPEN) {
UA_LOG_ERROR_CHANNEL(&server->config.logger, channel,
"Called renew on channel which is not open");
response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
goto error;
}

/* The channel is open */
channel->state = UA_SECURECHANNELSTATE_OPEN;
return UA_STATUSCODE_GOOD;
}
/* Check whether the nonce was reused */
if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE &&
UA_ByteString_equal(&channel->remoteNonce, &request->clientNonce)) {
UA_LOG_ERROR_CHANNEL(&server->config.logger, channel,
"The client reused the last nonce");
response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
goto error;
}

static UA_StatusCode
UA_SecureChannelManager_renew(UA_Server *server, UA_SecureChannel *channel,
const UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelResponse *response) {
if(channel->state != UA_SECURECHANNELSTATE_OPEN) {
UA_LOG_ERROR_CHANNEL(&server->config.logger, channel,
"Called renew on channel which is not open");
return UA_STATUSCODE_BADINTERNALERROR;
}
break;

/* Check whether the nonce was reused */
if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE &&
UA_ByteString_equal(&channel->remoteNonce, &request->clientNonce)) {
UA_LOG_ERROR_CHANNEL(&server->config.logger, channel,
"The client reused the last nonce");
return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
/* Unknown request type */
default:
response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
return;
}

/* Create a new SecurityToken. Will be switched over when the first message
* is received. The ChannelId is left unchanged. */
/* Create a new SecurityToken. It will be switched over when the first
* message is received. The ChannelId is left unchanged. */
UA_EventLoop *el = server->config.eventLoop;
channel->altSecurityToken = channel->securityToken;
channel->altSecurityToken.channelId = channel->securityToken.channelId;
channel->altSecurityToken.tokenId = generateSecureChannelTokenId(server);
channel->altSecurityToken.createdAt = el->dateTime_nowMonotonic(el);
channel->altSecurityToken.revisedLifetime =
(request->requestedLifetime > server->config.maxSecurityTokenLifetime) ?
server->config.maxSecurityTokenLifetime : request->requestedLifetime;
if(channel->altSecurityToken.revisedLifetime == 0) /* lifetime 0 -> return the max lifetime */
if(channel->altSecurityToken.revisedLifetime == 0)
channel->altSecurityToken.revisedLifetime = server->config.maxSecurityTokenLifetime;

/* Replace the nonces */
/* Set the nonces. The remote nonce will be "rotated in" when it is first used. */
UA_ByteString_clear(&channel->remoteNonce);
UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
if(retval != UA_STATUSCODE_GOOD)
return retval;
channel->remoteNonce = request->clientNonce;
UA_ByteString_init(&request->clientNonce);

retval = UA_SecureChannel_generateLocalNonce(channel);
if(retval != UA_STATUSCODE_GOOD)
return retval;
response->responseHeader.serviceResult = UA_SecureChannel_generateLocalNonce(channel);
UA_CHECK_STATUS(response->responseHeader.serviceResult, goto error);

/* Update the channel state */
channel->renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER;
channel->state = UA_SECURECHANNELSTATE_OPEN;

/* Set the response */
response->securityToken = channel->altSecurityToken;
response->securityToken.createdAt = UA_DateTime_now(); /* Only for sending */
response->securityToken.createdAt = UA_DateTime_now(); /* only for sending */
response->responseHeader.timestamp = response->securityToken.createdAt;
response->responseHeader.requestHandle = request->requestHeader.requestHandle;
retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
if(retval != UA_STATUSCODE_GOOD)
return retval;

channel->renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER;
return UA_STATUSCODE_GOOD;
}

void
Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel *channel,
const UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelResponse *response) {
/* Renew the channel */
if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_RENEW) {
response->responseHeader.serviceResult =
UA_SecureChannelManager_renew(server, channel, request, response);
if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel,
"Renewing SecureChannel failed");
return;
}
response->responseHeader.serviceResult =
UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
UA_CHECK_STATUS(response->responseHeader.serviceResult, goto error);

/* Log the renewal and the lifetime */
UA_Float lifetime = (UA_Float)response->securityToken.revisedLifetime / 1000;
/* Success */
if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_ISSUE) {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"SecureChannel opened with SecurityPolicy %.*s "
"and a revised lifetime of %.2fs",
(int)channel->securityPolicy->policyUri.length,
channel->securityPolicy->policyUri.data,
(UA_Float)response->securityToken.revisedLifetime / 1000);
} else {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "SecureChannel renewed "
"with a revised lifetime of %.2fs", lifetime);
return;
"with a revised lifetime of %.2fs",
(UA_Float)response->securityToken.revisedLifetime / 1000);
}

/* Must be ISSUE or RENEW */
if(request->requestType != UA_SECURITYTOKENREQUESTTYPE_ISSUE) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
return;
}
return;

/* Open the channel */
response->responseHeader.serviceResult =
UA_SecureChannelManager_open(server, channel, request, response);
if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
error:
if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_ISSUE) {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Opening a SecureChannel failed");
return;
} else {
UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel,
"Renewing SecureChannel failed");
}

/* Log the lifetime */
UA_Float lifetime = (UA_Float)response->securityToken.revisedLifetime / 1000;
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"SecureChannel opened with SecurityPolicy %.*s "
"and a revised lifetime of %.2fs",
(int)channel->securityPolicy->policyUri.length,
channel->securityPolicy->policyUri.data, lifetime);
}

/* The server does not send a CloseSecureChannel response */
Expand Down
Loading

0 comments on commit 77e703a

Please sign in to comment.