Coverage Report

Created: 2026-10-15 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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(&notifySCData[0].value, &channel->securityToken.channelId,
249
0
                         &UA_TYPES[UA_TYPES_UINT32]);
250
0
    UA_Variant_setScalar(&notifySCData[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(&notifySCData[2].value, &connectionId,
255
0
                         &UA_TYPES[UA_TYPES_UINT64]);
256
0
    UA_Variant_setScalar(&notifySCData[3].value, &channel->remoteAddress,
257
0
                         &UA_TYPES[UA_TYPES_STRING]);
258
0
    UA_Variant_setScalar(&notifySCData[4].value, &channel->config.protocolVersion,
259
0
                         &UA_TYPES[UA_TYPES_UINT32]);
260
0
    UA_Variant_setScalar(&notifySCData[5].value, &channel->config.recvBufferSize,
261
0
                         &UA_TYPES[UA_TYPES_UINT32]);
262
0
    UA_Variant_setScalar(&notifySCData[6].value, &channel->config.localMaxMessageSize,
263
0
                         &UA_TYPES[UA_TYPES_UINT32]);
264
0
    UA_Variant_setScalar(&notifySCData[7].value, &channel->config.localMaxChunkCount,
265
0
                         &UA_TYPES[UA_TYPES_UINT32]);
266
0
    UA_Variant_setScalar(&notifySCData[8].value, &channel->config.sendBufferSize,
267
0
                         &UA_TYPES[UA_TYPES_UINT32]);
268
0
    UA_Variant_setScalar(&notifySCData[9].value, &channel->config.remoteMaxMessageSize,
269
0
                         &UA_TYPES[UA_TYPES_UINT32]);
270
0
    UA_Variant_setScalar(&notifySCData[10].value, &channel->config.remoteMaxChunkCount,
271
0
                         &UA_TYPES[UA_TYPES_UINT32]);
272
0
    UA_Variant_setScalar(&notifySCData[11].value, &channel->endpointUrl,
273
0
                         &UA_TYPES[UA_TYPES_STRING]);
274
0
    UA_Variant_setScalar(&notifySCData[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(&notifySCData[13].value, &securityPolicyUri,
280
0
                         &UA_TYPES[UA_TYPES_STRING]);
281
0
    UA_Variant_setScalar(&notifySCData[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
}