123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
- * Copyright 2014-2017 (c) Florian Palm
- * Copyright 2015-2016 (c) Sten Grüner
- * Copyright 2015 (c) Oleksiy Vasylyev
- * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
- * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
- */
- #include "ua_securechannel_manager.h"
- #include "ua_session.h"
- #include "ua_server_internal.h"
- #include "ua_transport_generated_handling.h"
- #define STARTCHANNELID 1
- #define STARTTOKENID 1
- UA_StatusCode
- UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_Server *server) {
- TAILQ_INIT(&cm->channels);
- // TODO: use an ID that is likely to be unique after a restart
- cm->lastChannelId = STARTCHANNELID;
- cm->lastTokenId = STARTTOKENID;
- cm->currentChannelCount = 0;
- cm->server = server;
- return UA_STATUSCODE_GOOD;
- }
- void
- UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
- channel_entry *entry, *temp;
- TAILQ_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
- TAILQ_REMOVE(&cm->channels, entry, pointers);
- UA_SecureChannel_close(&entry->channel);
- UA_SecureChannel_deleteMembers(&entry->channel);
- UA_free(entry);
- }
- }
- static void
- removeSecureChannelCallback(void *_, channel_entry *entry) {
- UA_SecureChannel_deleteMembers(&entry->channel);
- }
- static void
- removeSecureChannel(UA_SecureChannelManager *cm, channel_entry *entry) {
- /* Close the SecureChannel */
- UA_SecureChannel_close(&entry->channel);
- /* Detach the channel and make the capacity available */
- TAILQ_REMOVE(&cm->channels, entry, pointers);
- UA_atomic_subUInt32(&cm->currentChannelCount, 1);
- /* Add a delayed callback to remove the channel when the currently
- * scheduled jobs have completed */
- entry->cleanupCallback.callback = (UA_ApplicationCallback)removeSecureChannelCallback;
- entry->cleanupCallback.application = NULL;
- entry->cleanupCallback.data = entry;
- UA_WorkQueue_enqueueDelayed(&cm->server->workQueue, &entry->cleanupCallback);
- }
- /* remove channels that were not renewed or who have no connection attached */
- void
- UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm,
- UA_DateTime nowMonotonic) {
- channel_entry *entry, *temp;
- TAILQ_FOREACH_SAFE(entry, &cm->channels, pointers, temp) {
- /* The channel was closed internally */
- if(entry->channel.state == UA_SECURECHANNELSTATE_CLOSED ||
- !entry->channel.connection) {
- removeSecureChannel(cm, entry);
- continue;
- }
- /* The channel has timed out */
- UA_DateTime timeout =
- entry->channel.securityToken.createdAt +
- (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_DATETIME_MSEC);
- if(timeout < nowMonotonic) {
- UA_LOG_INFO_CHANNEL(&cm->server->config.logger, &entry->channel,
- "SecureChannel has timed out");
- removeSecureChannel(cm, entry);
- continue;
- }
- /* Revolve the channel tokens */
- if(entry->channel.nextSecurityToken.tokenId > 0) {
- UA_SecureChannel_revolveTokens(&entry->channel);
- }
- }
- }
- /* remove the first channel that has no session attached */
- static UA_Boolean
- purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
- channel_entry *entry;
- TAILQ_FOREACH(entry, &cm->channels, pointers) {
- if(LIST_EMPTY(&entry->channel.sessions)) {
- UA_LOG_INFO_CHANNEL(&cm->server->config.logger, &entry->channel,
- "Channel was purged since maxSecureChannels was "
- "reached and channel had no session attached");
- removeSecureChannel(cm, entry);
- return true;
- }
- }
- return false;
- }
- UA_StatusCode
- UA_SecureChannelManager_create(UA_SecureChannelManager *const cm, UA_Connection *const connection,
- const UA_SecurityPolicy *const securityPolicy,
- const UA_AsymmetricAlgorithmSecurityHeader *const asymHeader) {
- /* connection already has a channel attached. */
- if(connection->channel != NULL)
- return UA_STATUSCODE_BADINTERNALERROR;
- /* Check if there exists a free SC, otherwise try to purge one SC without a
- * session the purge has been introduced to pass CTT, it is not clear what
- * strategy is expected here */
- if(cm->currentChannelCount >= cm->server->config.maxSecureChannels &&
- !purgeFirstChannelWithoutSession(cm))
- return UA_STATUSCODE_BADOUTOFMEMORY;
- UA_LOG_INFO(&cm->server->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
- "Creating a new SecureChannel");
- channel_entry *entry = (channel_entry *)UA_malloc(sizeof(channel_entry));
- if(!entry)
- return UA_STATUSCODE_BADOUTOFMEMORY;
- /* Create the channel context and parse the sender (remote) certificate used for the
- * secureChannel. */
- UA_SecureChannel_init(&entry->channel);
- UA_StatusCode retval =
- UA_SecureChannel_setSecurityPolicy(&entry->channel, securityPolicy,
- &asymHeader->senderCertificate);
- if(retval != UA_STATUSCODE_GOOD) {
- UA_free(entry);
- return retval;
- }
- /* Channel state is fresh (0) */
- entry->channel.securityToken.channelId = 0;
- entry->channel.securityToken.tokenId = cm->lastTokenId++;
- entry->channel.securityToken.createdAt = UA_DateTime_now();
- entry->channel.securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
- TAILQ_INSERT_TAIL(&cm->channels, entry, pointers);
- UA_atomic_addUInt32(&cm->currentChannelCount, 1);
- UA_Connection_attachSecureChannel(connection, &entry->channel);
- return UA_STATUSCODE_GOOD;
- }
- UA_StatusCode
- UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_SecureChannel *channel,
- const UA_OpenSecureChannelRequest *request,
- UA_OpenSecureChannelResponse *response) {
- if(channel->state != UA_SECURECHANNELSTATE_FRESH) {
- UA_LOG_ERROR_CHANNEL(&cm->server->config.logger, channel,
- "Called open on already open or closed channel");
- return UA_STATUSCODE_BADINTERNALERROR;
- }
- if(request->securityMode != UA_MESSAGESECURITYMODE_NONE &&
- UA_ByteString_equal(&channel->securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI)) {
- return UA_STATUSCODE_BADSECURITYMODEREJECTED;
- }
- channel->securityMode = request->securityMode;
- channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
- channel->securityToken.channelId = cm->lastChannelId++;
- channel->securityToken.createdAt = UA_DateTime_now();
- /* Set the lifetime. Lifetime 0 -> set the maximum possible */
- channel->securityToken.revisedLifetime =
- (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
- cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime;
- if(channel->securityToken.revisedLifetime == 0)
- channel->securityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
- /* Set the nonces and generate the keys */
- UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- retval = UA_SecureChannel_generateLocalNonce(channel);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- retval = UA_SecureChannel_generateNewKeys(channel);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- /* Set the response */
- retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- retval = UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- response->responseHeader.timestamp = UA_DateTime_now();
- response->responseHeader.requestHandle = request->requestHeader.requestHandle;
- /* The channel is open */
- channel->state = UA_SECURECHANNELSTATE_OPEN;
- return UA_STATUSCODE_GOOD;
- }
- UA_StatusCode
- UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_SecureChannel *channel,
- const UA_OpenSecureChannelRequest *request,
- UA_OpenSecureChannelResponse *response) {
- if(channel->state != UA_SECURECHANNELSTATE_OPEN) {
- UA_LOG_ERROR_CHANNEL(&cm->server->config.logger, channel,
- "Called renew on channel which is not open");
- return UA_STATUSCODE_BADINTERNALERROR;
- }
- /* If no security token is already issued */
- if(channel->nextSecurityToken.tokenId == 0) {
- channel->nextSecurityToken.channelId = channel->securityToken.channelId;
- channel->nextSecurityToken.tokenId = cm->lastTokenId++;
- channel->nextSecurityToken.createdAt = UA_DateTime_now();
- channel->nextSecurityToken.revisedLifetime =
- (request->requestedLifetime > cm->server->config.maxSecurityTokenLifetime) ?
- cm->server->config.maxSecurityTokenLifetime : request->requestedLifetime;
- if(channel->nextSecurityToken.revisedLifetime == 0) /* lifetime 0 -> return the max lifetime */
- channel->nextSecurityToken.revisedLifetime = cm->server->config.maxSecurityTokenLifetime;
- }
- /* Replace the nonces */
- UA_ByteString_deleteMembers(&channel->remoteNonce);
- UA_StatusCode retval = UA_ByteString_copy(&request->clientNonce, &channel->remoteNonce);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- retval = UA_SecureChannel_generateLocalNonce(channel);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- /* Set the response */
- response->responseHeader.requestHandle = request->requestHeader.requestHandle;
- retval = UA_ByteString_copy(&channel->localNonce, &response->serverNonce);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- retval = UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken);
- if(retval != UA_STATUSCODE_GOOD)
- return retval;
- /* Reset the internal creation date to the monotonic clock */
- channel->nextSecurityToken.createdAt = UA_DateTime_nowMonotonic();
- return UA_STATUSCODE_GOOD;
- }
- UA_SecureChannel *
- UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
- channel_entry *entry;
- TAILQ_FOREACH(entry, &cm->channels, pointers) {
- if(entry->channel.securityToken.channelId == channelId)
- return &entry->channel;
- }
- return NULL;
- }
- UA_StatusCode
- UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
- channel_entry *entry;
- TAILQ_FOREACH(entry, &cm->channels, pointers) {
- if(entry->channel.securityToken.channelId == channelId)
- break;
- }
- if(!entry)
- return UA_STATUSCODE_BADINTERNALERROR;
- removeSecureChannel(cm, entry);
- return UA_STATUSCODE_GOOD;
- }
|