Coverage Report

Created: 2026-02-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/plugins/crypto/ua_certificategroup_filestore.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 2022 (c) Mark Giraud, Fraunhofer IOSB
6
 *    Copyright 2023 (c) Fraunhofer IOSB (Author: Kai Huebl)
7
 *    Copyright 2024 (c) Fraunhofer IOSB (Author: Noel Graf)
8
 */
9
10
#include <open62541/util.h>
11
#include <open62541/plugin/certificategroup_default.h>
12
13
#include "ua_filestore_common.h"
14
#include "mp_printf.h"
15
16
#ifdef UA_ENABLE_ENCRYPTION
17
18
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__)
19
20
#ifdef __linux__
21
0
#define EVENT_SIZE (sizeof(struct inotify_event))
22
0
#define BUF_LEN (1024 * ( EVENT_SIZE + 16 ))
23
#endif /* __linux__ */
24
25
typedef struct {
26
    /* Memory cert store as a base */
27
    UA_CertificateGroup *store;
28
29
#ifdef __linux__
30
    int inotifyFd;
31
#endif /* __linux__ */
32
33
    UA_String trustedCertFolder;
34
    UA_String trustedCrlFolder;
35
    UA_String issuerCertFolder;
36
    UA_String issuerCrlFolder;
37
    UA_String rejectedCertFolder;
38
    UA_String ownCertFolder;
39
    UA_String ownKeyFolder;
40
    UA_String rootFolder;
41
} FileCertStore;
42
43
static int
44
0
mkpath(char *dir, UA_MODE mode) {
45
0
    struct UA_STAT sb;
46
47
0
    if(dir == NULL)
48
0
        return 1;
49
50
0
    if(!UA_stat(dir, &sb))
51
0
        return 0;  /* Directory already exist */
52
53
0
    size_t len = strlen(dir) + 1;
54
0
    char *tmp_dir = (char*)UA_malloc(len);
55
0
    if(!tmp_dir)
56
0
        return 1;
57
0
    memcpy(tmp_dir, dir, len);
58
59
    /* Before the actual target directory is created, the recursive call ensures
60
     * that all parent directories are created or already exist. */
61
0
    mkpath(UA_dirname(tmp_dir), mode);
62
0
    UA_free(tmp_dir);
63
64
0
    return UA_mkdir(dir, mode);
65
0
}
66
67
static UA_StatusCode
68
0
removeAllFilesFromDir(const char *const path, bool removeSubDirs) {
69
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
70
71
    /* Check parameter */
72
0
    if(path == NULL)
73
0
        return UA_STATUSCODE_BADINTERNALERROR;
74
75
    /* remove all regular files from directory */
76
0
    UA_DIR *dir = UA_opendir(path);
77
0
    if(!dir)
78
0
        return UA_STATUSCODE_BADINTERNALERROR;
79
80
0
    struct UA_DIRENT *dirent;
81
0
    while((dirent = UA_readdir(dir)) != NULL) {
82
0
        if(dirent->d_type == UA_DT_REG) {
83
0
            char file_name[UA_FILENAME_MAX];
84
0
            mp_snprintf(file_name, UA_FILENAME_MAX, "%s/%s", path,
85
0
                        (char *)dirent->d_name);
86
0
            UA_remove(file_name);
87
0
        }
88
0
        if(dirent->d_type == UA_DT_DIR && removeSubDirs == true) {
89
0
            char *directory = (char*)dirent->d_name;
90
91
0
            char dir_name[UA_FILENAME_MAX];
92
0
            mp_snprintf(dir_name, UA_FILENAME_MAX, "%s/%s", path, (char *)dirent->d_name);
93
94
0
            if(strlen(directory) == 1 && directory[0] == '.')
95
0
                continue;
96
0
            if(strlen(directory) == 2 && directory[0] == '.' && directory[1] == '.')
97
0
                continue;
98
99
0
            retval = removeAllFilesFromDir(dir_name, removeSubDirs);
100
0
        }
101
0
    }
102
0
    UA_closedir(dir);
103
104
0
    return retval;
105
0
}
106
107
static UA_StatusCode
108
getCertFileName(const char *path, const UA_ByteString *certificate,
109
0
                char *fileNameBuf, size_t fileNameLen) {
110
    /* Check parameter */
111
0
    if(path == NULL || certificate == NULL || fileNameBuf == NULL)
112
0
        return UA_STATUSCODE_BADINTERNALERROR;
113
114
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
115
116
0
    UA_String thumbprint = UA_STRING_NULL;
117
0
    thumbprint.length = 40;
118
0
    thumbprint.data = (UA_Byte*)UA_calloc(thumbprint.length, sizeof(UA_Byte));
119
120
0
    UA_String subjectName = UA_STRING_NULL;
121
122
0
    UA_CertificateUtils_getThumbprint((UA_ByteString*)(uintptr_t)certificate, &thumbprint);
123
0
    UA_CertificateUtils_getSubjectName((UA_ByteString*)(uintptr_t)certificate, &subjectName);
124
125
0
    if((thumbprint.length + subjectName.length + 2) > fileNameLen) {
126
0
        UA_String_clear(&thumbprint);
127
0
        UA_String_clear(&subjectName);
128
0
        return UA_STATUSCODE_BADINTERNALERROR;
129
0
    }
130
131
0
    char *thumbprintBuffer = (char*)UA_malloc(thumbprint.length + 1);
132
0
    char *subjectNameBuffer = (char*)UA_malloc(subjectName.length + 1);
133
134
0
    memcpy(thumbprintBuffer, thumbprint.data, thumbprint.length);
135
0
    thumbprintBuffer[thumbprint.length] = '\0';
136
0
    memcpy(subjectNameBuffer, subjectName.data, subjectName.length);
137
0
    subjectNameBuffer[subjectName.length] = '\0';
138
139
0
    char *subName = NULL;
140
0
    char *substring = "CN=";
141
0
    char *ptr = strstr(subjectNameBuffer, substring);
142
143
0
    if(ptr != NULL) {
144
0
        subName = ptr + 3;
145
0
    } else {
146
0
        subName = subjectNameBuffer;
147
0
    }
148
149
0
    if(mp_snprintf(fileNameBuf, fileNameLen, "%s/%s[%s]", path, subName,
150
0
                   thumbprintBuffer) < 0)
151
0
        retval = UA_STATUSCODE_BADINTERNALERROR;
152
153
0
    UA_String_clear(&thumbprint);
154
0
    UA_String_clear(&subjectName);
155
0
    UA_free(thumbprintBuffer);
156
0
    UA_free(subjectNameBuffer);
157
158
0
    return retval;
159
0
}
160
161
static UA_StatusCode
162
0
readCertificates(UA_ByteString **list, size_t *listSize, const UA_String path) {
163
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
164
165
0
    char listPath[UA_PATH_MAX] = {0};
166
0
    mp_snprintf(listPath, UA_PATH_MAX, "%.*s",
167
0
                (int)path.length, (char*)path.data);
168
169
    /* Determine number of certificates */
170
0
    size_t numCerts = 0;
171
0
    UA_DIR *dir = UA_opendir(listPath);
172
0
    if(!dir)
173
0
        return UA_STATUSCODE_BADINTERNALERROR;
174
175
0
    struct UA_DIRENT *dirent;
176
0
    while((dirent = UA_readdir(dir)) != NULL) {
177
0
        if(dirent->d_type != UA_DT_REG)
178
0
            continue;
179
0
        numCerts++;
180
0
    }
181
182
0
    retval = UA_Array_resize((void **)list, listSize, numCerts, &UA_TYPES[UA_TYPES_BYTESTRING]);
183
0
    if(retval != UA_STATUSCODE_GOOD) {
184
0
        UA_closedir(dir);
185
0
        return retval;
186
0
    }
187
188
    /* Read files from directory */
189
0
    size_t numActCerts = 0;
190
0
    UA_rewinddir(dir);
191
192
0
    while((dirent = UA_readdir(dir)) != NULL) {
193
0
        if(dirent->d_type != UA_DT_REG)
194
0
            continue;
195
0
        if(numActCerts < numCerts) {
196
            /* Create filename to load */
197
0
            char filename[UA_PATH_MAX] = {0};
198
0
            if(mp_snprintf(filename, UA_PATH_MAX, "%s/%s", listPath, dirent->d_name) < 0) {
199
0
                UA_closedir(dir);
200
0
                return UA_STATUSCODE_BADINTERNALERROR;
201
0
            }
202
203
            /* Load data from file */
204
0
            retval = readFileToByteString(filename, &((*list)[numActCerts]));
205
0
            if(retval != UA_STATUSCODE_GOOD) {
206
0
                UA_closedir(dir);
207
0
                return retval;
208
0
            }
209
0
        }
210
0
        numActCerts++;
211
0
    }
212
0
    UA_closedir(dir);
213
214
0
    return retval;
215
0
}
216
217
static UA_StatusCode
218
0
readTrustStore(UA_CertificateGroup *certGroup, UA_TrustListDataType *trustList) {
219
0
    if(certGroup == NULL)
220
0
        return UA_STATUSCODE_BADINTERNALERROR;
221
222
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
223
224
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
225
0
    retval |= readCertificates(&trustList->trustedCertificates, &trustList->trustedCertificatesSize,
226
0
                               context->trustedCertFolder);
227
0
    retval |= readCertificates(&trustList->trustedCrls, &trustList->trustedCrlsSize,
228
0
                               context->trustedCrlFolder);
229
0
    retval |= readCertificates(&trustList->issuerCertificates, &trustList->issuerCertificatesSize,
230
0
                               context->issuerCertFolder);
231
0
    retval |= readCertificates(&trustList->issuerCrls, &trustList->issuerCrlsSize,
232
0
                               context->issuerCrlFolder);
233
234
0
    return retval;
235
0
}
236
237
static UA_StatusCode
238
0
reloadAndWriteTrustStore(UA_CertificateGroup *certGroup) {
239
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
240
241
0
    UA_TrustListDataType trustList;
242
0
    UA_TrustListDataType_init(&trustList);
243
0
    trustList.specifiedLists = UA_TRUSTLISTMASKS_ALL;
244
245
0
    UA_StatusCode retval = readTrustStore(certGroup, &trustList);
246
0
    if(retval != UA_STATUSCODE_GOOD) {
247
0
        UA_TrustListDataType_clear(&trustList);
248
0
        return retval;
249
0
    }
250
251
0
    retval = context->store->setTrustList(context->store, &trustList);
252
0
    UA_TrustListDataType_clear(&trustList);
253
254
0
    return retval;
255
0
}
256
257
static UA_StatusCode
258
0
reloadTrustStore(UA_CertificateGroup *certGroup) {
259
0
    if(certGroup == NULL)
260
0
        return UA_STATUSCODE_BADINTERNALERROR;
261
262
0
 #ifdef __linux__
263
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
264
265
0
    char buffer[BUF_LEN];
266
0
    const ssize_t length = read(context->inotifyFd, buffer, BUF_LEN );
267
0
    if(length == -1 && errno != EAGAIN)
268
0
        return UA_STATUSCODE_BADINTERNALERROR;
269
#else
270
    /* TODO: Implement a way to check for changes in the pki folder */
271
    const ssize_t length = 0;
272
#endif /* __linux__ */
273
274
    /* No events, which means no changes to the pki folder */
275
    /* If the nonblocking read() found no events to read, then
276
     * it returns -1 with errno set to EAGAIN. In that case,
277
     * we exit the loop. */
278
0
    if(length <= 0)
279
0
        return UA_STATUSCODE_GOOD;
280
281
0
    return reloadAndWriteTrustStore(certGroup);
282
0
}
283
284
static UA_StatusCode
285
writeCertificates(UA_CertificateGroup *certGroup, const UA_ByteString *list,
286
0
                  size_t listSize, const char *listPath) {
287
    /* Check parameter */
288
0
    if(listPath == NULL)
289
0
        return UA_STATUSCODE_BADINTERNALERROR;
290
0
    if(listSize > 0 && list == NULL)
291
0
        return UA_STATUSCODE_BADINTERNALERROR;
292
293
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
294
0
    for(size_t i = 0; i < listSize; i++) {
295
        /* Create filename to load */
296
0
        char filename[UA_PATH_MAX] = {0};
297
0
        retval = getCertFileName(listPath, &list[i], filename, UA_PATH_MAX);
298
0
        if(retval != UA_STATUSCODE_GOOD)
299
0
            return UA_STATUSCODE_BADINTERNALERROR;
300
301
        /* Store data in file */
302
0
        retval = writeByteStringToFile(filename, &list[i]);
303
0
        if(retval != UA_STATUSCODE_GOOD)
304
0
            return retval;
305
0
    }
306
307
0
    return retval;
308
0
}
309
310
static UA_StatusCode
311
writeTrustList(UA_CertificateGroup *certGroup, const UA_ByteString *list,
312
0
               size_t listSize, const UA_String path) {
313
    /* Check parameter */
314
0
    if(path.length == 0)
315
0
        return UA_STATUSCODE_BADINTERNALERROR;
316
0
    if(listSize > 0 && list == NULL)
317
0
        return UA_STATUSCODE_BADINTERNALERROR;
318
319
0
    char listPath[UA_PATH_MAX] = {0};
320
0
    mp_snprintf(listPath, UA_PATH_MAX, "%.*s", (int)path.length, (char *)path.data);
321
    /* remove existing files in directory */
322
0
    UA_StatusCode retval = removeAllFilesFromDir(listPath, false);
323
0
    if(retval != UA_STATUSCODE_GOOD)
324
0
        return retval;
325
326
0
    return writeCertificates(certGroup, list, listSize, listPath);
327
0
}
328
329
static UA_StatusCode
330
0
writeTrustStore(UA_CertificateGroup *certGroup, const UA_UInt32 trustListMask) {
331
    /* Check parameter */
332
0
    if(certGroup == NULL)
333
0
        return UA_STATUSCODE_BADINTERNALERROR;
334
335
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
336
337
0
    UA_TrustListDataType trustList;
338
0
    UA_TrustListDataType_init(&trustList);
339
0
    trustList.specifiedLists = trustListMask;
340
341
0
    context->store->getTrustList(context->store, &trustList);
342
343
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
344
0
    if(trustList.specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) {
345
0
        retval = writeTrustList(certGroup, trustList.trustedCertificates,
346
0
                                trustList.trustedCertificatesSize, context->trustedCertFolder);
347
0
        if(retval != UA_STATUSCODE_GOOD)
348
0
            return retval;
349
0
    }
350
0
    if(trustList.specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCRLS) {
351
0
        retval = writeTrustList(certGroup, trustList.trustedCrls,
352
0
                                trustList.trustedCrlsSize, context->trustedCrlFolder);
353
0
        if(retval != UA_STATUSCODE_GOOD)
354
0
            return retval;
355
0
    }
356
0
    if(trustList.specifiedLists & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) {
357
0
        retval = writeTrustList(certGroup, trustList.issuerCertificates,
358
0
                                trustList.issuerCertificatesSize, context->issuerCertFolder);
359
0
        if(retval != UA_STATUSCODE_GOOD)
360
0
            return retval;
361
0
    }
362
0
    if(trustList.specifiedLists & UA_TRUSTLISTMASKS_ISSUERCRLS) {
363
0
        retval = writeTrustList(certGroup, trustList.issuerCrls,
364
0
                                trustList.issuerCrlsSize, context->issuerCrlFolder);
365
0
        if(retval != UA_STATUSCODE_GOOD)
366
0
            return retval;
367
0
    }
368
0
    UA_TrustListDataType_clear(&trustList);
369
370
0
    return retval;
371
0
}
372
373
static UA_StatusCode
374
FileCertStore_setupStorePath(char *directory, char *rootDirectory,
375
0
                             size_t rootDirectorySize, UA_String *out) {
376
0
    char path[UA_PATH_MAX] = {0};
377
0
    size_t pathSize = 0;
378
379
0
    strncpy(path, rootDirectory, UA_PATH_MAX);
380
0
    pathSize = strnlen(path, UA_PATH_MAX);
381
382
0
    strncpy(&path[pathSize], directory, UA_PATH_MAX - pathSize);
383
384
0
    *out = UA_STRING_ALLOC(path);
385
386
0
    mkpath(path, 0777);
387
0
    return UA_STATUSCODE_GOOD;
388
0
}
389
390
static UA_StatusCode
391
0
FileCertStore_createPkiDirectory(UA_CertificateGroup *certGroup, const UA_String directory) {
392
0
    if(certGroup == NULL)
393
0
        return UA_STATUSCODE_BADINTERNALERROR;
394
395
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
396
0
    if(!context)
397
0
        return UA_STATUSCODE_BADINTERNALERROR;
398
399
0
    char rootDirectory[UA_PATH_MAX] = {0};
400
0
    size_t rootDirectorySize = 0;
401
402
0
    if(directory.length <= 0 || directory.length >= UA_PATH_MAX)
403
0
        return UA_STATUSCODE_BADINTERNALERROR;
404
405
0
    memcpy(rootDirectory, directory.data, directory.length);
406
0
    rootDirectorySize = strnlen(rootDirectory, UA_PATH_MAX);
407
408
    /* Add Certificate Group Id */
409
0
    UA_NodeId applCertGroup =
410
0
        UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
411
0
    UA_NodeId httpCertGroup =
412
0
        UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTHTTPSGROUP);
