Coverage Report

Created: 2026-08-13 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/server/ua_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 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
6
 *    Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA
7
 *    Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green)
8
 */
9
10
#include "ua_session.h"
11
#include "open62541/types.h"
12
#include "ua_server_internal.h"
13
#ifdef UA_ENABLE_SUBSCRIPTIONS
14
#include "ua_subscription.h"
15
#endif
16
17
28.3k
void UA_Session_init(UA_Session *session) {
18
28.3k
    memset(session, 0, sizeof(UA_Session));
19
28.3k
    TAILQ_INIT(&session->continuationPoints);
20
28.3k
#ifdef UA_ENABLE_SUBSCRIPTIONS
21
28.3k
    SIMPLEQ_INIT(&session->responseQueue);
22
28.3k
    TAILQ_INIT(&session->subscriptions);
23
28.3k
#endif
24
28.3k
}
25
26
27.9k
void UA_Session_clear(UA_Session *session, UA_Server* server) {
27
27.9k
    UA_LOCK_ASSERT(&server->serviceMutex);
28
29
    /* Remove all Subscriptions. This may send out remaining publish
30
     * responses. */
31
27.9k
#ifdef UA_ENABLE_SUBSCRIPTIONS
32
27.9k
    UA_Subscription *sub, *tempsub;
33
27.9k
    TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, tempsub) {
34
16.4k
        UA_Subscription_delete(server, sub);
35
16.4k
    }
36
27.9k
#endif
37
38
27.9k
#ifdef UA_ENABLE_DIAGNOSTICS
39
27.9k
    deleteNode(server, session->sessionId, true);
40
27.9k
#endif
41
42
27.9k
    UA_Session_detachFromSecureChannel(server, session);
43
27.9k
    UA_ApplicationDescription_clear(&session->clientDescription);
44
27.9k
    UA_ByteString_clear(&session->clientCertificate);
45
27.9k
    UA_NodeId_clear(&session->authenticationToken);
46
27.9k
    UA_String_clear(&session->clientUserIdOfSession);
47
27.9k
    UA_NodeId_clear(&session->sessionId);
48
27.9k
    UA_String_clear(&session->sessionName);
49
27.9k
    UA_ByteString_clear(&session->serverNonce);
50
27.9k
    UA_ByteString_clear(&session->clientNonce);
51
27.9k
    ContinuationPointQueue_clear(&session->continuationPoints);
52
27.9k
    session->continuationPointsSize = 0;
53
54
27.9k
    UA_KeyValueMap_clear(&session->attributes);
55
56
27.9k
    UA_Array_delete(session->localeIds, session->localeIdsSize,
57
27.9k
                    &UA_TYPES[UA_TYPES_STRING]);
58
27.9k
    session->localeIds = NULL;
59
27.9k
    session->localeIdsSize = 0;
60
61
27.9k
#ifdef UA_ENABLE_DIAGNOSTICS
62
27.9k
    UA_SessionDiagnosticsDataType_clear(&session->diagnostics);
63
27.9k
    UA_SessionSecurityDiagnosticsDataType_clear(&session->securityDiagnostics);
64
27.9k
#endif
65
66
27.9k
    if(session->sessionSp && session->sessionSpContext) {
67
0
        session->sessionSp->
68
0
            deleteChannelContext(session->sessionSp, session->sessionSpContext);
69
0
        session->sessionSp = NULL;
70
0
        session->sessionSpContext = NULL;
71
0
    }
