Coverage Report

Created: 2026-08-13 06:39

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