/** * Compare 2 keys. * * The returned flags bit array has 1s (differ) or 0s (equal) for each key * meta info compared, that can be logically ORed using @c #keyswitch_t flags. * @link keyswitch_t::KEY_NAME KEY_NAME @endlink, * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink, * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink, * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink, * @link keyswitch_t::KEY_UID KEY_UID @endlink, * @link keyswitch_t::KEY_GID KEY_GID @endlink, * @link keyswitch_t::KEY_MODE KEY_MODE @endlink and * * @par A very simple example would be * @code Key *key1, *key; uint32_t changes; // omited key1 and key2 initialization and manipulation changes=keyCompare(key1,key2); if (changes == 0) printf("key1 and key2 are identicall\n"); if (changes & KEY_VALUE) printf("key1 and key2 have different values\n"); if (changes & KEY_UID) printf("key1 and key2 have different UID\n"); * * @endcode * * * @par Example of very powerful specific Key lookup in a KeySet: * @code KDB *handle = kdbOpen(); KeySet *ks=ksNew(0); Key *base = keyNew ("user/sw/MyApp/something", KEY_END); Key *current; uint32_t match; uint32_t interests; kdbGetByName(handle, ks, "user/sw/MyApp", 0); // we are interested only in key type and access permissions interests=(KEY_TYPE | KEY_MODE); ksRewind(ks); // put cursor in the beginning while ((curren=ksNext(ks))) { match=keyCompare(current,base); if ((~match & interests) == interests) printf("Key %s has same type and permissions of base key",keyName(current)); // continue walking in the KeySet.... } // now we want same name and/or value interests=(KEY_NAME | KEY_VALUE); // we don't really need ksRewind(), since previous loop achieved end of KeySet ksRewind(ks); while ((current=ksNext(ks))) { match=keyCompare(current,base); if ((~match & interests) == interests) { printf("Key %s has same name, value, and sync status of base key",keyName(current)); } // continue walking in the KeySet.... } keyDel(base); ksDel(ks); kdbClose (handle); * @endcode * * @return a bit array pointing the differences * @param key1 first key * @param key2 second key * @see #keyswitch_t * @ingroup keytest */ keyswitch_t keyCompare(const Key *key1, const Key *key2) { if (!key1 && !key2) return 0; if (!key1 || !key2) return KEY_NULL; keyswitch_t ret=0; ssize_t nsize1 = keyGetNameSize(key1); ssize_t nsize2 = keyGetNameSize(key2); const char *name1 = keyName(key1); const char *name2 = keyName(key2); const char *comment1 = keyComment(key1); const char *comment2 = keyComment(key2); const char *owner1 = keyOwner(key1); const char *owner2 = keyOwner(key2); const void *value1 = keyValue(key1); const void *value2 = keyValue(key2); ssize_t size1 = keyGetValueSize(key1); ssize_t size2 = keyGetValueSize(key2); if (keyGetUID(key1) != keyGetUID(key2)) ret|=KEY_UID; if (keyGetGID(key1) != keyGetGID(key2)) ret|=KEY_GID; if (keyGetMode(key1)!= keyGetMode(key2)) ret|=KEY_MODE; if (nsize1 != nsize2) ret|=KEY_NAME; if (strcmp(name1, name2)) ret|=KEY_NAME; if (strcmp(comment1, comment2)) ret|=KEY_COMMENT; if (strcmp(owner1, owner2)) ret|=KEY_OWNER; if (size1 != size2) ret|=KEY_VALUE; if (memcmp(value1, value2, size1)) ret|=KEY_VALUE; return ret; }
static void validateArrayRange (Key * parent, long validCount, Key * specKey) { const Key * arrayRange = keyGetMeta (specKey, "array"); if (arrayRange != NULL) { char * rangeString = elektraMalloc (keyGetValueSize (arrayRange)); keyGetString (arrayRange, rangeString, keyGetValueSize (arrayRange)); char * delimPtr = strchr (rangeString, '-'); long min = 0; long max = 0; if (delimPtr) { char * maxString = delimPtr + 1; *delimPtr = '\0'; char * minString = rangeString; min = atoi (minString); max = atoi (maxString); } else { min = max = atoi (rangeString); } if (validCount < min || validCount > max) { char buffer[MAX_CHARS_IN_LONG + 1]; snprintf (buffer, sizeof (buffer), "%ld", validCount); keySetMeta (parent, "conflict/range", buffer); } elektraFree (rangeString); } }
static void test_enc_and_dec_with_null() { elektraCryptoHandle *handle; KeySet *config; Key *errorKey = keyNew(KEY_END); Key *k = keyNew("user/plugins/crypto/gcrypt/test-enc-dec-null", KEY_END); keySetBinary(k, 0, 0); succeed_if( keyGetValueSize(k) == 0, "key is not NULL"); getWorkingConfiguration(&config); succeed_if( elektraCryptoInit(errorKey) == 1, "crypto initialization failed" ); // 1. encrypt succeed_if( elektraCryptoHandleCreate(&handle, config, errorKey) == 1, "handle initialization with compliant config failed" ); succeed_if( elektraCryptoEncrypt(handle, k, errorKey) == 1, "encryption failed" ); elektraCryptoHandleDestroy(handle); // 2. decrypt succeed_if( elektraCryptoHandleCreate(&handle, config, errorKey) == 1, "handle initialization with compliant config failed" ); succeed_if( elektraCryptoDecrypt(handle, k, errorKey) == 1, "decryption failed" ); elektraCryptoHandleDestroy(handle); // 3. check result succeed_if( keyGetValueSize(k) == 0, "key is not NULL"); keyDel(k); keyDel(errorKey); ksDel(config); elektraCryptoTeardown(); }
char * elektraMetaArrayToString (Key * key, const char * metaName, const char * delim) { char * result = NULL; Key * lookupElem = keyDup (keyGetMeta (key, metaName)); keyAddBaseName (lookupElem, "#0"); Key * elem = (Key *)keyGetMeta (key, keyName (lookupElem)); if (elem != NULL) { elektraRealloc ((void **)&result, keyGetValueSize (elem)); snprintf (result, keyGetValueSize (elem), "%s", keyString (elem)); } elektraArrayIncName (lookupElem); elem = (Key *)keyGetMeta (key, keyName (lookupElem)); while (elem != NULL) { elektraRealloc ((void **)&result, elektraStrLen (result) + keyGetValueSize (elem) + 1); // String (incl. +2 times \0) + delimiter + whitespace strcat (result, delim); strcat (result, keyString (elem)); elektraArrayIncName (lookupElem); elem = (Key *)keyGetMeta (key, keyName (lookupElem)); } keyDel (lookupElem); return result; }
int elektraIconvSet (Plugin * handle, KeySet * returned, Key * parentKey) { Key * cur; if (!kdbbNeedsUTF8Conversion (handle)) return 0; ksRewind (returned); while ((cur = ksNext (returned)) != 0) { if (keyIsString (cur)) { /* String or similar type of value */ size_t convertedDataSize = keyGetValueSize (cur); char * convertedData = elektraMalloc (convertedDataSize); memcpy (convertedData, keyString (cur), keyGetValueSize (cur)); if (kdbbUTF8Engine (handle, UTF8_TO, &convertedData, &convertedDataSize)) { ELEKTRA_SET_ERRORF (46, parentKey, "Could not convert string %s, got result %s," " encoding settings are from %s to %s (but swapped for write)", keyString (cur), convertedData, getFrom (handle), getTo (handle)); elektraFree (convertedData); return -1; } keySetString (cur, convertedData); elektraFree (convertedData); } const Key * meta = keyGetMeta (cur, "comment"); if (meta) { /* String or similar type of value */ size_t convertedDataSize = keyGetValueSize (meta); char * convertedData = elektraMalloc (convertedDataSize); memcpy (convertedData, keyString (meta), keyGetValueSize (meta)); if (kdbbUTF8Engine (handle, UTF8_TO, &convertedData, &convertedDataSize)) { ELEKTRA_SET_ERRORF (46, parentKey, "Could not convert string %s, got result %s," " encodings settings are from %s to %s (but swapped for write)", keyString (meta), convertedData, getFrom (handle), getTo (handle)); elektraFree (convertedData); return -1; } keySetMeta (cur, "comment", convertedData); elektraFree (convertedData); } } return 1; /* success */ }
static int handleOutOfRangeConflict (Key * parentKey, Key * key, Key * specKey, Key * conflictMeta, OnConflict onConflict) { int ret = 0; switch (onConflict) { case ERROR: ELEKTRA_SET_ERRORF (142, parentKey, "%s has invalid number of members: %s. Expected: %s\n", keyName (key), keyString (conflictMeta), keyString (keyGetMeta (specKey, "array"))); ret = -1; break; case WARNING: ELEKTRA_ADD_WARNINGF (143, parentKey, "%s has invalid number of members: %s. Expected: %s\n", keyName (key), keyString (conflictMeta), keyString (keyGetMeta (specKey, "array"))); break; case INFO: { const char * infoString = "%s has invalid number of member: %s. Expected: %s"; const size_t len = elektraStrLen (infoString) + elektraStrLen (keyName (key)) + MAX_CHARS_IN_LONG + keyGetValueSize (keyGetMeta (specKey, "array")) - 2; char * buffer = elektraMalloc (len); snprintf (buffer, len, infoString, keyName (key), keyString (conflictMeta), keyString (keyGetMeta (specKey, "array"))); elektraMetaArrayAdd (key, "logs/spec/info", buffer); elektraFree (buffer); } break; case IGNORE: break; } return ret; }
/** * @internal * * Set raw data as the value of a key. * If NULL pointers are passed, key value is cleaned. * This method will not change or set the key type, and should only * be used internally in elektra. * * @param key the key object to work with * @param newBinary array of bytes to set as the value * @param dataSize number bytes to use from newBinary, including the final NULL * @return The number of bytes actually set in internal buffer. * @retval 1 if it was a string which was deleted * @retval 0 if it was a binary which was deleted * @see keySetType(), keySetString(), keySetBinary() * @ingroup keyvalue */ ssize_t keySetRaw(Key *key, const void *newBinary, size_t dataSize) { if (!key) return -1; if (key->flags & KEY_FLAG_RO_VALUE) return -1; if (!dataSize || !newBinary) { if (key->data.v) { elektraFree (key->data.v); key->data.v=0; } key->dataSize = 0; set_bit(key->flags, KEY_FLAG_SYNC); if (keyIsBinary(key)) return 0; return 1; } key->dataSize=dataSize; if (key->data.v) { char *p=0; p=realloc(key->data.v,key->dataSize); if (0==p) return -1; key->data.v=p; } else { char *p=elektraMalloc(key->dataSize); if (0==p) return -1; key->data.v=p; } memcpy(key->data.v,newBinary,key->dataSize); set_bit(key->flags, KEY_FLAG_SYNC); return keyGetValueSize (key); }
static void test_keyGetString (const size_t storagePlugin, const char * tmpFile) { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); open_storage_plugin (storagePlugin); Plugin * plugin = plugins[storagePlugin]; KeySet * ks = metaTestKeySet (); const char * name = "user/tests/storage/specialkey"; const char * value = "special value"; size_t realValueSize = elektraStrLen (value); Key * key = keyNew (name, KEY_VALUE, value, KEY_END); ksAppendKey (ks, key); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful"); Key * found = ksLookupByName (ks, name, 0); succeed_if (found, "did not find key"); ssize_t apiValueSize = keyGetValueSize (found); char * apiString = elektraMalloc (apiValueSize); succeed_if (keyGetString (found, apiString, apiValueSize) == (ssize_t) realValueSize, "Key string has wrong size"); succeed_if (elektraStrNCmp (value, apiString, realValueSize) == 0, "Key string value is wrong"); elektraFree (apiString); keyDel (parentKey); ksDel (ks); closeStoragePlugin (storagePlugin); }
static KeySet * getGlobKeys (Key * parentKey, KeySet * keys, enum GlobDirection direction) { KeySet * glob = ksNew (0, KS_END); Key * k = 0; size_t parentsize = keyGetNameSize (parentKey); Key * userGlobConfig = 0; Key * systemGlobConfig = 0; Key * userDirGlobConfig = 0; Key * systemDirGlobConfig = 0; userGlobConfig = keyNew ("user/glob", KEY_END); systemGlobConfig = keyNew ("system/glob", KEY_END); switch (direction) { case GET: userDirGlobConfig = keyNew ("user/glob/get", KEY_END); systemDirGlobConfig = keyNew ("system/glob/get", KEY_END); break; case SET: userDirGlobConfig = keyNew ("user/glob/set", KEY_END); systemDirGlobConfig = keyNew ("system/glob/set", KEY_END); break; } while ((k = ksNext (keys)) != 0) { /* use only glob keys for the current direction */ if (keyIsDirectBelow (userGlobConfig, k) || keyIsDirectBelow (systemGlobConfig, k) || keyIsDirectBelow (userDirGlobConfig, k) || keyIsDirectBelow (systemDirGlobConfig, k)) { keySetMeta (k, "glob/flags", getGlobFlags (keys, k)); /* Look if we have a string */ size_t valsize = keyGetValueSize (k); if (valsize < 2) continue; /* We now know we want that key. Dup it to not change the configuration. */ Key * ins = keyDup (k); /* Now look if we want cascading for the key */ if (keyString (k)[0] == '/') { char * newstring = elektraMalloc (valsize + parentsize); strcpy (newstring, keyName (parentKey)); strcat (newstring, keyString (k)); keySetString (ins, newstring); elektraFree (newstring); } ksAppendKey (glob, ins); } } keyDel (userGlobConfig); keyDel (systemGlobConfig); keyDel (userDirGlobConfig); keyDel (systemDirGlobConfig); return glob; }
/** Reads the value of the key and decodes all escaping * codes into the buffer. * @pre the buffer needs to be as large as value's size. * @param cur the key holding the value to decode * @param buf the buffer to write to */ void elektraHexcodeDecode (Key * cur, CHexData * hd) { size_t valsize = keyGetValueSize (cur); const char * val = keyValue (cur); if (!val) return; size_t out = 0; for (size_t in = 0; in < valsize - 1; ++in) { char c = val[in]; char * n = hd->buf + out; if (c == hd->escape) { in += 2; /* Advance twice (2 hex numbers) */ char first = val[in - 1]; char second = val[in]; int res; res = elektraHexcodeConvFromHex (second); res += elektraHexcodeConvFromHex (first) * 16; *n = res & 255; } else { *n = c; } ++out; /* Only one char is written */ } hd->buf[out] = 0; // null termination for keyString() keySetRaw (cur, hd->buf, out + 1); }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { gcry_error_t gcry_err; kdb_octet_t keyBuffer[KEY_BUFFER_SIZE]; kdb_octet_t * saltBuffer = NULL; kdb_unsigned_long_t saltBufferLen = 0; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // get the salt if (CRYPTO_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1) { return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromPayload)() } // get the iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // derive the cryptographic key and the IV if ((gcry_err = gcry_kdf_derive (keyValue (masterKey), keyGetValueSize (masterKey), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, saltBuffer, saltBufferLen, iterations, KEY_BUFFER_SIZE, keyBuffer))) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to restore the cryptographic key for decryption because: %s", gcry_strerror (gcry_err)); return -1; } keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE); return 1; }
/** * Get the key comment. * * @section comment Comments * * A Key comment is description for humans what this key is for. It may be a * textual explanation of valid values, when and why a user or administrator * changed the key or any other text that helps the user or administrator related * to that key. * * Don't depend on a comment in your program. A user is * always allowed to remove or change it in any way he wants to. But you are * allowed or even encouraged to always show the content of the comment * to the user and allow him to change it. * * @param key the key object to work with * @param returnedComment pre-allocated memory to copy the comments to * @param maxSize number of bytes that will fit returnedComment * @return the number of bytes actually copied to @p returnedString, including * final NULL * @retval 1 if the string is empty * @retval -1 on NULL pointer * @retval -1 if maxSize is 0, not enough to store the comment or when larger then SSIZE_MAX * @see keyGetCommentSize(), keySetComment() */ ssize_t keyGetComment (const Key * key, char * returnedComment, size_t maxSize) { const char * comment; size_t commentSize; if (!key) return -1; if (!maxSize) return -1; if (!returnedComment) return -1; if (maxSize > SSIZE_MAX) return -1; comment = keyValue (keyGetMeta (key, "comment")); commentSize = keyGetValueSize (keyGetMeta (key, "comment")); if (!comment) { /*errno=KDB_ERR_NODESC;*/ returnedComment[0] = 0; return 1; } strncpy (returnedComment, comment, maxSize); if (maxSize < commentSize) { /*errno=KDB_ERR_TRUNC;*/ return -1; } return commentSize; }
static void elektraDropCurrentKey (KeySet * ks, Key * warningKey, const Backend * curHandle, const char * msg) { const Key * k = ksCurrent (ks); const size_t sizeOfStaticText = 100; char * warningMsg = elektraMalloc (keyGetNameSize (curHandle->mountpoint) + keyGetValueSize (curHandle->mountpoint) + keyGetNameSize (k) + strlen (msg) + sizeOfStaticText); strcpy (warningMsg, "drop key "); const char * name = keyName (k); if (name) { strcat (warningMsg, name); } else { strcat (warningMsg, "(no name)"); } strcat (warningMsg, " not belonging to "); strcat (warningMsg, keyName (curHandle->mountpoint)); strcat (warningMsg, " with name "); strcat (warningMsg, keyString (curHandle->mountpoint)); strcat (warningMsg, " because "); strcat (warningMsg, msg); ELEKTRA_ADD_WARNING (79, warningKey, warningMsg); elektraFree (warningMsg); cursor_t c = ksGetCursor (ks); keyDel (elektraKsPopAtCursor (ks, c)); ksSetCursor (ks, c); elektraKsPrev (ks); // next ksNext() will point correctly again }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { kdb_octet_t keyBuffer[KEY_BUFFER_SIZE]; kdb_octet_t * saltBuffer = NULL; kdb_unsigned_long_t saltBufferLen = 0; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // get the salt if (CRYPTO_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1) { return -1; // error set by CRYPTO_PLUGIN_FUNCTION(getSaltFromPayload)() } // get the iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // derive the cryptographic key and the IV pthread_mutex_lock (&mutex_ssl); if (!PKCS5_PBKDF2_HMAC_SHA1 (keyValue (masterKey), keyGetValueSize (masterKey), saltBuffer, saltBufferLen, iterations, KEY_BUFFER_SIZE, keyBuffer)) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to restore the cryptographic key for decryption. Libcrypto returned the error code: %lu", ERR_get_error ()); pthread_mutex_unlock (&mutex_ssl); return -1; } pthread_mutex_unlock (&mutex_ssl); keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_SSL_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_SSL_KEYSIZE, ELEKTRA_CRYPTO_SSL_BLOCKSIZE); return 1; }
/** Reads the value of the key and encodes it in * c-style in the buffer. * * @param cur the key which value is to encode * @param buf the buffer * @pre the buffer needs to have thrice as much space as the value's size */ void elektraHexcodeEncode (Key * cur, CHexData * hd) { size_t valsize = keyGetValueSize (cur); const char * val = keyValue (cur); if (!val) return; size_t out = 0; for (size_t in = 0; in < valsize - 1; ++in) { unsigned char c = val[in]; // need to encode char? if (hd->hd[c & 255]) { hd->buf[out] = hd->escape; out++; hd->buf[out] = elektraHexcodeConvToHex (c / 16); out++; hd->buf[out] = elektraHexcodeConvToHex (c % 16); out++; } else { // just copy one character hd->buf[out] = val[in]; // advance out cursor out++; } } hd->buf[out] = 0; // null termination for keyString() keySetRaw (cur, hd->buf, out + 1); }
// TODO: this is very similar to elektraKeyAppendMetaLine in keytometa static int elektraKeyAppendLine (Key *target, const char *line) { if (!target) return 0; if (!line) return 0; char *buffer = elektraMalloc (keyGetValueSize(target) + strlen (line) + 1); if (!buffer) return 0; keyGetString(target, buffer, keyGetValueSize(target)); strcat (buffer, "\n"); strncat (buffer, line, strlen (line)); keySetString(target, buffer); elektraFree (buffer); return keyGetValueSize(target); }
/** * @brief Builds together a format string by the plugin's configuration * * @param handle to plugin * @param first format string for key * @param second format string for value * * @return newly allocated format (to be freed with elektraFree); */ static char * getFormat (Plugin * handle, const char * first, const char * second) { char * format; Key * key = ksLookupByName (elektraPluginGetConfig (handle), "/format", 0); if (!key) { format = elektraStrDup ("%s = %s\n"); } else { const size_t maxFactor = 2; // at maximum every char is a %, %% -> %%%% const size_t newLineAtEnd = 2; const size_t userFormatSize = keyGetValueSize (key); format = elektraMalloc (userFormatSize * maxFactor + newLineAtEnd); const char * userFormat = keyString (key); int gotPercent = 0; size_t j = 0; for (size_t i = 0; i < userFormatSize; ++i, ++j) { const char c = userFormat[i]; if (gotPercent) { if (c == '%') { // escaped %% -> %%%% format[j++] = '%'; format[j++] = '%'; format[j] = '%'; } else { // single % -> %s format[j++] = 's'; format[j] = c; } gotPercent = 0; } else if (c == '%') { format[j] = c; gotPercent = 1; } else { format[j] = c; } } --j; // discard null byte that is already there ELEKTRA_ASSERT (format[j] == '\0', "should be null byte at end of string but was %c", format[j]); format[j++] = '\n'; format[j] = '\0'; } char * ret = elektraFormat (format, first, second); elektraFree (format); return ret; }
static void appendToKey (Key * key, const char * line) { char * buffer; size_t len = keyGetValueSize (key) + elektraStrLen (line) - 1; buffer = elektraMalloc (len); snprintf (buffer, len, "%s%s", keyString (key), line); keySetString (key, buffer); elektraFree (buffer); }
/** * Generate a C-Style key and stream it. * * This keyset can be used to include as c-code for * applikations using elektra. * * @param key the key object to work with * @param stream the file pointer where to send the stream * @param options KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO * @retval 1 on success * @ingroup stream */ int keyGenerate (const Key * key, FILE * stream, option_t options) { size_t n = keyGetNameSize (key); if (n > 1) { char * nam = (char *) elektraMalloc (n); if (nam == NULL) return -1; keyGetName (key, nam, n); fprintf (stream, "\tkeyNew (\"%s\"", nam); elektraFree (nam); } size_t s = keyGetValueSize (key); if (s > 1) { char * str = (char *) elektraMalloc (s); if (str == NULL) return -1; if (keyIsBinary (key)) { keyGetBinary (key, str, s); fprintf (stream, ", KEY_SIZE, \"%zd\"", keyGetValueSize (key)); } else { keyGetString (key, str, s); } fprintf (stream, ", KEY_VALUE, \"%s\"", str); elektraFree (str); } const Key * meta; Key * dup = keyDup (key); keyRewindMeta (dup); while ((meta = keyNextMeta (dup))) { fprintf (stream, ", KEY_META, \"%s\", \"%s\"", keyName (meta), keyString (meta)); } keyDel (dup); fprintf (stream, ", KEY_END)"); if (options == 0) return 1; /* dummy to make icc happy */ return 1; }
/** * @internal * * Compare 2 keys. * * The returned flags bit array has 1s (differ) or 0s (equal) for each key * meta info compared, that can be logically ORed using @c #keyswitch_t flags. * @link keyswitch_t::KEY_NAME KEY_NAME @endlink, * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink, * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink, * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink, * @link keyswitch_t::KEY_META KEY_META @endlink (will be set in addition to owner and comment), * * @par A very simple example would be * @code Key *key1, *key; uint32_t changes; // omited key1 and key2 initialization and manipulation changes=keyCompare(key1,key2); if (changes == 0) printf("key1 and key2 are identicall\n"); if (changes & KEY_VALUE) printf("key1 and key2 have different values\n"); if (changes & KEY_UID) printf("key1 and key2 have different UID\n"); * * @endcode * * * @par Example of very powerful specific Key lookup in a KeySet: * @code Key *base = keyNew ("/sw/MyApp/something", KEY_END); KDB *handle = kdbOpen(base); KeySet *ks=ksNew(0, KS_END); Key *current; uint32_t match; uint32_t interests; kdbGet(handle, ks, base); // we are interested only in key type and access permissions interests=(KEY_TYPE | KEY_MODE); ksRewind(ks); // put cursor in the beginning while ((curren=ksNext(ks))) { match=keyCompare(current,base); if ((~match & interests) == interests) printf("Key %s has same type and permissions of base key",keyName(current)); // continue walking in the KeySet.... } // now we want same name and/or value interests=(KEY_NAME | KEY_VALUE); // we don't really need ksRewind(), since previous loop achieved end of KeySet ksRewind(ks); while ((current=ksNext(ks))) { match=keyCompare(current,base); if ((~match & interests) == interests) { printf("Key %s has same name, value, and sync status of base key",keyName(current)); } // continue walking in the KeySet.... } ksDel(ks); kdbClose (handle, base); keyDel(base); * @endcode * * @return a bit array pointing the differences * @param key1 first key * @param key2 second key * @see #keyswitch_t * @ingroup keytest */ keyswitch_t keyCompare (const Key * key1, const Key * key2) { if (!key1 && !key2) return 0; if (!key1 || !key2) return KEY_NULL; keyswitch_t ret = 0; ssize_t nsize1 = keyGetNameSize (key1); ssize_t nsize2 = keyGetNameSize (key2); const char * name1 = keyName (key1); const char * name2 = keyName (key2); const Key * comment1 = keyGetMeta (key1, "comment"); const Key * comment2 = keyGetMeta (key2, "comment"); const char * owner1 = keyOwner (key1); const char * owner2 = keyOwner (key2); const void * value1 = keyValue (key1); const void * value2 = keyValue (key2); ssize_t size1 = keyGetValueSize (key1); ssize_t size2 = keyGetValueSize (key2); // TODO: might be (binary) by chance if (strcmp (keyString (comment1), keyString (comment2))) ret |= KEY_COMMENT; if (strcmp (owner1, owner2)) ret |= KEY_OWNER; if (keyCompareMeta (key1, key2)) ret |= KEY_META; if (nsize1 != nsize2) ret |= KEY_NAME; else if (!name1 || !name2) ret |= KEY_NAME; else if (strcmp (name1, name2)) ret |= KEY_NAME; if (size1 != size2) ret |= KEY_VALUE; else if (!value1 || !value2) ret |= KEY_VALUE; else if (memcmp (value1, value2, size1)) ret |= KEY_VALUE; // TODO: rewind metadata to previous position return ret; }
/** * Generate a C-Style key and stream it. * * This keyset can be used to include as c-code for * applikations using elektra. * * @param key the key object to work with * @param stream the file pointer where to send the stream * @param options KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO * @retval 1 on success * @ingroup stream */ int keyGenerate(const Key * key, FILE *stream, option_t options) { size_t s; char * str; size_t c; char * com; size_t n; char * nam; n = keyGetNameSize (key); if (n>1) { nam = (char*) elektraMalloc (n); if (nam == NULL) return -1; keyGetName (key, nam, n); fprintf(stream,"\tkeyNew (\"%s\"", nam); elektraFree (nam); } s = keyGetValueSize (key); if (s>1) { str = (char*) elektraMalloc (s); if (str == NULL) return -1; if (keyIsBinary(key)) keyGetBinary(key, str, s); else keyGetString (key, str, s); fprintf(stream,", KEY_VALUE, \"%s\"", str); elektraFree (str); } c = keyGetCommentSize (key); if (c>1) { com = (char*) elektraMalloc (c); if (com == NULL) return -1; keyGetComment (key, com, c); fprintf(stream,", KEY_COMMENT, \"%s\"", com); elektraFree (com); } if (! (keyGetMode(key) == 0664 || (keyGetMode(key) == 0775))) { fprintf(stream,", KEY_MODE, 0%3o", keyGetMode(key)); } fprintf(stream,", KEY_END)"); if (options == 0) return 1; /* dummy to make icc happy */ return 1; }
/** * Get key full name, including the user domain name. * * @return number of bytes written * @retval 1 on empty name * @retval -1 on NULL pointers * @retval -1 if maxSize is 0 or larger than SSIZE_MAX * @param key the key object * @param returnedName pre-allocated memory to write the key name * @param maxSize maximum number of bytes that will fit in returnedName, including the final NULL * @ingroup keyname */ ssize_t keyGetFullName (const Key * key, char * returnedName, size_t maxSize) { size_t userSize = sizeof ("user") - 1; size_t ownerSize; ssize_t length; ssize_t maxSSize; char * cursor; if (!key) return -1; if (!returnedName) return -1; if (!maxSize) return -1; if (maxSize > SSIZE_MAX) return -1; maxSSize = maxSize; length = keyGetFullNameSize (key); if (length == 1) { /*errno=KDB_ERR_NOKEY;*/ returnedName[0] = 0; return length; } else if (length < 0) return length; else if (length > maxSSize) { /* errno=KDB_ERR_TRUNC; */ return -1; } cursor = returnedName; if (keyIsUser (key)) { strncpy (cursor, key->key, userSize); cursor += userSize; if (keyGetMeta (key, "owner")) { *cursor = ':'; ++cursor; ownerSize = keyGetValueSize (keyGetMeta (key, "owner")) - 1; strncpy (cursor, keyValue (keyGetMeta (key, "owner")), ownerSize); cursor += ownerSize; } strcpy (cursor, key->key + userSize); } else strcpy (cursor, key->key); return length; }
int main () { Key * k; Key * c; const Key * meta; k = keyNew ("user/metakey", KEY_END); c = keyNew ("user/metacopy", KEY_END); keySetMeta (k, "hello", "hello_world"); keySetMeta (k, "mode", "0644"); keySetMeta (k, "time", "1271234264"); keySetMeta (k, "empty", ""); meta = keyGetMeta (k, "hello"); printf ("Metadata %s has the value %s with the value size %zd\n", keyName (meta), (const char *)keyValue (meta), keyGetValueSize (meta)); printf ("Metadata mode has the value %s\n", (const char *)keyValue (keyGetMeta (k, "mode"))); printf ("Metadata time has the value %s\n", (const char *)keyValue (keyGetMeta (k, "time"))); printf ("Metadata empty has the value %s\n", (const char *)keyValue (keyGetMeta (k, "empty"))); if (!keyGetMeta (k, "nonexist")) printf ("Check if a metadata exist\n"); keySetMeta (k, "hello", "between"); keyCopyMeta (c, k, "hello"); if (keyGetMeta (k, "hello") == keyGetMeta (c, "hello")) printf ("Check if they point to the same metadata after a copy\n"); printf ("Metadata hello now has the value %s\n", (const char *)keyValue (keyGetMeta (k, "hello"))); keySetMeta (k, "hello", 0); printf ("Metadata hello now has the value %s (after dropping)\n", (const char *)keyValue (keyGetMeta (k, "hello"))); keySetMeta (k, "hello", "goodbye"); printf ("Metadata hello now has the value %s\n", (const char *)keyValue (keyGetMeta (k, "hello"))); printf ("Now we will output all metadata of the key:\n"); keyRewindMeta (k); while ((meta = keyNextMeta (k)) != 0) { printf ("%s=%s\n", keyName (meta), (const char *)keyValue (meta)); } keyDel (k); return 0; }
static void test_keySetBinary (const size_t storagePlugin, const char * tmpFile) { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); open_storage_plugin (storagePlugin); Plugin * plugin = plugins[storagePlugin]; KeySet * ks = metaTestKeySet (); const char * name = "user/tests/storage/specialkey"; size_t realValueSize = 42; void * value = elektraMalloc (realValueSize); memset (value, 42, realValueSize); Key * key = keyNew (name, KEY_END); keySetBinary (key, value, realValueSize); ksAppendKey (ks, key); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful"); Key * found = ksLookupByName (ks, name, KDB_O_POP); succeed_if (found, "did not find key"); // now set a new key value to the Key _after_ kdbGet size_t newValueSize = 4096; void * newValue = elektraMalloc (newValueSize); memset (newValue, 253, newValueSize); succeed_if (keySetBinary (found, newValue, newValueSize) == (ssize_t) newValueSize, "Key binary could not be set"); ksAppendKey (ks, found); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful"); found = ksLookupByName (ks, name, 0); succeed_if (found, "did not find key"); ssize_t apiValueSize = keyGetValueSize (found); char * apiValue = elektraMalloc (apiValueSize); succeed_if (keyGetBinary (found, apiValue, apiValueSize) == (ssize_t) newValueSize, "Key binary has wrong size"); succeed_if (elektraStrNCmp (value, apiValue, realValueSize) != 0, "Key binary value is wrong"); succeed_if (elektraStrNCmp (newValue, apiValue, newValueSize) == 0, "Key binary value is wrong"); elektraFree (newValue); elektraFree (apiValue); elektraFree (value); keyDel (parentKey); ksDel (ks); closeStoragePlugin (storagePlugin); }
/* * Appends a line to the MetaKey of the supplied Key * If no MetaKey with the given name exists yet, a new * one is created containing the supplied line. If * the MetaKey exists, the supplied line is added as * a new line to the value of the MetaKey (i.e. a newline * followed by the given line is appended to the metadata) * * @param target the Key whose MetaKey is to be modified * @param metaName the name of the MetaKey which is to be modified * @param line the line to be appended to the matadata * @return the new value size of the modified MetaKey * @retval -1 on NULL pointers or if a memory allocation error occurs * * @see keyGetValueSize(Key *key) * */ int elektraKeyAppendMetaLine (Key * target, const char * metaName, const char * line) { if (!target) return 0; if (!metaName) return 0; if (!line) return 0; if (!keyGetMeta (target, metaName)) { keySetMeta (target, metaName, line); return keyGetValueSize (keyGetMeta (target, metaName)); } const Key * existingMeta = keyGetMeta (target, metaName); char * buffer = elektraMalloc (keyGetValueSize (existingMeta) + strlen (line) + 1); if (!buffer) return 0; keyGetString (existingMeta, buffer, keyGetValueSize (existingMeta)); strcat (buffer, "\n"); strncat (buffer, line, strlen (line)); keySetMeta (target, metaName, buffer); elektraFree (buffer); return keyGetValueSize (keyGetMeta (target, metaName)); }
void j (Key *k) { size_t size = keyGetValueSize (k); char *value = malloc (size); int bstring = keyIsString (k); // receive key g_c memcpy (value, keyValue(k), size); keyCopy (k, g_c); if (bstring) keySetString (k, value); else keySetBinary (k, value, size); free (value); // the caller will see the changed key k // with the metadata from g_c }
/** * Calculates number of bytes needed to store a key comment, including * final NULL. * * Use this method to know to size for allocated memory to retrieve * a key comment. * * See keySetComment() for more information on comments. * * For an empty key name you need one byte to store the ending NULL. * For that reason 1 is returned. * * @code char *buffer; buffer = elektraMalloc (keyGetCommentSize (key)); // use this buffer to store the comment // pass keyGetCommentSize (key) for maxSize * @endcode * * @param key the key object to work with * @return number of bytes needed * @retval 1 if there is no comment * @retval -1 on NULL pointer * @see keyGetComment(), keySetComment() */ ssize_t keyGetCommentSize (const Key * key) { ssize_t size; if (!key) return -1; size = keyGetValueSize (keyGetMeta (key, "comment")); if (!size || size == -1) { /*errno=KDB_ERR_NODESC;*/ return 1; } return size; }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { kdb_octet_t salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN] = { 0 }; kdb_octet_t keyBuffer[KEY_BUFFER_SIZE] = { 0 }; char * saltHexString = NULL; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // generate the salt pthread_mutex_lock (&mutex_ssl); if (!RAND_bytes (salt, ELEKTRA_CRYPTO_DEFAULT_SALT_LEN - 1)) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "failed to generate random salt with error code %lu", ERR_get_error ()); pthread_mutex_unlock (&mutex_ssl); return -1; } pthread_mutex_unlock (&mutex_ssl); saltHexString = ELEKTRA_PLUGIN_FUNCTION (ELEKTRA_PLUGIN_NAME_C, base64Encode) (salt, sizeof (salt)); if (!saltHexString) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString); elektraFree (saltHexString); // read iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // generate/derive the cryptographic key and the IV pthread_mutex_lock (&mutex_ssl); if (!PKCS5_PBKDF2_HMAC_SHA1 (keyValue (masterKey), keyGetValueSize (masterKey), salt, sizeof (salt), iterations, KEY_BUFFER_SIZE, keyBuffer)) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to create a cryptographic key for encryption. Libcrypto returned error code: %lu", ERR_get_error ()); pthread_mutex_unlock (&mutex_ssl); return -1; } pthread_mutex_unlock (&mutex_ssl); keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_SSL_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_SSL_KEYSIZE, ELEKTRA_CRYPTO_SSL_BLOCKSIZE); return 1; }
int elektraHexcodeGet (Plugin * handle, KeySet * returned, Key * parentKey) { /* get all keys */ if (!strcmp (keyName (parentKey), "system/elektra/modules/hexcode")) { KeySet * pluginConfig = ksNew (30, keyNew ("system/elektra/modules/hexcode", KEY_VALUE, "hexcode plugin waits for your orders", KEY_END), keyNew ("system/elektra/modules/hexcode/exports", KEY_END), keyNew ("system/elektra/modules/hexcode/exports/get", KEY_FUNC, elektraHexcodeGet, KEY_END), keyNew ("system/elektra/modules/hexcode/exports/set", KEY_FUNC, elektraHexcodeSet, KEY_END), keyNew ("system/elektra/modules/hexcode/exports/open", KEY_FUNC, elektraHexcodeOpen, KEY_END), keyNew ("system/elektra/modules/hexcode/exports/close", KEY_FUNC, elektraHexcodeClose, KEY_END), #include "readme_hexcode.c" keyNew ("system/elektra/modules/hexcode/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END); ksAppend (returned, pluginConfig); ksDel (pluginConfig); return 1; } CHexData * hd = elektraPluginGetData (handle); if (!hd->buf) { hd->buf = elektraMalloc (1000); hd->bufalloc = 1000; } Key * cur; ksRewind (returned); while ((cur = ksNext (returned)) != 0) { size_t valsize = keyGetValueSize (cur); if (valsize > hd->bufalloc) { hd->bufalloc = valsize; hd->buf = realloc (hd->buf, hd->bufalloc); } elektraHexcodeDecode (cur, hd); } return 1; /* success */ }
/** * @brief derive the cryptographic key and IV for a given (Elektra) Key k * @param config KeySet holding the plugin/backend configuration * @param errorKey holds an error description in case of failure * @param masterKey holds the decrypted master password from the plugin configuration * @param k the (Elektra)-Key to be encrypted * @param cKey (Elektra)-Key holding the cryptographic material * @param cIv (Elektra)-Key holding the initialization vector * @retval -1 on failure. errorKey holds the error description. * @retval 1 on success */ static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, Key * cKey, Key * cIv) { gcry_error_t gcry_err; kdb_octet_t salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN]; kdb_octet_t keyBuffer[KEY_BUFFER_SIZE]; char * saltHexString = NULL; ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL"); // generate the salt gcry_create_nonce (salt, sizeof (salt)); const int encodingResult = CRYPTO_PLUGIN_FUNCTION (base64Encode) (errorKey, salt, sizeof (salt), &saltHexString); if (encodingResult < 0) { // error in libinvoke - errorKey has been set by base64Encode return -1; } if (!saltHexString) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString); elektraFree (saltHexString); // read iteration count const kdb_unsigned_long_t iterations = CRYPTO_PLUGIN_FUNCTION (getIterationCount) (errorKey, config); // generate/derive the cryptographic key and the IV if ((gcry_err = gcry_kdf_derive (keyValue (masterKey), keyGetValueSize (masterKey), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, salt, sizeof (salt), iterations, KEY_BUFFER_SIZE, keyBuffer))) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Failed to create a cryptographic key for encryption because: %s", gcry_strerror (gcry_err)); return -1; } keySetBinary (cKey, keyBuffer, ELEKTRA_CRYPTO_GCRY_KEYSIZE); keySetBinary (cIv, keyBuffer + ELEKTRA_CRYPTO_GCRY_KEYSIZE, ELEKTRA_CRYPTO_GCRY_BLOCKSIZE); return 1; }