72
27.9k
}
73
74
void
75
UA_Session_attachToSecureChannel(UA_Server *server, UA_Session *session,
76
7.76k
                                 UA_SecureChannel *channel) {
77
    /* Ensure the Session is not attached to another SecureChannel */
78
7.76k
    UA_Session_detachFromSecureChannel(server, session);
79
80
    /* Add to singly-linked list */
81
7.76k
    session->next = channel->sessions;
82
7.76k
    channel->sessions = session;
83
84
    /* Add backpointer */
85
7.76k
    session->channel = channel;
86
7.76k
}
87
88
void
89
51.1k
UA_Session_detachFromSecureChannel(UA_Server *server, UA_Session *session) {
90
    /* Clean up the response queue. Their RequestId is bound to the
91
     * SecureChannel so they cannot be reused. */
92
51.1k
#ifdef UA_ENABLE_SUBSCRIPTIONS
93
51.1k
    UA_PublishResponseEntry *pre;
94
51.1k
    while((pre = UA_Session_dequeuePublishReq(session))) {
95
0
        UA_PublishResponse_clear(&pre->response);
96
0
        UA_free(pre);
97
0
    }
98
51.1k
#endif
99
100
    /* Remove from singly-linked list */
101
51.1k
    UA_SecureChannel *channel = session->channel;
102
51.1k
    if(!channel)
103
43.3k
        return;
104
105
7.76k
    if(channel->sessions == session) {
106
7.76k
        channel->sessions = session->next;
107
7.76k
    } else {
108
0
        UA_Session *elm =  channel->sessions;
109
0
        while(elm->next != session)
110
0
            elm = elm->next;
111
0
        elm->next = session->next;
112
0
    }
113
114
    /* Reset the backpointer */
115
7.76k
    session->channel = NULL;
116
117
    /* Notify the application */
118
7.76k
    notifySession(server, session,
119
7.76k
                  UA_APPLICATIONNOTIFICATIONTYPE_SESSION_DEACTIVATED);
120
7.76k
}
121
122
UA_StatusCode
123
9.76k
UA_Session_generateNonce(UA_Session *session) {
124
9.76k
    UA_SecureChannel *channel = session->channel;
125
9.76k
    if(!channel || !channel->securityPolicy)
126
0
        return UA_STATUSCODE_BADINTERNALERROR;
127
128
    /* The nonce needs to be between 32 and 128 byte.
129
     * The #Nonce SecurityPolicy might not do that. */
130
9.76k
    UA_SecurityPolicy *sp = channel->securityPolicy;
131
9.76k
    void *spC = channel->channelContext;
132
9.76k
    if(session->sessionSp && session->sessionSpContext) {
133
0
        sp = session->sessionSp;
134
0
        spC = session->sessionSpContext;
135
0
    }
136
137
    /* The session ServerNonce is a 32-byte application nonce. This is
138
     * independent of the SecurityPolicy's SecureChannel nonce length (for ECC
139
     * policies that is the 64-byte ephemeral key, which is NOT the session
140
     * nonce). */
141
9.76k
    size_t nonceLength = 32;
142
143
    /* Is the length of the previous nonce correct? */
144
9.76k
    if(session->serverNonce.length != nonceLength) {
145
7.76k
        UA_ByteString_clear(&session->serverNonce);
146
7.76k
        UA_StatusCode res =
147
7.76k
            UA_ByteString_allocBuffer(&session->serverNonce, nonceLength);
148
7.76k
        if(res != UA_STATUSCODE_GOOD)
149
0
            return res;
150
7.76k
    }
151
152
    /* Generate plain random data. Ensure data[0] is not the 'e' of the "eph"
153
     * trigger that some ECC policies use to produce an ephemeral key. */
154
9.76k
    session->serverNonce.data[0] = 0;
155
9.76k
    return sp->generateNonce(sp, spC, &session->serverNonce);
156
9.76k
}
157
158
void
159
UA_Session_updateLifetime(UA_Session *session, UA_DateTime now,
160
11.1k
                          UA_DateTime nowMonotonic) {
161
11.1k
    session->validTill = nowMonotonic +
162
11.1k
        (UA_DateTime)(session->timeout * UA_DATETIME_MSEC);
163
11.1k
#ifdef UA_ENABLE_DIAGNOSTICS
164
11.1k
    session->diagnostics.clientLastContactTime = now;
165
11.1k
#endif
166
11.1k
}
167
168
#ifdef UA_ENABLE_SUBSCRIPTIONS
169
170
void
171
16.4k
UA_Session_attachSubscription(UA_Session *session, UA_Subscription *sub) {
172
    /* Attach to the session */
173
16.4k
    sub->session = session;
174
175
    /* Increase the count */
176
16.4k
    session->subscriptionsSize++;
177
178
    /* Increase the number of outstanding retransmissions */
179
16.4k
    session->totalRetransmissionQueueSize += sub->retransmissionQueueSize;
180
181
    /* Insert at the end of the subscriptions of the same priority / just before
182
     * the subscriptions with the next lower priority. */
183
16.4k
    UA_Subscription *after = NULL;
184
16.4k
    TAILQ_FOREACH(after, &session->subscriptions, sessionListEntry) {
185
0
        if(after->priority < sub->priority) {
186
0
            TAILQ_INSERT_BEFORE(after, sub, sessionListEntry);
187
0
            return;
188
0
        }
189
0
    }
190
16.4k
    TAILQ_INSERT_TAIL(&session->subscriptions, sub, sessionListEntry);
191
16.4k
}
192
193
void
194
UA_Session_detachSubscription(UA_Server *server, UA_Session *session,
195
16.4k
                              UA_Subscription *sub, UA_Boolean releasePublishResponses) {
196
    /* Detach from the session */
197
16.4k
    sub->session = NULL;
198
16.4k
    TAILQ_REMOVE(&session->subscriptions, sub, sessionListEntry);
199
200
    /* Reduce the count */
201
16.4k
    UA_assert(session->subscriptionsSize > 0);
202
16.4k
    session->subscriptionsSize--;
203
204
    /* Reduce the number of outstanding retransmissions */
205
16.4k
    session->totalRetransmissionQueueSize -= sub->retransmissionQueueSize;
206
207
    /* Send remaining publish responses if the last subscription was removed */
208
16.4k
    if(!releasePublishResponses || !TAILQ_EMPTY(&session->subscriptions))
209
0
        return;
210
16.4k
    UA_PublishResponseEntry *pre;
211
16.4k
    while((pre = UA_Session_dequeuePublishReq(session))) {
212
0
        UA_PublishResponse *response = &pre->response;
213
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
214
0
        sendResponse(server, session->channel, pre->requestId,
215
0
                     (UA_Response*)response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
216
0
        UA_PublishResponse_clear(response);
217
0
        UA_free(pre);
218
0
    }
219
16.4k
}
220
221
UA_Subscription *
222
1.91k
UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) {
223
1.91k
    UA_Subscription *sub;
224
1.91k
    TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) {
225
        /* Prevent lookup of subscriptions that are to be deleted with a statuschange */
226
0
        if(sub->statusChange != UA_STATUSCODE_GOOD)
227
0
            continue;
228
0
        if(sub->subscriptionId == subscriptionId)
229
0
            break;
230
0
    }
