Beispiel #1
0
int elektraCryptoGcryDecrypt(elektraCryptoHandle *handle, Key *k, Key *errorKey)
{
	kdb_octet_t *value = (kdb_octet_t*)keyValue(k);
	const size_t valueLen = keyGetValueSize(k);

	kdb_octet_t *output;
	kdb_octet_t cipherBuffer[ELEKTRA_CRYPTO_GCRY_BLOCKSIZE];
	kdb_octet_t contentBuffer[ELEKTRA_CRYPTO_GCRY_BLOCKSIZE];
	kdb_unsigned_long_t written = 0;
	gcry_error_t gcry_err;

	// initialize crypto header data
	kdb_unsigned_long_t contentLen = 0;
	kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE;

	// check if key has been encrypted in the first place
	const Key *metaEncrypted = keyGetMeta(k, ELEKTRA_CRYPTO_META_ENCRYPT);
	if (metaEncrypted == NULL || strlen(keyValue(metaEncrypted)) == 0)
	{
		// nothing to do
		return 1;
	}

	// plausibility check
	if (valueLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE != 0)
	{
		ELEKTRA_SET_ERROR(128, errorKey, "value length is not a multiple of the block size");
		return (-1);
	}

	// prepare buffer for plain text output
	output = elektraMalloc(valueLen);
	if (output == NULL)
	{
		ELEKTRA_SET_ERROR(87, errorKey, "Memory allocation failed");
		return (-1);
	}

	// decrypt the header (1st block)
	memcpy(cipherBuffer, value, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
	gcry_err = gcry_cipher_decrypt(*handle, contentBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, cipherBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
	if (gcry_err != 0)
	{
		ELEKTRA_SET_ERRORF(128, errorKey, "Decryption failed because: %s", gcry_strerror(gcry_err));
		elektraFree(output);
		return (-1);
	}

	// restore the header data
	memcpy(&flags, contentBuffer, sizeof(flags));
	memcpy(&contentLen, contentBuffer+sizeof(flags), sizeof(contentLen));

	// decrypt content block by block
	// (i = start of the current block and the 1st block has already been consumed)
	for (kdb_unsigned_long_t i = ELEKTRA_CRYPTO_GCRY_BLOCKSIZE; i < valueLen; i += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE)
	{
		memcpy(cipherBuffer, (value + i), ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
		gcry_err = gcry_cipher_decrypt(*handle, contentBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, cipherBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
		if (gcry_err != 0)
		{
			ELEKTRA_SET_ERRORF(128, errorKey, "Decryption failed because: %s", gcry_strerror(gcry_err));
			elektraFree(output);
			return (-1);
		}
		memcpy((output + i - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE), contentBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
		written += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
	}

	if (written < contentLen)
	{
		ELEKTRA_SET_ERROR(128, errorKey, "Content was shorter than described in the header");
		elektraFree(output);
		return (-1);
	}

	// write back the cipher text to the key
	if ((flags & ELEKTRA_CRYPTO_FLAG_STRING) == ELEKTRA_CRYPTO_FLAG_STRING)
	{
		keySetString(k, (const char*)output);
	}
	else if ((flags & ELEKTRA_CRYPTO_FLAG_NULL) == ELEKTRA_CRYPTO_FLAG_NULL || contentLen == 0)
	{
		keySetBinary(k, NULL, 0);
	}
	else
	{
		keySetBinary(k, output, contentLen);
	}

	elektraFree(output);
	return 1;
}
static int evalCondition(const char *leftSide, Comparator cmpOp, const char *rightSide, KeySet *ks, Key *parentKey)
{
	char *lookupName = NULL;
	char *compareTo = NULL;
	Key *key;
	int len;
	long result = 0;
	if(rightSide[0] == '\'')
	{
		char *endPos = strchr(rightSide+1, '\'');
		if(!endPos)
		{
			result = -1;
			goto Cleanup;
		}
		if(elektraRealloc((void **)&compareTo, endPos-rightSide) < 0)
		{
			ELEKTRA_SET_ERROR(87, parentKey, "Out of memory");
			result = -1;
			goto Cleanup;
		}
		memset(compareTo, 0, endPos-rightSide);
		strncat(compareTo, rightSide+1, endPos-rightSide-1);
	}
	else if(rightSide && elektraStrLen(rightSide) > 1)
	{
		len = keyGetNameSize(parentKey)+elektraStrLen(rightSide);
		if(elektraRealloc((void **)&lookupName, len) < 0)
		{
			ELEKTRA_SET_ERROR(87, parentKey, "Out of memory");
			result = -1;
			goto Cleanup;
		}
		snprintf(lookupName, len, "%s/%s", keyName(parentKey), rightSide);
		key = ksLookupByName(ks, lookupName, 0);
		if(!key)
		{
			ELEKTRA_SET_ERRORF(133, parentKey, "Key %s doesn't exist", lookupName);
			result = -1;
			goto Cleanup;
		}
		if(elektraRealloc((void **)&compareTo, keyGetValueSize(key)) < 0)
		{
			ELEKTRA_SET_ERROR(87, parentKey, "Out of memory");
			result = -1;
			goto Cleanup;
		}
		strcpy(compareTo, keyString(key));
	}

	len = keyGetNameSize(parentKey)+elektraStrLen(leftSide);
	if(elektraRealloc((void **)&lookupName, len) < 0)
	{
		ELEKTRA_SET_ERROR(87, parentKey, "Out of memory");
		result = -1;
		goto Cleanup;
	}
	snprintf(lookupName, len, "%s/%s", keyName(parentKey), leftSide);
	key = ksLookupByName(ks, lookupName, 0);
	if(!key)
	{
		ELEKTRA_SET_ERRORF(133, parentKey, "Key %s doesn't exist", lookupName);
		result = -1;
		goto Cleanup;
	}
	long ret;
	ret = compareStrings(keyString(key), compareTo);
	switch(cmpOp)
	{
		case EQU:
			if(!ret)
				result = 1;
			break;
		case NOT:
			if(ret)
				result = 1;
			break;
		case LT:
			if(ret < 0)
				result = 1;
			break;
		case LE:
			if(ret <= 0)
				result = 1;
			break;
		case GT:
			if(ret > 0)
				result = 1;
			break;
		case GE:
			if(ret >= 0)
				result = 1;
			break;
		case SET:
			keySetString(key, compareTo);
			result = 1;
			break;
		default:
			result = -1;
			break;
	}
//freeing allocated heap
Cleanup:
	if(lookupName)
		elektraFree(lookupName);
	if(compareTo)
		elektraFree(compareTo);
	return result;
}
Beispiel #3
0
static int iniKeyToElektraKey (void *vhandle, const char *section, const char *name, const char *value, unsigned short lineContinuation)
{
	CallbackHandle *handle = (CallbackHandle *)vhandle;
	Key *appendKey = keyDup (handle->parentKey);
	keySetMeta(appendKey, "ini/lastSection", 0);
	if (!section || *section == '\0')
	{
		section = INTERNAL_ROOT_SECTION;
	}
	appendKey = createUnescapedKey(appendKey, section);
	short mergeSections = 0;
	Key *existingKey = NULL;
	if ((existingKey = ksLookup(handle->result, appendKey, KDB_O_NONE)))
	{
		if (keyGetMeta(existingKey, "ini/duplicate"))
		{
			mergeSections = 1;
		}
	}
	setSectionNumber(handle->parentKey, appendKey, handle->result);
	appendKey = createUnescapedKey(appendKey, name);
	existingKey = ksLookup(handle->result, appendKey, KDB_O_NONE);

	if (existingKey)
	{
		//a key with the same name already exists
		if (handle->array)
		{
			//array support is turned on 
			keySetMeta(appendKey, "ini/section", 0);
			if (keyGetMeta(existingKey, "ini/array"))
			{
				//array already exists, appending new key
				const char *lastIndex = keyString(keyGetMeta(existingKey, "ini/array"));
				keyAddBaseName(appendKey, lastIndex);
				keySetMeta(appendKey, "order/parent", 0);
				keySetMeta(appendKey, "ini/array", 0);
				keySetMeta(appendKey, "order", 0);
				if (elektraArrayIncName(appendKey) == 1)
				{
					return -1;
				}
				keySetString(appendKey, value);
				keySetMeta(appendKey, "ini/key", 0);
				ksAppendKey(handle->result, appendKey);
				keySetMeta(existingKey, "ini/array", keyBaseName(appendKey));
				ksAppendKey(handle->result, existingKey);
			}
			else
			{
				//creating a new array
				Key *sectionKey = keyDup(appendKey);
				keyAddName(sectionKey, "..");
				char *origVal = strdup(keyString(existingKey));
				keySetString(appendKey, "");
				keySetMeta(appendKey, "ini/array", "#1");
				keySetMeta(appendKey, "order/parent", keyName(sectionKey));
				setSectionNumber(handle->parentKey, appendKey, handle->result);
				setOrderNumber(handle->parentKey, appendKey);
				keySetMeta(appendKey, "ini/key", "");
				ksAppendKey(handle->result, keyDup(appendKey));
				keySetMeta(appendKey, "ini/key", 0);
				keySetMeta(appendKey, "ini/array", 0);
				keySetMeta(appendKey, "parent", 0);
				keyAddName(appendKey, "#");
				keySetMeta(appendKey, "order", 0);
				if (elektraArrayIncName(appendKey) == -1)
				{
					free(origVal);
					return -1;
				}
				keySetString(appendKey, origVal);
				ksAppendKey(handle->result, keyDup(appendKey));
				free(origVal);
				if (elektraArrayIncName(appendKey) == -1)
				{
					return -1;
				}
				keySetMeta(appendKey, "parent", 0);
				keySetString(appendKey, value);
				ksAppendKey(handle->result, keyDup(appendKey));
				keyDel(appendKey);
				keyDel(sectionKey);
			}
			return 1;
		}
		else if(!lineContinuation)
		{
			ELEKTRA_SET_ERRORF(141, handle->parentKey, "Key: %s\n", name);
			return -1;
		}
	}

	setSectionNumber(handle->parentKey, appendKey, handle->result);
	if (value == NULL)
		keySetMeta(appendKey, "ini/empty", "");
	if (!lineContinuation)
	{
		flushCollectedComment (handle, appendKey);
		keySetString (appendKey, value);
		keySetMeta(appendKey, "ini/key", "");
		ksAppendKey (handle->result, appendKey);
		if (mergeSections)
		{
			keySetMeta(appendKey, "order", 0);
			insertNewKeyIntoExistendOrder(appendKey, handle->result);
		}
		else
		{
			setOrderNumber(handle->parentKey, appendKey);
		}
	}
	else
	{
		existingKey = ksLookup (handle->result, appendKey, KDB_O_NONE);
		keyDel (appendKey);
		/* something went wrong before because this key should exist */
		if (!existingKey) return -1;

		elektraKeyAppendLine(existingKey, value);
	}


	return 1;
}
Beispiel #4
0
int elektraCryptoGcryEncrypt(elektraCryptoHandle *handle, Key *k, Key *errorKey)
{
	const kdb_octet_t *value = (kdb_octet_t*)keyValue(k);
	size_t outputLen;
	gcry_error_t gcry_err;

	kdb_octet_t *output;
	kdb_octet_t cipherBuffer[ELEKTRA_CRYPTO_GCRY_BLOCKSIZE];
	kdb_octet_t contentBuffer[ELEKTRA_CRYPTO_GCRY_BLOCKSIZE] = { 0 };

	// check if key has been marked for encryption
	const Key *metaEncrypt = keyGetMeta(k, ELEKTRA_CRYPTO_META_ENCRYPT);
	if (metaEncrypt == NULL || strlen(keyValue(metaEncrypt)) == 0)
	{
		// nothing to do
		return 1;
	}

	// prepare the crypto header data
	const kdb_unsigned_long_t contentLen = keyGetValueSize(k);
	kdb_octet_t flags;

	switch (keyIsString(k))
	{
	case 1: // string
		flags = ELEKTRA_CRYPTO_FLAG_STRING;
		break;
	case -1: // NULL pointer
		flags = ELEKTRA_CRYPTO_FLAG_NULL;
		break;
	default: // binary
		flags = ELEKTRA_CRYPTO_FLAG_NONE;
		break;
	}

	// prepare buffer for cipher text output
	// NOTE the header goes into the first block
	if (contentLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE == 0)
	{
		outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 1;
	}
	else
	{
		outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 2;
	}
	outputLen *= ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
	output = elektraMalloc(outputLen);
	if (output == NULL)
	{
		ELEKTRA_SET_ERROR(87, errorKey, "Memory allocation failed");
		return (-1);
	}

	// encrypt the header (1st block)
	memcpy(contentBuffer, &flags, sizeof(flags));
	memcpy(contentBuffer+sizeof(flags), &contentLen, sizeof(contentLen));
	gcry_err = gcry_cipher_encrypt(*handle, cipherBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, contentBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
	if (gcry_err != 0)
	{
		ELEKTRA_SET_ERRORF(127, errorKey, "Encryption failed because: %s", gcry_strerror(gcry_err));
		elektraFree(output);
		return (-1);
	}
	memcpy(output, cipherBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);

	// encrypt content block by block (i = start of the current block)
	for (kdb_unsigned_long_t i = 0; i < contentLen; i += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE)
	{
		// load content partition into the content buffer
		kdb_unsigned_long_t partitionLen = ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
		if ((i + 1) * ELEKTRA_CRYPTO_GCRY_BLOCKSIZE > contentLen)
		{
			partitionLen = contentLen - (i * ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
		}
		memcpy(contentBuffer, (value + i), partitionLen);

		gcry_err = gcry_cipher_encrypt(*handle, cipherBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, contentBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
		if (gcry_err != 0)
		{
			ELEKTRA_SET_ERRORF(127, errorKey, "Encryption failed because: %s", gcry_strerror(gcry_err));
			elektraFree(output);
			return (-1);
		}
		memcpy((output + i + ELEKTRA_CRYPTO_GCRY_BLOCKSIZE), cipherBuffer, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE);
	}

	// write back the cipher text to the key
	keySetBinary(k, output, outputLen);
	elektraFree(output);

	return 1;
}
Beispiel #5
0
/**
 * @brief Retrieve keys in an atomic and universal way.
 *
 * @pre The @p handle must be passed as returned from kdbOpen().
 *
 * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
 *     with ksNew().
 *
 * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
 *     keyNew().
 *
 * If you pass NULL on any parameter kdbGet() will fail immediately without doing anything.
 *
 * The @p returned KeySet may already contain some keys, e.g. from previous
 * kdbGet() calls. The new retrieved keys will be appended using
 * ksAppendKey().
 *
 * If not done earlier kdbGet() will fully retrieve all keys under the @p parentKey
 * folder recursively (See Optimization below when it will not be done).
 *
 * @note kdbGet() might retrieve more keys than requested (that are not
 *     below parentKey). These keys must be passed to calls of kdbSet(),
 *     otherwise they will be lost. This stems from the fact that the
 *     user has the only copy of the whole configuration and backends
 *     only write configuration that was passed to them.
 *     For example, if you kdbGet() "system/mountpoint/interest"
 *     you will not only get all keys below system/mountpoint/interest,
 *     but also all keys below system/mountpoint (if system/mountpoint
 *     is a mountpoint as the name suggests, but
 *     system/mountpoint/interest is not a mountpoint).
 *     Make sure to not touch or remove keys outside the keys of interest,
 *     because others may need them!
 *
 * @par Example:
 * This example demonstrates the typical usecase within an application
 * (without error handling).
 *
 * @include kdbget.c
 *
 * When a backend fails kdbGet() will return -1 with all
 * error and warning information in the @p parentKey.
 * The parameter @p returned will not be changed.
 *
 * @par Optimization:
 * In the first run of kdbGet all requested (or more) keys are retrieved. On subsequent
 * calls only the keys are retrieved where something was changed
 * inside the key database. The other keys stay in the
 * KeySet returned as passed.
 *
 * It is your responsibility to save the original keyset if you
 * need it afterwards.
 *
 * If you want to be sure to get a fresh keyset again, you need to open a
 * second handle to the key database using kdbOpen().
 *
 * @param handle contains internal information of @link kdbOpen() opened @endlink key database
 * @param parentKey is used to add warnings and set an error
 *         information. Additionally, its name is a hint which keys
 *         should be retrieved (it is possible that more are retrieved, see Note above).
 *           - cascading keys (starting with /) will retrieve the same path in all namespaces
 *           - / will retrieve all keys
 * @param ks the (pre-initialized) KeySet returned with all keys found
 * 	will not be changed on error or if no update is required
 * @see ksLookup(), ksLookupByName() for powerful
 * 	lookups after the KeySet was retrieved
 * @see kdbOpen() which needs to be called before
 * @see kdbSet() to save the configuration afterwards and kdbClose() to
 * 	finish affairs with the key database.
 * @retval 1 if the keys were retrieved successfully
 * @retval 0 if there was no update - no changes are made to the keyset then
 * @retval -1 on failure - no changes are made to the keyset then
 * @ingroup kdb
 */
int kdbGet (KDB * handle, KeySet * ks, Key * parentKey)
{
	elektraNamespace ns = keyGetNamespace (parentKey);
	if (ns == KEY_NS_NONE)
	{
		return -1;
	}

	Key * oldError = keyNew (keyName (parentKey), KEY_END);
	copyError (oldError, parentKey);

	if (ns == KEY_NS_META)
	{
		clearError (parentKey);
		keyDel (oldError);
		ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbGet", keyName (parentKey));
		return -1;
	}

	if (ns == KEY_NS_EMPTY)
	{
		ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbGet");
	}

	int errnosave = errno;
	Key * initialParent = keyDup (parentKey);

	ELEKTRA_LOG ("now in new kdbGet (%s)", keyName (parentKey));

	Split * split = splitNew ();

	if (!handle || !ks)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer");
		goto error;
	}

	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, INIT);
	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, MAXONCE);
	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, DEINIT);

	if (splitBuildup (split, handle, parentKey) == -1)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup");
		goto error;
	}

	// Check if a update is needed at all
	switch (elektraGetCheckUpdateNeeded (split, parentKey))
	{
	case 0: // We don't need an update so let's do nothing
		keySetName (parentKey, keyName (initialParent));
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
		splitUpdateFileName (split, handle, parentKey);
		keyDel (initialParent);
		splitDel (split);
		errno = errnosave;
		keyDel (oldError);
		return 0;
	case -1:
		goto error;
		// otherwise fall trough
	}

	// Appoint keys (some in the bypass)
	if (splitAppoint (split, handle, ks) == -1)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitAppoint");
		goto error;
	}

	if (handle->globalPlugins[POSTGETSTORAGE][FOREACH] || handle->globalPlugins[POSTGETCLEANUP][FOREACH])
	{
		clearError (parentKey);
		if (elektraGetDoUpdateWithGlobalHooks (NULL, split, NULL, parentKey, initialParent, FIRST) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}

		keySetName (parentKey, keyName (initialParent));

		if (splitGet (split, parentKey, handle) == -1)
		{
			ELEKTRA_ADD_WARNING (108, parentKey, keyName (ksCurrent (ks)));
			// continue, because sizes are already updated
		}
		ksClear (ks);
		splitMerge (split, ks);

		clearError (parentKey);
		if (elektraGetDoUpdateWithGlobalHooks (handle, split, ks, parentKey, initialParent, LAST) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}
	}
	else
	{

		/* Now do the real updating,
		   but not for bypassed keys in split->size-1 */
		clearError (parentKey);
		if (elektraGetDoUpdate (split, parentKey) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}
		/* Now post-process the updated keysets */
		if (splitGet (split, parentKey, handle) == -1)
		{
			ELEKTRA_ADD_WARNING (108, parentKey, keyName (ksCurrent (ks)));
			// continue, because sizes are already updated
		}
		/* We are finished, now just merge everything to returned */
		ksClear (ks);

		splitMerge (split, ks);
	}

	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);

	ksRewind (ks);

	keySetName (parentKey, keyName (initialParent));

	splitUpdateFileName (split, handle, parentKey);
	keyDel (initialParent);
	keyDel (oldError);
	splitDel (split);
	errno = errnosave;
	return 1;

error:
	keySetName (parentKey, keyName (initialParent));
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, INIT);
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);

	keySetName (parentKey, keyName (initialParent));
	if (handle) splitUpdateFileName (split, handle, parentKey);
	keyDel (initialParent);
	keyDel (oldError);
	splitDel (split);
	errno = errnosave;
	return -1;
}
Beispiel #6
0
/** @brief Set keys in an atomic and universal way.
 *
 * @pre kdbGet() must be called before kdbSet():
 *    - initially (after kdbOpen())
 *    - after conflict errors in kdbSet().
 *
 * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
 *     with ksNew().
 *
 * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
 *     keyNew().
 *
 * If you pass NULL on any parameter kdbSet() will fail immediately without doing anything.
 *
 * With @p parentKey you can give an hint which part of the given keyset
 * is of interest for you. Then you promise to only modify or
 * remove keys below this key. All others would be passed back
 * as they were retrieved by kdbGet().
 *
 * @par Errors
 * If some error occurs:
 * - kdbSet() will leave the KeySet's * internal cursor on the key that generated the error.
 * - Error information will be written into the metadata of
 *   the parent key.
 * - None of the keys are actually committed in this situation, i.e. no
 *   configuration file will be modified.
 *
 * In case of errors you should present the error message to the user and let the user decide what
 * to do. Possible solutions are:
 * - remove the problematic key and use kdbSet() again (for validation or type errors)
 * - change the value of the problematic key and use kdbSet() again (for validation errors)
 * - do a kdbGet() (for conflicts, i.e. error 30) and then
 *   - set the same keyset again (in favour of what was set by this user)
 *   - drop the old keyset (in favour of what was set from another application)
 *   - merge the original, your own and the other keyset
 * - export the configuration into a file (for unresolvable errors)
 * - repeat the same kdbSet might be of limited use if the user does
 *   not explicitly request it, because temporary
 *   errors are rare and its unlikely that they fix themselves
 *   (e.g. disc full, permission problems)
 *
 * @par Optimization
 * Each key is checked with keyNeedSync() before being actually committed.
 * If no key of a backend needs to be synced
 * any affairs to backends are omitted and 0 is returned.
 *
 * @snippet kdbset.c set
 *
 * showElektraErrorDialog() and doElektraMerge() need to be implemented
 * by the user of Elektra. For doElektraMerge a 3-way merge algorithm exists in
 * libelektra-tools.
 *
 * @param handle contains internal information of @link kdbOpen() opened @endlink key database
 * @param ks a KeySet which should contain changed keys, otherwise nothing is done
 * @param parentKey is used to add warnings and set an error
 *         information. Additionally, its name is an hint which keys
 *         should be committed (it is possible that more are changed).
 *           - cascading keys (starting with /) will set the path in all namespaces
 *           - / will commit all keys
 *           - metanames will be rejected (error 104)
 *           - empty/invalid (error 105)
 * @retval 1 on success
 * @retval 0 if nothing had to be done, no changes in KDB
 * @retval -1 on failure, no changes in KDB
 * @see keyNeedSync()
 * @see ksCurrent() contains the error key
 * @see kdbOpen() and kdbGet() that must be called first
 * @see kdbClose() that must be called afterwards
 * @ingroup kdb
 */
