/** * Check if the key check is direct below the key key or not. * @verbatim Example: key user/sw/app check user/sw/app/key returns true because check is below key Example: key user/sw/app check user/sw/app/folder/key does not return true, because there is only a indirect relation @endverbatim * * @param key the key object to work with * @param check the key to find the relative position of * @retval 1 if check is below key * @retval 0 if it is not below or if it is the same key * @retval -1 on null pointer * @see keyIsBelow(), keySetName(), keyGetName() * @ingroup keytest * */ int keyIsDirectBelow (const Key * key, const Key * check) { if (!key || !check) return -1; if (!keyIsBelow (key, check)) return 0; const char * checkname = keyUnescapedName (check); ssize_t keysize = keyGetUnescapedNameSize (key); ssize_t checksize = keyGetUnescapedNameSize (check); char * startPtr = NULL; if (keyName (key)[0] != '/') { startPtr = strrchr (checkname + keysize, '\0'); } else { if (keyName (check)[0] != '/') { startPtr = strrchr (checkname, '\0'); startPtr = strrchr (startPtr + keysize, '\0'); } else { startPtr = strrchr (checkname + keysize, '\0'); } } if (startPtr == checkname + checksize - 1) return 1; return 0; }
/** * Check if a key is below or same or not. * * @param key the key object to work with * @see keyIsBelow() */ int keyIsBelowOrSame (const Key *key, const Key *check) { if (!key || !check) return -1; const char *name1 = keyName(key); const char *name2 = keyName(check); if (keyIsBelow (key, check)) return 1; else if (!strcmp (name1, name2)) return 1; return 0; }
/* The KeySet MUST be sorted alphabetically (or at least ascending * by the length of keynames) for this function to work */ static Key * findNearestParent (Key * key, KeySet * ks) { Key * current; ksSetCursor (ks, ksGetSize (ks) - 1); while ((current = ksPrev (ks)) != 0) { if (keyIsBelow (current, key)) { return current; } } return 0; }
/** * @internal * Check if a key has the same name or is below a given key. * * @param key key * @param check check * @retval 1 if key has the same name or is below * @retval 0 otherwise */ static int checkKeyIsBelowOrSame (Key * key, Key * check) { int result = 0; if (keyIsBelow (key, check)) { result = 1; } else { result = checkKeyIsSame (key, check); } return result; }
/** * Check if the key check is direct below the key key or not. * @verbatim Example: key user/sw/app check user/sw/app/key returns true because check is below key Example: key user/sw/app check user/sw/app/folder/key does not return true, because there is only a indirect relation @endverbatim * * @param key the key object to work with * @param check the key to find the relative position of * @retval 1 if check is below key * @retval 0 if it is not below or if it is the same key * @retval -1 on null pointer * @see keyIsBelow(), keySetName(), keyGetName() * @ingroup keytest * */ int keyIsDirectBelow(const Key *key, const Key *check) { if (!key || !check) return -1; if (!keyIsBelow(key, check)) return 0; const char * checkname = keyUnescapedName(check); ssize_t keysize = keyGetUnescapedNameSize(key); ssize_t checksize = keyGetUnescapedNameSize(check); if (strchr(checkname + keysize, '\0') == checkname + checksize - 1) return 1; return 0; }
/** * Check if the key check is direct below the key key or not. * @verbatim Example: key user/sw/app check user/sw/app/key returns true because check is below key Example: key user/sw/app check user/sw/app/folder/key does not return true, because there is only a indirect relation @endverbatim * * @param key the key object to work with * @param check the key to find the relative position of * @return 1 if check is below key * @return 0 if it is not below or if it is the same key * @return -1 on null pointer * @see keyIsBelow(), keySetName(), keyGetName() * @ingroup keytest * */ int keyIsDirectBelow(const Key *key, const Key *check) { const char * checkname = 0; ssize_t keysize = 0; if (!key || !check) return -1; checkname = keyName(check); keysize = keyGetNameSize(key); if (!keyIsBelow(key, check)) return 0; if (strchr(checkname + keysize, '/')) return 0; return 1; }
/** * Information about the relation in the hierarchy between * two keys. * * Unlike keyCmp() the number gives information * about hierarchical information. * * * - If the keys are the same 0 is returned. * So it is the key itself. @verbatim user/key user/key @endverbatim * *@code keySetName (key, "user/key/folder"); keySetName (check, "user/key/folder"); succeed_if (keyRel (key, check) == 0, "should be same"); *@endcode * * @note this relation can be checked with keyCmp() too. * * * - If the key is direct below the other one 1 is returned. * That means that, in terms of hierarchy, no other key is * between them - it is a direct child. @verbatim user/key/folder user/key/folder/child @endverbatim * *@code keySetName (key, "user/key/folder"); keySetName (check, "user/key/folder/child"); succeed_if (keyRel (key, check) == 1, "should be direct below"); *@endcode * * * - If the key is below the other one, but not directly 2 is returned. * This is also called grand-child. @verbatim user/key/folder user/key/folder/any/depth/deeper/grand-child @endverbatim * * *@code keySetName (key, "user/key/folder"); keySetName (check, "user/key/folder/any/depth/deeper/grand-child"); succeed_if (keyRel (key, check) >= 2, "should be below (but not direct)"); succeed_if (keyRel (key, check) > 0, "should be below"); succeed_if (keyRel (key, check) >= 0, "should be the same or below"); *@endcode * * * - If a invalid or null ptr key is passed, -1 is returned * * * - If the keys have no relations, but are not invalid, -2 is returned. * * * - If the keys are in the same hierarchy, a value smaller then -2 is returned. * It means that the key is not below. @verbatim user/key/myself user/key/sibling @endverbatim * * @code keySetName (key, "user/key/folder"); keySetName (check, "user/notsame/folder"); succeed_if (keyRel (key, check) < -2, "key is not below, but same namespace"); * @endcode * * @code * @endcode * * * TODO Below is an idea how it could be extended: * It could continue the search into the other direction * if any (grand-)parents are equal. * * - If the keys are direct below a key which is next to the key, -2 is returned. * This is also called nephew. (TODO not implemented) * @verbatim user/key/myself user/key/sibling @endverbatim * * - If the keys are direct below a key which is next to the key, -2 is returned. * This is also called nephew. (TODO not implemented) * @verbatim user/key/myself user/key/sibling/nephew @endverbatim * * - If the keys are below a key which is next to the key, -3 is returned. * This is also called grand-nephew. (TODO not implemented) @verbatim user/key/myself user/key/sibling/any/depth/deeper/grand-nephew @endverbatim * * The same holds true for the other direction, but with negative values. * For no relation INT_MIN is returned. * * @note to check if the keys are the same, you must use * keyCmp() == 0! * keyRel() does not give you the information if it did not * find a relation or if it is the same key. * * @return depending on the relation * @retval 2 if below * @retval 1 if direct below * @retval 0 if the same * @retval -1 on null or invalid keys * @retval -2 if none of any other relation * @retval -3 if same hierarchy (none of those below) * @retval -4 if sibling (in same hierarchy) * @retval -5 if nephew (in same hierarchy) * * @param key the key object to work with * @param check the second key object to check the relation with * @ingroup keytest */ int keyRel (const Key * key, const Key * check) { if (!key || !check) return -1; if (!key->key || !check->key) return -1; if (!keyCmp (key, check)) return 0; if (keyIsDirectBelow (key, check)) return 1; if (keyIsBelow (key, check)) return 2; if (keyIsUser (key) && keyIsUser (check)) return -3; if (keyIsSystem (key) && keyIsSystem (check)) return -3; // if (keyIsSibling(key, check)) return -4; // if (keyIsNephew(key, check)) return -5; return -2; }
// keyRel2 helper, returns how many levels check is below key, or 0 if check isn't below int keyGetLevelsBelow (const Key * key, const Key * check) { if (!keyIsBelow (key, check)) return 0; if (keyGetNamespace (key) != keyGetNamespace (check)) return 0; Key * toCheck = keyDup (check); int levels = 0; while (strcmp (keyName (key), keyName (toCheck))) { keySetBaseName (toCheck, 0); if (keyName (toCheck)[0] == '\0') keySetName (toCheck, "/"); ++levels; } keyDel (toCheck); return levels; }
/** * @brief Read number of total GPG recipient keys from the plugin configuration. * @param config holds the plugin configuration * @param keyName holds the name of the root key to look up * @returns the number of GPG recipient keys. */ static size_t getRecipientCount (KeySet * config, const char * keyName) { Key * k; size_t recipientCount = 0; Key * root = ksLookupByName (config, keyName, 0); if (!root) return 0; // toplevel if (strlen (keyString (root)) > 0) { recipientCount++; } ksRewind (config); while ((k = ksNext (config)) != 0) { if (keyIsBelow (k, root) && strlen (keyString (k)) > 0) { recipientCount++; } } return recipientCount; }
/** * @brief encrypt or sign the file specified at parentKey * @param pluginConfig holds the plugin configuration * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure. * @retval 1 on success * @retval -1 on error, errorKey holds an error description */ static int fcryptEncrypt (KeySet * pluginConfig, Key * parentKey) { Key * k; const size_t recipientCount = getRecipientCount (pluginConfig, ELEKTRA_RECIPIENT_KEY); const size_t signatureCount = getRecipientCount (pluginConfig, ELEKTRA_SIGNATURE_KEY); if (recipientCount == 0 && signatureCount == 0) { ELEKTRA_SET_ERRORF ( ELEKTRA_ERROR_NO_GPG_RECIPIENTS, parentKey, "Missing GPG recipient key (specified as %s) or GPG signature key (specified as %s) in plugin configuration.", ELEKTRA_RECIPIENT_KEY, ELEKTRA_SIGNATURE_KEY); return -1; } int tmpFileFd = -1; char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd); if (!tmpFile) { ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed"); return -1; } const size_t testMode = inTestMode (pluginConfig); const size_t textMode = inTextMode (pluginConfig); // prepare argument vector for gpg call // 7 static arguments (magic number below) are: // 1. path to the binary // 2. --batch // 3. -o // 4. path to tmp file // 5. yes // 6. file to be encrypted // 7. NULL terminator int argc = 7 + (2 * recipientCount) + (2 * signatureCount) + (2 * testMode) + textMode + (recipientCount > 0 ? 1 : 0) + (signatureCount > 0 ? 1 : 0); kdb_unsigned_short_t i = 0; char * argv[argc]; argv[i++] = NULL; argv[i++] = "--batch"; argv[i++] = "-o"; argv[i++] = tmpFile; argv[i++] = "--yes"; // overwrite files if they exist // add recipients Key * gpgRecipientRoot = ksLookupByName (pluginConfig, ELEKTRA_RECIPIENT_KEY, 0); // append root (gpg/key) as gpg recipient if (gpgRecipientRoot && strlen (keyString (gpgRecipientRoot)) > 0) { argv[i++] = "-r"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) keyString (gpgRecipientRoot); } // append keys beneath root (crypto/key/#_) as gpg recipients if (gpgRecipientRoot) { ksRewind (pluginConfig); while ((k = ksNext (pluginConfig)) != 0) { const char * kStringVal = keyString (k); if (keyIsBelow (k, gpgRecipientRoot) && strlen (kStringVal) > 0) { argv[i++] = "-r"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) kStringVal; } } } // add signature keys Key * gpgSignatureRoot = ksLookupByName (pluginConfig, ELEKTRA_SIGNATURE_KEY, 0); // append root signature key if (gpgSignatureRoot && strlen (keyString (gpgSignatureRoot)) > 0) { argv[i++] = "-u"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) keyString (gpgSignatureRoot); } // append keys beneath root (fcrypt/sign/#_) as gpg signature keys if (gpgSignatureRoot) { ksRewind (pluginConfig); while ((k = ksNext (pluginConfig)) != 0) { const char * kStringVal = keyString (k); if (keyIsBelow (k, gpgSignatureRoot) && strlen (kStringVal) > 0) { argv[i++] = "-u"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) kStringVal; } } } // if we are in test mode we add the trust model if (testMode > 0) { argv[i++] = "--trust-model"; argv[i++] = "always"; } // ASCII armor in text mode if (textMode) { argv[i++] = "--armor"; } // prepare rest of the argument vector if (recipientCount > 0) { // encrypt the file argv[i++] = "-e"; } if (signatureCount > 0) { if (textMode && recipientCount == 0) { // clear-sign the file argv[i++] = "--clearsign"; } else { // sign the file argv[i++] = "-s"; } } argv[i++] = (char *) keyString (parentKey); argv[i++] = NULL; // NOTE the encryption process works like this: // gpg2 --batch --yes -o encryptedFile -r keyID -e configFile // mv encryptedFile configFile return fcryptGpgCallAndCleanup (parentKey, pluginConfig, argv, argc, tmpFileFd, tmpFile); }
int below_a (Key *check) { return keyIsBelow(global_a, check); }