Coverage Report

Created: 2025-07-11 07:03

/src/open62541/plugins/ua_accesscontrol_default.c
Line
Count
Source (jump to first uncovered line)
1
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
2
 * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
3
 *
4
 *    Copyright 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
5
 *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
6
 *    Copyright 2019 (c) HMS Industrial Networks AB (Author: Jonas Green)
7
 */
8
9
#include <open62541/plugin/accesscontrol_default.h>
10
11
/* Example access control management. Anonymous and username / password login.
12
 * The access rights are maximally permissive.
13
 *
14
 * FOR PRODUCTION USE, THIS EXAMPLE PLUGIN SHOULD BE REPLACED WITH LESS
15
 * PERMISSIVE ACCESS CONTROL.
16
 *
17
 * For TransferSubscriptions, we check whether the transfer happens between
18
 * Sessions for the same user. */
19
20
typedef struct {
21
    UA_Boolean allowAnonymous;
22
    size_t usernamePasswordLoginSize;
23
    UA_UsernamePasswordLogin *usernamePasswordLogin;
24
    UA_UsernamePasswordLoginCallback loginCallback;
25
    void *loginContext;
26
    UA_CertificateGroup verifyX509;
27
} AccessControlContext;
28
29
#define ANONYMOUS_POLICY "open62541-anonymous-policy"
30
#define CERTIFICATE_POLICY "open62541-certificate-policy"
31
#define USERNAME_POLICY "open62541-username-policy"
32
const UA_String anonymous_policy = UA_STRING_STATIC(ANONYMOUS_POLICY);
33
const UA_String certificate_policy = UA_STRING_STATIC(CERTIFICATE_POLICY);
34
const UA_String username_policy = UA_STRING_STATIC(USERNAME_POLICY);
35
36
/************************/
37
/* Access Control Logic */
38
/************************/
39
40
static UA_StatusCode
41
activateSession_default(UA_Server *server, UA_AccessControl *ac,
42
                        const UA_EndpointDescription *endpointDescription,
43
                        const UA_ByteString *secureChannelRemoteCertificate,
44
                        const UA_NodeId *sessionId,
45
                        const UA_ExtensionObject *userIdentityToken,
46
0
                        void **sessionContext) {
47
0
    AccessControlContext *context = (AccessControlContext*)ac->context;
48
0
    UA_ServerConfig *config = UA_Server_getConfig(server);
49
50
    /* The empty token is interpreted as anonymous */
51
0
    UA_AnonymousIdentityToken anonToken;
52
0
    UA_ExtensionObject tmpIdentity;
53
0
    if(userIdentityToken->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
54
0
        UA_AnonymousIdentityToken_init(&anonToken);
55
0
        UA_ExtensionObject_init(&tmpIdentity);
56
0
        UA_ExtensionObject_setValueNoDelete(&tmpIdentity,
57
0
                                            &anonToken,
58
0
                                            &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]);
59
0
        userIdentityToken = &tmpIdentity;
60
0
    }
61
62
    /* Could the token be decoded? */
63
0
    if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED)
64
0
        return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
65
66
0
    const UA_DataType *tokenType = userIdentityToken->content.decoded.type;
67
0
    if(tokenType == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) {
68
        /* Anonymous login */
69
0
        if(!context->allowAnonymous)
70
0
            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
71
72
0
        const UA_AnonymousIdentityToken *token = (UA_AnonymousIdentityToken*)
73
0
            userIdentityToken->content.decoded.data;
74
75
        /* Match the beginnig of the PolicyId.
76
         * Compatibility notice: Siemens OPC Scout v10 provides an empty
77
         * policyId. This is not compliant. For compatibility, assume that empty
78
         * policyId == ANONYMOUS_POLICY */
79
0
        if(token->policyId.data &&
80
0
           (token->policyId.length < anonymous_policy.length ||
81
0
            strncmp((const char*)token->policyId.data,
82
0
                    (const char*)anonymous_policy.data,
83
0
                    anonymous_policy.length) != 0)) {
84
0
            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
85
0
        }
86
0
    } else if(tokenType == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
87
        /* Username and password */
88
0
        const UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken*)
89
0
            userIdentityToken->content.decoded.data;
90
91
        /* Match the beginnig of the PolicyId */
