/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 */ |