int kdbSet (KDB * handle, KeySet * ks, Key * parentKey)
{
	elektraNamespace ns = keyGetNamespace (parentKey);
	if (ns == KEY_NS_NONE)
	{
		return -1;
	}
	Key * oldError = keyNew (keyName (parentKey), KEY_END);
	copyError (oldError, parentKey);

	if (ns == KEY_NS_META)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbSet", keyName (parentKey));
		keyDel (oldError);
		return -1;
	}

	if (ns == KEY_NS_EMPTY)
	{
		ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbSet");
	}

	if (!handle || !ks)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer");
		keyDel (oldError);
		return -1;
	}

	int errnosave = errno;
	Key * initialParent = keyDup (parentKey);

	ELEKTRA_LOG ("now in new kdbSet (%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks));

	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, INIT);
	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, DEINIT);

	ELEKTRA_LOG ("after presetstorage maxonce(%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks));

	Split * split = splitNew ();
	Key * errorKey = 0;

	if (splitBuildup (split, handle, parentKey) == -1)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup");
		goto error;
	}

	// 1.) Search for syncbits
	int syncstate = splitDivide (split, handle, ks);
	if (syncstate == -1)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (8, parentKey, keyName (ksCurrent (ks)));
		goto error;
	}
	ELEKTRA_ASSERT (syncstate == 0 || syncstate == 1, "syncstate not 0 or 1, but %d", syncstate);

	// 2.) Search for changed sizes
	syncstate |= splitSync (split);
	ELEKTRA_ASSERT (syncstate <= 1, "syncstate not equal or below 1, but %d", syncstate);
	if (syncstate != 1)
	{
		/* No update is needed */
		keySetName (parentKey, keyName (initialParent));
		if (syncstate < 0) clearError (parentKey); // clear previous error to set new one
		if (syncstate == -1)
		{
			ELEKTRA_SET_ERROR (107, parentKey, "Assert failed: invalid namespace");
		}
		else if (syncstate < -1)
		{
			ELEKTRA_SET_ERROR (107, parentKey, keyName (split->parents[-syncstate - 2]));
		}
		keyDel (initialParent);
		splitDel (split);
		errno = errnosave;
		keyDel (oldError);
		return syncstate == 0 ? 0 : -1;
	}
	ELEKTRA_ASSERT (syncstate == 1, "syncstate not 1, but %d", syncstate);

	splitPrepare (split);

	clearError (parentKey); // clear previous error to set new one
	if (elektraSetPrepare (split, parentKey, &errorKey, handle->globalPlugins) == -1)
	{
		goto error;
	}
	else
	{
		// no error, restore old error
		copyError (parentKey, oldError);
	}
	keySetName (parentKey, keyName (initialParent));

	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, DEINIT);

	elektraSetCommit (split, parentKey);

	elektraGlobalSet (handle, ks, parentKey, COMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, COMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, COMMIT, DEINIT);

	splitUpdateSize (split);

	keySetName (parentKey, keyName (initialParent));

	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, DEINIT);

	for (size_t i = 0; i < ks->size; ++i)
	{
		// remove all flags from all keys
		clear_bit (ks->array[i]->flags, KEY_FLAG_SYNC);
	}

	keySetName (parentKey, keyName (initialParent));
	keyDel (initialParent);
	splitDel (split);

	keyDel (oldError);
	errno = errnosave;
	return 1;

