Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief module for calling the GPG binary
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "gpg.h"
11 : #include <assert.h>
12 : #include <errno.h>
13 : #include <kdberrors.h>
14 : #include <kdbhelper.h>
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #include <string.h>
18 : #include <sys/wait.h>
19 : #include <unistd.h>
20 :
21 : #define GPG_OUTPUT_DEFAULT_BUFFER_SIZE 1024
22 : #define GPG_MAX_KEYID_LENGTH 32
23 : #define GPG_ERROR_MISSING_KEY_LIST \
24 : "Missing GPG key (specified as " ELEKTRA_RECIPIENT_KEY ") in plugin configuration. Available key IDs are: "
25 : #define GPG_ERROR_MISSING_KEY \
26 : "Missing GPG key (specified as " ELEKTRA_RECIPIENT_KEY \
27 : ") in plugin configuration. GPG could not find any secret keys. Please generate a secret key first!"
28 : #define GPG_ERROR_INVALID_KEY "'%s' does not identify a valid GPG private key."
29 :
30 : /**
31 : * List of states for the state machine that parses the gpg key list output.
32 : */
33 : enum gpgKeyListState
34 : {
35 : GPG_KEYLIST_STATE_START,
36 : GPG_KEYLIST_STATE_FPR2,
37 : GPG_KEYLIST_STATE_FPR3,
38 : GPG_KEYLIST_STATE_COLON,
39 : GPG_KEYLIST_STATE_KEYID
40 : };
41 :
42 : /**
43 : * Return codes for the forked child process that starts the gpg binary.
44 : * List of possible errors.
45 : */
46 : enum gpgCallErrorCode
47 : {
48 :
49 : /** Failed to duplicate the stdin pipe */
50 : GPG_CALL_DUP_STDIN = 0x4200,
51 :
52 : /** Failed to duplicate the stdout pipe */
53 : GPG_CALL_DUP_STDOUT = 0x4201,
54 :
55 : /** Failed to dupliate the stderr pipe */
56 : GPG_CALL_DUP_STDERR = 0x4202,
57 :
58 : /** Failed to execv the gpg binary */
59 : GPG_CALL_EXECV = 0x4203
60 : };
61 :
62 : struct gpgKeyListElement
63 : {
64 : size_t start;
65 : size_t end;
66 : struct gpgKeyListElement * next;
67 : };
68 :
69 : static inline void closePipe (int * pipe)
70 : {
71 0 : close (pipe[0]);
72 0 : close (pipe[1]);
73 : }
74 :
75 : /**
76 : * @brief checks whether or not a given file exists and is executable.
77 : * @param file holds the path to the file that should be checked
78 : * @param errorKey holds an error description if the file does not exist or if it is not executable. Ignored if set to NULL.
79 : * @retval 1 if the file exists and is executable
80 : * @retval -1 if the file can not be found
81 : * @retval -2 if the file exsits but it can not be executed
82 : */
83 4 : static int isExecutable (const char * file, Key * errorKey)
84 : {
85 4 : if (access (file, F_OK))
86 : {
87 0 : if (errorKey)
88 : {
89 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Gpg binary %s not found", file);
90 : }
91 : return -1;
92 : }
93 :
94 4 : if (access (file, X_OK))
95 : {
96 0 : if (errorKey)
97 : {
98 0 : ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Gpg binary %s has no permission to execute", file);
99 : }
100 : return -2;
101 : }
102 :
103 : return 1;
104 : }
105 :
106 : /**
107 : * @brief concatenates dir and file.
108 : * @param errorKey holds an error description in case of failure.
109 : * @param dir contains the path to a directory
110 : * @param file contains a file name
111 : * @returns an allocated string containing "dir/file" which must be freed by the caller or NULL in case of error.
112 : */
113 65 : static char * genGpgCandidate (Key * errorKey, char * dir, const char * file)
114 : {
115 65 : const size_t resultLen = strlen (dir) + strlen (file) + 2;
116 65 : char * result = elektraMalloc (resultLen);
117 65 : if (!result)
118 : {
119 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
120 0 : return NULL;
121 : }
122 65 : snprintf (result, resultLen, "%s/%s", dir, file);
123 65 : return result;
124 : }
125 :
126 : /**
127 : * @brief lookup binary file bin in the PATH environment variable.
128 : * @param errorKey holds an error description in case of failure.
129 : * @param bin the binary file to look for
130 : * @param result holds an allocated string containing the full path to the binary file or NULL in case of error. Must be freed by the
131 : * caller.
132 : * @retval -1 if an error occurred. See errorKey for a description.
133 : * @retval 0 if the binary could not be found within PATH.
134 : * @retval 1 if the binary was found and the full path was stored in result.
135 : */
136 10 : static int searchPathForBin (Key * errorKey, const char * bin, char ** result)
137 : {
138 10 : *result = NULL;
139 :
140 10 : const char * envPath = getenv ("PATH");
141 10 : if (envPath)
142 : {
143 10 : const size_t envPathLen = strlen (envPath) + 1;
144 : char * dir;
145 :
146 10 : char * path = elektraMalloc (envPathLen);
147 10 : if (!path)
148 : {
149 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
150 0 : return -1;
151 : }
152 10 : memcpy (path, envPath, envPathLen);
153 : // save start of path as strsep() modifies path while splitting it up
154 10 : char * pathBegin = path;
155 150 : while ((dir = strsep (&path, ":")) != NULL)
156 : {
157 65 : char * candidate = genGpgCandidate (errorKey, dir, bin);
158 65 : if (!candidate)
159 : {
160 0 : elektraFree (pathBegin);
161 0 : return -1;
162 : }
163 65 : if (access (candidate, X_OK) == 0)
164 : {
165 5 : *result = candidate;
166 5 : elektraFree (pathBegin);
167 5 : return 1;
168 : }
169 60 : elektraFree (candidate);
170 : }
171 5 : elektraFree (pathBegin);
172 : }
173 : return 0;
174 : }
175 :
176 : /**
177 : * @brief lookup the path to the gpg binary in conf.
178 : * @param gpgBin holds allocated path to the gpg binary to be used or NULL in case of an error. Must bee freed by the caller.
179 : * @param conf KeySet holding the plugin configuration.
180 : * @param errorKey holds an error description if something goes wrong.
181 : * @retval 1 on success.
182 : * @retval -1 on error. In this case errorkey holds an error description.
183 : */
184 5 : int ELEKTRA_PLUGIN_FUNCTION (gpgGetBinary) (char ** gpgBin, KeySet * conf, Key * errorKey)
185 : {
186 5 : *gpgBin = NULL;
187 :
188 : // plugin configuration has highest priority
189 5 : Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_BIN, 0);
190 5 : if (k)
191 : {
192 0 : const char * configPath = keyString (k);
193 0 : const size_t configPathLen = strlen (configPath) + 1; // NULL-terminator
194 0 : if (configPathLen > 0)
195 : {
196 0 : *gpgBin = elektraMalloc (configPathLen + 1);
197 0 : if (!(*gpgBin))
198 : {
199 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
200 0 : return -1;
201 : }
202 0 : strncpy (*gpgBin, configPath, configPathLen);
203 0 : return 1;
204 : }
205 : }
206 :
207 : // search PATH for gpg and gpg2 binaries
208 5 : switch (searchPathForBin (errorKey, "gpg2", gpgBin))
209 : {
210 : case 1: // success
211 : return 1;
212 :
213 : case -1: // error
214 0 : return -1;
215 :
216 : default: // not found
217 : break;
218 : }
219 :
220 5 : switch (searchPathForBin (errorKey, "gpg", gpgBin))
221 : {
222 : case 1: // success
223 : return 1;
224 :
225 : case -1: // error
226 0 : return -1;
227 :
228 : default: // not found
229 : break;
230 : }
231 :
232 :
233 : // last resort number one - check for gpg2 at /usr/bin/gpg2
234 : // NOTE this might happen if the PATH variable is empty
235 0 : if (isExecutable (ELEKTRA_CRYPTO_DEFAULT_GPG2_BIN, NULL) == 1)
236 : {
237 0 : *gpgBin = elektraStrDup (ELEKTRA_CRYPTO_DEFAULT_GPG2_BIN);
238 0 : if (!(*gpgBin))
239 : {
240 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
241 0 : return -1;
242 : }
243 : return 1;
244 : }
245 :
246 : // last last resort - check for /usr/bin/gpg
247 0 : if (isExecutable (ELEKTRA_CRYPTO_DEFAULT_GPG1_BIN, NULL) == 1)
248 : {
249 0 : *gpgBin = elektraStrDup (ELEKTRA_CRYPTO_DEFAULT_GPG1_BIN);
250 0 : if (!(*gpgBin))
251 : {
252 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
253 0 : return -1;
254 : }
255 : return 1;
256 : }
257 :
258 : // no GPG for us :-(
259 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "No gpg binary found. Please make sure GnuPG is installed and executable");
260 0 : return -1;
261 : }
262 :
263 : /**
264 : * @brief frees a linked list of key ids
265 : * @param head holds the pointer to the head of the linked list.
266 : */
267 : static void freeKeyList (struct gpgKeyListElement * head)
268 : {
269 : struct gpgKeyListElement * e;
270 0 : while (head)
271 : {
272 0 : e = head;
273 0 : head = head->next;
274 0 : elektraFree (e);
275 : }
276 : }
277 :
278 : /**
279 : * @brief parses the key IDs from the GPG output and writes it to gpgMissingKeyErrorBuffer.
280 : * @param msgKey holds the output of the GPG process
281 : * @param totalChars is set to the total number of characters of all key IDs read from the GPG output
282 : * @param keyCount is set to the numer of key Ids parsed from the GPG output
283 : * @returns a newly allocated linked list of key IDs. Must be freed by the caller!
284 : *
285 : * The parsing process is expressed as a state machine:
286 : *
287 : * +-------+-------------+------------+----------------------------+
288 : * | State | Input | Next State | Action |
289 : * +-------+-------------+------------+----------------------------+
290 : * | START | 'f' | FPR2 | |
291 : * | START | * | START | |
292 : * | ----- | ----------- | ---------- | -------------------------- |
293 : * | FPR2 | 'p' | FPR3 | |
294 : * | FPR2 | * | START | |
295 : * | ----- | ----------- | ---------- | -------------------------- |
296 : * | FPR3 | 'r' | COLON | |
297 : * | FPR3 | * | START | |
298 : * | ----- | ----------- | ---------- | -------------------------- |
299 : * | COLON | ':' | COLON | |
300 : * | COLON | [A-Za-z0-9] | KEYID | start new key ID |
301 : * | COLON | * | START | |
302 : * | ----- | ----------- | ---------- | -------------------------- |
303 : * | KEYID | [A-Za-z0-9] | KEYID | save key ID |
304 : * | KEYID | * | START | end key ID, append to list |
305 : * +-------+-------------+------------+----------------------------+
306 : *
307 : */
308 0 : static struct gpgKeyListElement * parseGpgKeyIdFromOutput (Key * msgKey, size_t * totalChars, size_t * keyCount)
309 : {
310 : // generate a list of secret key IDs
311 0 : const char * input = (char *) keyValue (msgKey);
312 0 : const ssize_t inputLen = keyGetValueSize (msgKey);
313 0 : *totalChars = 0;
314 0 : *keyCount = 0;
315 0 : struct gpgKeyListElement * keylistHead = NULL;
316 0 : enum gpgKeyListState state = GPG_KEYLIST_STATE_START;
317 :
318 0 : for (int i = 0; i < inputLen; i++)
319 : {
320 0 : switch (state)
321 : {
322 : case GPG_KEYLIST_STATE_START:
323 0 : if (input[i] == 'f')
324 : {
325 0 : state = GPG_KEYLIST_STATE_FPR2;
326 : }
327 : break;
328 :
329 : case GPG_KEYLIST_STATE_FPR2:
330 0 : if (input[i] == 'p')
331 : {
332 : state = GPG_KEYLIST_STATE_FPR3;
333 : }
334 : else
335 : {
336 0 : state = GPG_KEYLIST_STATE_START;
337 : }
338 : break;
339 :
340 : case GPG_KEYLIST_STATE_FPR3:
341 0 : if (input[i] == 'r')
342 : {
343 : state = GPG_KEYLIST_STATE_COLON;
344 : }
345 : else
346 : {
347 0 : state = GPG_KEYLIST_STATE_START;
348 : }
349 : break;
350 :
351 : case GPG_KEYLIST_STATE_COLON:
352 0 : if (input[i] == ':')
353 : {
354 0 : continue;
355 : }
356 0 : else if ((input[i] >= 'A' && input[i] <= 'Z') || (input[i] >= 'a' && input[i] <= 'z') ||
357 0 : (input[i] >= '0' && input[i] <= '9'))
358 : {
359 0 : state = GPG_KEYLIST_STATE_KEYID;
360 : // start new key id entry
361 0 : if (keylistHead)
362 : {
363 0 : struct gpgKeyListElement * elem = elektraMalloc (sizeof (struct gpgKeyListElement));
364 0 : if (!elem)
365 : {
366 0 : freeKeyList (keylistHead);
367 0 : *totalChars = 0;
368 0 : *keyCount = 0;
369 0 : return NULL;
370 : }
371 0 : elem->start = i;
372 0 : elem->next = keylistHead;
373 0 : keylistHead = elem;
374 : }
375 : else
376 : {
377 0 : keylistHead = elektraMalloc (sizeof (struct gpgKeyListElement));
378 0 : if (!keylistHead)
379 : {
380 : return NULL;
381 : }
382 0 : keylistHead->start = i;
383 0 : keylistHead->next = NULL;
384 : }
385 0 : *keyCount += 1;
386 : }
387 : else
388 : {
389 : state = GPG_KEYLIST_STATE_START;
390 : }
391 : break;
392 :
393 : case GPG_KEYLIST_STATE_KEYID:
394 0 : if ((input[i] < 'A' || input[i] > 'Z') && (input[i] < 'a' || input[i] > 'z') && (input[i] < '0' || input[i] > '9'))
395 : {
396 0 : keylistHead->end = i;
397 0 : *totalChars += keylistHead->end - keylistHead->start;
398 0 : state = GPG_KEYLIST_STATE_START;
399 : }
400 : break;
401 :
402 : default:
403 : state = GPG_KEYLIST_STATE_START;
404 : break;
405 : }
406 : }
407 : return keylistHead;
408 : }
409 :
410 : /**
411 : * @brief verifies if given value is a valid GPG private key.
412 : * @param conf holds the plugin configuration
413 : * @param value to be checked
414 : * @retval 1 if value is a valid GPG private key
415 : * @retval -1 otherwise
416 : */
417 0 : static int isValidGpgKey (KeySet * conf, const char * value)
418 : {
419 : // NOTE it is save to discard the const modifier (although it is not pretty) - the value is not being modified
420 0 : char * argv[] = { "", "--batch", "--with-colons", "--fixed-list-mode", "--list-secret-keys", (char *) value, NULL };
421 0 : Key * errorKey = keyNew (0);
422 0 : Key * msgKey = keyNew (0);
423 :
424 0 : int status = ELEKTRA_PLUGIN_FUNCTION (gpgCall) (conf, errorKey, msgKey, argv, 7);
425 :
426 0 : keyDel (msgKey);
427 0 : keyDel (errorKey);
428 :
429 0 : return status;
430 : }
431 :
432 : /**
433 : * @brief check all keys in conf at and under given root key, whether or not they hold an valid GPG private key identifier.
434 : * @param root the root of the configuration key to look for
435 : * @param conf holds the plugin configuration
436 : * @param errorKey holds error information in case of failure.
437 : * @retval 1 on success.
438 : * @retval -1 on error, check errorKey for further details.
439 : */
440 0 : static int verifyGpgKeysInConf (Key * root, KeySet * conf, Key * errorKey)
441 : {
442 0 : if (!root) return 1; // success
443 :
444 : // verify top level key elements
445 0 : const char * rootValue = keyString (root);
446 0 : if (strlen (rootValue) > 0)
447 : {
448 0 : if (isValidGpgKey (conf, rootValue) != 1)
449 : {
450 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, GPG_ERROR_INVALID_KEY, rootValue);
451 0 : return -1; // failure
452 : }
453 : }
454 :
455 : // verify child elements
456 : Key * k;
457 0 : ksRewind (conf);
458 0 : while ((k = ksNext (conf)) != 0)
459 : {
460 0 : if (keyIsBelow (k, root))
461 : {
462 0 : const char * childValue = keyString (k);
463 0 : if (isValidGpgKey (conf, childValue) != 1)
464 : {
465 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, GPG_ERROR_INVALID_KEY, childValue);
466 0 : return -1; // failure
467 : }
468 : }
469 : }
470 :
471 : return 1; // success
472 : }
473 :
474 : /**
475 : * @brief verifies that the config only holds valid GPG private key identifiers for encryption or signing.
476 : * However, this method does NOT verify that the config does contain any GPG keys at all.
477 : *
478 : * @param conf holds the plugin configuration
479 : * @param errorKey holds an error description in case of failure.
480 : * @retval 1 on success
481 : * @retval -1 on error. check errorKey for further details.
482 : */
483 0 : int ELEKTRA_PLUGIN_FUNCTION (gpgVerifyGpgKeysInConfig) (KeySet * conf, Key * errorKey)
484 : {
485 0 : Key * rootEncrypting = ksLookupByName (conf, ELEKTRA_RECIPIENT_KEY, 0);
486 0 : if (verifyGpgKeysInConf (rootEncrypting, conf, errorKey) != 1)
487 : {
488 : // errorKey has been set by verifyGpgKeysInConf
489 : return -1; // failure
490 : }
491 :
492 0 : Key * rootSignature = ksLookupByName (conf, ELEKTRA_SIGNATURE_KEY, 0);
493 0 : if (verifyGpgKeysInConf (rootSignature, conf, errorKey) != 1)
494 : {
495 : // errorKey has been set by verifyGpgKeysInConf
496 : return -1; // failure
497 : }
498 :
499 0 : return 1; // success
500 : }
501 :
502 : /**
503 : * @brief prepare the error text in case of missing GPG recipient specification in the configuration.
504 : * @param conf holds the backend/plugin configuration
505 : * @returns the error text. This pointer must be freed by the caller!
506 : */
507 0 : char * ELEKTRA_PLUGIN_FUNCTION (getMissingGpgKeyErrorText) (KeySet * conf)
508 : {
509 0 : Key * msgKey = keyNew (0);
510 0 : Key * errorKey = keyNew (0);
511 :
512 : char * errorBuffer;
513 0 : size_t errorBufferLen = 0;
514 :
515 0 : keySetBinary (msgKey, NULL, 0);
516 0 : char * argv[] = { "", "--batch", "--list-secret-keys", "--with-fingerprint", "--with-colons", "--fixed-list-mode", NULL };
517 0 : if (ELEKTRA_PLUGIN_FUNCTION (gpgCall) (conf, errorKey, msgKey, argv, 7) == 1)
518 : {
519 0 : size_t totalKeyIdChars = 0;
520 0 : size_t keyCount = 0;
521 0 : struct gpgKeyListElement * listHead = parseGpgKeyIdFromOutput (msgKey, &totalKeyIdChars, &keyCount);
522 :
523 0 : if (keyCount > 0)
524 : {
525 : // error message + list of all key ids separated by a coma ',' + null terminator
526 0 : errorBufferLen = strlen (GPG_ERROR_MISSING_KEY_LIST) + totalKeyIdChars + keyCount;
527 0 : errorBuffer = elektraMalloc (errorBufferLen);
528 0 : if (!errorBuffer)
529 : {
530 : freeKeyList (listHead);
531 0 : return NULL;
532 : }
533 :
534 0 : const char * content = (const char *) keyValue (msgKey);
535 :
536 0 : size_t index = strlen (GPG_ERROR_MISSING_KEY_LIST);
537 0 : strncpy (errorBuffer, GPG_ERROR_MISSING_KEY_LIST, errorBufferLen);
538 :
539 : // construct the error list with the key ids
540 0 : struct gpgKeyListElement * iterator = listHead;
541 0 : while (iterator)
542 : {
543 0 : if (iterator != listHead)
544 : {
545 0 : errorBuffer[index++] = ',';
546 : }
547 :
548 0 : strncpy (&errorBuffer[index], &content[iterator->start], iterator->end - iterator->start);
549 0 : index += iterator->end - iterator->start;
550 0 : iterator = iterator->next;
551 : }
552 0 : errorBuffer[index] = '\0';
553 :
554 0 : freeKeyList (listHead);
555 0 : keyDel (msgKey);
556 0 : keyDel (errorKey);
557 0 : return errorBuffer;
558 : }
559 : }
560 :
561 : // default message - we could not find a GPG secret key
562 0 : errorBufferLen = strlen (GPG_ERROR_MISSING_KEY) + 1;
563 0 : errorBuffer = elektraMalloc (errorBufferLen);
564 0 : if (errorBuffer)
565 : {
566 0 : strncpy (errorBuffer, GPG_ERROR_MISSING_KEY, errorBufferLen);
567 : }
568 0 : keyDel (msgKey);
569 0 : keyDel (errorKey);
570 0 : return errorBuffer;
571 : }
572 :
573 : /**
574 : * @brief call the gpg binary to perform the requested operation.
575 : *
576 : * @param conf holds the backend/plugin configuration
577 : * @param errorKey holds the error description in case of failure
578 : * @param msgKey holds the message to be transformed. Ignored if set to NULL, i.e. you do not want to send a stdin message to gpg.
579 : * @param argv array holds the arguments passed on to the gpg process
580 : * @param argc contains the number of elements in argv, i.e. the size of argv
581 : *
582 : * @retval 1 on success
583 : * @retval -1 on failure
584 : */
585 4 : int ELEKTRA_PLUGIN_FUNCTION (gpgCall) (KeySet * conf, Key * errorKey, Key * msgKey, char * argv[], size_t argc)
586 : {
587 : pid_t pid;
588 : int status;
589 : int pipe_stdin[2];
590 : int pipe_stdout[2];
591 : int pipe_stderr[2];
592 4 : char errorBuffer[512] = "";
593 4 : kdb_octet_t * buffer = NULL;
594 4 : ssize_t bufferSize = 2 * keyGetValueSize (msgKey);
595 : ssize_t outputLen;
596 :
597 : assert (argc > 1);
598 :
599 : // check if bufferSize is valid
600 4 : if (bufferSize <= 0)
601 : {
602 0 : bufferSize = GPG_OUTPUT_DEFAULT_BUFFER_SIZE;
603 : }
604 :
605 : // sanitize the argument vector
606 4 : if (ELEKTRA_PLUGIN_FUNCTION (gpgGetBinary) (&argv[0], conf, errorKey) != 1)
607 : {
608 : return -1;
609 : }
610 4 : argv[argc - 1] = NULL;
611 :
612 : // check that the gpg binary exists and that it is executable
613 4 : if (isExecutable (argv[0], errorKey) != 1)
614 : {
615 0 : elektraFree (argv[0]);
616 0 : return -1; // error set by isExecutable()
617 : }
618 :
619 : // initialize pipes
620 4 : if (pipe (pipe_stdin))
621 : {
622 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Pipe initialization failed");
623 0 : elektraFree (argv[0]);
624 0 : return -1;
625 : }
626 :
627 4 : if (pipe (pipe_stdout))
628 : {
629 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Pipe initialization failed");
630 0 : closePipe (pipe_stdin);
631 0 : elektraFree (argv[0]);
632 0 : return -1;
633 : }
634 :
635 4 : if (pipe (pipe_stderr))
636 : {
637 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Pipe initialization failed");
638 0 : closePipe (pipe_stdin);
639 0 : closePipe (pipe_stdout);
640 0 : elektraFree (argv[0]);
641 0 : return -1;
642 : }
643 :
644 : // allocate buffer for gpg output
645 : // estimated maximum output size = 2 * input (including headers, etc.)
646 4 : if (msgKey && !(buffer = elektraMalloc (bufferSize)))
647 : {
648 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
649 0 : closePipe (pipe_stdin);
650 0 : closePipe (pipe_stdout);
651 0 : closePipe (pipe_stderr);
652 0 : elektraFree (argv[0]);
653 0 : return -1;
654 : }
655 :
656 : // fork into the gpg binary
657 4 : switch (pid = fork ())
658 : {
659 : case -1:
660 : // fork() failed
661 0 : ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Fork failed. Reason: %s", strerror (errno));
662 0 : closePipe (pipe_stdin);
663 0 : closePipe (pipe_stdout);
664 0 : closePipe (pipe_stderr);
665 0 : elektraFree (buffer);
666 0 : elektraFree (argv[0]);
667 0 : return -1;
668 :
669 : case 0:
670 : // start of the forked child process
671 4 : close (pipe_stdin[1]);
672 4 : close (pipe_stdout[0]);
673 4 : close (pipe_stderr[0]);
674 :
675 : // redirect stdin to pipe
676 4 : if (msgKey)
677 : {
678 4 : close (STDIN_FILENO);
679 4 : if (dup (pipe_stdin[0]) < 0)
680 : {
681 0 : exit (GPG_CALL_DUP_STDIN);
682 : }
683 : }
684 4 : close (pipe_stdin[0]);
685 :
686 : // redirect stdout to pipe
687 4 : close (STDOUT_FILENO);
688 4 : if (dup (pipe_stdout[1]) < 0)
689 : {
690 0 : exit (GPG_CALL_DUP_STDOUT);
691 : }
692 4 : close (pipe_stdout[1]);
693 :
694 : // redirect stderr to pipe
695 4 : close (STDERR_FILENO);
696 4 : if (dup (pipe_stderr[1]) < 0)
697 : {
698 0 : exit (GPG_CALL_DUP_STDERR);
699 : }
700 4 : close (pipe_stderr[1]);
701 :
702 : // finally call the gpg executable
703 4 : if (execv (argv[0], argv) < 0)
704 : {
705 0 : exit (GPG_CALL_EXECV);
706 : }
707 : // end of the child process
708 : }
709 :
710 : // parent process
711 8 : close (pipe_stdin[0]);
712 4 : close (pipe_stdout[1]);
713 4 : close (pipe_stderr[1]);
714 :
715 : // pass the message to the gpg process
716 4 : const ssize_t sendMessageSize = keyGetValueSize (msgKey);
717 4 : if (msgKey && sendMessageSize > 0)
718 : {
719 4 : if (write (pipe_stdin[1], keyValue (msgKey), sendMessageSize) != sendMessageSize)
720 : {
721 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "The communication with the GPG process failed");
722 0 : closePipe (pipe_stdin);
723 0 : closePipe (pipe_stdout);
724 0 : closePipe (pipe_stderr);
725 0 : elektraFree (buffer);
726 0 : elektraFree (argv[0]);
727 0 : return -1;
728 : }
729 : }
730 4 : close (pipe_stdin[1]);
731 :
732 : // wait for the gpg process to finish
733 4 : waitpid (pid, &status, 0);
734 :
735 : // evaluate return code of finished child process
736 4 : int retval = -1;
737 4 : switch (status)
738 : {
739 : case 0:
740 : // everything ok - receive the output of the gpg process
741 4 : if (msgKey)
742 : {
743 4 : outputLen = read (pipe_stdout[0], buffer, bufferSize);
744 4 : keySetBinary (msgKey, buffer, outputLen);
745 : }
746 : retval = 1;
747 : break;
748 :
749 : case 1:
750 : // bad signature
751 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "GPG reported a bad signature. Reason: %s", strerror (errno));
752 0 : break;
753 :
754 : case GPG_CALL_DUP_STDIN:
755 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Failed to redirect stdin");
756 0 : break;
757 :
758 : case GPG_CALL_DUP_STDOUT:
759 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Failed to redirect stdout");
760 0 : break;
761 :
762 : case GPG_CALL_DUP_STDERR:
763 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Failed to redirect stderr");
764 0 : break;
765 :
766 : case GPG_CALL_EXECV:
767 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Failed to start the gpg binary: %s", argv[0]);
768 0 : break;
769 :
770 : default:
771 : // other errors
772 0 : outputLen = read (pipe_stderr[0], errorBuffer, sizeof (errorBuffer));
773 0 : if (outputLen < 1)
774 : {
775 0 : errorBuffer[0] = '\0';
776 : }
777 0 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "GPG failed with return value %d. %s", status, errorBuffer);
778 0 : break;
779 : }
780 :
781 4 : elektraFree (buffer);
782 4 : elektraFree (argv[0]);
783 4 : close (pipe_stdout[0]);
784 4 : close (pipe_stderr[0]);
785 4 : return retval;
786 : }
|