blob: 4b0908c04213add81532657721f231c6aa0ff171 [file] [log] [blame]
/* 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-2016 (c) Sten GrĂ¼ner
* Copyright 2014-2015, 2017 (c) Florian Palm
* Copyright 2015-2016 (c) Chris Iatrou
* Copyright 2015-2016 (c) Oleksiy Vasylyev
* Copyright 2016 (c) Joakim L. Gilje
* Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
* Copyright 2016 (c) TorbenD
* Copyright 2017 (c) frax2222
* Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
* Copyright 2019 (c) Kalycito Infotech Private Limited
*/
#include <open62541/transport_generated.h>
#include <open62541/transport_generated_encoding_binary.h>
#include <open62541/transport_generated_handling.h>
#include <open62541/types_generated_encoding_binary.h>
#include <open62541/types_generated_handling.h>
#include "ua_securechannel_manager.h"
#include "ua_server_internal.h"
#include "ua_services.h"
#include "ua_session_manager.h"
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// store the authentication token and session ID so we can help fuzzing by setting
// these values in the next request automatically
UA_NodeId unsafe_fuzz_authenticationToken = {0, UA_NODEIDTYPE_NUMERIC, {0}};
#endif
#ifdef UA_DEBUG_DUMP_PKGS_FILE
void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection,
UA_ByteString *messageBuffer);
#endif
/********************/
/* Helper Functions */
/********************/
static UA_StatusCode
sendServiceFaultWithRequest(UA_SecureChannel *channel,
const UA_RequestHeader *requestHeader,
const UA_DataType *responseType,
UA_UInt32 requestId, UA_StatusCode error) {
UA_STACKARRAY(UA_Byte, response, responseType->memSize);
UA_init(response, responseType);
UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
responseHeader->requestHandle = requestHeader->requestHandle;
responseHeader->timestamp = UA_DateTime_now();
responseHeader->serviceResult = error;
/* Send error message. Message type is MSG and not ERR, since we are on a
* SecureChannel! */
UA_StatusCode retval =
UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
response, responseType);
UA_LOG_DEBUG(channel->securityPolicy->logger, UA_LOGCATEGORY_SERVER,
"Sent ServiceFault with error code %s", UA_StatusCode_name(error));
return retval;
}
/* This is not an ERR message, the connection is not closed afterwards */
static UA_StatusCode
sendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg,
size_t offset, const UA_DataType *responseType,
UA_UInt32 requestId, UA_StatusCode error) {
UA_RequestHeader requestHeader;
UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader);
if(retval != UA_STATUSCODE_GOOD)
return retval;
retval = sendServiceFaultWithRequest(channel, &requestHeader, responseType,
requestId, error);
UA_RequestHeader_deleteMembers(&requestHeader);
return retval;
}
static void
getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
const UA_DataType **responseType, UA_Service *service,
UA_Boolean *requiresSession) {
switch(requestTypeId) {
case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_GetEndpoints;
*requestType = &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE];
*requiresSession = false;
break;
case UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_FindServers;
*requestType = &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE];
*requiresSession = false;
break;
#ifdef UA_ENABLE_DISCOVERY
# ifdef UA_ENABLE_DISCOVERY_MULTICAST
case UA_NS0ID_FINDSERVERSONNETWORKREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_FindServersOnNetwork;
*requestType = &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST];
*responseType = &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE];
*requiresSession = false;
break;
# endif
case UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_RegisterServer;
*requestType = &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST];
*responseType = &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE];
*requiresSession = false;
break;
case UA_NS0ID_REGISTERSERVER2REQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_RegisterServer2;
*requestType = &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST];
*responseType = &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE];
*requiresSession = false;
break;
#endif
case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
*service = NULL; //(UA_Service)Service_CreateSession;
*requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
*requiresSession = false;
break;
case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
*service = NULL; //(UA_Service)Service_ActivateSession;
*requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
break;
case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_CloseSession;
*requestType = &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE];
break;
case UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY:
*service = NULL;
*service = (UA_Service)Service_Read;
*requestType = &UA_TYPES[UA_TYPES_READREQUEST];
*responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
break;
case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_Write;
*requestType = &UA_TYPES[UA_TYPES_WRITEREQUEST];
*responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE];
break;
case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_Browse;
*requestType = &UA_TYPES[UA_TYPES_BROWSEREQUEST];
*responseType = &UA_TYPES[UA_TYPES_BROWSERESPONSE];
break;
case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_BrowseNext;
*requestType = &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST];
*responseType = &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE];
break;
case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_RegisterNodes;
*requestType = &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST];
*responseType = &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE];
break;
case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_UnregisterNodes;
*requestType = &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST];
*responseType = &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE];
break;
case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_TranslateBrowsePathsToNodeIds;
*requestType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE];
break;
#ifdef UA_ENABLE_SUBSCRIPTIONS
case UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_CreateSubscription;
*requestType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE];
break;
case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY:
*requestType = &UA_TYPES[UA_TYPES_PUBLISHREQUEST];
*responseType = &UA_TYPES[UA_TYPES_PUBLISHRESPONSE];
break;
case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_Republish;
*requestType = &UA_TYPES[UA_TYPES_REPUBLISHREQUEST];
*responseType = &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE];
break;
case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_ModifySubscription;
*requestType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE];
break;
case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_SetPublishingMode;
*requestType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST];
*responseType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE];
break;
case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_DeleteSubscriptions;
*requestType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE];
break;
case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_CreateMonitoredItems;
*requestType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE];
break;
case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_DeleteMonitoredItems;
*requestType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE];
break;
case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_ModifyMonitoredItems;
*requestType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST];
*responseType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE];
break;
case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_SetMonitoringMode;
*requestType = &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST];
*responseType = &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE];
break;
#endif
#ifdef UA_ENABLE_HISTORIZING
/* For History read */
case UA_NS0ID_HISTORYREADREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_HistoryRead;
*requestType = &UA_TYPES[UA_TYPES_HISTORYREADREQUEST];
*responseType = &UA_TYPES[UA_TYPES_HISTORYREADRESPONSE];
break;
/* For History update */
case UA_NS0ID_HISTORYUPDATEREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_HistoryUpdate;
*requestType = &UA_TYPES[UA_TYPES_HISTORYUPDATEREQUEST];
*responseType = &UA_TYPES[UA_TYPES_HISTORYUPDATERESPONSE];
break;
#endif
#ifdef UA_ENABLE_METHODCALLS
case UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_Call;
*requestType = &UA_TYPES[UA_TYPES_CALLREQUEST];
*responseType = &UA_TYPES[UA_TYPES_CALLRESPONSE];
break;
#endif
#ifdef UA_ENABLE_NODEMANAGEMENT
case UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_AddNodes;
*requestType = &UA_TYPES[UA_TYPES_ADDNODESREQUEST];
*responseType = &UA_TYPES[UA_TYPES_ADDNODESRESPONSE];
break;
case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_AddReferences;
*requestType = &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST];
*responseType = &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE];
break;
case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_DeleteNodes;
*requestType = &UA_TYPES[UA_TYPES_DELETENODESREQUEST];
*responseType = &UA_TYPES[UA_TYPES_DELETENODESRESPONSE];
break;
case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_DeleteReferences;
*requestType = &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST];
*responseType = &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE];
break;
#endif
default:
break;
}
}
/*************************/
/* Process Message Types */
/*************************/
/* HEL -> Open up the connection */
static UA_StatusCode
processHEL(UA_Server *server, UA_Connection *connection,
const UA_ByteString *msg, size_t *offset) {
UA_TcpHelloMessage helloMessage;
UA_StatusCode retval = UA_TcpHelloMessage_decodeBinary(msg, offset, &helloMessage);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Currently not checked */
UA_String_deleteMembers(&helloMessage.endpointUrl);
/* TODO: Use the config of the exact NetworkLayer */
if(server->config.networkLayersSize == 0)
return UA_STATUSCODE_BADOUTOFMEMORY;
const UA_ConnectionConfig *localConfig = &server->config.networkLayers[0].localConnectionConfig;
/* Parameterize the connection */
UA_ConnectionConfig remoteConfig;
remoteConfig.protocolVersion = helloMessage.protocolVersion;
remoteConfig.sendBufferSize = helloMessage.sendBufferSize;
remoteConfig.recvBufferSize = helloMessage.receiveBufferSize;
remoteConfig.maxMessageSize = helloMessage.maxMessageSize;
remoteConfig.maxChunkCount = helloMessage.maxChunkCount;
retval = UA_Connection_processHELACK(connection, localConfig, &remoteConfig);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_NETWORK,
"Connection %i | Error during the HEL/ACK handshake",
(int)(connection->sockfd));
return retval;
}
/* Build acknowledge response */
UA_TcpAcknowledgeMessage ackMessage;
memcpy(&ackMessage, localConfig, sizeof(UA_TcpAcknowledgeMessage)); /* Same struct layout.. */
UA_TcpMessageHeader ackHeader;
ackHeader.messageTypeAndChunkType = UA_MESSAGETYPE_ACK + UA_CHUNKTYPE_FINAL;
ackHeader.messageSize = 8 + 20; /* ackHeader + ackMessage */
/* Get the send buffer from the network layer */
UA_ByteString ack_msg;
UA_ByteString_init(&ack_msg);
retval = connection->getSendBuffer(connection, connection->config.sendBufferSize, &ack_msg);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Encode and send the response */
UA_Byte *bufPos = ack_msg.data;
const UA_Byte *bufEnd = &ack_msg.data[ack_msg.length];
retval = UA_TcpMessageHeader_encodeBinary(&ackHeader, &bufPos, bufEnd);
if(retval != UA_STATUSCODE_GOOD) {
connection->releaseSendBuffer(connection, &ack_msg);
return retval;
}
retval = UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &bufPos, bufEnd);
if(retval != UA_STATUSCODE_GOOD) {
connection->releaseSendBuffer(connection, &ack_msg);
return retval;
}
ack_msg.length = ackHeader.messageSize;
return connection->send(connection, &ack_msg);
}
/* OPN -> Open up/renew the securechannel */
static UA_StatusCode
processOPN(UA_Server *server, UA_SecureChannel *channel,
const UA_UInt32 requestId, const UA_ByteString *msg) {
/* Decode the request */
size_t offset = 0;
UA_NodeId requestType;
UA_OpenSecureChannelRequest openSecureChannelRequest;
UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestType);
if(retval != UA_STATUSCODE_GOOD) {
UA_NodeId_deleteMembers(&requestType);
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Could not decode the NodeId. Closing the connection");
UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId);
return retval;
}
retval = UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &openSecureChannelRequest);
/* Error occurred */
if(retval != UA_STATUSCODE_GOOD ||
requestType.identifier.numeric != UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId) {
UA_NodeId_deleteMembers(&requestType);
UA_OpenSecureChannelRequest_deleteMembers(&openSecureChannelRequest);
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Could not decode the OPN message. Closing the connection.");
UA_SecureChannelManager_close(&server->secureChannelManager, channel->securityToken.channelId);
return retval;
}
UA_NodeId_deleteMembers(&requestType);
/* Call the service */
UA_OpenSecureChannelResponse openScResponse;
UA_OpenSecureChannelResponse_init(&openScResponse);
Service_OpenSecureChannel(server, channel, &openSecureChannelRequest, &openScResponse);
UA_OpenSecureChannelRequest_deleteMembers(&openSecureChannelRequest);
if(openScResponse.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel, "Could not open a SecureChannel. "
"Closing the connection.");
UA_SecureChannelManager_close(&server->secureChannelManager,
channel->securityToken.channelId);
return openScResponse.responseHeader.serviceResult;
}
/* Send the response */
retval = UA_SecureChannel_sendAsymmetricOPNMessage(channel, requestId, &openScResponse,
&UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]);
UA_OpenSecureChannelResponse_deleteMembers(&openScResponse);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Could not send the OPN answer with error code %s",
UA_StatusCode_name(retval));
UA_SecureChannelManager_close(&server->secureChannelManager,
channel->securityToken.channelId);
return retval;
}
return retval;
}
static UA_StatusCode
sendResponse(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 requestHandle,
UA_ResponseHeader *responseHeader, const UA_DataType *responseType) {
/* Prepare the ResponseHeader */
responseHeader->requestHandle = requestHandle;
responseHeader->timestamp = UA_DateTime_now();
/* Start the message context */
UA_MessageContext mc;
UA_StatusCode retval = UA_MessageContext_begin(&mc, channel, requestId, UA_MESSAGETYPE_MSG);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Assert's required for clang-analyzer */
UA_assert(mc.buf_pos == &mc.messageBuffer.data[UA_SECURE_MESSAGE_HEADER_LENGTH]);
UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]);
/* Encode the response type */
UA_NodeId typeId = UA_NODEID_NUMERIC(0, responseType->binaryEncodingId);
retval = UA_MessageContext_encode(&mc, &typeId, &UA_TYPES[UA_TYPES_NODEID]);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Encode the response */
retval = UA_MessageContext_encode(&mc, responseHeader, responseType);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Finish / send out */
return UA_MessageContext_finish(&mc);
}
static UA_StatusCode
processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId,
UA_Service service, const UA_RequestHeader *requestHeader,
const UA_DataType *requestType, UA_ResponseHeader *responseHeader,
const UA_DataType *responseType, UA_Boolean sessionRequired) {
/* CreateSession doesn't need a session */
if(requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) {
Service_CreateSession(server, channel,
(const UA_CreateSessionRequest *)requestHeader,
(UA_CreateSessionResponse *)responseHeader);
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/* Store the authentication token and session ID so we can help fuzzing
* by setting these values in the next request automatically */
UA_CreateSessionResponse *res = (UA_CreateSessionResponse *)responseHeader;
UA_NodeId_copy(&res->authenticationToken, &unsafe_fuzz_authenticationToken);
#endif
return sendResponse(channel, requestId, requestHeader->requestHandle,
responseHeader, responseType);
}
/* Find the matching session */
UA_Session *session = (UA_Session*)
UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken);
if(!session && !UA_NodeId_isNull(&requestHeader->authenticationToken))
session = UA_SessionManager_getSessionByToken(&server->sessionManager,
&requestHeader->authenticationToken);
if(requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]) {
if(!session) {
UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel,
"Trying to activate a session that is " \
"not known in the server");
return sendServiceFaultWithRequest(channel, requestHeader, responseType,
requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
}
Service_ActivateSession(server, channel, session,
(const UA_ActivateSessionRequest*)requestHeader,
(UA_ActivateSessionResponse*)responseHeader);
return sendResponse(channel, requestId, requestHeader->requestHandle,
responseHeader, responseType);
}
/* Set an anonymous, inactive session for services that need no session */
UA_Session anonymousSession;
if(!session) {
if(sessionRequired) {
#ifdef UA_ENABLE_TYPEDESCRIPTION
UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
"%s refused without a valid session",
requestType->typeName);
#else
UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
"Service %i refused without a valid session",
requestType->binaryEncodingId);
#endif
return sendServiceFaultWithRequest(channel, requestHeader, responseType,
requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
}
UA_Session_init(&anonymousSession);
anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL);
anonymousSession.header.channel = channel;
session = &anonymousSession;
}
/* Trying to use a non-activated session? Do not allow if request is of type
* CloseSessionRequest */
if(sessionRequired && !session->activated &&
requestType != &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) {
#ifdef UA_ENABLE_TYPEDESCRIPTION
UA_LOG_WARNING_SESSION(&server->config.logger, session,
"%s refused on a non-activated session",
requestType->typeName);
#else
UA_LOG_WARNING_SESSION(&server->config.logger, session,
"Service %i refused on a non-activated session",
requestType->binaryEncodingId);
#endif
UA_SessionManager_removeSession(&server->sessionManager,
&session->header.authenticationToken);
return sendServiceFaultWithRequest(channel, requestHeader, responseType,
requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
}
/* The session is bound to another channel */
if(session != &anonymousSession && session->header.channel != channel) {
UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
"Client tries to use a Session that is not "
"bound to this SecureChannel");
return sendServiceFaultWithRequest(channel, requestHeader, responseType,
requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
}
/* Update the session lifetime */
UA_Session_updateLifetime(session);
#ifdef UA_ENABLE_SUBSCRIPTIONS
/* The publish request is not answered immediately */
if(requestType == &UA_TYPES[UA_TYPES_PUBLISHREQUEST]) {
Service_Publish(server, session, (const UA_PublishRequest*)requestHeader, requestId);
return UA_STATUSCODE_GOOD;
}
#endif
/* Dispatch the synchronous service call and send the response */
UA_LOCK(server->serviceMutex);
service(server, session, requestHeader, responseHeader);
UA_UNLOCK(server->serviceMutex);
return sendResponse(channel, requestId, requestHeader->requestHandle,
responseHeader, responseType);
}
static UA_StatusCode
processMSG(UA_Server *server, UA_SecureChannel *channel,
UA_UInt32 requestId, const UA_ByteString *msg) {
/* Decode the nodeid */
size_t offset = 0;
UA_NodeId requestTypeId;
UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
if(retval != UA_STATUSCODE_GOOD)
return retval;
if(requestTypeId.namespaceIndex != 0 ||
requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
size_t requestPos = offset; /* Store the offset (for sendServiceFault) */
/* Get the service pointers */
UA_Service service = NULL;
UA_Boolean sessionRequired = true;
const UA_DataType *requestType = NULL;
const UA_DataType *responseType = NULL;
getServicePointers(requestTypeId.identifier.numeric, &requestType,
&responseType, &service, &sessionRequired);
if(!requestType) {
if(requestTypeId.identifier.numeric == 787) {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Client requested a subscription, " \
"but those are not enabled in the build");
} else {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Unknown request with type identifier %i",
requestTypeId.identifier.numeric);
}
return sendServiceFault(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT],
requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
}
UA_assert(responseType);
/* Decode the request */
UA_STACKARRAY(UA_Byte, request, requestType->memSize);
retval = UA_decodeBinary(msg, &offset, request, requestType, server->config.customDataTypes);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_DEBUG_CHANNEL(&server->config.logger, channel,
"Could not decode the request");
return sendServiceFault(channel, msg, requestPos, responseType, requestId, retval);
}
/* Check timestamp in the request header */
UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
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 = sendServiceFaultWithRequest(channel, requestHeader, responseType,
requestId, UA_STATUSCODE_BADINVALIDTIMESTAMP);
UA_deleteMembers(request, requestType);
return retval;
}
}
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/* Set the authenticationToken from the create session request to help
* fuzzing cover more lines */
UA_NodeId_deleteMembers(&requestHeader->authenticationToken);
if(!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken))
UA_NodeId_copy(&unsafe_fuzz_authenticationToken, &requestHeader->authenticationToken);
#endif
/* Prepare the respone */
UA_STACKARRAY(UA_Byte, response, responseType->memSize);
UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
UA_init(response, responseType);
/* Continue with the decoded Request */
retval = processMSGDecoded(server, channel, requestId, service, requestHeader, requestType,
responseHeader, responseType, sessionRequired);
/* Clean up */
UA_deleteMembers(request, requestType);
UA_deleteMembers(responseHeader, responseType);
return retval;
}
/* Takes decoded messages starting at the nodeid of the content type. */
static void
processSecureChannelMessage(void *application, UA_SecureChannel *channel,
UA_MessageType messagetype, UA_UInt32 requestId,
const UA_ByteString *message) {
UA_Server *server = (UA_Server*)application;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
switch(messagetype) {
case UA_MESSAGETYPE_OPN:
UA_LOG_TRACE_CHANNEL(&server->config.logger, channel,
"Process an OPN on an open channel");
retval = processOPN(server, channel, requestId, message);
break;
case UA_MESSAGETYPE_MSG:
UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process a MSG");
retval = processMSG(server, channel, requestId, message);
break;
case UA_MESSAGETYPE_CLO:
UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Process a CLO");
Service_CloseSecureChannel(server, channel);
break;
default:
UA_LOG_TRACE_CHANNEL(&server->config.logger, channel, "Invalid message type");
retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
break;
}
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_INFO_CHANNEL(&server->config.logger, channel,
"Processing the message failed with StatusCode %s. "
"Closing the channel.", UA_StatusCode_name(retval));
Service_CloseSecureChannel(server, channel);
}
}
static UA_StatusCode
createSecureChannel(void *application, UA_Connection *connection,
UA_AsymmetricAlgorithmSecurityHeader *asymHeader) {
UA_Server *server = (UA_Server*)application;
/* Iterate over available endpoints and choose the correct one */
UA_SecurityPolicy *securityPolicy = NULL;
for(size_t i = 0; i < server->config.securityPoliciesSize; ++i) {
UA_SecurityPolicy *policy = &server->config.securityPolicies[i];
if(!UA_ByteString_equal(&asymHeader->securityPolicyUri, &policy->policyUri))
continue;
UA_StatusCode retval = policy->asymmetricModule.
compareCertificateThumbprint(policy, &asymHeader->receiverCertificateThumbprint);
if(retval != UA_STATUSCODE_GOOD)
continue;
/* We found the correct policy (except for security mode). The endpoint
* needs to be selected by the client / server to match the security
* mode in the endpoint for the session. */
securityPolicy = policy;
break;
}
if(!securityPolicy)
return UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
/* Create a new channel */
return UA_SecureChannelManager_create(&server->secureChannelManager, connection,
securityPolicy, asymHeader);
}
static UA_StatusCode
processCompleteChunkWithoutChannel(UA_Server *server, UA_Connection *connection,
UA_ByteString *message) {
/* Process chunk without a channel; must be OPN */
UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK,
"Connection %i | No channel attached to the connection. "
"Process the chunk directly", (int)(connection->sockfd));
size_t offset = 0;
UA_TcpMessageHeader tcpMessageHeader;
UA_StatusCode retval =
UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
if(retval != UA_STATUSCODE_GOOD)
return retval;
// Only HEL and OPN messages possible without a channel (on the server side)
switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffffu) {
case UA_MESSAGETYPE_HEL:
retval = processHEL(server, connection, message, &offset);
break;
case UA_MESSAGETYPE_OPN:
{
UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK,
"Connection %i | Process OPN message", (int)(connection->sockfd));
/* Called before HEL */
if(connection->state != UA_CONNECTION_ESTABLISHED) {
retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
break;
}
// Decode the asymmetric algorithm security header since it is not encrypted and
// needed to decide what security policy to use.
UA_AsymmetricAlgorithmSecurityHeader asymHeader;
UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
size_t messageHeaderOffset = UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH;
retval = UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(message,
&messageHeaderOffset,
&asymHeader);
if(retval != UA_STATUSCODE_GOOD)
break;
retval = createSecureChannel(server, connection, &asymHeader);
UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
if(retval != UA_STATUSCODE_GOOD)
break;
retval = UA_SecureChannel_decryptAddChunk(connection->channel, message, false);
if(retval != UA_STATUSCODE_GOOD)
break;
UA_SecureChannel_processCompleteMessages(connection->channel, server,
processSecureChannelMessage);
break;
}
default:
UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK,
"Connection %i | Expected OPN or HEL message on a connection "
"without a SecureChannel", (int)(connection->sockfd));
retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
break;
}
return retval;
}
static UA_StatusCode
processCompleteChunk(void *const application, UA_Connection *connection,
UA_ByteString *chunk) {
UA_Server *server = (UA_Server*)application;
#ifdef UA_DEBUG_DUMP_PKGS_FILE
UA_debug_dumpCompleteChunk(server, connection, chunk);
#endif
if(!connection->channel)
return processCompleteChunkWithoutChannel(server, connection, chunk);
return UA_SecureChannel_decryptAddChunk(connection->channel, chunk, false);
}
void
UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
UA_ByteString *message) {
UA_LOG_TRACE(&server->config.logger, UA_LOGCATEGORY_NETWORK,
"Connection %i | Received a packet.", (int)(connection->sockfd));
#ifdef UA_DEBUG_DUMP_PKGS
UA_dump_hex_pkg(message->data, message->length);
#endif
UA_StatusCode retval = UA_Connection_processChunks(connection, server,
processCompleteChunk, message);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_NETWORK,
"Connection %i | Processing the message failed with "
"error %s", (int)(connection->sockfd), UA_StatusCode_name(retval));
/* Send an ERR message and close the connection */
UA_TcpErrorMessage error;
error.error = retval;
error.reason = UA_STRING_NULL;
UA_Connection_sendError(connection, &error);
connection->close(connection);
return;
}
UA_SecureChannel *channel = connection->channel;
if(!channel)
return;
/* Process complete messages */
UA_SecureChannel_processCompleteMessages(channel, server, processSecureChannelMessage);
/* Is the channel still open? */
if(channel->state == UA_SECURECHANNELSTATE_CLOSED)
return;
/* Store unused decoded chunks internally in the SecureChannel */
UA_SecureChannel_persistIncompleteMessages(connection->channel);
}
#if UA_MULTITHREADING >= 200
static void
deleteConnection(UA_Server *server, UA_Connection *connection) {
connection->free(connection);
}
#endif
void
UA_Server_removeConnection(UA_Server *server, UA_Connection *connection) {
UA_Connection_detachSecureChannel(connection);
#if UA_MULTITHREADING >= 200
UA_DelayedCallback *dc = (UA_DelayedCallback*)UA_malloc(sizeof(UA_DelayedCallback));
if(!dc)
return; /* Malloc cannot fail on OS's that support multithreading. They
* rather kill the process. */
dc->callback = (UA_ApplicationCallback)deleteConnection;
dc->application = server;
dc->data = connection;
UA_WorkQueue_enqueueDelayed(&server->workQueue, dc);
#else
connection->free(connection);
#endif
}