LCOV - code coverage report
Current view: top level - src/plugins/crypto - gpg.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 118 308 38.3 %
Date: 2022-05-21 16:19:22 Functions: 10 14 71.4 %

          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          46 : static int isExecutable (const char * file, Key * errorKey)
      84             : {
      85          46 :         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           0 :                 return -1;
      92             :         }
      93             : 
      94          46 :         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           0 :                 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         804 : static char * genGpgCandidate (Key * errorKey, char * dir, const char * file)
     114             : {
     115         804 :         const size_t resultLen = strlen (dir) + strlen (file) + 2;
     116         804 :         char * result = elektraMalloc (resultLen);
     117         804 :         if (!result)
     118             :         {
     119           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
     120           0 :                 return NULL;
     121             :         }
     122         804 :         snprintf (result, resultLen, "%s/%s", dir, file);
     123         804 :         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         100 : static int searchPathForBin (Key * errorKey, const char * bin, char ** result)
     137             : {
     138         100 :         *result = NULL;
     139             : 
     140         100 :         const char * envPath = getenv ("PATH");
     141         100 :         if (envPath)
     142             :         {
     143         100 :                 const size_t envPathLen = strlen (envPath) + 1;
     144         100 :                 char * dir;
     145             : 
     146         100 :                 char * path = elektraMalloc (envPathLen);
     147         100 :                 if (!path)
     148             :                 {
     149           0 :                         ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
     150          50 :                         return -1;
     151             :                 }
     152         100 :                 memcpy (path, envPath, envPathLen);
     153             :                 // save start of path as strsep() modifies path while splitting it up
     154         100 :                 char * pathBegin = path;
     155         854 :                 while ((dir = strsep (&path, ":")) != NULL)
     156             :                 {
     157         804 :                         char * candidate = genGpgCandidate (errorKey, dir, bin);
     158         804 :                         if (!candidate)
     159             :                         {
     160           0 :                                 elektraFree (pathBegin);
     161           0 :                                 return -1;
     162             :                         }
     163         804 :                         if (access (candidate, X_OK) == 0)
     164             :                         {
     165          50 :                                 *result = candidate;
     166          50 :                                 elektraFree (pathBegin);
     167          50 :                                 return 1;
     168             :                         }
     169         754 :                         elektraFree (candidate);
     170             :                 }
     171          50 :                 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          50 : int ELEKTRA_PLUGIN_FUNCTION (gpgGetBinary) (char ** gpgBin, KeySet * conf, Key * errorKey)
     185             : {
     186          50 :         *gpgBin = NULL;
     187             : 
     188             :         // plugin configuration has highest priority
     189          50 :         Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_BIN, 0);
     190          50 :         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);
     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          50 :         switch (searchPathForBin (errorKey, "gpg2", gpgBin))
     209             :         {
     210             :         case 1: // success
     211             :                 return 1;
     212             : 
     213           0 :         case -1: // error
     214           0 :                 return -1;
     215             : 
     216             :         default: // not found
     217          50 :                 break;
     218             :         }
     219             : 
     220          50 :         switch (searchPathForBin (errorKey, "gpg", gpgBin))
     221             :         {
     222             :         case 1: // success
     223             :                 return 1;
     224             : 
     225           0 :         case -1: // error
     226           0 :                 return -1;
     227             : 
     228             :         default: // not found
     229           0 :                 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);
     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);
     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           0 :         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           0 :                 case GPG_KEYLIST_STATE_START:
     323           0 :                         if (input[i] == 'f')
     324             :                         {
     325           0 :                                 state = GPG_KEYLIST_STATE_FPR2;
     326             :                         }
     327             :                         break;
     328             : 
     329           0 :                 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           0 :                 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           0 :                 case GPG_KEYLIST_STATE_COLON:
     352           0 :                         if (input[i] == ':')
     353             :                         {
     354             :                                 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           0 :                 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           4 : 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           4 :         char * argv[] = { "", "--batch", "--with-colons", "--fixed-list-mode", "--list-secret-keys", (char *) value, NULL };
     421           4 :         Key * errorKey = keyNew ("/", KEY_END);
     422           4 :         Key * msgKey = keyNew ("/", KEY_END);
     423             : 
     424           4 :         int status = ELEKTRA_PLUGIN_FUNCTION (gpgCall) (conf, errorKey, msgKey, argv, 7);
     425             : 
     426           4 :         keyDel (msgKey);
     427           4 :         keyDel (errorKey);
     428             : 
     429           4 :         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           6 : static int verifyGpgKeysInConf (Key * root, KeySet * conf, Key * errorKey)
     441             : {
     442           6 :         if (!root) return 1; // success
     443             : 
     444             :         // verify top level key elements
     445           4 :         const char * rootValue = keyString (root);
     446           4 :         if (strlen (rootValue) > 0)
     447             :         {
     448           4 :                 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           4 :         Key * k;
     457           4 :         ksRewind (conf);
     458          14 :         while ((k = ksNext (conf)) != 0)
     459             :         {
     460           6 :                 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           3 : int ELEKTRA_PLUGIN_FUNCTION (gpgVerifyGpgKeysInConfig) (KeySet * conf, Key * errorKey)
     484             : {
     485           3 :         Key * rootEncrypting = ksLookupByName (conf, ELEKTRA_RECIPIENT_KEY, 0);
     486           3 :         if (verifyGpgKeysInConf (rootEncrypting, conf, errorKey) != 1)
     487             :         {
     488             :                 // errorKey has been set by verifyGpgKeysInConf
     489             :                 return -1; // failure
     490             :         }
     491             : 
     492           3 :         Key * rootSignature = ksLookupByName (conf, ELEKTRA_SIGNATURE_KEY, 0);
     493           3 :         if (verifyGpgKeysInConf (rootSignature, conf, errorKey) != 1)
     494             :         {
     495             :                 // errorKey has been set by verifyGpgKeysInConf
     496           0 :                 return -1; // failure
     497             :         }
     498             : 
     499             :         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 ("/", KEY_END);
     510           0 :         Key * errorKey = keyNew ("/", KEY_END);
     511             : 
     512           0 :         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          46 : int ELEKTRA_PLUGIN_FUNCTION (gpgCall) (KeySet * conf, Key * errorKey, Key * msgKey, char * argv[], size_t argc)
     586             : {
     587          46 :         pid_t pid;
     588          46 :         int status;
     589          46 :         int pipe_stdin[2];
     590          46 :         int pipe_stdout[2];
     591          46 :         int pipe_stderr[2];
     592          46 :         char errorBuffer[512] = "";
     593          46 :         kdb_octet_t * buffer = NULL;
     594          46 :         ssize_t bufferSize = 2 * keyGetValueSize (msgKey);
     595          46 :         ssize_t outputLen;
     596             : 
     597          46 :         assert (argc > 1);
     598             : 
     599             :         // check if bufferSize is valid
     600          46 :         if (bufferSize <= 0)
     601             :         {
     602          19 :                 bufferSize = GPG_OUTPUT_DEFAULT_BUFFER_SIZE;
     603             :         }
     604             : 
     605             :         // sanitize the argument vector
     606          46 :         if (ELEKTRA_PLUGIN_FUNCTION (gpgGetBinary) (&argv[0], conf, errorKey) != 1)
     607             :         {
     608             :                 return -1;
     609             :         }
     610          46 :         argv[argc - 1] = NULL;
     611             : 
     612             :         // check that the gpg binary exists and that it is executable
     613          46 :         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          46 :         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          46 :         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          46 :         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          46 :         if (msgKey && !(buffer = elektraMalloc (bufferSize)))
     647             :         {
     648           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey);
     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          46 :         switch (pid = fork ())
     658             :         {
     659           0 :         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          46 :         case 0:
     670             :                 // start of the forked child process
     671          46 :                 close (pipe_stdin[1]);
     672          46 :                 close (pipe_stdout[0]);
     673          46 :                 close (pipe_stderr[0]);
     674             : 
     675             :                 // redirect stdin to pipe
     676          46 :                 if (msgKey)
     677             :                 {
     678          27 :                         close (STDIN_FILENO);
     679          27 :                         if (dup (pipe_stdin[0]) < 0)
     680             :                         {
     681           0 :                                 exit (GPG_CALL_DUP_STDIN);
     682             :                         }
     683             :                 }
     684          46 :                 close (pipe_stdin[0]);
     685             : 
     686             :                 // redirect stdout to pipe
     687          46 :                 close (STDOUT_FILENO);
     688          46 :                 if (dup (pipe_stdout[1]) < 0)
     689             :                 {
     690           0 :                         exit (GPG_CALL_DUP_STDOUT);
     691             :                 }
     692          46 :                 close (pipe_stdout[1]);
     693             : 
     694             :                 // redirect stderr to pipe
     695          46 :                 close (STDERR_FILENO);
     696          46 :                 if (dup (pipe_stderr[1]) < 0)
     697             :                 {
     698           0 :                         exit (GPG_CALL_DUP_STDERR);
     699             :                 }
     700          46 :                 close (pipe_stderr[1]);
     701             : 
     702             :                 // finally call the gpg executable
     703          46 :                 if (execv (argv[0], argv) < 0)
     704             :                 {
     705             :                         // errno is set according to the man page of execv
     706           0 :                         fprintf (stderr, "%d", errno);
     707           0 :                         exit (GPG_CALL_EXECV);
     708             :                 }
     709             :                 // end of the child process
     710             :         }
     711             : 
     712             :         // parent process
     713          92 :         close (pipe_stdin[0]);
     714          46 :         close (pipe_stdout[1]);
     715          46 :         close (pipe_stderr[1]);
     716             : 
     717             :         // pass the message to the gpg process
     718          46 :         const ssize_t sendMessageSize = keyGetValueSize (msgKey);
     719          46 :         if (msgKey && sendMessageSize > 0)
     720             :         {
     721          27 :                 if (write (pipe_stdin[1], keyValue (msgKey), sendMessageSize) != sendMessageSize)
     722             :                 {
     723           0 :                         ELEKTRA_SET_RESOURCE_ERROR (errorKey, "The communication with the GPG process failed");
     724           0 :                         closePipe (pipe_stdin);
     725           0 :                         closePipe (pipe_stdout);
     726           0 :                         closePipe (pipe_stderr);
     727           0 :                         elektraFree (buffer);
     728           0 :                         elektraFree (argv[0]);
     729           0 :                         return -1;
     730             :                 }
     731             :         }
     732          46 :         close (pipe_stdin[1]);
     733             : 
     734             :         // wait for the gpg process to finish
     735          46 :         waitpid (pid, &status, 0);
     736             : 
     737             :         // evaluate return code of finished child process
     738          46 :         int retval = -1;
     739          46 :         switch (status)
     740             :         {
     741          44 :         case 0:
     742             :                 // everything ok - receive the output of the gpg process
     743          44 :                 if (msgKey)
     744             :                 {
     745          27 :                         outputLen = read (pipe_stdout[0], buffer, bufferSize);
     746          27 :                         keySetBinary (msgKey, buffer, outputLen);
     747             :                 }
     748             :                 retval = 1;
     749             :                 break;
     750             : 
     751           0 :         case 1:
     752             :                 // bad signature
     753           0 :                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "GPG reported a bad signature. Reason: %s", strerror (errno));
     754           0 :                 break;
     755             : 
     756           0 :         case GPG_CALL_DUP_STDIN:
     757           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Failed to redirect stdin");
     758           0 :                 break;
     759             : 
     760           0 :         case GPG_CALL_DUP_STDOUT:
     761           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Failed to redirect stdout");
     762           0 :                 break;
     763             : 
     764           0 :         case GPG_CALL_DUP_STDERR:
     765           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Failed to redirect stderr");
     766           0 :                 break;
     767             : 
     768           0 :         case GPG_CALL_EXECV:
     769           0 :                 outputLen = read (pipe_stderr[0], errorBuffer, sizeof (errorBuffer));
     770           0 :                 if (outputLen < 1)
     771             :                 {
     772           0 :                         ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Failed to start the gpg binary \"%s\"", argv[0]);
     773             :                 }
     774             :                 else
     775             :                 {
     776           0 :                         ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Failed to start the gpg binary \"%s\", reason: %s", argv[0],
     777             :                                                          strerror (atoi (errorBuffer)));
     778             :                 }
     779             :                 break;
     780             : 
     781           2 :         default:
     782             :                 // other errors
     783           2 :                 outputLen = read (pipe_stderr[0], errorBuffer, sizeof (errorBuffer));
     784           2 :                 if (outputLen < 1)
     785             :                 {
     786           0 :                         errorBuffer[0] = '\0';
     787             :                 }
     788           2 :                 ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "GPG failed with return value %d. %s", status, errorBuffer);
     789           2 :                 break;
     790             :         }
     791             : 
     792          46 :         elektraFree (buffer);
     793          46 :         elektraFree (argv[0]);
     794          46 :         close (pipe_stdout[0]);
     795          46 :         close (pipe_stderr[0]);
     796          46 :         return retval;
     797             : }

Generated by: LCOV version 1.13