231
1.91k
    return sub;
232
1.91k
}
233
234
UA_Subscription *
235
52
getSubscriptionById(UA_Server *server, UA_UInt32 subscriptionId) {
236
52
    UA_Subscription *sub;
237
52
    LIST_FOREACH(sub, &server->subscriptions, serverListEntry) {
238
        /* Prevent lookup of subscriptions that are to be deleted with a statuschange */
239
0
        if(sub->statusChange != UA_STATUSCODE_GOOD)
240
0
            continue;
241
0
        if(sub->subscriptionId == subscriptionId)
242
0
            break;
243
0
    }
244
52
    return sub;
245
52
}
246
247
UA_PublishResponseEntry*
248
75.2k
UA_Session_dequeuePublishReq(UA_Session *session) {
249
75.2k
    UA_PublishResponseEntry *entry = SIMPLEQ_FIRST(&session->responseQueue);
250
75.2k
    if(entry) {
251
0
        SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
252
0
        session->responseQueueSize--;
253
0
    }
254
75.2k
    return entry;
255
75.2k
}
256
257
void
258
UA_Session_queuePublishReq(UA_Session *session, UA_PublishResponseEntry* entry,
259
0
                           UA_Boolean head) {
260
0
    if(!head)
261
0
        SIMPLEQ_INSERT_TAIL(&session->responseQueue, entry, listEntry);
262
0
    else
263
0
        SIMPLEQ_INSERT_HEAD(&session->responseQueue, entry, listEntry);
264
0
    session->responseQueueSize++;
265
0
}
266
267
#endif
268
269
/* Session Handling */
270
271
UA_StatusCode
272
0
UA_Server_closeSession(UA_Server *server, const UA_NodeId *sessionId) {
273
0
    lockServer(server);
274
0
    UA_Session *session = getSessionById(server, sessionId);
275
0
    if(!session) {
276
0
        unlockServer(server);
277
0
        return UA_STATUSCODE_BADSESSIONIDINVALID;
278
0
    }
279
0
    UA_Session_remove(server, session, UA_SHUTDOWNREASON_CLOSE);
280
0
    unlockServer(server);
281
0
    return UA_STATUSCODE_GOOD;
282
0
}
283
284
/* Session Attributes */
285
286
10.0k
#define UA_PROTECTEDATTRIBUTESSIZE 4
287
static const UA_QualifiedName protectedAttributes[UA_PROTECTEDATTRIBUTESSIZE] = {
288
    {0, UA_STRING_STATIC("localeIds")},
289
    {0, UA_STRING_STATIC("clientDescription")},
290
    {0, UA_STRING_STATIC("sessionName")},
291
    {0, UA_STRING_STATIC("clientUserId")}
292
};
293
294
static UA_Boolean
295
2.00k
protectedAttribute(const UA_QualifiedName key) {
296
10.0k
    for(size_t i = 0; i < UA_PROTECTEDATTRIBUTESSIZE; i++) {
297
8.00k
        if(UA_QualifiedName_equal(&key, &protectedAttributes[i]))
298
0
            return true;
299
8.00k
    }
300
2.00k
    return false;
301
2.00k
}
302
303
UA_StatusCode
304
UA_Server_setSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
305
2.00k
                              const UA_QualifiedName key, const UA_Variant *value) {
306
2.00k
    if(protectedAttribute(key))
307
0
        return UA_STATUSCODE_BADNOTWRITABLE;
308
2.00k
    if(!sessionId)
309
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
310
2.00k
    lockServer(server);
311
2.00k
    UA_Session *session = getSessionById(server, sessionId);
312
2.00k
    UA_StatusCode res = UA_STATUSCODE_BADSESSIONIDINVALID;
313
2.00k
    if(session)
314
2.00k
        res = UA_KeyValueMap_set(&session->attributes, key, value);
315
2.00k
    unlockServer(server);
316
2.00k
    return res;
317
2.00k
}
318
319
UA_StatusCode
320
UA_Server_deleteSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
321
0
                                 const UA_QualifiedName key) {
322
0
    if(protectedAttribute(key))
323
0
        return UA_STATUSCODE_BADNOTWRITABLE;
324
0
    if(!sessionId)
325
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
326
0
    lockServer(server);
327
0
    UA_Session *session = getSessionById(server, sessionId);
328
0
    if(!session) {
329
0
        unlockServer(server);
330
0
        return UA_STATUSCODE_BADSESSIONIDINVALID;
331
0
    }
332
0
    UA_StatusCode res = UA_KeyValueMap_remove(&session->attributes, key);
333
0
    unlockServer(server);
334
0
    return res;
335
0
}
336
337
static UA_StatusCode
338
getSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
339
                    const UA_QualifiedName key, UA_Variant *outValue,