92
0
        if(userToken->policyId.length < username_policy.length ||
93
0
           strncmp((const char*)userToken->policyId.data,
94
0
                   (const char*)username_policy.data,
95
0
                   username_policy.length) != 0) {
96
0
            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
97
0
        }
98
99
        /* The userToken has been decrypted by the server before forwarding
100
         * it to the plugin. This information can be used here. */
101
        /* if(userToken->encryptionAlgorithm.length > 0) {} */
102
103
        /* Empty username and password */
104
0
        if(userToken->userName.length == 0 && userToken->password.length == 0)
105
0
            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
106
107
        /* Try to match username/pw */
108
0
        UA_Boolean match = false;
109
0
        if(context->loginCallback) {
110
0
            if(context->loginCallback(&userToken->userName, &userToken->password,
111
0
               context->usernamePasswordLoginSize, context->usernamePasswordLogin,
112
0
               sessionContext, context->loginContext) == UA_STATUSCODE_GOOD)
113
0
                match = true;
114
0
        } else {
115
0
            for(size_t i = 0; i < context->usernamePasswordLoginSize; i++) {
116
0
                if(UA_String_equal(&userToken->userName, &context->usernamePasswordLogin[i].username) &&
117
0
                   UA_ByteString_equal(&userToken->password, &context->usernamePasswordLogin[i].password)) {
118
0
                    match = true;
119
0
                    break;
120
0
                }
121
0
            }
122
0
        }
123
0
        if(!match)
124
0
            return UA_STATUSCODE_BADUSERACCESSDENIED;
125
0
    } else if(tokenType == &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) {
126
        /* x509 certificate */
127
0
        const UA_X509IdentityToken *userToken = (UA_X509IdentityToken*)
128
0
            userIdentityToken->content.decoded.data;
129
130
        /* Match the beginnig of the PolicyId */
131
0
        if(userToken->policyId.length < certificate_policy.length ||
132
0
           strncmp((const char*)userToken->policyId.data,
133
0
                   (const char*)certificate_policy.data,
134
0
                   certificate_policy.length) != 0) {
135
0
            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
136
0
        }
137
138
0
        if(!config->sessionPKI.verifyCertificate)
139
0
            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
140
141
0
       UA_StatusCode res = config->sessionPKI.
142
0
            verifyCertificate(&config->sessionPKI, &userToken->certificateData);
143
0
        if(res != UA_STATUSCODE_GOOD)
144
0
            return UA_STATUSCODE_BADIDENTITYTOKENREJECTED;
145
0
    } else {
146
        /* Unsupported token type */
147
0
        return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
148
0
    }
149
150
0
    return UA_STATUSCODE_GOOD;