413
0
    UA_NodeId userTokenCertGroup =
414
0
        UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
415
416
0
    if(UA_NodeId_equal(&certGroup->certificateGroupId, &applCertGroup)) {
417
0
        strncpy(&rootDirectory[rootDirectorySize], "/ApplCerts", UA_PATH_MAX - rootDirectorySize);
418
0
    } else if(UA_NodeId_equal(&certGroup->certificateGroupId, &httpCertGroup)) {
419
0
        strncpy(&rootDirectory[rootDirectorySize], "/HttpCerts", UA_PATH_MAX - rootDirectorySize);
420
0
    } else if(UA_NodeId_equal(&certGroup->certificateGroupId, &userTokenCertGroup)) {
421
0
        strncpy(&rootDirectory[rootDirectorySize], "/UserTokenCerts", UA_PATH_MAX - rootDirectorySize);
422
0
    } else {
423
0
        UA_String nodeIdStr;
424
0
        UA_String_init(&nodeIdStr);
425
0
        UA_NodeId_print(&certGroup->certificateGroupId, &nodeIdStr);
426
0
        strncpy(&rootDirectory[rootDirectorySize], (char *)nodeIdStr.data, UA_PATH_MAX - rootDirectorySize);
427
0
        UA_String_clear(&nodeIdStr);
428
0
    }
