/src/open62541_15/src/server/ua_services_securechannel.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright 2014-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2014, 2017 (c) Florian Palm |
7 | | * Copyright 2015 (c) Oleksiy Vasylyev |
8 | | * Copyright 2017 (c) Stefan Profanter, fortiss GmbH |
9 | | * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB |
10 | | * Copyright 2023 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Phuong Nguyen) |
11 | | * Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer) |
12 | | */ |
13 | | |
14 | | #include <open62541/types.h> |
15 | | #include "ua_server_internal.h" |
16 | | #include "ua_services.h" |
17 | | |
18 | | /* The OpenSecureChannel Service in the server is split as follows: |
19 | | * |
20 | | * - Decode the OPN Asymmetric Header (SecureChannel) |
21 | | * - Process the OPN Asymmetric Header (here, via channel->processOPNHeader callback) |
22 | | * - Verify the remote certificate and configure the SecureChannel |
23 | | * - Verify the OPN message signature and decrypt (SecureChannel) |
24 | | * - Process the OpenSecureChannelRequest (here, via standard service call logic) |
25 | | */ |
26 | | |
27 | | UA_StatusCode |
28 | | processOPN_AsymHeader(void *application, UA_SecureChannel *channel, |
29 | 2.44k | const UA_AsymmetricAlgorithmSecurityHeader *asymHeader) { |
30 | 2.44k | if(channel->securityPolicy) |
31 | 0 | return UA_STATUSCODE_GOOD; |
32 | | |
33 | | /* Iterate over available endpoints and choose the correct one */ |
34 | 2.44k | UA_Server *server = (UA_Server *)application; |
35 | 2.44k | UA_ServerConfig *sc = &server->config; |
36 | 2.44k | UA_SecurityPolicy *securityPolicy = NULL; |
37 | 2.44k | for(size_t i = 0; i < sc->securityPoliciesSize; ++i) { |
38 | 2.44k | UA_SecurityPolicy *policy = &sc->securityPolicies[i]; |
39 | 2.44k | if(!UA_String_equal(&asymHeader->securityPolicyUri, &policy->policyUri)) |
40 | 0 | continue; |
41 | | |
42 | 2.44k | UA_StatusCode res = policy-> |
43 | 2.44k | compareCertThumbprint(policy, &asymHeader->receiverCertificateThumbprint); |
44 | 2.44k | if(res != UA_STATUSCODE_GOOD) |
45 | 0 | continue; |
46 | | |
47 | | /* We found the correct policy. The endpoint is selected later during |
48 | | * CreateSession. There the channel's SecurityMode also gets checked |
49 | | * against the endpoint definition. */ |
50 | 2.44k | securityPolicy = policy; |
51 | 2.44k | break; |
52 | 2.44k | } |
53 | | |
54 | 2.44k | if(!securityPolicy) |
55 | 0 | return UA_STATUSCODE_BADSECURITYPOLICYREJECTED; |
56 | | |
57 | | /* TODO: Check the URI in the certificate */ |
58 | | |
59 | | /* Verify the client certificate (chain). |
60 | | * Here we don't have the ApplicationDescription. |
61 | | * This check follows in the CreateSession service. */ |
62 | 2.44k | if(asymHeader->senderCertificate.length > 0) { |
63 | 0 | UA_StatusCode res = |
64 | 0 | validateCertificate(server, &sc->secureChannelPKI, channel, NULL, |
65 | 0 | "OpenSecureChannel", NULL, asymHeader->senderCertificate); |
66 | 0 | UA_CHECK_STATUS(res, return res); |
67 | 0 | } |
68 | | |
69 | | /* If the sender provides a chain of certificates then we shall extract the |
70 | | * ApplicationInstanceCertificate and ignore the extra bytes. See also: OPC |
71 | | * UA Part 6, V1.04, 6.7.2.3 Security Header, Table 42 - Asymmetric |
72 | | * algorithm Security header */ |
73 | 2.44k | UA_ByteString appInstCert = getLeafCertificate(asymHeader->senderCertificate); |
74 | | |
75 | | /* Create the channel context and parse the sender (remote) certificate used |
76 | | * for the secureChannel. This sets a "temporary SecurityMode" so that we |
77 | | * can properly decrypt the remaining OPN message. The final SecurityMode is |
78 | | * set in the OpenSecureChannel service. */ |
79 | 2.44k | return UA_SecureChannel_setSecurityPolicy(channel, securityPolicy, &appInstCert); |
80 | 2.44k | } |
81 | | |
82 | | void |
83 | | Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel *channel, |
84 | | UA_OpenSecureChannelRequest *request, |
85 | 0 | UA_OpenSecureChannelResponse *response) { |
86 | 0 | UA_ServerConfig *sc = &server->config; |
87 | 0 | UA_EventLoop *el = server->config.eventLoop; |
88 | 0 | const UA_SecurityPolicy *sp = channel->securityPolicy; |
89 | |
|
90 | 0 | switch(request->requestType) { |
91 | | /* Open the channel */ |
92 | 0 | case UA_SECURITYTOKENREQUESTTYPE_ISSUE: { |
93 | | /* We must expect an OPN handshake */ |
94 | 0 | if(channel->state != UA_SECURECHANNELSTATE_ACK_SENT) { |
95 | 0 | UA_LOG_ERROR_CHANNEL(sc->logging, channel, |
96 | 0 | "OpenSecureChannel: Cannot open " |
97 | 0 | "already open or closed channel"); |
98 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; |
99 | 0 | return; |
100 | 0 | } |
101 | | |
102 | | /* Ensure the SecurityMode does not cause a wrong array access during |
103 | | * logging */ |
104 | 0 | if((UA_UInt32)request->securityMode > UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) |
105 | 0 | request->securityMode = UA_MESSAGESECURITYMODE_INVALID; |
106 | | |
107 | | /* Set the SecurityMode. This overwrites the "temporary SecurityMode" |
108 | | * that has been set set in UA_SecureChannel_setSecurityPolicy.*/ |
109 | 0 | response->responseHeader.serviceResult = |
110 | 0 | UA_SecureChannel_setSecurityMode(channel, request->securityMode); |
111 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
112 | 0 | UA_LOG_ERROR_CHANNEL(sc->logging, channel, |
113 | 0 | "OpenSecureChannel: Client tries mismatching " |
114 | 0 | "SecurityMode %s for SecurityPolicy %S", |
115 | 0 | securityModeNames[request->securityMode], |
116 | 0 | sp->policyUri); |
117 | 0 | return; |
118 | 0 | } |
119 | 0 | break; |
120 | 0 | } |
121 | | |
122 | | /* Renew the channel */ |
123 | 0 | case UA_SECURITYTOKENREQUESTTYPE_RENEW: |
124 | | /* The channel must be open to be renewed */ |
125 | 0 | if(channel->state != UA_SECURECHANNELSTATE_OPEN) { |
126 | 0 | UA_LOG_ERROR_CHANNEL(sc->logging, channel, |
127 | 0 | "OpenSecureChannel: The client called renew on " |
128 | 0 | "channel which is not open"); |
129 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; |
130 | 0 | return; |
131 | 0 | } |
132 | | |
133 | | /* Check whether the nonce was reused */ |
134 | 0 | if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE && |
135 | 0 | UA_ByteString_equal(&channel->remoteNonce, &request->clientNonce)) { |
136 | 0 | UA_LOG_ERROR_CHANNEL(sc->logging, channel, |
137 | 0 | "OpenSecureChannel: The client called renew " |
138 | 0 | "reusing the previous nonce"); |
139 | 0 | response->responseHeader.serviceResult = |
140 | 0 | UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
141 | 0 | return; |
142 | 0 | } |
143 | | |
144 | 0 | break; |
145 | | |
146 | | /* Unknown request type */ |
147 | 0 | default: |
148 | 0 | UA_LOG_ERROR_CHANNEL(sc->logging, channel, |
149 | 0 | "OpenSecureChannel: Unknown request type"); |
150 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR; |
151 | 0 | return; |
152 | 0 | } |
153 | | |
154 | | /* Create a new SecurityToken. It will be switched over when the first |
155 | | * message is received. The ChannelId is left unchanged. */ |
156 | 0 | channel->altSecurityToken.channelId = channel->securityToken.channelId; |
157 | 0 | if(server->lastTokenId == 0) |
158 | 0 | server->lastTokenId++; /* TokenId zero is the placeholder */ |
159 | 0 | channel->altSecurityToken.tokenId = server->lastTokenId++; |
160 | 0 | channel->altSecurityToken.createdAt = el->dateTime_nowMonotonic(el); |
161 | 0 | channel->altSecurityToken.revisedLifetime = |
162 | 0 | (request->requestedLifetime > sc->maxSecurityTokenLifetime) ? |
163 | 0 | sc->maxSecurityTokenLifetime : request->requestedLifetime; |
164 | 0 | if(channel->altSecurityToken.revisedLifetime == 0) |
165 | 0 | channel->altSecurityToken.revisedLifetime = |
166 | 0 | sc->maxSecurityTokenLifetime; |
167 | | |
168 | | /* Set the nonces. The remote nonce will be "rotated in" when it is first used. */ |
169 | 0 | UA_ByteString_clear(&channel->remoteNonce); |
170 | 0 | channel->remoteNonce = request->clientNonce; |
171 | 0 | UA_ByteString_init(&request->clientNonce); |
172 | |
|
173 | 0 | response->responseHeader.serviceResult = UA_SecureChannel_generateLocalNonce(channel); |
174 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
175 | 0 | UA_LOG_ERROR_CHANNEL(sc->logging, channel, |
176 | 0 | "OpenSecureChannel: Cannot generate the local nonce"); |
177 | 0 | return; |
178 | 0 | } |
179 | | |
180 | | /* Update the channel state */ |
181 | 0 | channel->renewState = UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER; |
182 | 0 | channel->state = UA_SECURECHANNELSTATE_OPEN; |
183 | | |
184 | | /* Set the response */ |
185 | 0 | response->securityToken = channel->altSecurityToken; |
186 | 0 | response->securityToken.createdAt = el->dateTime_now(el); /* only for sending */ |
187 | 0 | response->responseHeader.timestamp = response->securityToken.createdAt; |
188 | 0 | response->responseHeader.requestHandle = request->requestHeader.requestHandle; |
189 | 0 | response->responseHeader.serviceResult = |
190 | 0 | UA_ByteString_copy(&channel->localNonce, &response->serverNonce); |
191 | 0 | UA_CHECK_STATUS(response->responseHeader.serviceResult, return); |
192 | | |
193 | | /* Success */ |
194 | 0 | if(request->requestType == UA_SECURITYTOKENREQUESTTYPE_ISSUE) { |
195 | 0 | UA_LOG_INFO_CHANNEL(sc->logging, channel, |
196 | 0 | "OpenSecureChannel: Channel opened with SecurityMode %s for " |
197 | 0 | "SecurityPolicy %S and a revised lifetime of %.2fs", |
198 | 0 | securityModeNames[channel->securityMode], |
199 | 0 | channel->securityPolicy->policyUri, |
200 | 0 | (UA_Float)response->securityToken.revisedLifetime / 1000); |
201 | | |
202 | | /* Notify the application about the open SecureChannel */ |
203 | 0 | notifySecureChannel(server, channel, |
204 | 0 | UA_APPLICATIONNOTIFICATIONTYPE_SECURECHANNEL_OPENED); |
205 | 0 | } else { |
206 | 0 | UA_LOG_INFO_CHANNEL(sc->logging, channel, |
207 | 0 | "OpenSecureChannel: Channel renewed with a revised " |
208 | 0 | "lifetime of %.2fs", |
209 | 0 | (UA_Float)response->securityToken.revisedLifetime / 1000); |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | /* The server does not send a CloseSecureChannel response */ |
214 | | void |
215 | 0 | Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel) { |
216 | 0 | UA_SecureChannel_shutdown(channel, UA_SHUTDOWNREASON_CLOSE); |
217 | 0 | } |
218 | | |
219 | | void |
220 | | notifySecureChannel(UA_Server *server, UA_SecureChannel *channel, |
221 | 825 | UA_ApplicationNotificationType type) { |
222 | 825 | UA_ServerConfig *sc = &server->config; |
223 | | |
224 | | /* Nothing to do? */ |
225 | 825 | if(!sc->globalNotificationCallback && !sc->secureChannelNotificationCallback) |
226 | 825 | return; |
227 | | |
228 | | /* Prepare the payload */ |
229 | 0 | static UA_THREAD_LOCAL UA_KeyValuePair notifySCData[15] = { |
230 | 0 | {{0, UA_STRING_STATIC("securechannel-id")}, {0}}, |
231 | 0 | {{0, UA_STRING_STATIC("connection-manager-name")}, {0}}, |
232 | 0 | {{0, UA_STRING_STATIC("connection-id")}, {0}}, |
233 | 0 | {{0, UA_STRING_STATIC("remote-address")}, {0}}, |
234 | 0 | {{0, UA_STRING_STATIC("protocol-version")}, {0}}, |
235 | 0 | {{0, UA_STRING_STATIC("recv-buffer-size")}, {0}}, |
236 | 0 | {{0, UA_STRING_STATIC("recv-max-message-size")}, {0}}, |
237 | 0 | {{0, UA_STRING_STATIC("recv-max-chunk-count")}, {0}}, |
238 | 0 | {{0, UA_STRING_STATIC("send-buffer-size")}, {0}}, |
239 | 0 | {{0, UA_STRING_STATIC("send-max-message-size")}, {0}}, |
240 | 0 | {{0, UA_STRING_STATIC("send-max-chunk-count")}, {0}}, |
241 | 0 | {{0, UA_STRING_STATIC("endpoint-url")}, {0}}, |
242 | 0 | {{0, UA_STRING_STATIC("security-mode")}, {0}}, |
243 | 0 | {{0, UA_STRING_STATIC("security-policy-url")}, {0}}, |
244 | 0 | {{0, UA_STRING_STATIC("remote-certificate")}, {0}} |
245 | 0 | }; |
246 | 0 | UA_KeyValueMap notifySCMap = {15, notifySCData}; |
247 | |
|
248 | 0 | UA_Variant_setScalar(¬ifySCData[0].value, &channel->securityToken.channelId, |
249 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
250 | 0 | UA_Variant_setScalar(¬ifySCData[1].value, |
251 | 0 | &channel->connectionManager->eventSource.name, |
252 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
253 | 0 | UA_UInt64 connectionId = channel->connectionId; |
254 | 0 | UA_Variant_setScalar(¬ifySCData[2].value, &connectionId, |
255 | 0 | &UA_TYPES[UA_TYPES_UINT64]); |
256 | 0 | UA_Variant_setScalar(¬ifySCData[3].value, &channel->remoteAddress, |
257 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
258 | 0 | UA_Variant_setScalar(¬ifySCData[4].value, &channel->config.protocolVersion, |
259 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
260 | 0 | UA_Variant_setScalar(¬ifySCData[5].value, &channel->config.recvBufferSize, |
261 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
262 | 0 | UA_Variant_setScalar(¬ifySCData[6].value, &channel->config.localMaxMessageSize, |
263 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
264 | 0 | UA_Variant_setScalar(¬ifySCData[7].value, &channel->config.localMaxChunkCount, |
265 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
266 | 0 | UA_Variant_setScalar(¬ifySCData[8].value, &channel->config.sendBufferSize, |
267 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
268 | 0 | UA_Variant_setScalar(¬ifySCData[9].value, &channel->config.remoteMaxMessageSize, |
269 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
270 | 0 | UA_Variant_setScalar(¬ifySCData[10].value, &channel->config.remoteMaxChunkCount, |
271 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
272 | 0 | UA_Variant_setScalar(¬ifySCData[11].value, &channel->endpointUrl, |
273 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
274 | 0 | UA_Variant_setScalar(¬ifySCData[12].value, &channel->securityMode, |
275 | 0 | &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE]); |
276 | 0 | UA_String securityPolicyUri = UA_STRING_NULL; |
277 | 0 | if(channel->securityPolicy) |
278 | 0 | securityPolicyUri = channel->securityPolicy->policyUri; |
279 | 0 | UA_Variant_setScalar(¬ifySCData[13].value, &securityPolicyUri, |
280 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
281 | 0 | UA_Variant_setScalar(¬ifySCData[14].value, &channel->remoteCertificate, |
282 | 0 | &UA_TYPES[UA_TYPES_BYTESTRING]); |
283 | | |
284 | | /* Notify the application */ |
285 | 0 | if(sc->secureChannelNotificationCallback) |
286 | 0 | sc->secureChannelNotificationCallback(server, type, notifySCMap); |
287 | 0 | if(sc->globalNotificationCallback) |
288 | 0 | sc->globalNotificationCallback(server, type, notifySCMap); |
289 | 0 | } |