151
0
}
152
153
static void
154
closeSession_default(UA_Server *server, UA_AccessControl *ac,
155
0
                     const UA_NodeId *sessionId, void *sessionContext) {
156
0
}
157
158
static UA_UInt32
159
getUserRightsMask_default(UA_Server *server, UA_AccessControl *ac,
160
                          const UA_NodeId *sessionId, void *sessionContext,
161
0
                          const UA_NodeId *nodeId, void *nodeContext) {
162
0
    return 0xFFFFFFFF;
163
0
}
164
165
static UA_Byte
166
getUserAccessLevel_default(UA_Server *server, UA_AccessControl *ac,
167
                           const UA_NodeId *sessionId, void *sessionContext,
168
0
                           const UA_NodeId *nodeId, void *nodeContext) {
169
0
    return 0xFF;
170
0
}
171
172
static UA_Boolean
173
getUserExecutable_default(UA_Server *server, UA_AccessControl *ac,
174
                          const UA_NodeId *sessionId, void *sessionContext,
175
0
                          const UA_NodeId *methodId, void *methodContext) {
176
0
    return true;
177
0
}
178
179
static UA_Boolean
180
getUserExecutableOnObject_default(UA_Server *server, UA_AccessControl *ac,
181
                                  const UA_NodeId *sessionId, void *sessionContext,
182
                                  const UA_NodeId *methodId, void *methodContext,
183
0
                                  const UA_NodeId *objectId, void *objectContext) {
184
0
    return true;
185
0
}
186
187
static UA_Boolean
188
allowAddNode_default(UA_Server *server, UA_AccessControl *ac,
189
                     const UA_NodeId *sessionId, void *sessionContext,
190
0
                     const UA_AddNodesItem *item) {
191
0
    return true;
192
0
}
193
194
static UA_Boolean
195
allowAddReference_default(UA_Server *server, UA_AccessControl *ac,
196
                          const UA_NodeId *sessionId, void *sessionContext,
197
0
                          const UA_AddReferencesItem *item) {
198
0
    return true;
199
0
}
200
201
static UA_Boolean
202
allowDeleteNode_default(UA_Server *server, UA_AccessControl *ac,
203
                        const UA_NodeId *sessionId, void *sessionContext,
204
0
                        const UA_DeleteNodesItem *item) {
205
0
    return true;
206
0
}
207
208
static UA_Boolean
209
allowDeleteReference_default(UA_Server *server, UA_AccessControl *ac,
210
                             const UA_NodeId *sessionId, void *sessionContext,
211
0
                             const UA_DeleteReferencesItem *item) {
212
0
    return true;
213
0
}
214
215
static UA_Boolean
216
allowBrowseNode_default(UA_Server *server, UA_AccessControl *ac,
217
                        const UA_NodeId *sessionId, void *sessionContext,
218
0
                        const UA_NodeId *nodeId, void *nodeContext) {
219
0
    return true;
220
0
}
221
222
#ifdef UA_ENABLE_SUBSCRIPTIONS
223
static UA_Boolean
224
allowTransferSubscription_default(UA_Server *server, UA_AccessControl *ac,
225
                                  const UA_NodeId *oldSessionId, void *oldSessionContext,
226
0
                                  const UA_NodeId *newSessionId, void *newSessionContext) {
227
0
    if(!oldSessionId)
228
0
        return true;
229
    /* Allow the transfer if the same user-id was used to activate both sessions */
230
0
    UA_Variant session1UserId;
231
0
    UA_Variant_init(&session1UserId);
232
0
    UA_Server_getSessionAttribute(server, oldSessionId,
233
0
                                  UA_QUALIFIEDNAME(0, "clientUserId"),
234
0
                                  &session1UserId);
235
0
    UA_Variant session2UserId;
236
0
    UA_Variant_init(&session2UserId);
237
0
    UA_Server_getSessionAttribute(server, newSessionId,
238
0
                                  UA_QUALIFIEDNAME(0, "clientUserId"),
239
0
                                  &session2UserId);
240
241
0
    return (UA_order(&session1UserId, &session2UserId,
242
0
                     &UA_TYPES[UA_TYPES_VARIANT]) == UA_ORDER_EQ);
243
0
}
244
#endif
245
246
#ifdef UA_ENABLE_HISTORIZING
247
static UA_Boolean
248
allowHistoryUpdateUpdateData_default(UA_Server *server, UA_AccessControl *ac,
249
                                     const UA_NodeId *sessionId, void *sessionContext,
250
                                     const UA_NodeId *nodeId,
251
                                     UA_PerformUpdateType performInsertReplace,
252
0
                                     const UA_DataValue *value) {
253
0
    return true;
254
0
}
255
256
static UA_Boolean
257
allowHistoryUpdateDeleteRawModified_default(UA_Server *server, UA_AccessControl *ac,
258
                                            const UA_NodeId *sessionId, void *sessionContext,
259
                                            const UA_NodeId *nodeId,
260
                                            UA_DateTime startTimestamp,
261
                                            UA_DateTime endTimestamp,
262
0
                                            bool isDeleteModified) {
263
0
    return true;
264
0
}
265
#endif
266
267
/***************************************/
268
/* Create Delete Access Control Plugin */
269
/***************************************/
270
271
0
static void clear_default(UA_AccessControl *ac) {
272
0
    UA_Array_delete((void*)(uintptr_t)ac->userTokenPolicies,
273
0
                    ac->userTokenPoliciesSize,
274
0
                    &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
275
0
    ac->userTokenPolicies = NULL;
276
0
    ac->userTokenPoliciesSize = 0;
277
278
0
    AccessControlContext *context = (AccessControlContext*)ac->context;
279
280
0
    if (context) {
281
0
        for(size_t i = 0; i < context->usernamePasswordLoginSize; i++) {
282
0
            UA_String_clear(&context->usernamePasswordLogin[i].username);
283
0
            UA_ByteString_clear(&context->usernamePasswordLogin[i].password);
284
0
        }
285
0
        if(context->usernamePasswordLoginSize > 0)
286
0
            UA_free(context->usernamePasswordLogin);
287
288
0
        UA_free(ac->context);
289
0
        ac->context = NULL;
290
0
    }
291
0
}
292
293
UA_StatusCode
294
UA_AccessControl_default(UA_ServerConfig *config,
295
                         UA_Boolean allowAnonymous,
296
                         const UA_String *userTokenPolicyUri,
297
                         size_t usernamePasswordLoginSize,
298
0
                         const UA_UsernamePasswordLogin *usernamePasswordLogin) {
299
0
    UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
300
0
                   "AccessControl: Unconfigured AccessControl. Users have all permissions.");
301
0
    UA_AccessControl *ac = &config->accessControl;
302
303
0
    if(ac->clear)
304
0
        ac->clear(ac);
305
306
0
    ac->clear = clear_default;
307
0
    ac->activateSession = activateSession_default;
308
0
    ac->closeSession = closeSession_default;
309
0
    ac->getUserRightsMask = getUserRightsMask_default;
310
0
    ac->getUserAccessLevel = getUserAccessLevel_default;
311
0
    ac->getUserExecutable = getUserExecutable_default;
312
0
    ac->getUserExecutableOnObject = getUserExecutableOnObject_default;
313
0
    ac->allowAddNode = allowAddNode_default;
314
0
    ac->allowAddReference = allowAddReference_default;
315
0
    ac->allowBrowseNode = allowBrowseNode_default;
316
317
0
#ifdef UA_ENABLE_SUBSCRIPTIONS
318
0
    ac->allowTransferSubscription = allowTransferSubscription_default;
319
0
#endif
320
321
0
#ifdef UA_ENABLE_HISTORIZING
322
0
    ac->allowHistoryUpdateUpdateData = allowHistoryUpdateUpdateData_default;
323
0
    ac->allowHistoryUpdateDeleteRawModified = allowHistoryUpdateDeleteRawModified_default;
324
0
#endif
325
326
0
    ac->allowDeleteNode = allowDeleteNode_default;
327
0
    ac->allowDeleteReference = allowDeleteReference_default;
328
329
0
    AccessControlContext *context = (AccessControlContext*)
330
0
            UA_malloc(sizeof(AccessControlContext));
331
0
    if(!context)
332
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
333
0
    memset(context, 0, sizeof(AccessControlContext));
334
0
    ac->context = context;
335
336
    /* Allow anonymous? */
337
0
    context->allowAnonymous = allowAnonymous;
338
0
    if(allowAnonymous) {
339
0
        UA_LOG_INFO(config->logging, UA_LOGCATEGORY_SERVER,
340
0
                    "AccessControl: Anonymous login is enabled");
341
0
    }
342
343
    /* Copy username/password to the access control plugin */
344
0
    if(usernamePasswordLoginSize > 0) {
345
0
        context->usernamePasswordLogin = (UA_UsernamePasswordLogin*)
346
0
            UA_malloc(usernamePasswordLoginSize * sizeof(UA_UsernamePasswordLogin));
347
0
        if(!context->usernamePasswordLogin)
348
0
            return UA_STATUSCODE_BADOUTOFMEMORY;
349
0
        context->usernamePasswordLoginSize = usernamePasswordLoginSize;
350
0
        for(size_t i = 0; i < usernamePasswordLoginSize; i++) {
351
0
            UA_String_copy(&usernamePasswordLogin[i].username,
352
0
                           &context->usernamePasswordLogin[i].username);
353
0
            UA_ByteString_copy(&usernamePasswordLogin[i].password,
354
0
                           &context->usernamePasswordLogin[i].password);
355
0
        }
356
0
    }
357
358
0
    size_t numOfPolcies = 1;
359
0
    if(!userTokenPolicyUri) {
360
0
        if(config->securityPoliciesSize > 0)
361
0
            numOfPolcies = config->securityPoliciesSize;
362
0
        else {
363
0
            UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
364
0
                           "No security policies defined for the secure channel.");
365
0
            return UA_STATUSCODE_BADINTERNALERROR;
366
0
        }
367
0
    }