429
0
    rootDirectorySize = strnlen(rootDirectory, UA_PATH_MAX);
430
431
0
    context->rootFolder = UA_STRING_ALLOC(rootDirectory);
432
433
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
434
0
    retval |= FileCertStore_setupStorePath("/trusted/certs", rootDirectory,
435
0
                                           rootDirectorySize, &context->trustedCertFolder);
436
0
    retval |= FileCertStore_setupStorePath("/trusted/crl", rootDirectory,
437
0
                                           rootDirectorySize, &context->trustedCrlFolder);
438
0
    retval |= FileCertStore_setupStorePath("/issuer/certs", rootDirectory,
439
0
                                           rootDirectorySize, &context->issuerCertFolder);
440
0
    retval |= FileCertStore_setupStorePath("/issuer/crl", rootDirectory,
441
0
                                           rootDirectorySize, &context->issuerCrlFolder);
442
0
    retval |= FileCertStore_setupStorePath("/rejected/certs", rootDirectory,
443
0
                                           rootDirectorySize, &context->rejectedCertFolder);
444
0
    retval |= FileCertStore_setupStorePath("/own/certs", rootDirectory,
445
0
                                           rootDirectorySize, &context->ownCertFolder);
446
0
    retval |= FileCertStore_setupStorePath("/own/private", rootDirectory,
447
0
                                           rootDirectorySize, &context->ownKeyFolder);