error:
	keySetName (parentKey, keyName (initialParent));

	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, INIT);
	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, DEINIT);

	elektraSetRollback (split, parentKey);

	if (errorKey)
	{
		Key * found = ksLookup (ks, errorKey, 0);
		if (!found)
		{
			ELEKTRA_ADD_WARNING (82, parentKey, keyName (errorKey));
		}
	}

	keySetName (parentKey, keyName (initialParent));

	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, INIT);
	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, DEINIT);

	keySetName (parentKey, keyName (initialParent));
	keyDel (initialParent);
	splitDel (split);
	errno = errnosave;
	keyDel (oldError);
	return -1;
}
int elektraCryptoGcryDecrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey)
{
	gcry_error_t gcry_err;

	// parse salt length from crypto payload
	kdb_unsigned_long_t saltLen = 0;
	if (CRYPTO_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, NULL, &saltLen) != 1)
	{
		return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromPayload)()
	}
	saltLen += sizeof (kdb_unsigned_long_t);

	// set payload pointer
	const kdb_octet_t * payload = ((kdb_octet_t *) keyValue (k)) + saltLen + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
	const size_t payloadLen = keyGetValueSize (k) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;

	// plausibility check
	if (payloadLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE != 0)
	{
		ELEKTRA_SET_ERROR (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, errorKey, "value length is not a multiple of the block size");
		return -1;
	}

	// prepare buffer for plain text output and crypto operations
	kdb_octet_t * output = elektraMalloc (payloadLen);
	if (!output)
	{
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		return -1;
	}

	// initialize crypto header data
	kdb_unsigned_long_t contentLen = 0;
	kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE;

	// in-place decryption
	memcpy (output, payload, payloadLen);
	gcry_err = gcry_cipher_decrypt (*handle, output, payloadLen, NULL, 0);
	if (gcry_err != 0)
	{
		ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, errorKey, "Decryption failed because: %s", gcry_strerror (gcry_err));
		memset (output, 0, payloadLen);
		elektraFree (output);
		return -1;
	}

	// restore the header data
	memcpy (&flags, output, sizeof (flags));
	memcpy (&contentLen, output + sizeof (flags), sizeof (contentLen));

	const kdb_octet_t * data = output + ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
	const size_t dataLen = payloadLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;

	// validate restored content length
	if (contentLen > dataLen)
	{
		ELEKTRA_SET_ERROR (
			ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, errorKey,
			"restored content length is bigger than the available amount of decrypted data. The header is possibly corrupted.");
		memset (output, 0, payloadLen);
		elektraFree (output);
		return -1;
	}

	// restore the key to its original status
	if ((flags & ELEKTRA_CRYPTO_FLAG_STRING) == ELEKTRA_CRYPTO_FLAG_STRING && contentLen > 0)
	{
		keySetString (k, (const char *) data);
	}
	else if ((flags & ELEKTRA_CRYPTO_FLAG_NULL) == ELEKTRA_CRYPTO_FLAG_NULL || contentLen == 0)
	{
		keySetBinary (k, NULL, 0);
	}
	else
	{
		keySetBinary (k, data, contentLen);
	}

	memset (output, 0, payloadLen);
	elektraFree (output);
	return 1;
}
int elektraCryptoGcryEncrypt (elektraCryptoHandle * handle, Key * k, Key * errorKey)
{
	size_t outputLen;
	gcry_error_t gcry_err;

	// prepare the salt for payload output
	kdb_unsigned_long_t saltLen = 0;
	kdb_octet_t * salt = NULL;

	if (CRYPTO_PLUGIN_FUNCTION (getSaltFromMetakey) (errorKey, k, &salt, &saltLen) != 1)
	{
		return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromMetakey)()
	}

	// remove salt as metakey because it will be encoded into the crypto payload
	keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, NULL);

	// prepare the crypto header data
	const kdb_octet_t * content = keyValue (k);
	const kdb_unsigned_long_t contentLen = keyGetValueSize (k);
	kdb_octet_t flags;

	switch (keyIsString (k))
	{
	case 1: // string
		flags = ELEKTRA_CRYPTO_FLAG_STRING;
		break;
	case -1: // NULL pointer
		flags = ELEKTRA_CRYPTO_FLAG_NULL;
		break;
	default: // binary
		flags = ELEKTRA_CRYPTO_FLAG_NONE;
		break;
	}

	// prepare buffer for cipher text output
	// NOTE the header goes into the first block
	if (contentLen % ELEKTRA_CRYPTO_GCRY_BLOCKSIZE == 0)
	{
		outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 1;
	}
	else
	{
		outputLen = (contentLen / ELEKTRA_CRYPTO_GCRY_BLOCKSIZE) + 2;
	}
	outputLen *= ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;
	outputLen += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
	outputLen += sizeof (kdb_unsigned_long_t) + saltLen;
	kdb_octet_t * output = elektraMalloc (outputLen);
	if (!output)
	{
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		elektraFree (salt);
		return -1;
	}

	kdb_octet_t * current = output;

	// output of the magic number
	memcpy (current, ELEKTRA_CRYPTO_MAGIC_NUMBER, ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN);
	current += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;

	// encode the salt into the crypto payload
	memcpy (current, &saltLen, sizeof (kdb_unsigned_long_t));
	current += sizeof (kdb_unsigned_long_t);
	memcpy (current, salt, saltLen);
	current += saltLen;

	// encrypt the header (1st block) using gcrypt's in-place encryption
	memcpy (current, &flags, sizeof (flags));
	memcpy (current + sizeof (flags), &contentLen, sizeof (contentLen));
	gcry_err = gcry_cipher_encrypt (*handle, current, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE, NULL, 0);
	if (gcry_err != 0)
	{
		ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_ENCRYPT_FAIL, errorKey, "Encryption failed because: %s", gcry_strerror (gcry_err));
		memset (output, 0, outputLen);
		elektraFree (output);
		elektraFree (salt);
		return -1;
	}
	current += ELEKTRA_CRYPTO_GCRY_BLOCKSIZE;

	// encrypt the value using gcrypt's in-place encryption
	const size_t dataLen =
		outputLen - ELEKTRA_CRYPTO_GCRY_BLOCKSIZE - sizeof (kdb_unsigned_long_t) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
	if (contentLen) memcpy (current, content, contentLen);
	gcry_err = gcry_cipher_encrypt (*handle, current, dataLen, NULL, 0);
	if (gcry_err != 0)
	{
		ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_ENCRYPT_FAIL, errorKey, "Encryption failed because: %s", gcry_strerror (gcry_err));
		memset (output, 0, outputLen);
		elektraFree (output);
		elektraFree (salt);
		return -1;
	}

	// write back the cipher text to the key
	keySetBinary (k, output, outputLen);
	memset (output, 0, outputLen);
	elektraFree (output);
	elektraFree (salt);
	return 1;
}
int elektraCryptoGcryHandleCreate (elektraCryptoHandle ** handle, KeySet * config, Key * errorKey, Key * masterKey, Key * k,
				   const enum ElektraCryptoOperation op)
{
	gcry_error_t gcry_err;
	unsigned char keyBuffer[64], ivBuffer[64];
	size_t keyLength, ivLength;

	(*handle) = NULL;

	// retrieve/derive the cryptographic material
	Key * key = keyNew (0);
	Key * iv = keyNew (0);
	switch (op)
	{
	case ELEKTRA_CRYPTO_ENCRYPT:
		if (getKeyIvForEncryption (config, errorKey, masterKey, k, key, iv) != 1)
		{
			keyDel (key);
			keyDel (iv);
			return -1;
		}
		break;

	case ELEKTRA_CRYPTO_DECRYPT:
		if (getKeyIvForDecryption (config, errorKey, masterKey, k, key, iv) != 1)
		{
			keyDel (key);
			keyDel (iv);
			return -1;
		}
		break;

	default: // not supported
		keyDel (key);
		keyDel (iv);
		return -1;
	}

	keyLength = keyGetBinary (key, keyBuffer, sizeof (keyBuffer));
	ivLength = keyGetBinary (iv, ivBuffer, sizeof (ivBuffer));

	// create the handle
	(*handle) = elektraMalloc (sizeof (elektraCryptoHandle));
	if (*handle == NULL)
	{
		memset (keyBuffer, 0, sizeof (keyBuffer));
		memset (ivBuffer, 0, sizeof (ivBuffer));
		keyDel (key);
		keyDel (iv);
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		return -1;
	}

	if ((gcry_err = gcry_cipher_open (*handle, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0)) != 0)
	{
		goto error;
	}

	if ((gcry_err = gcry_cipher_setkey (**handle, keyBuffer, keyLength)) != 0)
	{
		goto error;
	}

	if ((gcry_err = gcry_cipher_setiv (**handle, ivBuffer, ivLength)) != 0)
	{
		goto error;
	}

	memset (keyBuffer, 0, sizeof (keyBuffer));
	memset (ivBuffer, 0, sizeof (ivBuffer));
	keyDel (key);
	keyDel (iv);
	return 1;

error:
	memset (keyBuffer, 0, sizeof (keyBuffer));
	memset (ivBuffer, 0, sizeof (ivBuffer));
	ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_CONFIG_FAULT, errorKey, "Failed to create handle because: %s", gcry_strerror (gcry_err));
	gcry_cipher_close (**handle);
	elektraFree (*handle);
	(*handle) = NULL;
	keyDel (key);
	keyDel (iv);
	return -1;
}