368
369
    /* Set the allowed policies */
370
0
    size_t policies = 0;
371
0
    if(allowAnonymous)
372
0
        policies++;
373
0
    if(usernamePasswordLoginSize > 0)
374
0
        policies++;
375
0
    if(config->sessionPKI.verifyCertificate)
376
0
        policies++;
377
0
    ac->userTokenPoliciesSize = 0;
378
0
    ac->userTokenPolicies = (UA_UserTokenPolicy *)
379
0
        UA_Array_new(policies * numOfPolcies, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
380
0
    if(!ac->userTokenPolicies)
381
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
382
0
    ac->userTokenPoliciesSize = policies * numOfPolcies;
383
384
0
    if(policies == 0) {
385
0
        UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
386
0
                       "No allowed policies set.");
387
0
        return UA_STATUSCODE_GOOD;
388
0
    }
389
390
0
    const UA_String *utpUri = NULL;
391
0
    policies = 0;
392
0
    for(size_t i = 0; i < numOfPolcies; i++) {
393
0
        if(userTokenPolicyUri) {
394
0
            utpUri = userTokenPolicyUri;
395
0
        } else {
396
0
            utpUri = &config->securityPolicies[i].policyUri;
397
0
        }
398
0
        if(allowAnonymous) {
399
0
            ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
400
0
            ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(ANONYMOUS_POLICY);
401
0
            UA_String_copy(utpUri,
402
0
                               &ac->userTokenPolicies[policies].securityPolicyUri);
403
0
            policies++;
404
0
        }
405
406
0
        if(config->sessionPKI.verifyCertificate) {
407
0
            ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_CERTIFICATE;
408
0
            ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(CERTIFICATE_POLICY);
409
#if UA_LOGLEVEL <= 400
410
            if(UA_String_equal(utpUri, &UA_SECURITY_POLICY_NONE_URI)) {
411
                UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
412
                               "x509 Certificate Authentication configured, "
413
                               "but no encrypting SecurityPolicy. "
414
                               "This can leak credentials on the network.");
415
            }
416
#endif
417
0
            UA_String_copy(utpUri,
418
0
                               &ac->userTokenPolicies[policies].securityPolicyUri);
419
0
            policies++;
420
0
        }