448
449
0
    return retval;
450
0
}
451
452
#ifdef __linux__
453
454
static UA_StatusCode
455
0
FileCertStore_createInotifyEvent(UA_CertificateGroup *certGroup) {
456
0
    if(certGroup == NULL)
457
0
        return UA_STATUSCODE_BADINTERNALERROR;
458
459
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
460
461
0
    context->inotifyFd = inotify_init1(IN_NONBLOCK);
462
0
    if(context->inotifyFd == -1)
463
0
        return UA_STATUSCODE_BADINTERNALERROR;
464
465
0
    char folder[UA_PATH_MAX] = {0};
466
0
    mp_snprintf(folder, UA_PATH_MAX, "%.*s",
467
0
                (int)context->rootFolder.length, (char*)context->rootFolder.data);
468
0
    int wd = inotify_add_watch(context->inotifyFd, folder, IN_ALL_EVENTS);
469
0
    if(wd == -1) {
470
0
        close(context->inotifyFd);
471
0
        context->inotifyFd = -1;
472
0
        return UA_STATUSCODE_BADINTERNALERROR;
473
0
    }
474
475
0
    mp_snprintf(folder, UA_PATH_MAX, "%.*s",
476
0
                (int)context->trustedCertFolder.length, (char*)context->trustedCertFolder.data);
477
0
    wd = inotify_add_watch(context->inotifyFd, folder, IN_ALL_EVENTS);
478
0
    if(wd == -1) {
479
0
        close(context->inotifyFd);
480
0
        context->inotifyFd = -1;
481
0
        return UA_STATUSCODE_BADINTERNALERROR;
482
0
    }
483
484
0
    return UA_STATUSCODE_GOOD;
485
0
}
486
487
#endif /* __linux__ */
488
489
static UA_StatusCode
490
0
FileCertStore_getTrustList(UA_CertificateGroup *certGroup, UA_TrustListDataType *trustList) {
491
    /* Check parameter */
492
0
    if(certGroup == NULL || trustList == NULL)
493
0
        return UA_STATUSCODE_BADINTERNALERROR;
494
495
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
496
    /* It will only re-read the Cert store on the file system if there have been changes to files. */
497
0
    UA_StatusCode retval = reloadTrustStore(certGroup);
498
0
    if(retval != UA_STATUSCODE_GOOD)
499
0
        return retval;
500
501
0
    return context->store->getTrustList(context->store, trustList);
502
0
}
503
504
static UA_StatusCode
505
0
FileCertStore_setTrustList(UA_CertificateGroup *certGroup, const UA_TrustListDataType *trustList) {
506
    /* Check parameter */
507
0
    if(certGroup == NULL || trustList == NULL)
508
0
        return UA_STATUSCODE_BADINTERNALERROR;
509
510
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
511
    /* It will only re-read the Cert store on the file system if there have been changes to files. */
512
0
    UA_StatusCode retval = reloadTrustStore(certGroup);
513
0
    if(retval != UA_STATUSCODE_GOOD)
514
0
        return retval;
515
516
0
    retval = context->store->setTrustList(context->store, trustList);
517
0
    if(retval != UA_STATUSCODE_GOOD)
518
0
        return retval;
519
520
0
    return writeTrustStore(certGroup, trustList->specifiedLists);
521
0
}
522
523
static UA_StatusCode
524
0
FileCertStore_addToTrustList(UA_CertificateGroup *certGroup, const UA_TrustListDataType *trustList) {
525
    /* Check parameter */
526
0
    if(certGroup == NULL || trustList == NULL)
527
0
        return UA_STATUSCODE_BADINTERNALERROR;
528
529
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
530
    /* It will only re-read the Cert store on the file system if there have been changes to files. */
531
0
    UA_StatusCode retval = reloadTrustStore(certGroup);
532
0
    if(retval != UA_STATUSCODE_GOOD)
533
0
        return retval;
534
535
0
    retval = context->store->addToTrustList(context->store, trustList);
536
0
    if(retval != UA_STATUSCODE_GOOD)
537
0
        return retval;
538
539
0
    return writeTrustStore(certGroup, trustList->specifiedLists);
540
0
}
541
542
static UA_StatusCode
543
0
FileCertStore_removeFromTrustList(UA_CertificateGroup *certGroup, const UA_TrustListDataType *trustList) {
544
    /* Check parameter */
545
0
    if(certGroup == NULL || trustList == NULL)
546
0
        return UA_STATUSCODE_BADINTERNALERROR;
547
548
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
549
    /* It will only re-read the Cert store on the file system if there have been changes to files. */
550
0
    UA_StatusCode retval = reloadTrustStore(certGroup);
551
0
    if(retval != UA_STATUSCODE_GOOD)
552
0
        return retval;
553
554
0
    retval = context->store->removeFromTrustList(context->store, trustList);
555
0
    if(retval != UA_STATUSCODE_GOOD)
556
0
        return retval;
557
558
0
    return writeTrustStore(certGroup, trustList->specifiedLists);
559
0
}
560
561
static UA_StatusCode
562
0
FileCertStore_getRejectedList(UA_CertificateGroup *certGroup, UA_ByteString **rejectedList, size_t *rejectedListSize) {
563
    /* Check parameter */
564
0
    if(certGroup == NULL || rejectedList == NULL || rejectedListSize == NULL)
565
0
        return UA_STATUSCODE_BADINTERNALERROR;
566
567
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
568
0
    return context->store->getRejectedList(context->store, rejectedList, rejectedListSize);
569
0
}
570
571
static UA_StatusCode
572
FileCertStore_getCertificateCrls(UA_CertificateGroup *certGroup, const UA_ByteString *certificate,
573
                                 const UA_Boolean isTrusted, UA_ByteString **crls,
574
0
                                 size_t *crlsSize) {
575
    /* Check parameter */
576
0
    if(certGroup == NULL || certificate == NULL || crls == NULL) {
577
0
        return UA_STATUSCODE_BADINTERNALERROR;
578
0
    }
579
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
580
    /* It will only re-read the Cert store on the file system if there have been changes to files. */
581
0
    UA_StatusCode retval = reloadTrustStore(certGroup);
582
0
    if(retval != UA_STATUSCODE_GOOD)
583
0
        return retval;
584
585
0
    return context->store->getCertificateCrls(context->store, certificate, isTrusted, crls, crlsSize);
586
0
}
587
588
static UA_StatusCode
589
0
FileCertStore_verifyCertificate(UA_CertificateGroup *certGroup, const UA_ByteString *certificate) {
590
    /* Check parameter */
591
0
    if(certGroup == NULL || certificate == NULL)
592
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
593
594
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
595
    /* It will only re-read the Cert store on the file system if there have been changes to files. */
596
0
    UA_StatusCode retval = reloadTrustStore(certGroup);
597
0
    if(retval != UA_STATUSCODE_GOOD) {
598
0
        return retval;
599
0
    }
600
601
0
    retval = context->store->verifyCertificate(context->store, certificate);
602
0
    if(retval == UA_STATUSCODE_BADCERTIFICATEUNTRUSTED ||
603
0
       retval == UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED ||
604
0
       retval == UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN ||
605
0
       retval == UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN) {
606
        /* write rejectedList to filestore */
607
0
        UA_ByteString *rejectedList = NULL;
608
0
        size_t rejectedListSize = 0;
609
0
        context->store->getRejectedList(context->store, &rejectedList, &rejectedListSize);
610
0
        writeTrustList(certGroup, rejectedList, rejectedListSize, context->rejectedCertFolder);
611
0
        UA_Array_delete(rejectedList, rejectedListSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
612
0
    }
613
614
0
    return retval;
615
0
}
616
617
static void
618
0
FileCertStore_clear(UA_CertificateGroup *certGroup) {
619
    /* check parameter */
620
0
    if(!certGroup || !certGroup->context)
621
0
        return;
622
623
0
    UA_NodeId_clear(&certGroup->certificateGroupId);
624
625
0
    FileCertStore *context = (FileCertStore *)certGroup->context;
626
627
0
    if(context->store) {
628
0
        context->store->clear(context->store);
629
0
        UA_free(context->store);
630
0
    }
631
0
    UA_String_clear(&context->trustedCertFolder);
632
0
    UA_String_clear(&context->trustedCrlFolder);
633
0
    UA_String_clear(&context->issuerCertFolder);
634
0
    UA_String_clear(&context->issuerCrlFolder);
635
0
    UA_String_clear(&context->rejectedCertFolder);
636
0
    UA_String_clear(&context->ownCertFolder);
637
0
    UA_String_clear(&context->ownKeyFolder);
638
0
    UA_String_clear(&context->rootFolder);
639
640
0
#ifdef __linux__
641
0
    if(context->inotifyFd > 0)
642
0
        close(context->inotifyFd);
643
0
#endif /* __linux__ */
644
645
0
    UA_free(context);
646
0
    certGroup->context = NULL;
647
0
}
648
649
UA_StatusCode
650
UA_CertificateGroup_Filestore(UA_CertificateGroup *certGroup,
651
                              UA_NodeId *certificateGroupId,
652
                              const UA_String storePath,
653
                              const UA_Logger *logger,
654
0
                              const UA_KeyValueMap *params) {
655
0
    if(certGroup == NULL || certificateGroupId == NULL) {
656
0
        return UA_STATUSCODE_BADINTERNALERROR;
657
0
    }
658
659
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
660
661
    /* Clear if the plugin is already initialized */
662
0
    if(certGroup->clear)
663
0
        certGroup->clear(certGroup);
664
665
0
    UA_NodeId_copy(certificateGroupId, &certGroup->certificateGroupId);
666
0
    certGroup->logging = logger;
667
668
0
    certGroup->getTrustList = FileCertStore_getTrustList;
669
0
    certGroup->setTrustList = FileCertStore_setTrustList;
670
0
    certGroup->addToTrustList = FileCertStore_addToTrustList;
671
0
    certGroup->removeFromTrustList = FileCertStore_removeFromTrustList;
672
0
    certGroup->getRejectedList = FileCertStore_getRejectedList;
673
0
    certGroup->getCertificateCrls = FileCertStore_getCertificateCrls;
674
0
    certGroup->verifyCertificate = FileCertStore_verifyCertificate;
675
0
    certGroup->clear = FileCertStore_clear;
676
677
    /* Set PKI Store context data */
678
0
    FileCertStore *context = (FileCertStore *)UA_calloc(1, sizeof(FileCertStore));
679
0
    if(!context) {
680
0
        retval = UA_STATUSCODE_BADOUTOFMEMORY;
681
0
        goto cleanup;
682
0
    }
683
0
    certGroup->context = context;
684
685
0
    retval = FileCertStore_createPkiDirectory(certGroup, storePath);
686
0
    if(retval != UA_STATUSCODE_GOOD) {
687
0
        goto cleanup;
688
0
    }
689
690
0
    context->store = (UA_CertificateGroup*)UA_calloc(1, sizeof(UA_CertificateGroup));
691
0
    retval = UA_CertificateGroup_Memorystore(context->store, certificateGroupId, NULL, logger, params);
692
0
    if(retval != UA_STATUSCODE_GOOD) {
693
0
        goto cleanup;
694
0
    }
695
696
0
#ifdef __linux__
697
0
    retval = FileCertStore_createInotifyEvent(certGroup);
698
0
    if(retval != UA_STATUSCODE_GOOD) {
699
0
        goto cleanup;
700
0
    }
701
0
#endif /* __linux__ */
702
703
0
    retval = reloadAndWriteTrustStore(certGroup);
704
0
    if(retval != UA_STATUSCODE_GOOD) {
705
0
        goto cleanup;
706
0
    }
707
708
0
    return UA_STATUSCODE_GOOD;
709
710
0
    cleanup:
711
0
        certGroup->clear(certGroup);
712
0
    return retval;
713
0
}
714
715
#endif /* defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) */
716
717
#endif /* UA_ENABLE_ENCRYPTION */