/src/open62541/src/server/ua_services_session.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-2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2014-2017 (c) Florian Palm |
7 | | * Copyright 2014-2016 (c) Sten Grüner |
8 | | * Copyright 2015 (c) Chris Iatrou |
9 | | * Copyright 2015 (c) Oleksiy Vasylyev |
10 | | * Copyright 2017 (c) Stefan Profanter, fortiss GmbH |
11 | | * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB |
12 | | * Copyright 2019 (c) Kalycito Infotech Private Limited |
13 | | * Copyright 2018-2020 (c) HMS Industrial Networks AB (Author: Jonas Green) |
14 | | * Copyright 2025 (c) Siemens AG (Author: Tin Raic) |
15 | | */ |
16 | | |
17 | | #include "ua_server_internal.h" |
18 | | #include "ua_services.h" |
19 | | |
20 | | void |
21 | | notifySession(UA_Server *server, UA_Session *session, |
22 | 0 | UA_ApplicationNotificationType type) { |
23 | | /* Nothing to do */ |
24 | 0 | if(!server->config.globalNotificationCallback && |
25 | 0 | !server->config.sessionNotificationCallback) |
26 | 0 | return; |
27 | | |
28 | | /* Set up the payload */ |
29 | 0 | size_t payloadSize = 6 + session->attributes.mapSize; |
30 | 0 | UA_STACKARRAY(UA_KeyValuePair, payloadData, payloadSize); |
31 | 0 | UA_KeyValueMap payloadMap = {payloadSize, payloadData}; |
32 | 0 | payloadData[0].key = UA_QUALIFIEDNAME(0, "session-id"); |
33 | 0 | UA_Variant_setScalar(&payloadData[0].value, &session->sessionId, |
34 | 0 | &UA_TYPES[UA_TYPES_NODEID]); |
35 | 0 | payloadData[1].key = UA_QUALIFIEDNAME(0, "securechannel-id"); |
36 | 0 | UA_UInt32 secureChannelId = 0; |
37 | 0 | if(session->channel) |
38 | 0 | secureChannelId = session->channel->securityToken.channelId; |
39 | 0 | UA_Variant_setScalar(&payloadData[1].value, &secureChannelId, |
40 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
41 | 0 | payloadData[2].key = UA_QUALIFIEDNAME(0, "session-name"); |
42 | 0 | UA_Variant_setScalar(&payloadData[2].value, &session->sessionName, |
43 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
44 | 0 | payloadData[3].key = UA_QUALIFIEDNAME(0, "client-description"); |
45 | 0 | UA_Variant_setScalar(&payloadData[3].value, &session->clientDescription, |
46 | 0 | &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); |
47 | 0 | payloadData[4].key = UA_QUALIFIEDNAME(0, "client-user-id"); |
48 | 0 | UA_Variant_setScalar(&payloadData[4].value, &session->clientUserIdOfSession, |
49 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
50 | 0 | payloadData[5].key = UA_QUALIFIEDNAME(0, "locale-ids"); |
51 | 0 | UA_Variant_setArray(&payloadData[5].value, session->localeIds, |
52 | 0 | session->localeIdsSize, &UA_TYPES[UA_TYPES_STRING]); |
53 | |
|
54 | 0 | memcpy(&payloadData[6], session->attributes.map, |
55 | 0 | sizeof(UA_KeyValuePair) * session->attributes.mapSize); |
56 | | |
57 | | /* Call the notification callback */ |
58 | 0 | if(server->config.sessionNotificationCallback) |
59 | 0 | server->config.sessionNotificationCallback(server, type, payloadMap); |
60 | 0 | if(server->config.globalNotificationCallback) |
61 | 0 | server->config.globalNotificationCallback(server, type, payloadMap); |
62 | 0 | } |
63 | | |
64 | | /* Delayed callback to free the session memory */ |
65 | | static void |
66 | 0 | removeSessionCallback(UA_Server *server, session_list_entry *entry) { |
67 | 0 | lockServer(server); |
68 | 0 | UA_Session_clear(&entry->session, server); |
69 | 0 | unlockServer(server); |
70 | 0 | UA_free(entry); |
71 | 0 | } |
72 | | |
73 | | void |
74 | | UA_Session_remove(UA_Server *server, UA_Session *session, |
75 | 0 | UA_ShutdownReason shutdownReason) { |
76 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
77 | | |
78 | | /* Remove the Subscriptions */ |
79 | 0 | #ifdef UA_ENABLE_SUBSCRIPTIONS |
80 | 0 | UA_Subscription *sub, *tempsub; |
81 | 0 | TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, tempsub) { |
82 | 0 | UA_Subscription_delete(server, sub); |
83 | 0 | } |
84 | |
|
85 | 0 | UA_PublishResponseEntry *entry; |
86 | 0 | while((entry = UA_Session_dequeuePublishReq(session))) { |
87 | 0 | UA_PublishResponse_clear(&entry->response); |
88 | 0 | UA_free(entry); |
89 | 0 | } |
90 | 0 | #endif |
91 | | |
92 | | /* Callback into userland access control */ |
93 | 0 | if(server->config.accessControl.closeSession) { |
94 | 0 | server->config.accessControl. |
95 | 0 | closeSession(server, &server->config.accessControl, |
96 | 0 | &session->sessionId, session->context); |
97 | 0 | } |
98 | | |
99 | | /* Detach the Session from the SecureChannel */ |
100 | 0 | UA_Session_detachFromSecureChannel(server, session); |
101 | | |
102 | | /* Deactivate the session */ |
103 | 0 | if(session->activated) { |
104 | 0 | session->activated = false; |
105 | 0 | server->activeSessionCount--; |
106 | 0 | } |
107 | | |
108 | | /* Detach the session from the session manager and make the capacity |
109 | | * available */ |
110 | 0 | session_list_entry *sentry = container_of(session, session_list_entry, session); |
111 | 0 | LIST_REMOVE(sentry, pointers); |
112 | 0 | server->sessionCount--; |
113 | |
|
114 | 0 | switch(shutdownReason) { |
115 | 0 | case UA_SHUTDOWNREASON_CLOSE: |
116 | 0 | case UA_SHUTDOWNREASON_PURGE: |
117 | 0 | break; |
118 | 0 | case UA_SHUTDOWNREASON_TIMEOUT: |
119 | 0 | server->serverDiagnosticsSummary.sessionTimeoutCount++; |
120 | 0 | break; |
121 | 0 | case UA_SHUTDOWNREASON_REJECT: |
122 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; |
123 | 0 | break; |
124 | 0 | case UA_SHUTDOWNREASON_SECURITYREJECT: |
125 | 0 | server->serverDiagnosticsSummary.securityRejectedSessionCount++; |
126 | 0 | break; |
127 | 0 | case UA_SHUTDOWNREASON_ABORT: |
128 | 0 | server->serverDiagnosticsSummary.sessionAbortCount++; |
129 | 0 | break; |
130 | 0 | default: |
131 | 0 | UA_assert(false); |
132 | 0 | break; |
133 | 0 | } |
134 | | |
135 | | /* Notify the application */ |
136 | 0 | notifySession(server, session, UA_APPLICATIONNOTIFICATIONTYPE_SESSION_CLOSED); |
137 | | |
138 | | /* Add a delayed callback to remove the session when the currently |
139 | | * scheduled jobs have completed */ |
140 | 0 | sentry->cleanupCallback.callback = (UA_Callback)removeSessionCallback; |
141 | 0 | sentry->cleanupCallback.application = server; |
142 | 0 | sentry->cleanupCallback.context = sentry; |
143 | 0 | UA_EventLoop *el = server->config.eventLoop; |
144 | 0 | el->addDelayedCallback(el, &sentry->cleanupCallback); |
145 | 0 | } |
146 | | |
147 | | void |
148 | 0 | cleanupSessions(UA_Server *server, UA_DateTime nowMonotonic) { |
149 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
150 | 0 | session_list_entry *sentry, *temp; |
151 | 0 | LIST_FOREACH_SAFE(sentry, &server->sessions, pointers, temp) { |
152 | | /* Session has timed out? */ |
153 | 0 | if(sentry->session.validTill >= nowMonotonic) |
154 | 0 | continue; |
155 | 0 | UA_LOG_INFO_SESSION(server->config.logging, &sentry->session, |
156 | 0 | "Session has timed out"); |
157 | 0 | UA_Session_remove(server, &sentry->session, UA_SHUTDOWNREASON_TIMEOUT); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | /************/ |
162 | | /* Services */ |
163 | | /************/ |
164 | | |
165 | | UA_Session * |
166 | 0 | getSessionByToken(UA_Server *server, const UA_NodeId *token) { |
167 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
168 | |
|
169 | 0 | session_list_entry *current = NULL; |
170 | 0 | LIST_FOREACH(current, &server->sessions, pointers) { |
171 | | /* Token does not match */ |
172 | 0 | if(!UA_NodeId_equal(¤t->session.authenticationToken, token)) |
173 | 0 | continue; |
174 | | |
175 | | /* Session has timed out */ |
176 | 0 | UA_EventLoop *el = server->config.eventLoop; |
177 | 0 | UA_DateTime now = el->dateTime_nowMonotonic(el); |
178 | 0 | if(now > current->session.validTill) { |
179 | 0 | UA_LOG_INFO_SESSION(server->config.logging, ¤t->session, |
180 | 0 | "Client tries to use a session that has timed out"); |
181 | 0 | return NULL; |
182 | 0 | } |
183 | | |
184 | 0 | return ¤t->session; |
185 | 0 | } |
186 | | |
187 | 0 | return NULL; |
188 | 0 | } |
189 | | |
190 | | UA_Session * |
191 | 0 | getSessionById(UA_Server *server, const UA_NodeId *sessionId) { |
192 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
193 | |
|
194 | 0 | session_list_entry *current = NULL; |
195 | 0 | LIST_FOREACH(current, &server->sessions, pointers) { |
196 | | /* Token does not match */ |
197 | 0 | if(!UA_NodeId_equal(¤t->session.sessionId, sessionId)) |
198 | 0 | continue; |
199 | | |
200 | | /* Session has timed out */ |
201 | 0 | UA_EventLoop *el = server->config.eventLoop; |
202 | 0 | UA_DateTime now = el->dateTime_nowMonotonic(el); |
203 | 0 | if(now > current->session.validTill) { |
204 | 0 | UA_LOG_INFO_SESSION(server->config.logging, ¤t->session, |
205 | 0 | "Client tries to use a session that has timed out"); |
206 | 0 | return NULL; |
207 | 0 | } |
208 | | |
209 | 0 | return ¤t->session; |
210 | 0 | } |
211 | | |
212 | 0 | if(UA_NodeId_equal(sessionId, &server->adminSession.sessionId)) |
213 | 0 | return &server->adminSession; |
214 | | |
215 | 0 | return NULL; |
216 | 0 | } |
217 | | |
218 | | static UA_StatusCode |
219 | | signCreateSessionResponse(UA_Server *server, UA_SecureChannel *channel, |
220 | | const UA_CreateSessionRequest *request, |
221 | 0 | UA_CreateSessionResponse *response) { |
222 | 0 | if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && |
223 | 0 | channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) |
224 | 0 | return UA_STATUSCODE_GOOD; |
225 | | |
226 | 0 | const UA_SecurityPolicy *sp = channel->securityPolicy; |
227 | 0 | void *cc = channel->channelContext; |
228 | 0 | UA_SignatureData *signatureData = &response->serverSignature; |
229 | | |
230 | | /* Prepare the signature */ |
231 | 0 | const UA_SecurityPolicySignatureAlgorithm *signAlg = &sp->asymSignatureAlgorithm; |
232 | 0 | size_t signatureSize = signAlg->getLocalSignatureSize(sp, cc); |
233 | 0 | UA_StatusCode retval = UA_String_copy(&signAlg->uri, &signatureData->algorithm); |
234 | 0 | retval |= UA_ByteString_allocBuffer(&signatureData->signature, signatureSize); |
235 | 0 | if(retval != UA_STATUSCODE_GOOD) |
236 | 0 | return retval; |
237 | | |
238 | | /* Allocate a temp buffer */ |
239 | 0 | size_t dataToSignSize = |
240 | 0 | request->clientCertificate.length + request->clientNonce.length; |
241 | 0 | UA_ByteString dataToSign; |
242 | 0 | retval = UA_ByteString_allocBuffer(&dataToSign, dataToSignSize); |
243 | 0 | if(retval != UA_STATUSCODE_GOOD) |
244 | 0 | return retval; /* signatureData->signature is cleaned up with the response */ |
245 | | |
246 | | /* Sign the signature */ |
247 | 0 | memcpy(dataToSign.data, request->clientCertificate.data, |
248 | 0 | request->clientCertificate.length); |
249 | 0 | memcpy(dataToSign.data + request->clientCertificate.length, |
250 | 0 | request->clientNonce.data, request->clientNonce.length); |
251 | 0 | retval = signAlg->sign(sp, cc, &dataToSign, &signatureData->signature); |
252 | | |
253 | | /* Clean up */ |
254 | 0 | UA_ByteString_clear(&dataToSign); |
255 | 0 | return retval; |
256 | 0 | } |
257 | | |
258 | | static UA_StatusCode |
259 | | addEphemeralKeyAdditionalHeader(UA_Server *server, const UA_SecurityPolicy *sp, |
260 | 0 | void *channelContext, UA_ExtensionObject *ah) { |
261 | | /* Allocate additional parameters */ |
262 | 0 | UA_AdditionalParametersType *ap = UA_AdditionalParametersType_new(); |
263 | 0 | if(!ap) |
264 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
265 | | |
266 | | /* Set the additional parameters in the additional header. They also get |
267 | | * cleaned up from there in the error case. */ |
268 | 0 | UA_ExtensionObject_setValue(ah, ap, &UA_TYPES[UA_TYPES_ADDITIONALPARAMETERSTYPE]); |
269 | | |
270 | | /* UA_KeyValueMap has the identical layout. And better helper methods. */ |
271 | 0 | UA_KeyValueMap *map = (UA_KeyValueMap*)ap; |
272 | | |
273 | | /* Add the PolicyUri to the map */ |
274 | 0 | UA_StatusCode res = |
275 | 0 | UA_KeyValueMap_setScalar(map, UA_QUALIFIEDNAME(0, "ECDHPolicyUri"), |
276 | 0 | &sp->policyUri, &UA_TYPES[UA_TYPES_STRING]); |
277 | 0 | if(res != UA_STATUSCODE_GOOD) |
278 | 0 | return res; |
279 | | |
280 | | /* Allocate the EphemeralKey structure */ |
281 | 0 | UA_EphemeralKeyType *ephKey = UA_EphemeralKeyType_new(); |
282 | 0 | if(!ephKey) |
283 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
284 | | |
285 | | /* Add the EphemeralKeyto the map */ |
286 | 0 | res = UA_KeyValueMap_setScalarShallow(map, UA_QUALIFIEDNAME(0, "ECDHKey"), |
287 | 0 | ephKey, &UA_TYPES[UA_TYPES_EPHEMERALKEYTYPE]); |
288 | 0 | if(res != UA_STATUSCODE_GOOD) |
289 | 0 | return res; |
290 | | |
291 | | /* Allocate the ephemeral key buffer to the exact size of the ephemeral key |
292 | | * for the used ECC policy so that the nonce generation function knows that |
293 | | * it needs to generate an ephemeral key and not some other random byte |
294 | | * string. |
295 | | * |
296 | | * TODO: There should be a more stable way to signal the generation of an |
297 | | * ephemeral key */ |
298 | 0 | res = UA_ByteString_allocBuffer(&ephKey->publicKey, sp->nonceLength); |
299 | 0 | if(res != UA_STATUSCODE_GOOD) |
300 | 0 | return res; |
301 | | |
302 | | /* Generate the ephemeral key |
303 | | * TODO: Don't we have to persist the key locally? */ |
304 | 0 | res = sp->generateNonce(sp, channelContext, &ephKey->publicKey); |
305 | 0 | if(res != UA_STATUSCODE_GOOD) |
306 | 0 | return res; |
307 | | |
308 | | /* Create the signature |
309 | | * TODO: Check whether the symmetric or asymmetric signing algorithm is |
310 | | * needed here */ |
311 | 0 | size_t signatureSize = sp->symSignatureAlgorithm. |
312 | 0 | getLocalSignatureSize(sp, channelContext); |
313 | 0 | res = UA_ByteString_allocBuffer(&ephKey->signature, signatureSize); |
314 | 0 | if(res != UA_STATUSCODE_GOOD) |
315 | 0 | return res; |
316 | 0 | return sp->symSignatureAlgorithm. |
317 | 0 | sign(sp, channelContext, &ephKey->publicKey, &ephKey->signature); |
318 | 0 | } |
319 | | |
320 | | /* Creates and adds a session. But it is not yet attached to a secure channel. */ |
321 | | UA_StatusCode |
322 | | UA_Session_create(UA_Server *server, UA_SecureChannel *channel, |
323 | 0 | const UA_CreateSessionRequest *request, UA_Session **session) { |
324 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
325 | |
|
326 | 0 | if(server->sessionCount >= server->config.maxSessions) { |
327 | 0 | UA_LOG_WARNING_CHANNEL(server->config.logging, channel, |
328 | 0 | "Could not create a Session - Server limits reached"); |
329 | 0 | return UA_STATUSCODE_BADTOOMANYSESSIONS; |
330 | 0 | } |
331 | | |
332 | 0 | session_list_entry *newentry = (session_list_entry*) |
333 | 0 | UA_malloc(sizeof(session_list_entry)); |
334 | 0 | if(!newentry) |
335 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
336 | | |
337 | | /* Initialize the Session */ |
338 | 0 | UA_Session_init(&newentry->session); |
339 | 0 | newentry->session.sessionId = UA_NODEID_GUID(1, UA_Guid_random()); |
340 | 0 | newentry->session.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random()); |
341 | |
|
342 | 0 | newentry->session.timeout = server->config.maxSessionTimeout; |
343 | 0 | if(request->requestedSessionTimeout <= server->config.maxSessionTimeout && |
344 | 0 | request->requestedSessionTimeout > 0) |
345 | 0 | newentry->session.timeout = request->requestedSessionTimeout; |
346 | | |
347 | | /* Attach the session to the channel. But don't activate for now. */ |
348 | 0 | if(channel) |
349 | 0 | UA_Session_attachToSecureChannel(server, &newentry->session, channel); |
350 | |
|
351 | 0 | UA_EventLoop *el = server->config.eventLoop; |
352 | 0 | UA_DateTime now = el->dateTime_now(el); |
353 | 0 | UA_DateTime nowMonotonic = el->dateTime_nowMonotonic(el); |
354 | 0 | UA_Session_updateLifetime(&newentry->session, now, nowMonotonic); |
355 | | |
356 | | /* Add to the server */ |
357 | 0 | LIST_INSERT_HEAD(&server->sessions, newentry, pointers); |
358 | 0 | server->sessionCount++; |
359 | | |
360 | | /* Notify the application */ |
361 | 0 | notifySession(server, &newentry->session, |
362 | 0 | UA_APPLICATIONNOTIFICATIONTYPE_SESSION_CREATED); |
363 | | |
364 | | /* Return */ |
365 | 0 | *session = &newentry->session; |
366 | 0 | return UA_STATUSCODE_GOOD; |
367 | 0 | } |
368 | | |
369 | | void |
370 | | Service_CreateSession(UA_Server *server, UA_SecureChannel *channel, |
371 | | const UA_CreateSessionRequest *request, |
372 | 0 | UA_CreateSessionResponse *response) { |
373 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
374 | 0 | UA_LOG_DEBUG_CHANNEL(server->config.logging, channel, "Trying to create session"); |
375 | |
|
376 | 0 | const UA_SecurityPolicy *sp = channel->securityPolicy; |
377 | 0 | void *cc = channel->channelContext; |
378 | |
|
379 | 0 | if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || |
380 | 0 | channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { |
381 | | /* Compare the clientCertificate with the remoteCertificate of the |
382 | | * channel. Both the clientCertificate of this request and the |
383 | | * remoteCertificate of the channel may contain a partial or a complete |
384 | | * certificate chain. The compareCertificate function will compare the |
385 | | * first certificate of each chain. The end certificate shall be located |
386 | | * first in the chain according to the OPC UA specification Part 6 |
387 | | * (1.04), chapter 6.2.3.*/ |
388 | 0 | UA_StatusCode res = sp->compareCertificate(sp, cc, &request->clientCertificate); |
389 | 0 | if(res != UA_STATUSCODE_GOOD) { |
390 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
391 | 0 | "The client certificate did not validate"); |
392 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADCERTIFICATEINVALID; |
393 | 0 | server->serverDiagnosticsSummary.securityRejectedSessionCount++; |
394 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; |
395 | 0 | return; |
396 | 0 | } |
397 | 0 | } |
398 | | |
399 | 0 | UA_assert(channel->securityToken.channelId != 0); |
400 | | |
401 | | /* Check the nonce */ |
402 | 0 | if(channel->securityPolicy->policyType != UA_SECURITYPOLICYTYPE_NONE && |
403 | 0 | request->clientNonce.length < 32) { |
404 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
405 | 0 | "The nonce provided by the client has the wrong length"); |
406 | 0 | server->serverDiagnosticsSummary.securityRejectedSessionCount++; |
407 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; |
408 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADNONCEINVALID; |
409 | 0 | return; |
410 | 0 | } |
411 | | |
412 | | /* Check the client certificate (against the ApplicationDescription, |
413 | | * cryptographic checking is done separately in the SecureChannel) */ |
414 | 0 | if(request->clientCertificate.length > 0) { |
415 | 0 | UA_StatusCode res = |
416 | 0 | UA_CertificateUtils_verifyApplicationUri(&request->clientCertificate, |
417 | 0 | &request->clientDescription.applicationUri); |
418 | 0 | if(res != UA_STATUSCODE_GOOD) { |
419 | 0 | if(server->config.allowAllCertificateUris <= UA_RULEHANDLING_WARN) { |
420 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
421 | 0 | "The client certificate's ApplicationUri " |
422 | 0 | "could not be verified against the ApplicationUri " |
423 | 0 | "%S from the client's ApplicationDescription (%s)", |
424 | 0 | request->clientDescription.applicationUri, |
425 | 0 | UA_StatusCode_name(res)); |
426 | 0 | } |
427 | 0 | if(server->config.allowAllCertificateUris <= UA_RULEHANDLING_ABORT) { |
428 | 0 | response->responseHeader.serviceResult = res; |
429 | 0 | server->serverDiagnosticsSummary.securityRejectedSessionCount++; |
430 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; |
431 | 0 | return; |
432 | 0 | } |
433 | 0 | } |
434 | 0 | } |
435 | | |
436 | | /* Create the Session */ |
437 | 0 | UA_Session *newSession = NULL; |
438 | 0 | response->responseHeader.serviceResult = |
439 | 0 | UA_Session_create(server, channel, request, &newSession); |
440 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
441 | 0 | UA_LOG_WARNING_CHANNEL(server->config.logging, channel, |
442 | 0 | "Processing CreateSessionRequest failed"); |
443 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; |
444 | 0 | return; |
445 | 0 | } |
446 | | |
447 | | /* If the session name is empty, use the generated SessionId */ |
448 | 0 | response->responseHeader.serviceResult |= |
449 | 0 | UA_String_copy(&request->sessionName, &newSession->sessionName); |
450 | 0 | if(newSession->sessionName.length == 0) |
451 | 0 | response->responseHeader.serviceResult |= |
452 | 0 | UA_NodeId_print(&newSession->sessionId, &newSession->sessionName); |
453 | |
|
454 | 0 | response->responseHeader.serviceResult |= UA_Session_generateNonce(newSession); |
455 | 0 | newSession->maxResponseMessageSize = request->maxResponseMessageSize; |
456 | 0 | newSession->maxRequestMessageSize = channel->config.localMaxMessageSize; |
457 | 0 | response->responseHeader.serviceResult |= |
458 | 0 | UA_ApplicationDescription_copy(&request->clientDescription, |
459 | 0 | &newSession->clientDescription); |
460 | |
|
461 | 0 | #ifdef UA_ENABLE_DIAGNOSTICS |
462 | 0 | response->responseHeader.serviceResult |= |
463 | 0 | UA_String_copy(&request->serverUri, &newSession->diagnostics.serverUri); |
464 | 0 | response->responseHeader.serviceResult |= |
465 | 0 | UA_String_copy(&request->endpointUrl, &newSession->diagnostics.endpointUrl); |
466 | 0 | #endif |
467 | |
|
468 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
469 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
470 | 0 | "Could not create the new session (%s)", |
471 | 0 | UA_StatusCode_name(response->responseHeader.serviceResult)); |
472 | 0 | UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT); |
473 | 0 | return; |
474 | 0 | } |
475 | | |
476 | | /* Prepare the response */ |
477 | 0 | response->sessionId = newSession->sessionId; |
478 | 0 | response->revisedSessionTimeout = (UA_Double)newSession->timeout; |
479 | 0 | response->authenticationToken = newSession->authenticationToken; |
480 | 0 | response->responseHeader.serviceResult |= |
481 | 0 | UA_ByteString_copy(&newSession->serverNonce, &response->serverNonce); |
482 | | |
483 | | /* Copy the server's endpointdescriptions into the response */ |
484 | 0 | response->responseHeader.serviceResult |= |
485 | 0 | setCurrentEndPointsArray(server, request->endpointUrl, NULL, 0, |
486 | 0 | &response->serverEndpoints, |
487 | 0 | &response->serverEndpointsSize); |
488 | | |
489 | | /* Return the server certificate from the SecurityPolicy of the current |
490 | | * channel. Or, if the channel is unencrypted, return the standard policy |
491 | | * used for usertoken encryption. */ |
492 | 0 | if(sp->policyType == UA_SECURITYPOLICYTYPE_NONE || |
493 | 0 | sp->localCertificate.length == 0) |
494 | 0 | sp = getDefaultEncryptedSecurityPolicy(server); |
495 | 0 | if(sp) |
496 | 0 | response->responseHeader.serviceResult |= |
497 | 0 | UA_ByteString_copy(&sp->localCertificate, &response->serverCertificate); |
498 | |
|
499 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
500 | 0 | UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT); |
501 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
502 | 0 | "Could not prepare the CreateSessionResponse (%s)", |
503 | 0 | UA_StatusCode_name(response->responseHeader.serviceResult)); |
504 | 0 | return; |
505 | 0 | } |
506 | | |
507 | | /* If ECC policy, create an ephemeral key to be returned in the response */ |
508 | 0 | if(sp && sp->policyType == UA_SECURITYPOLICYTYPE_ECC) { |
509 | 0 | response->responseHeader.serviceResult = |
510 | 0 | addEphemeralKeyAdditionalHeader(server, sp, channel->channelContext, |
511 | 0 | &response->responseHeader.additionalHeader); |
512 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
513 | 0 | UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT); |
514 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
515 | 0 | "Could not prepare the ephemeral key (%s)", |
516 | 0 | UA_StatusCode_name(response->responseHeader.serviceResult)); |
517 | 0 | return; |
518 | 0 | } |
519 | 0 | UA_LOG_DEBUG(server->config.logging, UA_LOGCATEGORY_SESSION, |
520 | 0 | "[CreateSession] Ephemeral Key created"); |
521 | 0 | } |
522 | | |
523 | | /* Sign the signature */ |
524 | 0 | response->responseHeader.serviceResult |= |
525 | 0 | signCreateSessionResponse(server, channel, request, response); |
526 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
527 | 0 | UA_Session_remove(server, newSession, UA_SHUTDOWNREASON_REJECT); |
528 | 0 | UA_LOG_ERROR_CHANNEL(server->config.logging, channel, |
529 | 0 | "Could not sign the CreateSessionResponse (%s)", |
530 | 0 | UA_StatusCode_name(response->responseHeader.serviceResult)); |
531 | 0 | return; |
532 | 0 | } |
533 | | |
534 | 0 | #ifdef UA_ENABLE_DIAGNOSTICS |
535 | 0 | UA_EventLoop *el = server->config.eventLoop; |
536 | 0 | newSession->diagnostics.clientConnectionTime = el->dateTime_now(el); |
537 | 0 | newSession->diagnostics.clientLastContactTime = |
538 | 0 | newSession->diagnostics.clientConnectionTime; |
539 | | |
540 | | /* Create the object in the information model */ |
541 | 0 | createSessionObject(server, newSession); |
542 | 0 | #endif |
543 | |
|
544 | 0 | UA_LOG_INFO_SESSION(server->config.logging, newSession, "Session created"); |
545 | 0 | } |
546 | | |
547 | | static UA_StatusCode |
548 | | checkCertificateSignature(const UA_Server *server, const UA_SecurityPolicy *sp, |
549 | | void *channelContext, const UA_ByteString *serverNonce, |
550 | | const UA_SignatureData *signature, |
551 | 0 | const bool isUserTokenSignature) { |
552 | | /* Check for zero signature length */ |
553 | 0 | if(signature->signature.length == 0) { |
554 | 0 | if(isUserTokenSignature) |
555 | 0 | return UA_STATUSCODE_BADUSERSIGNATUREINVALID; |
556 | 0 | return UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID; |
557 | 0 | } |
558 | | |
559 | 0 | if(!sp) |
560 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
561 | | |
562 | | /* Data to verify is calculated by appending the serverNonce to the local |
563 | | * certificate */ |
564 | 0 | const UA_ByteString *localCertificate = &sp->localCertificate; |
565 | 0 | UA_ByteString dataToVerify; |
566 | 0 | size_t dataToVerifySize = localCertificate->length + serverNonce->length; |
567 | 0 | UA_StatusCode retval = UA_ByteString_allocBuffer(&dataToVerify, dataToVerifySize); |
568 | 0 | if(retval != UA_STATUSCODE_GOOD) |
569 | 0 | return retval; |
570 | | |
571 | 0 | memcpy(dataToVerify.data, localCertificate->data, localCertificate->length); |
572 | 0 | memcpy(dataToVerify.data + localCertificate->length, |
573 | 0 | serverNonce->data, serverNonce->length); |
574 | 0 | retval = sp->asymSignatureAlgorithm. |
575 | 0 | verify(sp, channelContext, &dataToVerify, &signature->signature); |
576 | 0 | UA_ByteString_clear(&dataToVerify); |
577 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
578 | 0 | if(isUserTokenSignature) |
579 | 0 | retval = UA_STATUSCODE_BADUSERSIGNATUREINVALID; |
580 | 0 | else |
581 | 0 | retval = UA_STATUSCODE_BADAPPLICATIONSIGNATUREINVALID; |
582 | 0 | } |
583 | 0 | return retval; |
584 | 0 | } |
585 | | |
586 | | /* Always sets tokenSp (default: SecurityPolicy of the channel) */ |
587 | | static const UA_UserTokenPolicy * |
588 | | selectTokenPolicy(UA_Server *server, UA_SecureChannel *channel, |
589 | | UA_Session *session, const UA_ExtensionObject *identityToken, |
590 | | const UA_EndpointDescription *ed, |
591 | 0 | const UA_SecurityPolicy **tokenSp) { |
592 | | /* If no UserTokenPolicies are configured in the endpoint, then use |
593 | | * those configured in the AccessControl plugin. */ |
594 | 0 | size_t identPoliciesSize = ed->userIdentityTokensSize; |
595 | 0 | const UA_UserTokenPolicy *identPolicies = ed->userIdentityTokens; |
596 | 0 | if(identPoliciesSize == 0) { |
597 | 0 | identPoliciesSize = server->config.accessControl.userTokenPoliciesSize; |
598 | 0 | identPolicies = server->config.accessControl.userTokenPolicies; |
599 | 0 | } |
600 | | |
601 | | /* Match the UserTokenType */ |
602 | 0 | const UA_DataType *tokenDataType = identityToken->content.decoded.type; |
603 | 0 | for(size_t j = 0; j < identPoliciesSize; j++) { |
604 | 0 | const UA_UserTokenPolicy *pol = &identPolicies[j]; |
605 | | |
606 | | /* Part 4, Section 5.6.3.2, Table 17: A NULL or empty |
607 | | * UserIdentityToken should be treated as Anonymous */ |
608 | 0 | if(identityToken->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY && |
609 | 0 | pol->tokenType == UA_USERTOKENTYPE_ANONYMOUS) { |
610 | 0 | *tokenSp = channel->securityPolicy; |
611 | 0 | return pol; |
612 | 0 | } |
613 | | |
614 | | /* Expect decoded content if not anonymous */ |
615 | 0 | if(!tokenDataType) |
616 | 0 | continue; |
617 | | |
618 | | /* Match the DataType of the provided token with the policy */ |
619 | 0 | switch(pol->tokenType) { |
620 | 0 | case UA_USERTOKENTYPE_ANONYMOUS: |
621 | 0 | if(tokenDataType != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) |
622 | 0 | continue; |
623 | 0 | break; |
624 | 0 | case UA_USERTOKENTYPE_USERNAME: |
625 | 0 | if(tokenDataType != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) |
626 | 0 | continue; |
627 | 0 | break; |
628 | 0 | case UA_USERTOKENTYPE_CERTIFICATE: |
629 | 0 | if(tokenDataType != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) |
630 | 0 | continue; |
631 | 0 | break; |
632 | 0 | case UA_USERTOKENTYPE_ISSUEDTOKEN: |
633 | 0 | if(tokenDataType != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]) |
634 | 0 | continue; |
635 | 0 | break; |
636 | 0 | default: |
637 | 0 | continue; |
638 | 0 | } |
639 | | |
640 | | /* All valid token data types start with a string policyId. Casting |
641 | | * to anonymous hence works for all of them. */ |
642 | 0 | UA_AnonymousIdentityToken *token = (UA_AnonymousIdentityToken*) |
643 | 0 | identityToken->content.decoded.data; |
644 | | |
645 | | /* In setCurrentEndPointsArray we prepend the PolicyId with the |
646 | | * SecurityMode of the endpoint and the postfix of the |
647 | | * SecurityPolicyUri to make it unique. Check the PolicyId. */ |
648 | 0 | if(pol->policyId.length > token->policyId.length) |
649 | 0 | continue; |
650 | 0 | UA_String policyPrefix = token->policyId; |
651 | 0 | policyPrefix.length = pol->policyId.length; |
652 | 0 | if(!UA_String_equal(&policyPrefix, &pol->policyId)) |
653 | 0 | continue; |
654 | | |
655 | | /* Get the SecurityPolicy for the endpoint from the postfix */ |
656 | 0 | UA_String utPolPostfix = securityPolicyUriPostfix(token->policyId); |
657 | 0 | UA_SecurityPolicy *candidateSp = |
658 | 0 | getSecurityPolicyByPostfix(server, utPolPostfix); |
659 | 0 | if(!candidateSp) { |
660 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
661 | 0 | "ActivateSession: The UserTokenPolicy of " |
662 | 0 | "the endpoint defines an unknown " |
663 | 0 | "SecurityPolicy %S", |
664 | 0 | pol->securityPolicyUri); |
665 | 0 | continue; |
666 | 0 | } |
667 | | |
668 | | /* A non-anonymous authentication token is transmitted over an |
669 | | * unencrypted SecureChannel */ |
670 | 0 | if(pol->tokenType != UA_USERTOKENTYPE_ANONYMOUS && |
671 | 0 | channel->securityPolicy->policyType == UA_SECURITYPOLICYTYPE_NONE && |
672 | 0 | candidateSp->policyType == UA_SECURITYPOLICYTYPE_NONE) { |
673 | | /* Check if the allowNonePolicyPassword option is set. |
674 | | * But this exception only works for Username/Password. */ |
675 | 0 | if(!server->config.allowNonePolicyPassword || |
676 | 0 | pol->tokenType != UA_USERTOKENTYPE_USERNAME) |
677 | 0 | continue; |
678 | 0 | } |
679 | | |
680 | | /* Found a match policy */ |
681 | 0 | *tokenSp = candidateSp; |
682 | 0 | return pol; |
683 | 0 | } |
684 | | |
685 | 0 | return NULL; |
686 | 0 | } |
687 | | |
688 | | static void |
689 | | selectEndpointAndTokenPolicy(UA_Server *server, UA_SecureChannel *channel, |
690 | | UA_Session *session, |
691 | | const UA_ExtensionObject *identityToken, |
692 | | const UA_EndpointDescription **ed, |
693 | | const UA_UserTokenPolicy **utp, |
694 | 0 | const UA_SecurityPolicy **tokenSp) { |
695 | 0 | UA_ServerConfig *sc = &server->config; |
696 | 0 | for(size_t i = 0; i < sc->endpointsSize; ++i) { |
697 | 0 | const UA_EndpointDescription *desc = &sc->endpoints[i]; |
698 | | |
699 | | /* Match the Security Mode */ |
700 | 0 | if(desc->securityMode != channel->securityMode) |
701 | 0 | continue; |
702 | | |
703 | | /* Match the SecurityPolicy of the endpoint with the current channel */ |
704 | 0 | if(!UA_String_equal(&desc->securityPolicyUri, |
705 | 0 | &channel->securityPolicy->policyUri)) |
706 | 0 | continue; |
707 | | |
708 | | /* Select the UserTokenPolicy from the Endpoint */ |
709 | 0 | *utp = selectTokenPolicy(server, channel, session, |
710 | 0 | identityToken, desc, tokenSp); |
711 | 0 | if(*utp) { |
712 | | /* Match found */ |
713 | 0 | *ed = desc; |
714 | 0 | return; |
715 | 0 | } |
716 | 0 | } |
717 | 0 | } |
718 | | |
719 | | static UA_StatusCode |
720 | | decryptUserToken(UA_Server *server, UA_Session *session, |
721 | | UA_SecureChannel *channel, const UA_SecurityPolicy *sp, |
722 | 0 | const UA_String encryptionAlgorithm, UA_ByteString *encrypted) { |
723 | | /* If SecurityPolicy is None there shall be no EncryptionAlgorithm */ |
724 | 0 | if(sp->policyType == UA_SECURITYPOLICYTYPE_NONE) { |
725 | 0 | if(encryptionAlgorithm.length > 0) |
726 | 0 | return UA_STATUSCODE_BADIDENTITYTOKENINVALID; |
727 | 0 | if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE) { |
728 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, "ActivateSession: " |
729 | 0 | "Received an unencrypted UserToken. " |
730 | 0 | "Is the server misconfigured to allow that?"); |
731 | 0 | } |
732 | 0 | return UA_STATUSCODE_GOOD; |
733 | 0 | } |
734 | | |
735 | | /* Test if the correct encryption algorithm is used */ |
736 | 0 | if(!UA_String_equal(&encryptionAlgorithm, &sp->asymEncryptionAlgorithm.uri)) |
737 | 0 | return UA_STATUSCODE_BADIDENTITYTOKENINVALID; |
738 | | |
739 | | /* Encrypted password -- Create a temporary channel context. |
740 | | * TODO: We should not need a ChannelContext at all for asymmetric |
741 | | * decryption where the remote certificate is not used. */ |
742 | 0 | void *tempChannelContext = NULL; |
743 | 0 | UA_StatusCode res = sp->newChannelContext(sp, &sp->localCertificate, |
744 | 0 | &tempChannelContext); |
745 | 0 | if(res != UA_STATUSCODE_GOOD) { |
746 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
747 | 0 | "ActivateSession: Failed to create a context for " |
748 | 0 | "the SecurityPolicy %S", sp->policyUri); |
749 | 0 | return res; |
750 | 0 | } |
751 | | |
752 | 0 | UA_UInt32 secretLen = 0; |
753 | 0 | UA_ByteString secret, tokenNonce; |
754 | 0 | size_t tokenpos = 0; |
755 | 0 | size_t offset = 0; |
756 | 0 | UA_ByteString *sn = &session->serverNonce; |
757 | 0 | const UA_SecurityPolicyEncryptionAlgorithm *asymEnc = &sp->asymEncryptionAlgorithm; |
758 | |
|
759 | 0 | res = UA_STATUSCODE_BADIDENTITYTOKENINVALID; |
760 | | |
761 | | /* Decrypt the secret */ |
762 | 0 | if(UA_ByteString_copy(encrypted, &secret) != UA_STATUSCODE_GOOD || |
763 | 0 | asymEnc->decrypt(sp, tempChannelContext, &secret) != UA_STATUSCODE_GOOD) |
764 | 0 | goto cleanup; |
765 | | |
766 | | /* The secret starts with a UInt32 length for the content */ |
767 | 0 | if(UA_UInt32_decodeBinary(&secret, &offset, &secretLen) != UA_STATUSCODE_GOOD) |
768 | 0 | goto cleanup; |
769 | | |
770 | | /* The decrypted data must be large enough to include the Encrypted Token |
771 | | * Secret Format and the length field must indicate enough data to include |
772 | | * the server nonce. */ |
773 | 0 | if(secret.length < sizeof(UA_UInt32) + sn->length || |
774 | 0 | secret.length < sizeof(UA_UInt32) + secretLen || |
775 | 0 | secretLen < sn->length) |
776 | 0 | goto cleanup; |
777 | | |
778 | | /* If the Encrypted Token Secret contains padding, the padding must be |
779 | | * zeroes according to the 1.04.1 specification errata, chapter 3. */ |
780 | 0 | for(size_t i = sizeof(UA_UInt32) + secretLen; i < secret.length; i++) { |
781 | 0 | if(secret.data[i] != 0) |
782 | 0 | goto cleanup; |
783 | 0 | } |
784 | | |
785 | | /* The server nonce must match according to the 1.04.1 specification errata, |
786 | | * chapter 3. */ |
787 | 0 | tokenpos = sizeof(UA_UInt32) + secretLen - sn->length; |
788 | 0 | tokenNonce.length = sn->length; |
789 | 0 | tokenNonce.data = &secret.data[tokenpos]; |
790 | 0 | if(!UA_ByteString_equal(sn, &tokenNonce)) |
791 | 0 | goto cleanup; |
792 | | |
793 | | /* The password was decrypted successfully. Replace usertoken with the |
794 | | * decrypted password. The encryptionAlgorithm and policyId fields are left |
795 | | * in the UserToken as an indication for the AccessControl plugin that |
796 | | * evaluates the decrypted content. */ |
797 | 0 | memcpy(encrypted->data, |
798 | 0 | &secret.data[sizeof(UA_UInt32)], secretLen - sn->length); |
799 | 0 | encrypted->length = secretLen - sn->length; |
800 | 0 | res = UA_STATUSCODE_GOOD; |
801 | |
|
802 | 0 | cleanup: |
803 | 0 | UA_ByteString_clear(&secret); |
804 | | |
805 | | /* Remove the temporary channel context */ |
806 | 0 | sp->deleteChannelContext(sp, tempChannelContext); |
807 | |
|
808 | 0 | if(res != UA_STATUSCODE_GOOD) { |
809 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
810 | 0 | "ActivateSession: Failed to decrypt the " |
811 | 0 | "password with the StatusCode %s", |
812 | 0 | UA_StatusCode_name(res)); |
813 | 0 | } |
814 | 0 | return res; |
815 | 0 | } |
816 | | |
817 | | static UA_StatusCode |
818 | | checkActivateSessionX509(UA_Server *server, UA_Session *session, |
819 | | const UA_SecurityPolicy *sp, UA_X509IdentityToken* token, |
820 | 0 | const UA_SignatureData *tokenSignature) { |
821 | | /* The SecurityPolicy must not be None for the signature */ |
822 | 0 | if(sp->policyType == UA_SECURITYPOLICYTYPE_NONE) |
823 | 0 | return UA_STATUSCODE_BADIDENTITYTOKENINVALID; |
824 | | |
825 | | /* We need a channel context with the user certificate in order to reuse |
826 | | * the signature checking code. */ |
827 | 0 | void *tempChannelContext; |
828 | 0 | UA_StatusCode res = sp->newChannelContext(sp, &token->certificateData, |
829 | 0 | &tempChannelContext); |
830 | 0 | if(res != UA_STATUSCODE_GOOD) { |
831 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
832 | 0 | "ActivateSession: Failed to create a context " |
833 | 0 | "for the SecurityPolicy %S", sp->policyUri); |
834 | 0 | return res; |
835 | 0 | } |
836 | | |
837 | | /* Check the user token signature */ |
838 | 0 | res = checkCertificateSignature(server, sp, tempChannelContext, |
839 | 0 | &session->serverNonce, tokenSignature, true); |
840 | 0 | if(res != UA_STATUSCODE_GOOD) { |
841 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
842 | 0 | "ActivateSession: User token signature check " |
843 | 0 | "failed with StatusCode %s", UA_StatusCode_name(res)); |
844 | 0 | } |
845 | | |
846 | | /* Delete the temporary channel context */ |
847 | 0 | sp->deleteChannelContext(sp, tempChannelContext); |
848 | 0 | return res; |
849 | 0 | } |
850 | | |
851 | | /* TODO: Check all of the following: The Server shall verify that the |
852 | | * Certificate the Client used to create the new SecureChannel is the same as |
853 | | * the Certificate used to create the original SecureChannel. In addition, the |
854 | | * Server shall verify that the Client supplied a UserIdentityToken that is |
855 | | * identical to the token currently associated with the Session. Once the Server |
856 | | * accepts the new SecureChannel it shall reject requests sent via the old |
857 | | * SecureChannel. */ |
858 | | |
859 | | #define UA_SESSION_REJECT \ |
860 | 0 | do { \ |
861 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; \ |
862 | 0 | return; \ |
863 | 0 | } while(0) |
864 | | |
865 | | #define UA_SECURITY_REJECT \ |
866 | 0 | do { \ |
867 | 0 | server->serverDiagnosticsSummary.securityRejectedSessionCount++; \ |
868 | 0 | server->serverDiagnosticsSummary.rejectedSessionCount++; \ |
869 | 0 | return; \ |
870 | 0 | } while(0) |
871 | | |
872 | | void |
873 | | Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel, |
874 | | const UA_ActivateSessionRequest *req, |
875 | 0 | UA_ActivateSessionResponse *resp) { |
876 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
877 | | |
878 | | /* Get the session */ |
879 | 0 | UA_Session *session = |
880 | 0 | getSessionByToken(server, &req->requestHeader.authenticationToken); |
881 | 0 | if(!session) { |
882 | 0 | UA_LOG_WARNING_CHANNEL(server->config.logging, channel, |
883 | 0 | "ActivateSession: Session not found"); |
884 | 0 | resp->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; |
885 | 0 | UA_SESSION_REJECT; |
886 | 0 | } |
887 | | |
888 | | /* Part 4, §5.6.3: When the ActivateSession Service is called for the |
889 | | * first time then the Server shall reject the request if the |
890 | | * SecureChannel is not same as the one associated with the |
891 | | * CreateSession request. Subsequent calls to ActivateSession may be |
892 | | * associated with different SecureChannels. */ |
893 | 0 | if(!session->activated && session->channel != channel) { |
894 | 0 | UA_LOG_WARNING_CHANNEL(server->config.logging, channel, |
895 | 0 | "ActivateSession: The Session has to be initially " |
896 | 0 | "activated on the SecureChannel that created it"); |
897 | 0 | resp->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; |
898 | 0 | UA_SESSION_REJECT; |
899 | 0 | } |
900 | | |
901 | | /* Has the session timed out? */ |
902 | 0 | UA_EventLoop *el = server->config.eventLoop; |
903 | 0 | UA_DateTime nowMonotonic = el->dateTime_nowMonotonic(el); |
904 | 0 | if(session->validTill < nowMonotonic) { |
905 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
906 | 0 | "ActivateSession: The Session has timed out"); |
907 | 0 | resp->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; |
908 | 0 | UA_SESSION_REJECT; |
909 | 0 | } |
910 | | |
911 | | /* Check the client signature */ |
912 | 0 | if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || |
913 | 0 | channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { |
914 | 0 | resp->responseHeader.serviceResult = |
915 | 0 | checkCertificateSignature(server, channel->securityPolicy, |
916 | 0 | channel->channelContext, |
917 | 0 | &session->serverNonce, |
918 | 0 | &req->clientSignature, false); |
919 | 0 | if(resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
920 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
921 | 0 | "ActivateSession: Client signature check failed " |
922 | 0 | "with StatusCode %s", |
923 | 0 | UA_StatusCode_name(resp->responseHeader.serviceResult)); |
924 | 0 | UA_SECURITY_REJECT; |
925 | 0 | } |
926 | 0 | } |
927 | | |
928 | | /* Find the matching Endpoint with UserTokenPolicy. |
929 | | * Also sets the SecurityPolicy used to encrypt the token. */ |
930 | 0 | const UA_EndpointDescription *ed = NULL; |
931 | 0 | const UA_UserTokenPolicy *utp = NULL; |
932 | 0 | const UA_SecurityPolicy *tokenSp = NULL; |
933 | 0 | selectEndpointAndTokenPolicy(server, channel, session, |
934 | 0 | &req->userIdentityToken, |
935 | 0 | &ed, &utp, &tokenSp); |
936 | 0 | if(!ed || !tokenSp) { |
937 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
938 | 0 | "ActivateSession: Requested Endpoint/UserTokenPolicy " |
939 | 0 | "not available"); |
940 | 0 | resp->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID; |
941 | 0 | UA_SESSION_REJECT; |
942 | 0 | } |
943 | | |
944 | | /* Decrypt (or validate the signature) of the UserToken. The DataType of the |
945 | | * UserToken was already checked in selectEndpointAndTokenPolicy */ |
946 | 0 | if(utp->tokenType == UA_USERTOKENTYPE_USERNAME) { |
947 | | /* If it is a UserNameIdentityToken, the password may be encrypted */ |
948 | 0 | UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken *) |
949 | 0 | req->userIdentityToken.content.decoded.data; |
950 | | /* Differentiate between ECC policy decrpytion and RSA decryption. |
951 | | * With ECC policies, the password is EccEncryptedSecret */ |
952 | 0 | if(tokenSp->policyType == UA_SECURITYPOLICYTYPE_ECC) { |
953 | 0 | resp->responseHeader.serviceResult = |
954 | 0 | decryptUserTokenEcc(server->config.logging, session->serverNonce, |
955 | 0 | tokenSp, userToken->encryptionAlgorithm, |
956 | 0 | &userToken->password); |
957 | 0 | } else { |
958 | 0 | resp->responseHeader.serviceResult = |
959 | 0 | decryptUserToken(server, session, channel, tokenSp, |
960 | 0 | userToken->encryptionAlgorithm, &userToken->password); |
961 | 0 | } |
962 | 0 | } else if(utp->tokenType == UA_USERTOKENTYPE_CERTIFICATE) { |
963 | | /* If it is a X509IdentityToken, check the userTokenSignature. Note this |
964 | | * only validates that the user has the corresponding private key for |
965 | | * the given user certificate. Checking whether the user certificate is |
966 | | * trusted has to be implemented in the access control plugin. The |
967 | | * entire token is forwarded in the call to ActivateSession. */ |
968 | 0 | UA_X509IdentityToken* x509token = (UA_X509IdentityToken*) |
969 | 0 | req->userIdentityToken.content.decoded.data; |
970 | 0 | resp->responseHeader.serviceResult = |
971 | 0 | checkActivateSessionX509(server, session, tokenSp, |
972 | 0 | x509token, &req->userTokenSignature); |
973 | 0 | } else if(utp->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN) { |
974 | | /* IssuedTokens are encrypted */ |
975 | 0 | UA_IssuedIdentityToken *issuedToken = (UA_IssuedIdentityToken*) |
976 | 0 | req->userIdentityToken.content.decoded.data; |
977 | 0 | resp->responseHeader.serviceResult = decryptUserToken( |
978 | 0 | server, session, channel, tokenSp, issuedToken->encryptionAlgorithm, |
979 | 0 | &issuedToken->tokenData); |
980 | 0 | } /* else Anonymous */ |
981 | 0 | if(resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) |
982 | 0 | UA_SECURITY_REJECT; |
983 | | |
984 | | /* Callback into userland access control */ |
985 | 0 | resp->responseHeader.serviceResult = server->config.accessControl. |
986 | 0 | activateSession(server, &server->config.accessControl, ed, |
987 | 0 | &channel->remoteCertificate, &session->sessionId, |
988 | 0 | &req->userIdentityToken, &session->context); |
989 | 0 | if(resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
990 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
991 | 0 | "ActivateSession: The AccessControl " |
992 | 0 | "plugin denied the activation with the StatusCode %s", |
993 | 0 | UA_StatusCode_name(resp->responseHeader.serviceResult)); |
994 | 0 | UA_SECURITY_REJECT; |
995 | 0 | } |
996 | | |
997 | | /* Attach the session to the currently used channel if the session isn't |
998 | | * attached to a channel or if the session is activated on a different |
999 | | * channel than it is attached to. */ |
1000 | 0 | if(!session->channel || session->channel != channel) { |
1001 | | /* Attach the new SecureChannel, the old channel will be detached if present */ |
1002 | 0 | UA_Session_attachToSecureChannel(server, session, channel); |
1003 | 0 | UA_LOG_INFO_SESSION(server->config.logging, session, |
1004 | 0 | "ActivateSession: Session attached to new channel"); |
1005 | 0 | } |
1006 | | |
1007 | | /* Generate a new session nonce for the next time ActivateSession is called */ |
1008 | 0 | resp->responseHeader.serviceResult = UA_Session_generateNonce(session); |
1009 | 0 | resp->responseHeader.serviceResult |= |
1010 | 0 | UA_ByteString_copy(&session->serverNonce, &resp->serverNonce); |
1011 | 0 | if(resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
1012 | 0 | UA_Session_detachFromSecureChannel(server, session); |
1013 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
1014 | 0 | "ActivateSession: Could not generate the server nonce"); |
1015 | 0 | UA_SESSION_REJECT; |
1016 | 0 | } |
1017 | | |
1018 | | /* Set the Locale */ |
1019 | 0 | if(req->localeIdsSize > 0) { |
1020 | | /* Part 4, §5.6.3.2: This parameter only needs to be specified during |
1021 | | * the first call to ActivateSession during a single application |
1022 | | * Session. If it is not specified the Server shall keep using the |
1023 | | * current localeIds for the Session. */ |
1024 | 0 | UA_String *tmpLocaleIds; |
1025 | 0 | resp->responseHeader.serviceResult |= |
1026 | 0 | UA_Array_copy(req->localeIds, req->localeIdsSize, |
1027 | 0 | (void**)&tmpLocaleIds, &UA_TYPES[UA_TYPES_STRING]); |
1028 | 0 | if(resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
1029 | 0 | UA_Session_detachFromSecureChannel(server, session); |
1030 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
1031 | 0 | "ActivateSession: Could not store the " |
1032 | 0 | "Session LocaleIds"); |
1033 | 0 | UA_SESSION_REJECT; |
1034 | 0 | } |
1035 | 0 | UA_Array_delete(session->localeIds, session->localeIdsSize, |
1036 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
1037 | 0 | session->localeIds = tmpLocaleIds; |
1038 | 0 | session->localeIdsSize = req->localeIdsSize; |
1039 | 0 | } |
1040 | | |
1041 | | /* Update the Session lifetime */ |
1042 | 0 | UA_DateTime now = el->dateTime_now(el); |
1043 | 0 | nowMonotonic = el->dateTime_nowMonotonic(el); |
1044 | 0 | UA_Session_updateLifetime(session, now, nowMonotonic); |
1045 | | |
1046 | | /* If ECC policy, create the new ephemeral key to be returned in the |
1047 | | * ActivateSession response */ |
1048 | 0 | const UA_SecurityPolicy *sp = channel->securityPolicy; |
1049 | 0 | if(sp && sp->policyType == UA_SECURITYPOLICYTYPE_ECC) { |
1050 | 0 | resp->responseHeader.serviceResult = |
1051 | 0 | addEphemeralKeyAdditionalHeader(server, sp, channel->channelContext, |
1052 | 0 | &resp->responseHeader.additionalHeader); |
1053 | 0 | if(resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) |
1054 | 0 | UA_SECURITY_REJECT; |
1055 | 0 | UA_LOG_DEBUG(server->config.logging, UA_LOGCATEGORY_SESSION, |
1056 | 0 | "[ActivateSession] Ephemeral Key created"); |
1057 | 0 | } |
1058 | | |
1059 | | /* Activate the session */ |
1060 | 0 | if(!session->activated) { |
1061 | 0 | session->activated = true; |
1062 | 0 | server->activeSessionCount++; |
1063 | 0 | server->serverDiagnosticsSummary.cumulatedSessionCount++; |
1064 | 0 | } |
1065 | | |
1066 | | /* Store the ClientUserId. tokenType can be NULL for the anonymous user. */ |
1067 | 0 | UA_String_clear(&session->clientUserIdOfSession); |
1068 | 0 | const UA_DataType *tokenType = req->userIdentityToken.content.decoded.type; |
1069 | 0 | if(tokenType == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) { |
1070 | 0 | const UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken*) |
1071 | 0 | req->userIdentityToken.content.decoded.data; |
1072 | 0 | UA_String_copy(&userToken->userName, &session->clientUserIdOfSession); |
1073 | 0 | } else if(tokenType == &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) { |
1074 | 0 | UA_X509IdentityToken* userCertToken = (UA_X509IdentityToken*) |
1075 | 0 | req->userIdentityToken.content.decoded.data; |
1076 | 0 | UA_CertificateUtils_getSubjectName(&session->clientUserIdOfSession, |
1077 | 0 | &userCertToken->certificateData); |
1078 | 0 | } else { |
1079 | | /* TODO: Handle issued token */ |
1080 | 0 | } |
1081 | |
|
1082 | 0 | #ifdef UA_ENABLE_DIAGNOSTICS |
1083 | | /* Add the ClientUserId to the diagnostics history. Ignoring errors in _appendCopy. */ |
1084 | 0 | UA_SessionSecurityDiagnosticsDataType *ssd = &session->securityDiagnostics; |
1085 | 0 | UA_Array_appendCopy((void**)&ssd->clientUserIdHistory, |
1086 | 0 | &ssd->clientUserIdHistorySize, |
1087 | 0 | &ssd->clientUserIdOfSession, |
1088 | 0 | &UA_TYPES[UA_TYPES_STRING]); |
1089 | | |
1090 | | /* Store the auth mechanism */ |
1091 | 0 | UA_String_clear(&ssd->authenticationMechanism); |
1092 | 0 | switch(utp->tokenType) { |
1093 | 0 | case UA_USERTOKENTYPE_ANONYMOUS: |
1094 | 0 | ssd->authenticationMechanism = UA_STRING_ALLOC("Anonymous"); break; |
1095 | 0 | case UA_USERTOKENTYPE_USERNAME: |
1096 | 0 | ssd->authenticationMechanism = UA_STRING_ALLOC("UserName"); break; |
1097 | 0 | case UA_USERTOKENTYPE_CERTIFICATE: |
1098 | 0 | ssd->authenticationMechanism = UA_STRING_ALLOC("Certificate"); break; |
1099 | 0 | case UA_USERTOKENTYPE_ISSUEDTOKEN: |
1100 | 0 | ssd->authenticationMechanism = UA_STRING_ALLOC("IssuedToken"); break; |
1101 | 0 | default: break; |
1102 | 0 | } |
1103 | 0 | #endif |
1104 | | |
1105 | | /* Notify the application */ |
1106 | 0 | notifySession(server, session, UA_APPLICATIONNOTIFICATIONTYPE_SESSION_ACTIVATED); |
1107 | | |
1108 | | /* Log the user for which the Session was activated */ |
1109 | 0 | UA_LOG_INFO_SESSION(server->config.logging, session, |
1110 | 0 | "ActivateSession: Session activated with ClientUserId \"%S\"", |
1111 | 0 | session->clientUserIdOfSession); |
1112 | 0 | } |
1113 | | |
1114 | | void |
1115 | | Service_CloseSession(UA_Server *server, UA_SecureChannel *channel, |
1116 | | const UA_CloseSessionRequest *request, |
1117 | 0 | UA_CloseSessionResponse *response) { |
1118 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
1119 | | |
1120 | | /* Part 4, 5.6.4: When the CloseSession Service is called before the Session |
1121 | | * is successfully activated, the Server shall reject the request if the |
1122 | | * SecureChannel is not the same as the one associated with the |
1123 | | * CreateSession request. |
1124 | | * |
1125 | | * A non-activated Session is already bound to the SecureChannel that |
1126 | | * created the Session. */ |
1127 | 0 | UA_Session *session = NULL; |
1128 | 0 | response->responseHeader.serviceResult = |
1129 | 0 | getBoundSession(server, channel, &request->requestHeader.authenticationToken, &session); |
1130 | 0 | if(!session && response->responseHeader.serviceResult == UA_STATUSCODE_GOOD) |
1131 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; |
1132 | 0 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
1133 | 0 | UA_LOG_WARNING_CHANNEL(server->config.logging, channel, |
1134 | 0 | "CloseSession: No Session activated to the SecureChannel"); |
1135 | 0 | return; |
1136 | 0 | } |
1137 | | |
1138 | 0 | UA_assert(session); /* Assured by the previous section */ |
1139 | 0 | UA_LOG_INFO_SESSION(server->config.logging, session, "Closing the Session"); |
1140 | |
|
1141 | 0 | #ifdef UA_ENABLE_SUBSCRIPTIONS |
1142 | | /* If Subscriptions are not deleted, detach them from the Session */ |
1143 | 0 | if(!request->deleteSubscriptions) { |
1144 | 0 | UA_Subscription *sub, *sub_tmp; |
1145 | 0 | TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, sub_tmp) { |
1146 | 0 | UA_LOG_INFO_SUBSCRIPTION(server->config.logging, sub, |
1147 | 0 | "Detaching the Subscription from the Session"); |
1148 | 0 | UA_Session_detachSubscription(server, session, sub, true); |
1149 | 0 | } |
1150 | 0 | } |
1151 | 0 | #endif |
1152 | | |
1153 | | /* Remove the sesison */ |
1154 | 0 | UA_Session_remove(server, session, UA_SHUTDOWNREASON_CLOSE); |
1155 | 0 | } |
1156 | | |
1157 | | UA_Boolean |
1158 | | Service_Cancel(UA_Server *server, UA_Session *session, |
1159 | 0 | const UA_CancelRequest *request, UA_CancelResponse *response) { |
1160 | | /* If multithreading is disabled, then there are no async services. If all |
1161 | | * services are answered "right away", then there are no services that can |
1162 | | * be cancelled. */ |
1163 | 0 | response->cancelCount = UA_AsyncManager_cancel(server, session, request->requestHandle); |
1164 | | |
1165 | | /* Publish requests for Subscriptions are stored separately */ |
1166 | 0 | #ifdef UA_ENABLE_SUBSCRIPTIONS |
1167 | 0 | UA_PublishResponseEntry *pre, *pre_tmp; |
1168 | 0 | UA_PublishResponseEntry *prev = NULL; |
1169 | 0 | SIMPLEQ_FOREACH_SAFE(pre, &session->responseQueue, listEntry, pre_tmp) { |
1170 | | /* Skip entry and set as the previous entry that is kept in the list */ |
1171 | 0 | if(pre->response.responseHeader.requestHandle != request->requestHandle) { |
1172 | 0 | prev = pre; |
1173 | 0 | continue; |
1174 | 0 | } |
1175 | | |
1176 | | /* Dequeue */ |
1177 | 0 | if(prev) |
1178 | 0 | SIMPLEQ_REMOVE_AFTER(&session->responseQueue, prev, listEntry); |
1179 | 0 | else |
1180 | 0 | SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry); |
1181 | 0 | session->responseQueueSize--; |
1182 | | |
1183 | | /* Send response and clean up */ |
1184 | 0 | response->responseHeader.serviceResult = UA_STATUSCODE_BADREQUESTCANCELLEDBYCLIENT; |
1185 | 0 | sendResponse(server, session->channel, pre->requestId, (UA_Response *)response, |
1186 | 0 | &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]); |
1187 | 0 | UA_PublishResponse_clear(&pre->response); |
1188 | 0 | UA_free(pre); |
1189 | | |
1190 | | /* Increase the CancelCount */ |
1191 | 0 | response->cancelCount++; |
1192 | 0 | } |
1193 | 0 | #endif |
1194 | |
|
1195 | | return true; |
1196 | 0 | } |