421
422
0
        if(usernamePasswordLoginSize > 0) {
423
0
            ac->userTokenPolicies[policies].tokenType = UA_USERTOKENTYPE_USERNAME;
424
0
            ac->userTokenPolicies[policies].policyId = UA_STRING_ALLOC(USERNAME_POLICY);
425
#if UA_LOGLEVEL <= 400
426
            if(UA_String_equal(utpUri, &UA_SECURITY_POLICY_NONE_URI)) {
427
                UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_SERVER,
428
                               "Username/Password Authentication configured, "
429
                               "but no encrypting SecurityPolicy. "
430
                               "This can leak credentials on the network.");
431
            }
432
#endif
433
0
            UA_String_copy(utpUri,
434
0
                               &ac->userTokenPolicies[policies].securityPolicyUri);
435
0
            policies++;
436
0
        }
437
0
    }
438
0
    return UA_STATUSCODE_GOOD;
439
0
}
440
441
UA_StatusCode
442
UA_AccessControl_defaultWithLoginCallback(UA_ServerConfig *config,
443
                                          UA_Boolean allowAnonymous,
444
                                          const UA_String *userTokenPolicyUri,
445
                                          size_t usernamePasswordLoginSize,
446
                                          const UA_UsernamePasswordLogin *usernamePasswordLogin,
447
                                          UA_UsernamePasswordLoginCallback loginCallback,
448
0
                                          void *loginContext) {
449
0
    AccessControlContext *context;
450
0
    UA_StatusCode sc =
451
0
        UA_AccessControl_default(config, allowAnonymous, userTokenPolicyUri,
452
0
                                 usernamePasswordLoginSize, usernamePasswordLogin);
453
0
    if(sc != UA_STATUSCODE_GOOD)
454
0
        return sc;
455
456
0
    context = (AccessControlContext *)config->accessControl.context;
457
0
    context->loginCallback = loginCallback;
458
0
    context->loginContext = loginContext;
459
460
0
    return UA_STATUSCODE_GOOD;
461
0
}
462