Coverage Report

Created: 2026-09-28 07:11

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