340
0
                    UA_Boolean copy) {
341
0
    if(!outValue)
342
0
        return UA_STATUSCODE_BADINTERNALERROR;
343
0
    if(!sessionId)
344
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
345
346
0
    UA_Session *session = getSessionById(server, sessionId);
347
0
    if(!session)
348
0
        return UA_STATUSCODE_BADSESSIONIDINVALID;
349
350
0
    const UA_Variant *attr;
351
0
    UA_Variant localAttr;
352
353
0
    if(UA_QualifiedName_equal(&key, &protectedAttributes[0])) {
354
        /* Return LocaleIds */
355
0
        UA_Variant_setArray(&localAttr, session->localeIds,
356
0
                            session->localeIdsSize, &UA_TYPES[UA_TYPES_STRING]);
357
0
        attr = &localAttr;
358
0
    } else if(UA_QualifiedName_equal(&key, &protectedAttributes[1])) {
359
        /* Return client description */
360
0
        UA_Variant_setScalar(&localAttr, &session->clientDescription,
361
0
                             &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
362
0
        attr = &localAttr;
363
0
    } else if(UA_QualifiedName_equal(&key, &protectedAttributes[2])) {
364
        /* Return session name */
365
0
        UA_Variant_setScalar(&localAttr, &session->sessionName,
366
0
                             &UA_TYPES[UA_TYPES_STRING]);
367
0
        attr = &localAttr;
368
0
    } else if(UA_QualifiedName_equal(&key, &protectedAttributes[3])) {
369
        /* Return client user id */
370
0
        UA_Variant_setScalar(&localAttr, &session->clientUserIdOfSession,
371
0
                             &UA_TYPES[UA_TYPES_STRING]);
372
0
        attr = &localAttr;
373
0
    } else {
374
        /* Get from the actual key-value list */
375
0
        attr = UA_KeyValueMap_get(&session->attributes, key);
376
0
        if(!attr)
377
0
            return UA_STATUSCODE_BADNOTFOUND;
378
0
    }
379
380
0
    if(copy)
381
0
        return UA_Variant_copy(attr, outValue);
382
383
0
    *outValue = *attr;
384
0
    outValue->storageType = UA_VARIANT_DATA_NODELETE;
385
0
    return UA_STATUSCODE_GOOD;
386
0
}
387
388
UA_StatusCode
389
UA_Server_getSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
390
0
                              const UA_QualifiedName key, UA_Variant *outValue) {
391
0
    lockServer(server);
392
0
    UA_StatusCode res = getSessionAttribute(server, sessionId, key, outValue, false);
393
0
    unlockServer(server);
394
0
    return res;
395
0
}
396
397
UA_StatusCode
398
UA_Server_getSessionAttributeCopy(UA_Server *server, const UA_NodeId *sessionId,
399
0
                                  const UA_QualifiedName key, UA_Variant *outValue) {
400
0
    lockServer(server);
401
0
    UA_StatusCode res = getSessionAttribute(server, sessionId, key, outValue, true);
402
0
    unlockServer(server);
403
0
    return res;
404
0
}
405
406
UA_StatusCode
407
UA_Server_getSessionAttribute_scalar(UA_Server *server,
408
                                     const UA_NodeId *sessionId,
409
                                     const UA_QualifiedName key,
410
                                     const UA_DataType *type,
411
0
                                     void *outValue) {
412
0
    lockServer(server);
413
414
0
    UA_Variant attr;
415
0
    UA_StatusCode res = getSessionAttribute(server, sessionId, key, &attr, false);
416
0
    if(res != UA_STATUSCODE_GOOD) {
417
0
        unlockServer(server);
418
0
        return res;
419
0
    }
420
421
0
    if(!UA_Variant_hasScalarType(&attr, type)) {
422
0
        unlockServer(server);
423
0
        return UA_STATUSCODE_BADNOTFOUND;
424
0
    }
425
426
0
    memcpy(outValue, attr.data, type->memSize);
427
428
0
    unlockServer(server);
429
0
    return UA_STATUSCODE_GOOD;
430
0
}