Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief filter plugin providing cryptographic operations
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #ifndef HAVE_KDBCONFIG
11 : #include "kdbconfig.h"
12 : #endif
13 :
14 : #include "fcrypt.h"
15 :
16 :
17 : #include <errno.h>
18 : #include <fcntl.h>
19 : #include <gpg.h>
20 : #include <libgen.h> // provides basename()
21 : #include <stdlib.h>
22 : #include <string.h>
23 : #include <sys/stat.h>
24 : #include <sys/time.h>
25 : #include <sys/types.h>
26 : #include <unistd.h>
27 :
28 : #include <kdb.h>
29 : #include <kdberrors.h>
30 : #include <kdbmacros.h>
31 : #include <kdbtypes.h>
32 :
33 : /**
34 : * @brief Defines the plugin state during the <code>kdb get</code> phase.
35 : */
36 : enum FcryptGetState
37 : {
38 :
39 : /** Perform a decryption run before <code>kdb get</code> reads from the storage. */
40 : PREGETSTORAGE = 0,
41 :
42 : /** Perform an encryption run after <code>kdb get</code> has read from the storage. */
43 : POSTGETSTORAGE = 1
44 : };
45 :
46 : struct _fcryptState
47 : {
48 : enum FcryptGetState getState;
49 : int tmpFileFd;
50 : char * tmpFilePath;
51 : char * originalFilePath;
52 : };
53 : typedef struct _fcryptState fcryptState;
54 :
55 : #define ELEKTRA_FCRYPT_TMP_FILE_SUFFIX "XXXXXX"
56 :
57 : /**
58 : * @brief Allocates a new string holding the name of the temporary file.
59 : * This method makes use of the "fcrypt/tmpdir" plugin configuration option.
60 : * @param conf holds the plugin configuration
61 : * @param file holds the path to the original file
62 : * @param fd will hold the file descriptor to the temporary file in case of success
63 : * @returns an allocated string holding the name of the encrypted file. Must be freed by the caller.
64 : */
65 0 : static char * getTemporaryFileName (KeySet * conf, const char * file, int * fd)
66 : {
67 : // read the temporary directory to use from the plugin configuration
68 : // NOTE the string contained in tmpDir must not be modified!
69 0 : const char * tmpDir = NULL;
70 0 : Key * k = ksLookupByName (conf, ELEKTRA_FCRYPT_CONFIG_TMPDIR, 0);
71 0 : if (k)
72 : {
73 0 : tmpDir = keyString (k);
74 : }
75 :
76 0 : if (!tmpDir)
77 : {
78 : // check the environment; returns NULL if no match is found
79 0 : tmpDir = getenv ("TMPDIR");
80 : }
81 :
82 0 : if (!tmpDir)
83 : {
84 : // fallback
85 0 : tmpDir = ELEKTRA_FCRYPT_DEFAULT_TMPDIR;
86 : }
87 :
88 : // extract the file name (base name) from the path
89 0 : char * fileDup = elektraStrDup (file);
90 0 : if (!fileDup) goto error;
91 0 : const char * baseName = basename (fileDup);
92 :
93 : // + 1 to add an additional '/' as path separator
94 : // + 1 to reserve space for the NULL terminator
95 : // ----------------------------------------------
96 : // + 2 characters in total
97 0 : const size_t newFileAllocated = strlen (tmpDir) + strlen (baseName) + strlen (ELEKTRA_FCRYPT_TMP_FILE_SUFFIX) + 2;
98 0 : char * newFile = elektraMalloc (newFileAllocated);
99 0 : if (!newFile) goto error;
100 0 : snprintf (newFile, newFileAllocated, "%s/%s" ELEKTRA_FCRYPT_TMP_FILE_SUFFIX, tmpDir, baseName);
101 0 : *fd = mkstemp (newFile);
102 0 : if (*fd < 0)
103 : {
104 0 : elektraFree (newFile);
105 0 : goto error;
106 : }
107 :
108 0 : elektraFree (fileDup);
109 0 : return newFile;
110 :
111 : error:
112 0 : elektraFree (fileDup);
113 0 : return NULL;
114 : }
115 :
116 : /**
117 : * @brief Overwrites the content of the given file with zeroes.
118 : * @param fd holds the file descriptor to the temporary file to be shredded
119 : * @param errorKey holds an error description in case of failure
120 : * @retval 1 on success
121 : * @retval -1 on failure. In this case errorKey holds an error description.
122 : */
123 0 : static int shredTemporaryFile (int fd, Key * errorKey)
124 : {
125 0 : kdb_octet_t buffer[512] = { 0 };
126 : struct stat tmpStat;
127 :
128 0 : if (fstat (fd, &tmpStat))
129 : {
130 0 : ELEKTRA_SET_RESOURCE_ERRORF (
131 : errorKey,
132 : "Failed to overwrite the temporary data. Cannot retrieve file status. Unencrypted data may leak. Errno: %s",
133 : strerror (errno));
134 0 : return -1;
135 : }
136 :
137 0 : if (lseek (fd, 0, SEEK_SET))
138 : {
139 : goto error;
140 : }
141 :
142 0 : for (off_t i = 0; i < tmpStat.st_size; i += sizeof (buffer))
143 : {
144 0 : if (write (fd, buffer, sizeof (buffer)) != sizeof (buffer))
145 : {
146 : goto error;
147 : }
148 : }
149 : return 1;
150 :
151 : error:
152 0 : ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Failed to overwrite the temporary data. Unencrypted data may leak. Reason: %s",
153 : strerror (errno));
154 0 : return -1;
155 : }
156 :
157 : /**
158 : * @brief lookup if the test mode for unit testing is enabled.
159 : * @param conf KeySet holding the plugin configuration.
160 : * @retval 0 test mode is not enabled
161 : * @retval 1 test mode is enabled
162 : */
163 0 : static int inTestMode (KeySet * conf)
164 : {
165 0 : Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_UNIT_TEST, 0);
166 0 : if (k && !strcmp (keyString (k), "1"))
167 : {
168 : return 1;
169 : }
170 : return 0;
171 : }
172 :
173 : /**
174 : * @brief lookup if the text mode is disabled in the plugin config.
175 : * It is enabled per default.
176 : * @param conf KeySet holding the plugin configuration.
177 : * @retval 0 text mode is not enabled
178 : * @retval 1 text mode is enabled
179 : */
180 0 : static int inTextMode (KeySet * conf)
181 : {
182 0 : Key * k = ksLookupByName (conf, ELEKTRA_FCRYPT_CONFIG_TEXTMODE, 0);
183 0 : if (k && !strcmp (keyString (k), "0"))
184 : {
185 : return 0;
186 : }
187 : return 1;
188 : }
189 :
190 : /**
191 : * @brief Read number of total GPG recipient keys from the plugin configuration.
192 : * @param config holds the plugin configuration
193 : * @param keyName holds the name of the root key to look up
194 : * @returns the number of GPG recipient keys.
195 : */
196 0 : static size_t getRecipientCount (KeySet * config, const char * keyName)
197 : {
198 : Key * k;
199 0 : size_t recipientCount = 0;
200 0 : Key * root = ksLookupByName (config, keyName, 0);
201 :
202 0 : if (!root) return 0;
203 :
204 : // toplevel
205 0 : if (strlen (keyString (root)) > 0)
206 : {
207 0 : recipientCount++;
208 : }
209 :
210 0 : ksRewind (config);
211 0 : while ((k = ksNext (config)) != 0)
212 : {
213 0 : if (keyIsBelow (k, root) && strlen (keyString (k)) > 0)
214 : {
215 0 : recipientCount++;
216 : }
217 : }
218 : return recipientCount;
219 : }
220 :
221 0 : static int fcryptGpgCallAndCleanup (Key * parentKey, KeySet * pluginConfig, char ** argv, int argc, int tmpFileFd, char * tmpFile)
222 : {
223 0 : int parentKeyFd = -1;
224 0 : int result = ELEKTRA_PLUGIN_FUNCTION (gpgCall) (pluginConfig, parentKey, NULL, argv, argc);
225 :
226 0 : if (result == 1)
227 : {
228 0 : parentKeyFd = open (keyString (parentKey), O_WRONLY);
229 :
230 : // gpg call returned success, overwrite the original file with the gpg payload data
231 0 : if (rename (tmpFile, keyString (parentKey)) != 0)
232 : {
233 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Renaming file %s to %s failed. Reason: %s", tmpFile, keyString (parentKey),
234 : strerror (errno));
235 0 : result = -1;
236 : }
237 : }
238 :
239 0 : if (result == 1)
240 : {
241 0 : if (parentKeyFd >= 0)
242 : {
243 0 : shredTemporaryFile (parentKeyFd, parentKey);
244 : }
245 : }
246 : else
247 : {
248 : // if anything went wrong above the temporary file is shredded and removed
249 0 : shredTemporaryFile (tmpFileFd, parentKey);
250 0 : if (unlink (tmpFile))
251 : {
252 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (
253 : parentKey,
254 : "Failed to unlink a temporary file. WARNING: unencrypted data may leak! Please try to delete "
255 : "the file manually. Affected file: %s. Reason: %s",
256 : tmpFile, strerror (errno));
257 : }
258 : }
259 :
260 0 : if (parentKeyFd >= 0 && close (parentKeyFd))
261 : {
262 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
263 : }
264 0 : if (close (tmpFileFd))
265 : {
266 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
267 : }
268 0 : elektraFree (tmpFile);
269 0 : return result;
270 : }
271 :
272 : /**
273 : * @brief encrypt or sign the file specified at parentKey
274 : * @param pluginConfig holds the plugin configuration
275 : * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure.
276 : * @retval 1 on success
277 : * @retval -1 on error, errorKey holds an error description
278 : */
279 0 : static int fcryptEncrypt (KeySet * pluginConfig, Key * parentKey)
280 0 : {
281 : Key * k;
282 0 : const size_t recipientCount = getRecipientCount (pluginConfig, ELEKTRA_RECIPIENT_KEY);
283 0 : const size_t signatureCount = getRecipientCount (pluginConfig, ELEKTRA_SIGNATURE_KEY);
284 :
285 0 : if (recipientCount == 0 && signatureCount == 0)
286 : {
287 0 : ELEKTRA_SET_INSTALLATION_ERRORF (
288 : parentKey,
289 : "Missing GPG recipient key (specified as %s) or GPG signature key (specified as %s) in plugin configuration",
290 : ELEKTRA_RECIPIENT_KEY, ELEKTRA_SIGNATURE_KEY);
291 0 : return -1;
292 : }
293 :
294 0 : int tmpFileFd = -1;
295 0 : char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd);
296 0 : if (!tmpFile)
297 : {
298 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Memory allocation failed");
299 0 : return -1;
300 : }
301 :
302 0 : const size_t testMode = inTestMode (pluginConfig);
303 0 : const size_t textMode = inTextMode (pluginConfig);
304 :
305 : // prepare argument vector for gpg call
306 : // 7 static arguments (magic number below) are:
307 : // 1. path to the binary
308 : // 2. --batch
309 : // 3. -o
310 : // 4. path to tmp file
311 : // 5. yes
312 : // 6. file to be encrypted
313 : // 7. NULL terminator
314 0 : int argc = 7 + (2 * recipientCount) + (2 * signatureCount) + (2 * testMode) + textMode + (recipientCount > 0 ? 1 : 0) +
315 : (signatureCount > 0 ? 1 : 0);
316 0 : kdb_unsigned_short_t i = 0;
317 0 : char * argv[argc];
318 0 : argv[i++] = NULL;
319 0 : argv[i++] = "--batch";
320 0 : argv[i++] = "-o";
321 0 : argv[i++] = tmpFile;
322 0 : argv[i++] = "--yes"; // overwrite files if they exist
323 :
324 : // add recipients
325 0 : Key * gpgRecipientRoot = ksLookupByName (pluginConfig, ELEKTRA_RECIPIENT_KEY, 0);
326 :
327 : // append root (gpg/key) as gpg recipient
328 0 : if (gpgRecipientRoot && strlen (keyString (gpgRecipientRoot)) > 0)
329 : {
330 0 : argv[i++] = "-r";
331 : // NOTE argv[] values will not be modified, so const can be discarded safely
332 0 : argv[i++] = (char *) keyString (gpgRecipientRoot);
333 : }
334 :
335 : // append keys beneath root (crypto/key/#_) as gpg recipients
336 0 : if (gpgRecipientRoot)
337 : {
338 0 : ksRewind (pluginConfig);
339 0 : while ((k = ksNext (pluginConfig)) != 0)
340 : {
341 0 : const char * kStringVal = keyString (k);
342 0 : if (keyIsBelow (k, gpgRecipientRoot) && strlen (kStringVal) > 0)
343 : {
344 0 : argv[i++] = "-r";
345 : // NOTE argv[] values will not be modified, so const can be discarded safely
346 0 : argv[i++] = (char *) kStringVal;
347 : }
348 : }
349 : }
350 :
351 :
352 : // add signature keys
353 0 : Key * gpgSignatureRoot = ksLookupByName (pluginConfig, ELEKTRA_SIGNATURE_KEY, 0);
354 :
355 : // append root signature key
356 0 : if (gpgSignatureRoot && strlen (keyString (gpgSignatureRoot)) > 0)
357 : {
358 0 : argv[i++] = "-u";
359 : // NOTE argv[] values will not be modified, so const can be discarded safely
360 0 : argv[i++] = (char *) keyString (gpgSignatureRoot);
361 : }
362 :
363 : // append keys beneath root (fcrypt/sign/#_) as gpg signature keys
364 0 : if (gpgSignatureRoot)
365 : {
366 0 : ksRewind (pluginConfig);
367 0 : while ((k = ksNext (pluginConfig)) != 0)
368 : {
369 0 : const char * kStringVal = keyString (k);
370 0 : if (keyIsBelow (k, gpgSignatureRoot) && strlen (kStringVal) > 0)
371 : {
372 0 : argv[i++] = "-u";
373 : // NOTE argv[] values will not be modified, so const can be discarded safely
374 0 : argv[i++] = (char *) kStringVal;
375 : }
376 : }
377 : }
378 :
379 : // if we are in test mode we add the trust model
380 0 : if (testMode > 0)
381 : {
382 0 : argv[i++] = "--trust-model";
383 0 : argv[i++] = "always";
384 : }
385 :
386 : // ASCII armor in text mode
387 0 : if (textMode)
388 : {
389 0 : argv[i++] = "--armor";
390 : }
391 :
392 : // prepare rest of the argument vector
393 0 : if (recipientCount > 0)
394 : {
395 : // encrypt the file
396 0 : argv[i++] = "-e";
397 : }
398 :
399 0 : if (signatureCount > 0)
400 : {
401 0 : if (textMode && recipientCount == 0)
402 : {
403 : // clear-sign the file
404 0 : argv[i++] = "--clearsign";
405 : }
406 : else
407 : {
408 : // sign the file
409 0 : argv[i++] = "-s";
410 : }
411 : }
412 :
413 0 : argv[i++] = (char *) keyString (parentKey);
414 0 : argv[i++] = NULL;
415 :
416 : // NOTE the encryption process works like this:
417 : // gpg2 --batch --yes -o encryptedFile -r keyID -e configFile
418 : // mv encryptedFile configFile
419 :
420 0 : return fcryptGpgCallAndCleanup (parentKey, pluginConfig, argv, argc, tmpFileFd, tmpFile);
421 : }
422 :
423 : /**
424 : * @brief decrypt the file specified at parentKey
425 : * @param pluginConfig holds the plugin configuration
426 : * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure.
427 : * @param state holds the plugin state
428 : * @retval 1 on success
429 : * @retval -1 on error, errorKey holds an error description
430 : */
431 0 : static int fcryptDecrypt (KeySet * pluginConfig, Key * parentKey, fcryptState * state)
432 0 : {
433 0 : int tmpFileFd = -1;
434 0 : char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd);
435 0 : if (!tmpFile)
436 : {
437 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Memory allocation failed");
438 0 : return -1;
439 : }
440 :
441 0 : const size_t testMode = inTestMode (pluginConfig);
442 :
443 : // prepare argument vector for gpg call
444 : // 8 static arguments (magic number below) are:
445 : // 1. path to the binary
446 : // 2. --batch
447 : // 3. -o
448 : // 4. path to tmp file
449 : // 5. yes
450 : // 6. -d
451 : // 7. file to be encrypted
452 : // 8. NULL terminator
453 0 : int argc = 8 + (2 * testMode);
454 0 : char * argv[argc];
455 0 : int i = 0;
456 :
457 0 : argv[i++] = NULL;
458 0 : argv[i++] = "--batch";
459 0 : argv[i++] = "--yes";
460 :
461 : // if we are in test mode we add the trust model
462 0 : if (testMode)
463 : {
464 0 : argv[i++] = "--trust-model";
465 0 : argv[i++] = "always";
466 : }
467 :
468 0 : argv[i++] = "-o";
469 0 : argv[i++] = tmpFile;
470 0 : argv[i++] = "-d";
471 : // safely discarding const from keyString() return value
472 0 : argv[i++] = (char *) keyString (parentKey);
473 0 : argv[i++] = NULL;
474 :
475 : // NOTE the decryption process works like this:
476 : // gpg2 --batch --yes -o tmpfile -d configFile
477 0 : int result = ELEKTRA_PLUGIN_FUNCTION (gpgCall) (pluginConfig, parentKey, NULL, argv, argc);
478 0 : if (result == 1)
479 : {
480 0 : state->originalFilePath = elektraStrDup (keyString (parentKey));
481 0 : state->tmpFilePath = tmpFile;
482 0 : state->tmpFileFd = tmpFileFd;
483 0 : keySetString (parentKey, tmpFile);
484 : }
485 : else
486 : {
487 : // if anything went wrong above the temporary file is shredded and removed
488 0 : shredTemporaryFile (tmpFileFd, parentKey);
489 0 : if (unlink (tmpFile))
490 : {
491 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (
492 : parentKey,
493 : "Failed to unlink a temporary file. WARNING: unencrypted data may leak! Please try to delete "
494 : "the file manually. Affected file: %s, error description: %s",
495 : tmpFile, strerror (errno));
496 : }
497 0 : if (close (tmpFileFd))
498 : {
499 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
500 : }
501 0 : elektraFree (tmpFile);
502 : }
503 : return result;
504 : }
505 :
506 : /**
507 : * @brief allocates plugin state handle and initializes the plugin state
508 : * @retval 1 on success
509 : * @retval -1 on failure
510 : */
511 20 : int ELEKTRA_PLUGIN_FUNCTION (open) (Plugin * handle, KeySet * ks ELEKTRA_UNUSED, Key * parentKey)
512 : {
513 20 : fcryptState * s = elektraMalloc (sizeof (fcryptState));
514 20 : if (!s)
515 : {
516 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Memory allocation failed");
517 0 : return -1;
518 : }
519 :
520 20 : s->getState = PREGETSTORAGE;
521 20 : s->tmpFileFd = -1;
522 20 : s->tmpFilePath = NULL;
523 20 : s->originalFilePath = NULL;
524 :
525 20 : elektraPluginSetData (handle, s);
526 20 : return 1;
527 : }
528 :
529 : /**
530 : * @brief frees the plugin state handle
531 : * @retval 1 on success
532 : * @retval -1 on failure
533 : */
534 20 : int ELEKTRA_PLUGIN_FUNCTION (close) (Plugin * handle, KeySet * ks ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
535 : {
536 20 : fcryptState * s = (fcryptState *) elektraPluginGetData (handle);
537 20 : if (s)
538 : {
539 20 : if (s->tmpFileFd > 0 && close (s->tmpFileFd))
540 : {
541 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
542 : }
543 20 : if (s->tmpFilePath)
544 : {
545 0 : elektraFree (s->tmpFilePath);
546 : }
547 20 : if (s->originalFilePath)
548 : {
549 0 : elektraFree (s->originalFilePath);
550 : }
551 20 : elektraFree (s);
552 20 : elektraPluginSetData (handle, NULL);
553 : }
554 20 : return 1;
555 : }
556 :
557 : /**
558 : * @brief establish the Elektra plugin contract and decrypt the file provided at parentKey using GPG.
559 : * @retval 1 on success
560 : * @retval -1 on failure
561 : */
562 20 : int ELEKTRA_PLUGIN_FUNCTION (get) (Plugin * handle, KeySet * ks ELEKTRA_UNUSED, Key * parentKey)
563 : {
564 : // Publish module configuration to Elektra (establish the contract)
565 20 : if (!strcmp (keyName (parentKey), "system/elektra/modules/" ELEKTRA_PLUGIN_NAME))
566 : {
567 20 : KeySet * moduleConfig = ksNew (30,
568 : #include "contract.h"
569 : KS_END);
570 20 : ksAppend (ks, moduleConfig);
571 20 : ksDel (moduleConfig);
572 20 : return 1;
573 : }
574 :
575 : // check plugin state
576 0 : KeySet * pluginConfig = elektraPluginGetConfig (handle);
577 0 : fcryptState * s = (fcryptState *) elektraPluginGetData (handle);
578 0 : if (!s)
579 : {
580 0 : ELEKTRA_SET_INSTALLATION_ERROR (parentKey, "No plugin state is available");
581 0 : return -1;
582 : }
583 :
584 0 : if (s->getState == POSTGETSTORAGE)
585 : {
586 : // postgetstorage call will re-direct the parent key to the original encrypted/signed file
587 0 : if (s->originalFilePath)
588 : {
589 0 : keySetString (parentKey, s->originalFilePath);
590 : }
591 : else
592 : {
593 0 : ELEKTRA_SET_INTERNAL_ERROR (parentKey, "The path to the original file is lost");
594 : // clean-up is performed by kdb close
595 0 : return -1;
596 : }
597 :
598 0 : if (s->tmpFileFd > 0)
599 : {
600 0 : shredTemporaryFile (s->tmpFileFd, parentKey);
601 0 : if (close (s->tmpFileFd))
602 : {
603 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
604 : }
605 0 : s->tmpFileFd = -1;
606 0 : if (unlink (s->tmpFilePath))
607 : {
608 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (
609 : parentKey,
610 : "Failed to unlink a temporary file. WARNING: unencrypted data may leak! Please try "
611 : "to delete the file manually. Affected file: %s, error description: %s",
612 : s->tmpFilePath, strerror (errno));
613 : }
614 0 : elektraFree (s->tmpFilePath);
615 0 : s->tmpFilePath = NULL;
616 : }
617 : return 1;
618 : }
619 :
620 : // now this is a pregetstorage call
621 : // next time treat the kdb get call as postgetstorage call to trigger encryption after the file has been read
622 0 : s->getState = POSTGETSTORAGE;
623 0 : return fcryptDecrypt (pluginConfig, parentKey, s);
624 : }
625 :
626 : /**
627 : * @brief Encrypt the file provided at parentKey using GPG.
628 : * @retval 1 on success
629 : * @retval -1 on failure
630 : */
631 0 : int ELEKTRA_PLUGIN_FUNCTION (set) (Plugin * handle, KeySet * ks ELEKTRA_UNUSED, Key * parentKey)
632 : {
633 0 : KeySet * pluginConfig = elektraPluginGetConfig (handle);
634 0 : int encryptionResult = fcryptEncrypt (pluginConfig, parentKey);
635 0 : if (encryptionResult != 1) return encryptionResult;
636 :
637 : /* set all keys */
638 0 : const char * configFile = keyString (parentKey);
639 0 : if (!strcmp (configFile, "")) return 1; // no underlying config file
640 0 : int fd = open (configFile, O_RDWR);
641 0 : if (fd == -1)
642 : {
643 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not open config file %s. Reason: %s", configFile, strerror (errno));
644 0 : return -1;
645 : }
646 0 : if (fsync (fd) == -1)
647 : {
648 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not fsync config file %s. Reason: %s", configFile, strerror (errno));
649 0 : if (close (fd))
650 : {
651 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
652 : }
653 : return -1;
654 : }
655 0 : if (close (fd))
656 : {
657 0 : ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to close a file descriptor: %s", strerror (errno));
658 : }
659 : return 1;
660 : }
661 :
662 : /**
663 : * @brief Checks if at least one GPG recipient or at least one GPG signature key has been provided within the plugin configuration.
664 : *
665 : * @retval 0 no changes were made to the configuration
666 : * @retval 1 the master password has been appended to the configuration
667 : * @retval -1 an error occurred. Check errorKey
668 : */
669 0 : int ELEKTRA_PLUGIN_FUNCTION (checkconf) (Key * errorKey, KeySet * conf)
670 : {
671 0 : const size_t recipientCount = getRecipientCount (conf, ELEKTRA_RECIPIENT_KEY);
672 0 : const size_t signatureCount = getRecipientCount (conf, ELEKTRA_SIGNATURE_KEY);
673 :
674 0 : if (recipientCount == 0 && signatureCount == 0)
675 : {
676 0 : char * errorDescription = ELEKTRA_PLUGIN_FUNCTION (getMissingGpgKeyErrorText) (conf);
677 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, errorDescription);
678 0 : elektraFree (errorDescription);
679 0 : return -1;
680 : }
681 0 : if (ELEKTRA_PLUGIN_FUNCTION (gpgVerifyGpgKeysInConfig) (conf, errorKey) != 1)
682 : {
683 : // error has been set by ELEKTRA_PLUGIN_FUNCTION (gpgVerifyGpgKeysInConfig)
684 : return -1;
685 : }
686 0 : return 0;
687 : }
688 :
689 20 : Plugin * ELEKTRA_PLUGIN_EXPORT
690 : {
691 : // clang-format off
692 20 : return elektraPluginExport(ELEKTRA_PLUGIN_NAME,
693 : ELEKTRA_PLUGIN_OPEN, &ELEKTRA_PLUGIN_FUNCTION(open),
694 : ELEKTRA_PLUGIN_CLOSE, &ELEKTRA_PLUGIN_FUNCTION(close),
695 : ELEKTRA_PLUGIN_GET, &ELEKTRA_PLUGIN_FUNCTION(get),
696 : ELEKTRA_PLUGIN_SET, &ELEKTRA_PLUGIN_FUNCTION(set),
697 : ELEKTRA_PLUGIN_END);
698 : }
|