/**Allocate memory for Elektra. * * Memory will be set to 0. * * @param size the requested size * @see elektraMalloc */ void * elektraCalloc (size_t size) { ELEKTRA_ASSERT (size, "Size to allocate is zero (implementation defined behavior)"); void * ret = calloc (1, size); ELEKTRA_ASSERT (ret, "Memory allocation failed with size %zu", size); return ret; }
/** * @internal * * @brief Unescapes (a part of) a key name. * * As described in Syntax for Key Names, slashes are * prefixed with a \\ (or uneven number thereof). This method removes all \\ that are such * escape characters. * * The new string will be written to dest. * May only need half the storage than the source string. * It is not safe to use the same string for source and dest. * * @param source the source to read from * @param size the number of bytes to process from source * @param dest the destination to write to * * @return the destination pointer how far it was written to */ char * elektraUnescapeKeyNamePart (const char * source, size_t size, char * dest) { const char * sp = source; char * dp = dest; size_t count = 0; ELEKTRA_ASSERT (sp != NULL && dp != NULL, "Got null pointer sp: %p dp: %p", (void *) sp, (void *) dp); while (size) { if (*sp == '\\') { ++count; } else if (*sp == '/') { // we escape a part, so there had to be a backslash ELEKTRA_ASSERT (count > 0, "no backslash found, count is %zu", count); ELEKTRA_ASSERT ((count % 2) == 1, "counted uneven number of backslashes: %zu", count); count /= 2; while (count) { *dp = '\\'; ++dp; --count; } *dp = *sp; ++dp; } else { // output delayed backslashes while (count) { *dp = '\\'; ++dp; --count; } *dp = *sp; ++dp; } ++sp; --size; } ELEKTRA_ASSERT ((count % 2) == 0, "uneven number of backslashes: %zu", count); count /= 2; while (count) { *dp = '\\'; ++dp; --count; } return dp; }
/**Copy string into new allocated memory. * * You need to free the memory yourself. * * @note that size is determined at runtime. * So if you have a size information, don't use * that function. * * @param s the null-terminated string to duplicate * * @ingroup internal * @return 0 if out of memory, a pointer otherwise * @pre s must be a c-string. * @see elektraFree * @see elektraStrLen * @see elektraStrNDup */ char * elektraStrDup (const char * s) { void * tmp = 0; size_t l = 0; ELEKTRA_ASSERT (s, "Tried to duplicate null pointer"); l = elektraStrLen (s); ELEKTRA_ASSERT (l, "Size of string to duplicate is zero"); tmp = elektraMalloc (l); if (tmp) memcpy (tmp, s, l); return tmp; }
/** * @internal * * @brief Write number backslashes to dest * * @param dest where to write to, will be updated to position after * the written backslashes * @param number of backslashes to write */ static void elektraWriteBackslashes (char ** dest, size_t number) { ELEKTRA_ASSERT (dest, "Got null pointer"); ELEKTRA_ASSERT (*dest, "Got null pointer (*dest)"); char * dp = *dest; while (number) { *dp = '\\'; ++dp; --number; } *dest = dp; }
/** * @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; }
/** * @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; }
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey) { resolverHandles * pks = elektraPluginGetData (handle); ELEKTRA_ASSERT (pks != NULL, "Unable to retrieve plugin data for handle %p with parentKey %s", (void *) handle, keyName (parentKey)); switch (keyGetNamespace (parentKey)) { case KEY_NS_SPEC: return &pks->spec; case KEY_NS_DIR: return &pks->dir; case KEY_NS_USER: return &pks->user; case KEY_NS_SYSTEM: return &pks->system; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: return 0; } return 0; }
/** * @internal * * @brief Unescapes a key name. * * Writes a null terminated sequence of key name parts to dest. * * May only need half the storage than the source string. * It is not safe to use the same string for source and dest. **/ size_t elektraUnescapeKeyName (const char * source, char * dest) { const char * sp = source; char * dp = dest; size_t size = 0; ELEKTRA_ASSERT (sp != NULL && dp != NULL, "Got null pointer sp: %p dp: %p", (void *) sp, (void *) dp); if (*sp == '/') { // handling for cascading names *dp = 0; ++dp; } while (*(sp = keyNameGetOneLevel (sp + size, &size))) { if (!elektraUnescapeKeyNamePartBegin (sp, size, &dp)) { dp = elektraUnescapeKeyNamePart (sp, size, dp); } *dp = 0; ++dp; } return dp - dest; }
/** * Calculates the length in bytes of a string. * * This function differs from strlen() because it is Unicode and multibyte * chars safe. While strlen() counts characters and ignores the final NULL, * elektraStrLen() count bytes including the ending NULL. * * It must not be used to search for / in the name, because it does not * consider escaping. Instead use the unescaped name. * * @see keyUnescapedName() * * @ingroup internal * @param s the string to get the length from * @return number of bytes used by the string, including the final NULL. * @ingroup internal */ size_t elektraStrLen (const char * s) { ELEKTRA_ASSERT (s, "Got null pointer"); char * found = strchr (s, 0); if (found) return found - s + 1; return 0; }
/** * @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; }
/** * Validates whether the supplied keyname is valid. * * The function looks for tangling escape characters in the end * and for a minimum length. * * Does not check for valid namespaces * * @pre size must be at least 2 * * @param name the key name that is to be checked * @param size a elektraStrLen of the key name * @retval true if the supplied keyname part is valid * @retval false if its invalid */ int elektraValidateKeyName (const char * name, size_t size) { ELEKTRA_ASSERT (name, "Got null pointer"); ELEKTRA_ASSERT (size >= 2, "size too small %zu", size); size_t escapeCount = 0; size -= 2; // forward null character to last character // now do backwards iteration while (size && name[size] == '\\') { ++escapeCount; --size; } return (escapeCount % 2) == 0; // only allow equal number of escapes in the end }
/** * @brief Does string formatting in fresh allocated memory * * @param format as in printf() * @param ... as in printf() * * @return new allocated memory (free with elektraFree) */ char * elektraFormat (const char * format, ...) { ELEKTRA_ASSERT (format, "Got null pointer"); va_list va; va_start (va, format); char * ret = elektraVFormat (format, va); va_end (va); return ret; }
/**Copy buffer into new allocated memory. * * You need to free the memory yourself. * * This function also works with \\0 characters * in the buffer. The length is taken as given, * it must be correct. * * @return 0 if out of memory, a pointer otherwise * @param s must be a allocated buffer * @param l the length of s * @ingroup internal */ char * elektraStrNDup (const char * s, size_t l) { void * tmp = 0; ELEKTRA_ASSERT (l, "Size for string duplicate is zero"); tmp = elektraMalloc (l); if (tmp) memcpy (tmp, s, l); return tmp; }
/**Reallocate Storage in a save way. * *@code if (elektraRealloc ((void **) & buffer, new_length) < 0) { // here comes the failure handler // you can still use the old buffer #if DEBUG fprintf (stderr, "Reallocation error\n"); #endif elektraFree (buffer); buffer = 0; // return with error } *@endcode * * @param buffer is a pointer to a elektraMalloc * @param size is the new size for the memory * @retval -1 on failure * @retval 0 on success * @ingroup internal */ int elektraRealloc (void ** buffer, size_t size) { ELEKTRA_ASSERT (size, "Size to allocate is zero (implementation defined behavior)"); void * ptr; void * svr = *buffer; ptr = realloc (*buffer, size); ELEKTRA_ASSERT (ptr, "Memory (re)allocation failed with size %zu", size); if (ptr == NULL) { *buffer = svr; /* restore old buffer*/ return -1; } else { *buffer = ptr; return 0; } }
/** * @brief Filter out keys not in the correct keyset * * @param split the split where to do it * @param i for which split * @param warningKey the key * @param handle where to do backend lookups * * @retval -1 on error (no backend, wrong namespace) * @retval 0 otherwise */ static int elektraSplitPostprocess (Split * split, int i, Key * warningKey, KDB * handle) { Key * cur = 0; Backend * curHandle = 0; ksRewind (split->keysets[i]); while ((cur = ksNext (split->keysets[i])) != 0) { curHandle = elektraMountGetBackend (handle, cur); if (!curHandle) return -1; keyClearSync (cur); if (curHandle != split->handles[i]) { elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is hidden by other mountpoint"); } else switch (keyGetNamespace (cur)) { case KEY_NS_SPEC: if (!keyIsSpec (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not spec"); break; case KEY_NS_DIR: if (!keyIsDir (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not dir"); break; case KEY_NS_USER: if (!keyIsUser (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not user"); break; case KEY_NS_SYSTEM: if (!keyIsSystem (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not system"); break; case KEY_NS_PROC: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a proc key name"); break; case KEY_NS_EMPTY: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has an empty name"); break; case KEY_NS_META: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a meta name"); break; case KEY_NS_CASCADING: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a cascading name"); break; case KEY_NS_NONE: ELEKTRA_ASSERT (0 && "wrong key namespace, should not be none"); return -1; } } return 0; }
/** * @brief Does string formatting in fresh allocated memory * * @param format as in vprintf() * @param arg_list as in vprintf() * * @return new allocated memory (free with elektraFree) */ char * elektraVFormat (const char * format, va_list arg_list) { ELEKTRA_ASSERT (format, "Got null pointer"); static int const default_size = 512; char * buffer = elektraMalloc (default_size); if (!buffer) return 0; va_list arg_list_adj; va_copy (arg_list_adj, arg_list); int const calculated_length = vsnprintf (buffer, default_size, format, arg_list); if (calculated_length == -1) { va_end (arg_list_adj); elektraFree (buffer); // before Glibc 2.0.6, always -1 is returned // we won't do Glibc job, please upgrade return 0; } if (calculated_length < default_size) { va_end (arg_list_adj); // content was written successfully into // default sized buffer return buffer; } // String is longer than default_size. // Allocate an intermediate buffer // according to the calculated length from our last try size_t const adjusted_buffer_size = calculated_length + 1; elektraRealloc ((void **) &buffer, adjusted_buffer_size); if (!buffer) { va_end (arg_list_adj); return 0; } int const ret = vsnprintf (buffer, adjusted_buffer_size, format, arg_list_adj); va_end (arg_list_adj); if (ret == -1) { elektraFree (buffer); return 0; } return buffer; }
/** * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterCallback */ int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, void * context) { PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context, 0); if (registeredKey == NULL) { return 0; } return 1; }
/** * @internal * * @brief Escapes character in the part of a key name. * * As described in Syntax for Key Names, special characters will be * prefixed with a \\. No existing escaping is assumed. That means * that even sequences that look like escapings will be escaped again. * For example, \\/ will be escaped (or quoted) to \\\\\\/. * * The string will be written to dest. * * @note May need twice the storage than the source string. * Do not use the source string as destination string. * * @param source the source pointer where escaping should start * @param dest the destination to write to (twice the size as sp) * * @return pointer to destination */ char * elektraEscapeKeyNamePart (const char * source, char * dest) { if (elektraEscapeKeyNamePartBegin (source, dest)) { return dest; } size_t count = 0; const char * sp = source; char * dp = dest; ELEKTRA_ASSERT (sp != NULL && dp != NULL, "Got null pointer sp: %p dp: %p", (void *) sp, (void *) dp); while (*sp) { if (*sp == '\\') { ++count; } else if (*sp == '/') { // escape every slash *dp = '\\'; ++dp; // and print escaped slashes while (count) { *dp = '\\'; ++dp; --count; } } else { count = 0; } *dp = *sp; ++dp; ++sp; } // print other escaped backslashes at end of part while (count) { *dp = '\\'; ++dp; --count; } *dp = 0; return dest; }
/** * @brief Remove one part of split. * * @param split the split object to work with * @param where the position to cut away * * @pre where must be within the size of the split * @post split will be removed * * @ingroup split */ void elektraSplitRemove (Split * split, size_t where) { ELEKTRA_ASSERT (where < split->size && "cannot remove behind size"); ksDel (split->keysets[where]); keyDel (split->parents[where]); --split->size; // reduce size for (size_t i = where; i < split->size; ++i) { split->keysets[i] = split->keysets[i + 1]; split->handles[i] = split->handles[i + 1]; split->parents[i] = split->parents[i + 1]; split->syncbits[i] = split->syncbits[i + 1]; } }
ssize_t elektraKeySetName(Key *key, const char *newName, option_t options) { if (!key) return -1; if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; elektraRemoveKeyName(key); if (!(options & KEY_META_NAME)) keySetOwner (key, NULL); switch (keyGetNameNamespace(newName)) { case KEY_NS_NONE: ELEKTRA_ASSERT(0); case KEY_NS_EMPTY: elektraFinalizeEmptyName(key); return 0; // as documented case KEY_NS_CASCADING: key->keyUSize=1;key->keySize=sizeof("/"); break; case KEY_NS_SPEC: key->keyUSize=key->keySize=sizeof("spec"); break; case KEY_NS_PROC: key->keyUSize=key->keySize=sizeof("proc"); break; case KEY_NS_DIR: key->keyUSize=key->keySize=sizeof("dir"); break; case KEY_NS_USER: elektraHandleUserName(key, newName); break; case KEY_NS_SYSTEM: key->keyUSize=key->keySize=sizeof("system"); break; case KEY_NS_META: if (!(options & KEY_META_NAME)) return -1; keyNameGetOneLevel(newName,&key->keySize); key->keyUSize = ++ key->keySize; // for null break; } // Note that we abused keyUSize for cascading and user:owner const size_t length = elektraStrLen(newName); key->key=elektraMalloc(key->keySize*2); memcpy(key->key, newName, key->keySize); if (length == key->keyUSize || length == key->keySize) { // use || because full length is keyUSize in user, but keySize for / // newName consisted of root only elektraFinalizeName(key); return key->keyUSize; } if (elektraOnlySlashes(newName+key->keyUSize-1)) { elektraFinalizeName(key); return key->keySize; } key->key[key->keySize-1] = '\0'; const ssize_t ret = keyAddName(key, newName+key->keyUSize); if (ret == -1) elektraRemoveKeyName(key); else return key->keySize; return ret; }
/** * @brief Compare two memory regions but make cmp chars uppercase before * comparison. * * @param s1 The first string to be compared * @param s2 The second string to be compared * @param size to be compared * * @ingroup internal * @return a negative number if s1 is less than s2 * @retval 0 if s1 matches s2 * @return a positive number if s1 is greater than s2 */ int elektraMemCaseCmp (const char * s1, const char * s2, size_t size) { size_t i; ELEKTRA_ASSERT (s1 != NULL && s2 != NULL, "Got null pointer s1: %p s2: %p", (void *) s1, (void *) s2); for (i = 0; i < size; i++) { const unsigned char cmp1 = s1[i]; const unsigned char cmp2 = s2[i]; const int CMP1 = toupper (cmp1); const int CMP2 = toupper (cmp2); const int diff = CMP1 - CMP2; if (diff) return diff; } return 0; }
/** * @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; }
/** * Copies the key array2 into where array1 points. * It copies size elements. * * Overlapping is prohibited, use elektraMemmove() instead. * * @param array1 the destination * @param array2 the source * @param size how many pointer to Keys to copy * @retval -1 on null pointers * @retval 0 if nothing was done * @return size how many keys were copied */ ssize_t elektraMemcpy (Key ** array1, Key ** array2, size_t size) { if (!array1) return -1; if (!array2) return -1; if (size > SSIZE_MAX) return -1; if (size == 0) return 0; #if DEBUG char * a = (char *) array1; char * b = (char *) array2; for (size_t i = 0; i < size; i++) { ELEKTRA_ASSERT (a + i != b && b + i != a, "memcpy overlap: %p and %p with size %zu", (void *) a, (void *) b, size); } #endif memcpy (array1, array2, size * sizeof (Key *)); return size; }
/** * @internal * * Escapes (a part of) a key name. */ int elektraEscapeKeyNamePartBegin (const char * source, char * dest) { const char * sp = source; char * dp = dest; ELEKTRA_ASSERT (sp != NULL && dp != NULL, "Got null pointer sp: %p dp: %p", (void *) sp, (void *) dp); if (!strcmp ("", sp)) { strcpy (dp, "%"); return 1; } size_t skippedBackslashes = 0; // skip all backslashes at start of a name while (*sp == '\\') { ++sp; ++skippedBackslashes; } if (!strcmp ("%", sp)) { elektraWriteBackslashes (&dp, skippedBackslashes); strcpy (dp, "\\%"); return 1; } if (!strcmp (".", sp)) { elektraWriteBackslashes (&dp, skippedBackslashes); strcpy (dp, "\\."); return 1; } if (!strcmp ("..", sp)) { elektraWriteBackslashes (&dp, skippedBackslashes); strcpy (dp, "\\.."); return 1; } return 0; }
/** * @internal * * @brief Check if an update is needed at all * * @retval -1 an error occurred * @retval 0 no update needed * @retval number of plugins which need update */ static int elektraGetCheckUpdateNeeded (Split * split, Key * parentKey) { int updateNeededOccurred = 0; for (size_t i = 0; i < split->size; i++) { int ret = -1; Backend * backend = split->handles[i]; clear_bit (split->syncbits[i], 1); if (backend->getplugins[RESOLVER_PLUGIN] && backend->getplugins[RESOLVER_PLUGIN]->kdbGet) { ksRewind (split->keysets[i]); keySetName (parentKey, keyName (split->parents[i])); keySetString (parentKey, ""); ret = backend->getplugins[RESOLVER_PLUGIN]->kdbGet (backend->getplugins[RESOLVER_PLUGIN], split->keysets[i], parentKey); // store resolved filename keySetString (split->parents[i], keyString (parentKey)); // no keys in that backend backendUpdateSize (backend, split->parents[i], 0); } // TODO: set error in else case! switch (ret) { case 1: // Seems like we need to sync that set_bit (split->syncbits[i], SPLIT_FLAG_SYNC); ++updateNeededOccurred; break; case 0: // Nothing to do here break; default: ELEKTRA_ASSERT (0, "resolver did not return 1 0 -1, but %d", ret); case -1: // Ohh, an error occurred, lets stop the // process. return -1; } } return updateNeededOccurred; }
/** * @brief Allows one to Export Methods for a Plugin. * * This function must be called within ELEKTRA_PLUGIN_EXPORT. * It define the plugin's methods that will be exported. * * All KDB methods implemented by the plugin basically could * have random names (convention is elektraName*), except * ELEKTRA_PLUGIN_EXPORT. * * This is the single symbol that will be looked up * when loading the plugin, and the first method of the backend * implementation that will be called. * * You need to use a macro so that both dynamic and static loading * of the plugin works. For example for the doc plugin: * @snippet doc.c export * * The first parameter is the name of the plugin. * Then every plugin should have: * @c ELEKTRA_PLUGIN_OPEN, * @c ELEKTRA_PLUGIN_CLOSE, * @c ELEKTRA_PLUGIN_GET, * @c ELEKTRA_PLUGIN_SET and optionally * @c ELEKTRA_PLUGIN_ERROR. * * The list is terminated with * @c ELEKTRA_PLUGIN_END. * * You must use static "char arrays" in a read only segment. * Don't allocate storage, it won't be freed. * * @param pluginName the name of this plugin * @return an object that contains all plugin information needed by * libelektra.so * @ingroup plugin */ Plugin * elektraPluginExport (const char * pluginName, ...) { va_list va; Plugin * returned; plugin_t method = 0; if (pluginName == 0) return 0; returned = elektraCalloc (sizeof (struct _Plugin)); /* Start processing parameters */ va_start (va, pluginName); returned->name = pluginName; while ((method = va_arg (va, plugin_t))) { switch (method) { case ELEKTRA_PLUGIN_OPEN: returned->kdbOpen = va_arg (va, kdbOpenPtr); break; case ELEKTRA_PLUGIN_CLOSE: returned->kdbClose = va_arg (va, kdbClosePtr); break; case ELEKTRA_PLUGIN_GET: returned->kdbGet = va_arg (va, kdbGetPtr); break; case ELEKTRA_PLUGIN_SET: returned->kdbSet = va_arg (va, kdbSetPtr); break; case ELEKTRA_PLUGIN_ERROR: returned->kdbError = va_arg (va, kdbErrorPtr); break; default: ELEKTRA_ASSERT (0, "plugin passed something unexpected"); // fallthrough, will end here case ELEKTRA_PLUGIN_END: va_end (va); return returned; } } return returned; }
/** * @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; }
static void elektraHandleUserName (Key * key, const char * newName) { const size_t userLength = sizeof ("user"); key->keyUSize = key->keySize = userLength; const char delim = newName[userLength - 1]; // no owner, we are finished if (delim == '/' || delim == '\0') return; ELEKTRA_ASSERT (delim == ':'); // handle owner (compatibility, to be removed) keyNameGetOneLevel (newName, &key->keyUSize); const size_t ownerLength = key->keyUSize - userLength; ++key->keyUSize; char * owner = elektraMalloc (ownerLength + 1); if (!owner) return; // out of memory, ok for owner strncpy (owner, newName + userLength, ownerLength); owner[ownerLength] = 0; keySetOwner (key, owner); elektraFree (owner); }
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey) { resolverHandles * pks = elektraPluginGetData (handle); switch (keyGetNamespace (parentKey)) { case KEY_NS_SPEC: return &pks->spec; case KEY_NS_DIR: return &pks->dir; case KEY_NS_USER: return &pks->user; case KEY_NS_SYSTEM: return &pks->system; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: break; } ELEKTRA_ASSERT (0, "namespace %d not valid for resolving", keyGetNamespace (parentKey)); return 0; }
/** * Updates all KeyRegistrations according to data from the given KeySet * @internal * * @param plugin internal plugin handle * @param keySet key set retrieved from hooks * e.g. elektraInternalnotificationGet or elektraInternalnotificationSet) * */ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * keySet) { PluginState * pluginState = elektraPluginGetData (plugin); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); KeyRegistration * registeredKey = pluginState->head; while (registeredKey != NULL) { int changed = 0; Key * key; if (registeredKey->sameOrBelow) { Key * checkKey = keyNew (registeredKey->name, KEY_END); if (keySetContainsSameOrBelow (checkKey, keySet)) { changed = 1; key = checkKey; } else { keyDel (checkKey); } } else { key = ksLookupByName (keySet, registeredKey->name, 0); if (key != NULL) { // Detect changes for string keys if (!keyIsString (key)) { // always notify for binary keys changed = 1; } else { const char * currentValue = keyString (key); changed = registeredKey->lastValue == NULL || strcmp (currentValue, registeredKey->lastValue) != 0; if (changed) { // Save last value char * buffer = elektraStrDup (currentValue); if (buffer) { if (registeredKey->lastValue != NULL) { // Free previous value elektraFree (registeredKey->lastValue); } registeredKey->lastValue = buffer; } } } } } if (changed) { ELEKTRA_LOG_DEBUG ("found changed registeredKey=%s with string value \"%s\". using context or variable=%p", registeredKey->name, keyString (key), registeredKey->context); // Invoke callback ElektraNotificationChangeCallback callback = *(ElektraNotificationChangeCallback) registeredKey->callback; callback (key, registeredKey->context); if (registeredKey->sameOrBelow) { keyDel (key); } } // proceed with next registered key registeredKey = registeredKey->next; } }