/** * @brief parse the hex-encoded salt from the metakey. * @param errorKey holds an error description in case of failure. * @param k holds the salt as metakey * @param salt is set to an allocated buffer containing the salt. Must be freed by the caller. * @param saltLen is set to the length of the salt. Ignored if NULL is provided. * @retval 1 on success * @retval -1 on error. errorKey holds a description. */ int CRYPTO_PLUGIN_FUNCTION (getSaltFromMetakey) (Key * errorKey, Key * k, kdb_octet_t ** salt, kdb_unsigned_long_t * saltLen) { size_t saltLenInternal = 0; const Key * meta = keyGetMeta (k, ELEKTRA_CRYPTO_META_SALT); if (!meta) { ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "missing salt as metakey %s in key %s", ELEKTRA_CRYPTO_META_SALT, keyName (k)); return -1; } int result = CRYPTO_PLUGIN_FUNCTION (base64Decode) (errorKey, keyString (meta), salt, &saltLenInternal); if (result == -1) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_CRYPTO_INTERNAL_ERROR, errorKey, "Salt was not stored Base64 encoded."); return -1; } else if (result == -2) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); return -1; } else if (result < -2) { // errorKey has been set by base64Decode (...) return -1; } *saltLen = saltLenInternal; return 1; }
int elektraAugeasOpen (Plugin * handle, Key * parentKey) { augeas * augeasHandle; augeasHandle = aug_init (NULL, NULL, AUG_NO_MODL_AUTOLOAD | AUG_NO_ERR_CLOSE); int ret; if (aug_error (augeasHandle) != AUG_NOERROR) { char * errormessage; ret = asprintf (&errormessage, "Unable to initialize augeas: %s", aug_error_message (augeasHandle)); if (ret >= 0) { ELEKTRA_SET_ERROR (87, parentKey, "Unable to allocate memory for a detailed augeas error message"); return -1; } ELEKTRA_SET_ERROR (85, parentKey, errormessage); elektraFree (errormessage); return -1; } elektraPluginSetData (handle, augeasHandle); return 0; }
/** * @brief Overwrites the content of the given file with zeroes. * @param fd holds the file descriptor to the temporary file to be shredded * @param errorKey holds an error description in case of failure * @retval 1 on success * @retval -1 on failure. In this case errorKey holds an error description. */ static int shredTemporaryFile (int fd, Key * errorKey) { kdb_octet_t buffer[512] = { 0 }; struct stat tmpStat; if (fstat (fd, &tmpStat)) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_FCRYPT_TMP_FILE, errorKey, "Failed to retrieve the file status of the temporary file."); return -1; } if (lseek (fd, 0, SEEK_SET)) { goto error; } for (off_t i = 0; i < tmpStat.st_size; i += sizeof (buffer)) { if (write (fd, buffer, sizeof (buffer)) != sizeof (buffer)) { goto error; } } return 1; error: ELEKTRA_SET_ERROR (ELEKTRA_ERROR_FCRYPT_TMP_FILE, errorKey, "Failed to overwrite the temporary file."); return -1; }
static int saveTree (augeas * augeasHandle, KeySet * ks, const char * lensPath, Key * parentKey) { int ret = 0; size_t prefixSize = keyGetNameSize (parentKey) - 1; size_t arraySize = ksGetSize (ks); Key ** keyArray = calloc (ksGetSize (ks), sizeof (Key *)); ret = elektraKsToMemArray (ks, keyArray); if (ret < 0) goto memoryerror; qsort (keyArray, arraySize, sizeof (Key *), keyCmpOrderWrapper); /* convert the Elektra KeySet to an Augeas tree */ for (size_t i = 0; i < arraySize; i++) { Key * key = keyArray[i]; char * nodeName; ret = asprintf (&nodeName, AUGEAS_TREE_ROOT "%s", (keyName (key) + prefixSize)); if (ret < 0) goto memoryerror; aug_set (augeasHandle, nodeName, keyString (key)); elektraFree (nodeName); } elektraFree (keyArray); /* remove keys not present in the KeySet */ struct OrphanSearch * data = elektraMalloc (sizeof (struct OrphanSearch)); if (!data) return -1; data->ks = ks; data->parentKey = parentKey; ret = foreachAugeasNode (augeasHandle, AUGEAS_TREE_ROOT, &removeOrphan, data); elektraFree (data); /* build the tree */ ret = aug_text_retrieve (augeasHandle, lensPath, AUGEAS_CONTENT_ROOT, AUGEAS_TREE_ROOT, AUGEAS_OUTPUT_ROOT); if (ret < 0) { /* report the augeas specific error */ ELEKTRA_SET_ERROR (85, parentKey, getAugeasError (augeasHandle)); } return ret; memoryerror: elektraFree (keyArray); ELEKTRA_SET_ERROR (87, parentKey, "Unable to allocate memory while saving the augeas tree"); return -1; }
static inline bool writeData (FILE * file, const char * data, kdb_unsigned_long_long_t size, Key * errorKey) { if (!writeUInt64 (file, size, errorKey)) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_WRITE_FAILED, errorKey, feof (file) ? "premature end of file" : "unknown error"); return false; } if (size > 0) { if (fwrite (data, sizeof (char), size, file) < size) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_WRITE_FAILED, errorKey, feof (file) ? "premature end of file" : "unknown error"); return false; } } return true; }
int elektraCryptoGcryHandleCreate (elektraCryptoHandle ** handle, KeySet * config, Key * errorKey) { gcry_error_t gcry_err; unsigned char keyBuffer[64], ivBuffer[64]; size_t keyLength, ivLength; (*handle) = NULL; // retrieve keys from configuration Key * key = elektraCryptoReadParamKey (config, errorKey); Key * iv = elektraCryptoReadParamIv (config, errorKey); if (key == NULL || iv == NULL) { 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)); 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)); 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; return (-1); }
static inline bool readUInt64 (FILE * file, kdb_unsigned_long_long_t * valuePtr, Key * errorKey) { if (fread (valuePtr, sizeof (kdb_unsigned_long_long_t), 1, file) < 1) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_READ_FAILED, errorKey, ""); return false; } *valuePtr = le64toh (*valuePtr); return true; }
static inline char * readString (FILE * file, Key * errorKey) { kdb_unsigned_long_long_t size; if (!readUInt64 (file, &size, errorKey)) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_READ_FAILED, errorKey, feof (file) ? "premature end of file" : "unknown error"); return NULL; } char * string = elektraMalloc (size + 1); if (fread (string, sizeof (char), size, file) < size) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_READ_FAILED, errorKey, feof (file) ? "premature end of file" : "unknown error"); elektraFree (string); return NULL; } string[size] = '\0'; return string; }
static inline bool writeUInt64 (FILE * file, kdb_unsigned_long_long_t value, Key * errorKey) { kdb_unsigned_long_long_t littleEndian = htole64 (value); if (fwrite (&littleEndian, sizeof (kdb_unsigned_long_long_t), 1, file) < 1) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_WRITE_FAILED, errorKey, feof (file) ? "premature end of file" : "unknown error"); return false; } return true; }
static CondResult parseCondition (Key * key, const char * condition, const Key * suffixList, KeySet * ks, Key * parentKey) { CondResult result = FALSE; const char * regexString = "((\\(([^\\(\\)]*)\\)))"; regex_t regex; if ((regcomp (®ex, regexString, REG_EXTENDED | REG_NEWLINE))) { ELEKTRA_SET_ERROR (87, parentKey, "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only // possible error would be out of // memory ksDel (ks); return ERROR; } char * localCondition = elektraStrDup (condition); int subMatches = 4; regmatch_t m[subMatches]; char * ptr = localCondition; while (1) { int nomatch = regexec (®ex, ptr, subMatches, m, 0); if (nomatch) { break; } if (m[3].rm_so == -1) { result = -1; break; } int startPos; int endPos; startPos = m[3].rm_so + (ptr - localCondition); endPos = m[3].rm_eo + (ptr - localCondition); char * singleCondition = elektraMalloc (endPos - startPos + 1); strncpy (singleCondition, localCondition + startPos, endPos - startPos); singleCondition[endPos - startPos] = '\0'; result = parseSingleCondition (key, singleCondition, suffixList, ks, parentKey); for (int i = startPos - 1; i < endPos + 1; ++i) localCondition[i] = ' '; localCondition[startPos - 1] = '\''; localCondition[startPos] = (result == TRUE) ? '1' : '0'; localCondition[startPos + 1] = '\''; elektraFree (singleCondition); } elektraFree (localCondition); regfree (®ex); return result; }
/** * @brief create a random sequence of characters with given length. * @param errorKey holds an error description in case of failure. * @param length the number of random bytes to be generated. * @returns allocated buffer holding a hex-encoded random string or NULL in case of error. Must be freed by the caller. */ char * elektraCryptoGcryCreateRandomString (Key * errorKey, const kdb_unsigned_short_t length) { char * encoded = NULL; kdb_octet_t buffer[length]; gcry_create_nonce (buffer, length); if (CRYPTO_PLUGIN_FUNCTION (base64Encode) (errorKey, buffer, length, &encoded) < 0) { return NULL; } if (!encoded) { ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed"); } return encoded; }
/** * @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 elektraWresolverOpen (Plugin * handle, Key * errorKey) { KeySet * resolverConfig = elektraPluginGetConfig (handle); const char * path = keyString (ksLookupByName (resolverConfig, "/path", 0)); if (!path) { ELEKTRA_SET_ERROR (34, errorKey, "Could not find file configuration"); return -1; } resolverHandles * p = elektraMalloc (sizeof (resolverHandles)); // switch is only present to forget no namespace and to get // a warning whenever a new namespace is present. // (also used below in close) // In fact its linear code executed: switch (KEY_NS_SPEC) { case KEY_NS_SPEC: resolverInit (&p->spec, path); elektraResolveSpec (&p->spec, errorKey); case KEY_NS_DIR: resolverInit (&p->dir, path); elektraResolveDir (&p->dir, errorKey); case KEY_NS_USER: resolverInit (&p->user, path); elektraResolveUser (&p->user, errorKey); case KEY_NS_SYSTEM: resolverInit (&p->system, path); elektraResolveSystem (&p->system, errorKey); case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: break; } elektraPluginSetData (handle, p); return 0; /* success */ }
int elektraAugeasOpen(Plugin *handle, Key *parentKey) { augeas *augeasHandle; augeasHandle = aug_init (NULL, NULL, AUG_NO_MODL_AUTOLOAD | AUG_NO_ERR_CLOSE); if (aug_error (augeasHandle) != AUG_NOERROR) { char *errormessage; asprintf (&errormessage, "Unable to initialize augeas: %s", aug_error_message (augeasHandle)); ELEKTRA_SET_ERROR(85, parentKey, errormessage); free (errormessage); return -1; } elektraPluginSetData (handle, augeasHandle); 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) { 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; }
/** @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; }
static CondResult parseConditionString (const Key * meta, const Key * suffixList, Key * parentKey, Key * key, KeySet * ks, Operation op) { const char * conditionString = keyString (meta); const char * regexString1 = "(\\(((.*)?)\\))[[:space:]]*\\?"; const char * regexString2 = "\\?[[:space:]]*(\\(((.*)?)\\))"; const char * regexString3 = "[[:space:]]*:[[:space:]]*(\\(((.*)?)\\))"; regex_t regex1, regex2, regex3; CondResult ret; if ((ret = regcomp (®ex1, regexString1, REGEX_FLAGS_CONDITION))) { ELEKTRA_SET_ERROR (87, parentKey, "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only // possible error would be out of // memory ksDel (ks); return ERROR; } if ((ret = regcomp (®ex2, regexString2, REGEX_FLAGS_CONDITION))) { ELEKTRA_SET_ERROR (87, parentKey, "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only // possible error would be out of // memory regfree (®ex1); ksDel (ks); return ERROR; } if ((ret = regcomp (®ex3, regexString3, REGEX_FLAGS_CONDITION))) { ELEKTRA_SET_ERROR (87, parentKey, "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only // possible error would be out of // memory regfree (®ex1); regfree (®ex2); ksDel (ks); return ERROR; } int subMatches = 6; regmatch_t m[subMatches]; int nomatch = regexec (®ex1, conditionString, subMatches, m, 0); if (nomatch) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", conditionString); regfree (®ex1); regfree (®ex2); regfree (®ex3); ksDel (ks); return ERROR; } if (m[1].rm_so == -1) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", conditionString); regfree (®ex1); regfree (®ex2); regfree (®ex3); ksDel (ks); return ERROR; } int startPos = m[1].rm_so; int endPos = m[1].rm_eo; char * condition = elektraMalloc (endPos - startPos + 1); char * thenexpr = NULL; char * elseexpr = NULL; strncpy (condition, conditionString + startPos, endPos - startPos); condition[endPos - startPos] = '\0'; nomatch = regexec (®ex2, conditionString, subMatches, m, 0); if (nomatch) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", conditionString); regfree (®ex1); regfree (®ex2); regfree (®ex3); ksDel (ks); return ERROR; } if (m[1].rm_so == -1) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", conditionString); regfree (®ex1); regfree (®ex2); regfree (®ex3); ksDel (ks); return ERROR; } startPos = m[1].rm_so; endPos = m[1].rm_eo; thenexpr = elektraMalloc (endPos - startPos + 1); strncpy (thenexpr, conditionString + startPos, endPos - startPos); thenexpr[endPos - startPos] = '\0'; nomatch = regexec (®ex3, conditionString, subMatches, m, 0); if (!nomatch) { if (m[1].rm_so == -1) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", conditionString); regfree (®ex1); regfree (®ex2); regfree (®ex3); ksDel (ks); return ERROR; } thenexpr[strlen (thenexpr) - ((m[0].rm_eo - m[0].rm_so))] = '\0'; startPos = m[1].rm_so; endPos = m[1].rm_eo; elseexpr = elektraMalloc (endPos - startPos + 1); strncpy (elseexpr, conditionString + startPos, endPos - startPos); elseexpr[endPos - startPos] = '\0'; } ret = parseCondition (key, condition, suffixList, ks, parentKey); if (ret == TRUE) { if (op == ASSIGN) { const char * assign = isAssign (key, thenexpr, parentKey, ks); if (assign != NULL) { keySetString (key, assign); ret = TRUE; goto CleanUp; } else { ret = ERROR; goto CleanUp; } } else { ret = parseCondition (key, thenexpr, suffixList, ks, parentKey); if (ret == FALSE) { ELEKTRA_SET_ERRORF (135, parentKey, "Validation of Key %s: %s failed. (%s failed)", keyName (key) + strlen (keyName (parentKey)) + 1, conditionString, thenexpr); } else if (ret == ERROR) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", thenexpr); } } } else if (ret == FALSE) { if (elseexpr) { if (op == ASSIGN) { const char * assign = isAssign (key, elseexpr, parentKey, ks); if (assign != NULL) { keySetString (key, assign); ret = TRUE; goto CleanUp; } else { ret = ERROR; goto CleanUp; } } else { ret = parseCondition (key, elseexpr, suffixList, ks, parentKey); if (ret == FALSE) { ELEKTRA_SET_ERRORF (135, parentKey, "Validation of Key %s: %s failed. (%s failed)", keyName (key) + strlen (keyName (parentKey)) + 1, conditionString, elseexpr); } else if (ret == ERROR) { ELEKTRA_SET_ERRORF ( 134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", elseexpr); } } } else { ret = NOEXPR; } } else if (ret == ERROR) { ELEKTRA_SET_ERRORF (134, parentKey, "Invalid syntax: \"%s\". Check kdb info conditionals for additional information", condition); } CleanUp: elektraFree (condition); elektraFree (thenexpr); if (elseexpr) elektraFree (elseexpr); regfree (®ex1); regfree (®ex2); regfree (®ex3); ksDel (ks); return ret; }
/** Initialize a plugin to be executed in its own process * * This will prepare all the required resources and then fork the current * process. After the initialization the child process will typically * call the command loop while the parent starts to send commands to it. * Also the resulting process information has to be stored for the plugin. * In order to allow users to handle custom plugin data this will not * automatically call elektraPluginSetData. * * Typically called in a plugin's open function like (assuming no custom plugin data): * @code int elektraPluginOpen (Plugin * handle, Key * errorKey) { ElektraPluginProcess * pp = elektraPluginGetData (handle); if (pp == NULL) { if ((pp = elektraPluginProcessInit (errorKey)) == NULL) return ELEKTRA_PLUGIN_STATUS_ERROR; elektraPluginSetData (handle, pp); if (!elektraPluginProcessIsParent (pp)) elektraPluginProcessStart (handle, pp); } if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessOpen (pp, errorKey); // actual plugin functionality to be executed in a child process return ELEKTRA_PLUGIN_STATUS_SUCCESS; } * @endcode * * @param handle the plugin's handle * @param errorKey a key where error messages will be set * @retval NULL if the initialization failed * @retval a pointer to the information * @ingroup processplugin **/ ElektraPluginProcess * elektraPluginProcessInit (Key * errorKey) { // First time initialization ElektraPluginProcess * pp; pp = elektraMalloc (sizeof (ElektraPluginProcess)); pp->counter = 0; pp->pluginData = NULL; pp->parentCommandPipeKey = NULL; pp->parentPayloadPipeKey = NULL; pp->childCommandPipeKey = NULL; pp->childPayloadPipeKey = NULL; pp->dump = elektraInvokeOpen ("dump", 0, errorKey); if (!pp->dump) { cleanupPluginData (pp, errorKey, 0); ELEKTRA_SET_ERROR (190, errorKey, "Failed to initialize the dump plugin"); return NULL; } // As generally recommended, ignore SIGPIPE because we will notice that the // commandKeySet has been transferred incorrectly anyway to detect broken pipes signal (SIGPIPE, SIG_IGN); // Prepare the pipes if (!makePipe (pp, errorKey, "parentCommandPipe", pp->parentCommandPipe) || !makePipe (pp, errorKey, "parentPayloadPipe", pp->parentPayloadPipe) || !makePipe (pp, errorKey, "childCommandPipe", pp->childCommandPipe) || !makePipe (pp, errorKey, "childPayloadPipe", pp->childPayloadPipe)) return NULL; pp->pid = fork (); if (pp->pid < 0) { cleanupPluginData (pp, errorKey, 1); ELEKTRA_SET_ERRORF (190, errorKey, "Failed to fork the plugin process, fork () returned %d", pp->pid); return NULL; } int pipeIdx = elektraPluginProcessIsParent (pp); close (pp->parentCommandPipe[!pipeIdx]); close (pp->parentPayloadPipe[!pipeIdx]); close (pp->childCommandPipe[pipeIdx]); close (pp->childPayloadPipe[pipeIdx]); ELEKTRA_LOG_DEBUG ("parentCommandPipe[%d] has file descriptor %d", pipeIdx, pp->parentCommandPipe[pipeIdx]); ELEKTRA_LOG_DEBUG ("parentPayloadPipe[%d] has file descriptor %d", pipeIdx, pp->parentPayloadPipe[pipeIdx]); ELEKTRA_LOG_DEBUG ("childCommandPipe[%d] has file descriptor %d", !pipeIdx, pp->childCommandPipe[!pipeIdx]); ELEKTRA_LOG_DEBUG ("childPayloadPipe[%d] has file descriptor %d", !pipeIdx, pp->childPayloadPipe[!pipeIdx]); // Prepare the keys for the pipes to use with dump pp->parentCommandPipeKey = makePipeKey ("parentCommandPipe", pp->parentCommandPipe[pipeIdx]); pp->parentPayloadPipeKey = makePipeKey ("parentPayloadPipe", pp->parentPayloadPipe[pipeIdx]); pp->childCommandPipeKey = makePipeKey ("childCommandPipe", pp->childCommandPipe[!pipeIdx]); pp->childPayloadPipeKey = makePipeKey ("childPayloadPipe", pp->childPayloadPipe[!pipeIdx]); ELEKTRA_LOG_DEBUG ("parentCommandPipeKey is %s on %d", keyString (pp->parentCommandPipeKey), pp->pid); ELEKTRA_LOG_DEBUG ("parentPayloadPipeKey is %s on %d", keyString (pp->parentPayloadPipeKey), pp->pid); ELEKTRA_LOG_DEBUG ("childCommandPipeKey is %s on %d", keyString (pp->childCommandPipeKey), pp->pid); ELEKTRA_LOG_DEBUG ("childPayloadPipeKey is %s on %d", keyString (pp->childPayloadPipeKey), pp->pid); ELEKTRA_LOG_DEBUG ("The pluginprocess is set with the pid %d", pp->pid); return pp; }
static CondResult evalCondition (const Key * curKey, const char * leftSide, Comparator cmpOp, const char * rightSide, const char * condition, const Key * suffixList, KeySet * ks, Key * parentKey) { char * lookupName = NULL; char * compareTo = NULL; Key * key; int len; long result = 0; if (rightSide) { if (rightSide[0] == '\'') { // right side of the statement is a literal enclosed by '' char * endPos = strchr (rightSide + 1, '\''); if (!endPos) { result = ERROR; goto Cleanup; } if (elektraRealloc ((void **) &compareTo, endPos - rightSide) < 0) { ELEKTRA_SET_ERROR (87, parentKey, "Out of memory"); result = ERROR; goto Cleanup; } memset (compareTo, 0, endPos - rightSide); strncat (compareTo, rightSide + 1, endPos - rightSide - 1); } else if (rightSide && elektraStrLen (rightSide) > 1) { // not a literal, it has to be a key if (rightSide[0] == '@') len = keyGetNameSize (parentKey) + elektraStrLen (rightSide); else if (!strncmp (rightSide, "..", 2) || (rightSide[0] == '.')) len = keyGetNameSize (curKey) + elektraStrLen (rightSide); else len = elektraStrLen (rightSide); if (elektraRealloc ((void **) &lookupName, len) < 0) { ELEKTRA_SET_ERROR (87, parentKey, "Out of memory"); result = ERROR; goto Cleanup; } if (rightSide[0] == '@') snprintf (lookupName, len, "%s/%s", keyName (parentKey), rightSide + 1); else if (rightSide[0] == '.') // either starts with . or .., doesn't matter at this point snprintf (lookupName, len, "%s/%s", keyName (curKey), rightSide); else snprintf (lookupName, len, "%s", rightSide); key = ksLookupByName (ks, lookupName, 0); if (!key) { if (!keyGetMeta (parentKey, "error")) { ELEKTRA_SET_ERRORF (133, parentKey, "Key %s not found but is required for the evaluation of %s", lookupName, condition); } result = FALSE; goto Cleanup; } if (elektraRealloc ((void **) &compareTo, keyGetValueSize (key)) < 0) { ELEKTRA_SET_ERROR (87, parentKey, "Out of memory"); result = ERROR; goto Cleanup; } strcpy (compareTo, keyString (key)); } } if (leftSide[0] == '@') len = keyGetNameSize (parentKey) + elektraStrLen (leftSide); else if (!strncmp (leftSide, "..", 2) || (leftSide[0] == '.')) len = keyGetNameSize (curKey) + elektraStrLen (leftSide); else len = elektraStrLen (leftSide); if (elektraRealloc ((void **) &lookupName, len) < 0) { ELEKTRA_SET_ERROR (87, parentKey, "Out of memory"); result = ERROR; goto Cleanup; } if (leftSide[0] == '@') snprintf (lookupName, len, "%s/%s", keyName (parentKey), leftSide + 1); else if (leftSide[0] == '.') // either . or .., doesn't matter here snprintf (lookupName, len, "%s/%s", keyName (curKey), leftSide); else snprintf (lookupName, len, "%s", leftSide); key = ksLookupByName (ks, lookupName, 0); if (cmpOp == NEX) { if (key) result = FALSE; else result = TRUE; goto Cleanup; } if (!key && cmpOp != OR && cmpOp != AND) { if (!keyGetMeta (parentKey, "error")) { ELEKTRA_SET_ERRORF (133, parentKey, "Key %s not found but is required for the evaluation of %s", lookupName, condition); } result = FALSE; goto Cleanup; } long ret; if (cmpOp == OR || cmpOp == AND) ret = compareStrings (leftSide, rightSide, NULL); else ret = compareStrings (keyString (key), compareTo, suffixList); switch (cmpOp) { case EQU: if (!ret) result = TRUE; break; case NOT: if (ret) result = TRUE; break; case LT: if (ret < 0) result = TRUE; break; case LE: if (ret <= 0) result = TRUE; break; case GT: if (ret > 0) result = TRUE; break; case GE: if (ret >= 0) result = TRUE; break; case SET: keySetString (key, compareTo); result = TRUE; break; case AND: if (ret == 0 && !strcmp (leftSide, "'1'")) result = TRUE; break; case OR: if (!strcmp (leftSide, "'1'") || (rightSide && !strcmp (rightSide, "'1'"))) result = TRUE; break; default: result = ERROR; break; } // freeing allocated heap Cleanup: if (lookupName) elektraFree (lookupName); if (compareTo) elektraFree (compareTo); return result; }
/** * @brief decrypt the file specified at parentKey * @param pluginConfig holds the plugin configuration * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure. * @param state holds the plugin state * @retval 1 on success * @retval -1 on error, errorKey holds an error description */ static int fcryptDecrypt (KeySet * pluginConfig, Key * parentKey, fcryptState * state) { int tmpFileFd = -1; char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd); if (!tmpFile) { ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed"); return -1; } const size_t testMode = inTestMode (pluginConfig); // prepare argument vector for gpg call // 8 static arguments (magic number below) are: // 1. path to the binary // 2. --batch // 3. -o // 4. path to tmp file // 5. yes // 6. -d // 7. file to be encrypted // 8. NULL terminator int argc = 8 + (2 * testMode); char * argv[argc]; int i = 0; argv[i++] = NULL; argv[i++] = "--batch"; argv[i++] = "--yes"; // if we are in test mode we add the trust model if (testMode) { argv[i++] = "--trust-model"; argv[i++] = "always"; } argv[i++] = "-o"; argv[i++] = tmpFile; argv[i++] = "-d"; // safely discarding const from keyString() return value argv[i++] = (char *) keyString (parentKey); argv[i++] = NULL; // NOTE the decryption process works like this: // gpg2 --batch --yes -o tmpfile -d configFile int result = ELEKTRA_PLUGIN_FUNCTION (gpgCall) (pluginConfig, parentKey, NULL, argv, argc); if (result == 1) { state->originalFilePath = elektraStrDup (keyString (parentKey)); state->tmpFilePath = tmpFile; state->tmpFileFd = tmpFileFd; keySetString (parentKey, tmpFile); } else { // if anything went wrong above the temporary file is shredded and removed shredTemporaryFile (tmpFileFd, parentKey); if (unlink (tmpFile)) { ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_FCRYPT_UNLINK, parentKey, "Affected file: %s, error description: %s", tmpFile, strerror (errno)); } if (close (tmpFileFd)) { ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_FCRYPT_CLOSE, parentKey, "%s", strerror (errno)); } elektraFree (tmpFile); } return result; }
/** Call a plugin's function in a child process * * This will wrap all the required information to execute the given * command in a keyset and send it over to the child process. Then * it waits for the child process's answer and copies the result * back into the original plugin keyset and plugin key. * * Typically called like * @code int elektraPluginSet (Plugin * handle, KeySet * returned, Key * parentKey) { ElektraPluginProcess * pp = elektraPluginGetData (handle); if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_SET, returned, parentKey); // actual plugin functionality to be executed in a child process return ELEKTRA_PLUGIN_STATUS_SUCCESS; } * @endcode * * @param pp the data structure containing the plugin's process information * @param command the plugin command that should be executed, e.g. ELEKTRA_PLUGINPROCESS_GET * @param originalKeySet the original key set that the parent process receives * @param key the original key the parent process receives * @retval ELEKTRA_PLUGIN_STATUS_ERROR if the child process communication failed * @retval the called plugin's return value otherwise * @see elektraPluginProcessIsParent for checking if we are in the parent or child process * @ingroup processplugin **/ int elektraPluginProcessSend (const ElektraPluginProcess * pp, pluginprocess_t command, KeySet * originalKeySet, Key * key) { // Ensure we have a keyset when trying to call GET SET and ERROR if ((command == ELEKTRA_PLUGINPROCESS_GET || command == ELEKTRA_PLUGINPROCESS_SET || command == ELEKTRA_PLUGINPROCESS_ERROR) && originalKeySet == NULL) { ELEKTRA_SET_ERROR (191, key, "originalKeySet has to exist when calling GET SET and ERROR via pluginprocess; but it is NULL"); return ELEKTRA_PLUGIN_STATUS_ERROR; } // Construct the command set that controls the pluginprocess communication KeySet * commandKeySet = ksNew (6, KS_END); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/parent/name", KEY_VALUE, keyName (key), KEY_END)); Key * parentKey = keyDup (key); keySetName (parentKey, "/pluginprocess/parent"); ksAppendKey (commandKeySet, parentKey); char * commandStr = longToStr (command); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/command", KEY_VALUE, commandStr, KEY_END)); elektraFree (commandStr); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/version", KEY_VALUE, "1", KEY_END)); // Some plugin functions don't use keysets, in that case don't send any actual payload, signal via flag KeySet * keySet = originalKeySet != NULL ? ksDup (originalKeySet) : NULL; char * payloadSizeStr = longToStr (ksGetSize (originalKeySet)); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/payload/size", KEY_VALUE, originalKeySet == NULL ? "-1" : payloadSizeStr, KEY_END)); elektraFree (payloadSizeStr); // Serialize, currently statically use dump as our default format, this already writes everything out to the pipe ELEKTRA_LOG ("Parent: Sending data to issue command %u it through pipe %s", command, keyString (pp->parentCommandPipeKey)); elektraInvoke2Args (pp->dump, "set", commandKeySet, pp->parentCommandPipeKey); if (keySet != NULL) { ELEKTRA_LOG ("Parent: Sending the payload keyset with %zd keys through the pipe %s", ksGetSize (keySet), keyString (pp->parentPayloadPipeKey)); elektraInvoke2Args (pp->dump, "set", keySet, pp->parentPayloadPipeKey); } // Deserialize ELEKTRA_LOG_DEBUG ("Parent: Waiting for the result now on pipe %s", keyString (pp->childCommandPipeKey)); elektraInvoke2Args (pp->dump, "get", commandKeySet, pp->childCommandPipeKey); if (keySet != NULL) { // clear the keyset before to avoid memleaks caused by dump char * endPtr; int prevErrno = errno; errno = 0; long payloadSize = strtol (keyString (ksLookupByName (commandKeySet, "/pluginprocess/payload/size", KDB_O_NONE)), &endPtr, 10); // in case the payload size fails to be transferred, that it shouldn't, we simply assume the previous size if (*endPtr != '\0' || errno == ERANGE || payloadSize < 0) payloadSize = ksGetSize (keySet); errno = prevErrno; ksDel (keySet); keySet = ksNew (payloadSize, KS_END); elektraInvoke2Args (pp->dump, "get", keySet, pp->childPayloadPipeKey); ELEKTRA_LOG ("Parent: We received %zd keys in return", ksGetSize (keySet)); } // Bring everything back in order by removing our process-related keys Key * parentDeserializedKey = ksLookupByName (commandKeySet, "/pluginprocess/parent", KDB_O_NONE); Key * resultKey = ksLookupByName (commandKeySet, "/pluginprocess/result", KDB_O_NONE); // Parse the result value char * endPtr; int prevErrno = errno; errno = 0; long lresult = strtol (keyString (resultKey), &endPtr, 10); if (*endPtr != '\0' || errno == ERANGE || lresult > INT_MAX || lresult < INT_MIN) { ELEKTRA_SET_ERRORF (191, key, "Received invalid return code or no KeySet: %s", keyString (resultKey)); lresult = ELEKTRA_PLUGIN_STATUS_ERROR; } else // Copy everything back into the actual keysets { Key * parentKeyInOriginalKeySet = keySet != NULL ? ksLookup (originalKeySet, key, KDB_O_NONE) : NULL; // maybe there are just 2 keys with the same name, can happen in theory, so compare memory int parentKeyExistsInOriginalKeySet = parentKeyInOriginalKeySet == key; // if the child added the parent key to the keyset pop it from the keyset // then reinsert key after we copied the data and delete this serialized copy Key * parentKeyInKeySet = keySet != NULL ? ksLookup (keySet, key, KDB_O_POP) : NULL; int childAddedParentKey = parentKeyInKeySet != NULL; // Unfortunately we can't use keyCopy here as ksAppendKey locks it so it will fail // This is the case if the parent key is also contained in the originalKeySet / has been appended // As an invariant we assume plugins don't change the parent key's name during a plugin call // This would interfere with keyset memberships keySetString (key, keyString (parentDeserializedKey)); // Clear metadata before, we allow children to modify it keyRewindMeta (key); const Key * currentMeta; while ((currentMeta = keyNextMeta (key)) != NULL) { keySetMeta (key, keyName (currentMeta), 0); } keyCopyAllMeta (key, parentDeserializedKey); if (childAddedParentKey) keyCopyAllMeta (key, parentKeyInKeySet); if (keySet != NULL) { // in case originalKeySet contains key this would make it stuck // thus remove it here and re-add it afterwards if (parentKeyExistsInOriginalKeySet) ksLookup (originalKeySet, parentKeyInOriginalKeySet, KDB_O_POP); ksCopy (originalKeySet, keySet); if (parentKeyExistsInOriginalKeySet || childAddedParentKey) ksAppendKey (originalKeySet, key); if (childAddedParentKey) keyDel (parentKeyInKeySet); } } errno = prevErrno; // Command finished, cleanup the remaining memory now ksDel (commandKeySet); if (keySet != NULL) ksDel (keySet); return lresult; // Safe, we had a bound check before, and plugins should return values in the int range }
/** * @brief Opens the session with the Key database. * * @pre errorKey must be a valid key, e.g. created with keyNew() * * The method will bootstrap itself the following way. * The first step is to open the default backend. With it * system/elektra/mountpoints will be loaded and all needed * libraries and mountpoints will be determined. * These libraries for backends will be loaded and with it the * @p KDB data structure will be initialized. * * You must always call this method before retrieving or committing any * keys to the database. In the end of the program, * after using the key database, you must not forget to kdbClose(). * * The pointer to the @p KDB structure returned will be initialized * like described above, and it must be passed along on any kdb*() * method your application calls. * * Get a @p KDB handle for every thread using elektra. Don't share the * handle across threads, and also not the pointer accessing it: * * @snippet kdbopen.c open * * You don't need kdbOpen() if you only want to * manipulate plain in-memory Key or KeySet objects. * * @pre errorKey must be a valid key, e.g. created with keyNew() * * @param errorKey the key which holds errors and warnings which were issued * @see kdbGet(), kdbClose() to end all affairs to the key database. * @retval handle on success * @retval NULL on failure * @ingroup kdb */ KDB * kdbOpen (Key * errorKey) { if (!errorKey) { ELEKTRA_LOG ("no error key passed"); return 0; } ELEKTRA_LOG ("called with %s", keyName (errorKey)); int errnosave = errno; KDB * handle = elektraCalloc (sizeof (struct _KDB)); Key * initialParent = keyDup (errorKey); handle->modules = ksNew (0, KS_END); if (elektraModulesInit (handle->modules, errorKey) == -1) { ksDel (handle->modules); elektraFree (handle); ELEKTRA_SET_ERROR (94, errorKey, "elektraModulesInit returned with -1"); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; } KeySet * keys = ksNew (0, KS_END); int inFallback = 0; switch (elektraOpenBootstrap (handle, keys, errorKey)) { case -1: ksDel (handle->modules); elektraFree (handle); ELEKTRA_SET_ERROR (40, errorKey, "could not open default backend"); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; case 0: ELEKTRA_ADD_WARNING (17, errorKey, "Initial kdbGet() failed, you should either fix " KDB_DB_INIT " or the fallback " KDB_DB_FILE); break; case 2: ELEKTRA_LOG ("entered fallback code for bootstrapping"); inFallback = 1; break; } keySetString (errorKey, "kdbOpen(): mountGlobals"); if (mountGlobals (handle, ksDup (keys), handle->modules, errorKey) == -1) { // mountGlobals also sets a warning containing the name of the plugin that failed to load ELEKTRA_ADD_WARNING (139, errorKey, "Mounting global plugins failed"); } keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, "kdbOpen(): backendClose"); backendClose (handle->defaultBackend, errorKey); splitDel (handle->split); handle->defaultBackend = 0; handle->trie = 0; #ifdef HAVE_LOGGER if (inFallback) ELEKTRA_LOG_WARNING ("fallback for bootstrapping: you might want to run `kdb upgrade-bootstrap`"); Key * key; ksRewind (keys); for (key = ksNext (keys); key; key = ksNext (keys)) { ELEKTRA_LOG_DEBUG ("config for createTrie name: %s value: %s", keyName (key), keyString (key)); } #endif handle->split = splitNew (); keySetString (errorKey, "kdbOpen(): mountOpen"); // Open the trie, keys will be deleted within mountOpen if (mountOpen (handle, keys, handle->modules, errorKey) == -1) { ELEKTRA_ADD_WARNING (93, errorKey, "Initial loading of trie did not work"); } keySetString (errorKey, "kdbOpen(): mountDefault"); if (mountDefault (handle, handle->modules, inFallback, errorKey) == -1) { ELEKTRA_SET_ERROR (40, errorKey, "could not reopen and mount default backend"); keySetString (errorKey, "kdbOpen(): close"); kdbClose (handle, errorKey); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; } keySetString (errorKey, "kdbOpen(): mountVersion"); mountVersion (handle, errorKey); keySetString (errorKey, "kdbOpen(): mountModules"); if (mountModules (handle, handle->modules, errorKey) == -1) { ELEKTRA_ADD_WARNING (92, errorKey, "Mounting modules did not work"); } keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return handle; }
/** * @brief encrypt or sign the file specified at parentKey * @param pluginConfig holds the plugin configuration * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure. * @retval 1 on success * @retval -1 on error, errorKey holds an error description */ static int fcryptEncrypt (KeySet * pluginConfig, Key * parentKey) { Key * k; const size_t recipientCount = getRecipientCount (pluginConfig, ELEKTRA_RECIPIENT_KEY); const size_t signatureCount = getRecipientCount (pluginConfig, ELEKTRA_SIGNATURE_KEY); if (recipientCount == 0 && signatureCount == 0) { ELEKTRA_SET_ERRORF ( ELEKTRA_ERROR_NO_GPG_RECIPIENTS, parentKey, "Missing GPG recipient key (specified as %s) or GPG signature key (specified as %s) in plugin configuration.", ELEKTRA_RECIPIENT_KEY, ELEKTRA_SIGNATURE_KEY); return -1; } int tmpFileFd = -1; char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd); if (!tmpFile) { ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed"); return -1; } const size_t testMode = inTestMode (pluginConfig); const size_t textMode = inTextMode (pluginConfig); // prepare argument vector for gpg call // 7 static arguments (magic number below) are: // 1. path to the binary // 2. --batch // 3. -o // 4. path to tmp file // 5. yes // 6. file to be encrypted // 7. NULL terminator int argc = 7 + (2 * recipientCount) + (2 * signatureCount) + (2 * testMode) + textMode + (recipientCount > 0 ? 1 : 0) + (signatureCount > 0 ? 1 : 0); kdb_unsigned_short_t i = 0; char * argv[argc]; argv[i++] = NULL; argv[i++] = "--batch"; argv[i++] = "-o"; argv[i++] = tmpFile; argv[i++] = "--yes"; // overwrite files if they exist // add recipients Key * gpgRecipientRoot = ksLookupByName (pluginConfig, ELEKTRA_RECIPIENT_KEY, 0); // append root (gpg/key) as gpg recipient if (gpgRecipientRoot && strlen (keyString (gpgRecipientRoot)) > 0) { argv[i++] = "-r"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) keyString (gpgRecipientRoot); } // append keys beneath root (crypto/key/#_) as gpg recipients if (gpgRecipientRoot) { ksRewind (pluginConfig); while ((k = ksNext (pluginConfig)) != 0) { const char * kStringVal = keyString (k); if (keyIsBelow (k, gpgRecipientRoot) && strlen (kStringVal) > 0) { argv[i++] = "-r"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) kStringVal; } } } // add signature keys Key * gpgSignatureRoot = ksLookupByName (pluginConfig, ELEKTRA_SIGNATURE_KEY, 0); // append root signature key if (gpgSignatureRoot && strlen (keyString (gpgSignatureRoot)) > 0) { argv[i++] = "-u"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) keyString (gpgSignatureRoot); } // append keys beneath root (fcrypt/sign/#_) as gpg signature keys if (gpgSignatureRoot) { ksRewind (pluginConfig); while ((k = ksNext (pluginConfig)) != 0) { const char * kStringVal = keyString (k); if (keyIsBelow (k, gpgSignatureRoot) && strlen (kStringVal) > 0) { argv[i++] = "-u"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) kStringVal; } } } // if we are in test mode we add the trust model if (testMode > 0) { argv[i++] = "--trust-model"; argv[i++] = "always"; } // ASCII armor in text mode if (textMode) { argv[i++] = "--armor"; } // prepare rest of the argument vector if (recipientCount > 0) { // encrypt the file argv[i++] = "-e"; } if (signatureCount > 0) { if (textMode && recipientCount == 0) { // clear-sign the file argv[i++] = "--clearsign"; } else { // sign the file argv[i++] = "-s"; } } argv[i++] = (char *) keyString (parentKey); argv[i++] = NULL; // NOTE the encryption process works like this: // gpg2 --batch --yes -o encryptedFile -r keyID -e configFile // mv encryptedFile configFile return fcryptGpgCallAndCleanup (parentKey, pluginConfig, argv, argc, tmpFileFd, tmpFile); }
int 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 (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, 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 (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, 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 (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, 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 (ELEKTRA_ERROR_CRYPTO_DECRYPT_FAIL, 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; }
/** * @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; }
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 (ELEKTRA_ERROR_CRYPTO_ENCRYPT_FAIL, 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 (ELEKTRA_ERROR_CRYPTO_ENCRYPT_FAIL, 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; }
int elektraSimpleiniGet (Plugin * handle, KeySet * returned, Key * parentKey) { /* get all keys */ if (!strcmp (keyName (parentKey), "system/elektra/modules/simpleini")) { KeySet * moduleConfig = ksNew ( 30, keyNew ("system/elektra/modules/simpleini", KEY_VALUE, "simpleini plugin waits for your orders", KEY_END), keyNew ("system/elektra/modules/simpleini/exports", KEY_END), keyNew ("system/elektra/modules/simpleini/exports/get", KEY_FUNC, elektraSimpleiniGet, KEY_END), keyNew ("system/elektra/modules/simpleini/exports/set", KEY_FUNC, elektraSimpleiniSet, KEY_END), #include "readme_simpleini.c" keyNew ("system/elektra/modules/simpleini/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), keyNew ("system/elektra/modules/simpleini/config/needs", KEY_VALUE, "the needed configuration to work in a backend", KEY_END), keyNew ("system/elektra/modules/simpleini/config/needs/chars", KEY_VALUE, "Characters needed", KEY_END), // space in value now works: // TODO: characters present in format should be escaped /* keyNew ("system/elektra/modules/simpleini/config/needs/chars/20", KEY_VALUE, "61", KEY_END), // space -> a keyNew ("system/elektra/modules/simpleini/config/needs/chars/23", KEY_VALUE, "62", KEY_END), // # -> b keyNew ("system/elektra/modules/simpleini/config/needs/chars/25", KEY_VALUE, "63", KEY_END), // % -> c (escape character) keyNew ("system/elektra/modules/simpleini/config/needs/chars/3B", KEY_VALUE, "64", KEY_END), // ; -> d keyNew ("system/elektra/modules/simpleini/config/needs/chars/3D", KEY_VALUE, "65", KEY_END), // = -> e keyNew ("system/elektra/modules/simpleini/config/needs/chars/5C", KEY_VALUE, "66", KEY_END), // \\ -> f */ keyNew ("system/elektra/modules/simpleini/config/needs/chars/0A", KEY_VALUE, "67", KEY_END), // enter (NL) -> g keyNew ("system/elektra/modules/simpleini/config/needs/chars/0D", KEY_VALUE, "68", KEY_END), // CR -> h keyNew ("system/elektra/modules/simpleini/config/needs/escape", KEY_VALUE, "25", KEY_END), KS_END); ksAppend (returned, moduleConfig); ksDel (moduleConfig); return 1; } char * key = 0; char * value = 0; int errnosave = errno; FILE * fp = fopen (keyString (parentKey), "r"); if (!fp) { ELEKTRA_SET_ERROR_GET (parentKey); errno = errnosave; return -1; } char * format = getFormat (handle, "%ms", "%m[^\n]"); ELEKTRA_LOG ("Read from '%s' with format '%s'", keyString (parentKey), format); int n = 0; size_t size = 0; ssize_t ksize = 0; #pragma GCC diagnostic ignored "-Wformat" // icc warning #269: invalid format string conversion while ((n = fscanf (fp, format, &key, &value)) >= 0) { ELEKTRA_LOG_DEBUG ("Read %d parts: '%s' with value '%s'", n, key, value); if (n == 0) { // discard line getline (&key, &size, fp); ELEKTRA_LOG_DEBUG ("Discard '%s'", key); elektraFree (key); key = 0; continue; } Key * read = keyNew (keyName (parentKey), KEY_END); if (keyAddName (read, key) == -1) { ELEKTRA_ADD_WARNING (ELEKTRA_WARNING_INVALID_KEY, parentKey, key); keyDel (read); continue; } if (n == 2) { keySetString (read, value); elektraFree (value); value = 0; } if (ksAppendKey (returned, read) != ksize + 1) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_NOEOF, parentKey, "duplicated key"); return -1; } ++ksize; elektraFree (key); key = 0; } if (feof (fp) == 0) { elektraFree (format); fclose (fp); ELEKTRA_SET_ERROR (ELEKTRA_ERROR_NOEOF, parentKey, "not at the end of file"); return -1; } elektraFree (format); fclose (fp); return 1; /* success */ }
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; }
static int parseConditionString(const Key *meta, Key *parentKey, KeySet *ks) { const char *conditionString = keyString(meta); const char *regexString = "(\\(([^\\)]*)\\))\\s*(\\?)\\s*(\\(([^\\)]*)\\))\\s*(:\\s*(\\(([^\\)]*)\\))){0,1}"; regex_t regex; int ret; if((ret = regcomp(®ex, regexString, REG_EXTENDED|REG_NEWLINE))) { ELEKTRA_SET_ERROR(87, parentKey, "Couldn't compile regex: most likely out of memory"); //the regex compilers so the only possible error would be out of memory ksDel(ks); return -1; } int subMatches = 9; regmatch_t m[subMatches]; char *ptr = (char *)conditionString; int nomatch = regexec(®ex, ptr, subMatches, m, 0); if(nomatch) { ELEKTRA_SET_ERRORF(134, parentKey, "Invalid syntax: \"%s\". See README\n", conditionString); regfree(®ex); ksDel(ks); return -1; } if(m[2].rm_so == -1 || m[5].rm_so == -1) { ELEKTRA_SET_ERRORF(134, parentKey, "Invalid syntax: \"%s\". See README\n", conditionString); regfree(®ex); ksDel(ks); return -1; } char *condition = NULL; char *thenexpr = NULL; char *elseexpr = NULL; int startPos; int endPos; startPos = m[2].rm_so + (ptr - conditionString); endPos = m[2].rm_eo + (ptr - conditionString); condition = elektraMalloc(endPos - startPos +1); strncpy(condition, conditionString+startPos, endPos - startPos); condition[endPos-startPos] = '\0'; startPos = m[5].rm_so + (ptr - conditionString); endPos = m[5].rm_eo + (ptr - conditionString); thenexpr = elektraMalloc(endPos - startPos +1); strncpy(thenexpr, conditionString+startPos, endPos - startPos); thenexpr[endPos-startPos] = '\0'; if(m[8].rm_so != -1) { startPos = m[8].rm_so + (ptr - conditionString); endPos = m[8].rm_eo + (ptr - conditionString); elseexpr = elektraMalloc(endPos - startPos +1); strncpy(elseexpr, conditionString+startPos, endPos - startPos); elseexpr[endPos-startPos] = '\0'; } ret = parseSingleCondition(condition, ks, parentKey); if(ret == 1) ret = parseSingleCondition(thenexpr, ks, parentKey); else if(ret == 0) { if(elseexpr) ret = parseSingleCondition(elseexpr, ks, parentKey); else ret = 1; } elektraFree(condition); elektraFree(thenexpr); if(elseexpr) elektraFree(elseexpr); regfree(®ex); ksDel(ks); return ret; }
static int csvRead(KeySet *returned, Key *parentKey, char delim, short useHeader, unsigned long fixColumnCount, const char **colNames) { const char *fileName; fileName = keyString(parentKey); FILE *fp = NULL; fp = fopen(fileName, "rb"); if(!fp) { ELEKTRA_SET_ERRORF(116, parentKey, "couldn't open file %s\n", fileName); return -1; } unsigned long length = 0; length = getLineLength(fp); if(length == 0) { ELEKTRA_ADD_WARNING(118, parentKey, "Empty file"); fclose(fp); return -2; } char *lineBuffer; lineBuffer = elektraMalloc((length * sizeof(char))+1); if(!lineBuffer) { ELEKTRA_SET_ERROR(87, parentKey, "Out of memory"); return -1; } if(!fgets(lineBuffer, length, fp)) { ELEKTRA_SET_ERROR(116, parentKey, "Cant read from file"); return -1; } unsigned long columns = 0; columns = getColumnCount(lineBuffer, delim); if(fixColumnCount) { if(columns != fixColumnCount) { ELEKTRA_SET_ERROR(117, parentKey, "illegal number of columns in Header line"); elektraFree(lineBuffer); fclose(fp); return -1; } } unsigned long colCounter = 0; unsigned long lineCounter = 0; unsigned long offset = 0; char *col; char buf[INTSTR_MAX]; int nr_keys = 1; KeySet *header = ksNew(0, KS_END); Key *key; if(useHeader == 1) { colCounter = 0; offset = 0; while((col = parseLine(lineBuffer, delim, offset, parentKey, lineCounter)) != NULL) { offset += elektraStrLen(col); key = keyDup(parentKey); if(colNames && (colNames+colCounter)) { keyAddBaseName(key, colNames[colCounter]); } else { keyAddBaseName(key, col); } keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); ksAppendKey(header, key); ++colCounter; } fseek(fp, 0, SEEK_SET); } else { colCounter = 0; //if no headerline exists name the columns 0..N where N is the number of columns key = keyDup(parentKey); keyAddName(key, "#"); while(colCounter < columns) { if(elektraArrayIncName(key) == -1) { elektraFree(lineBuffer); keyDel(key); ksDel(header); fclose(fp); return -1; } keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); if(colNames && (colNames+colCounter)) keySetBaseName(key, colNames[colCounter]); ksAppendKey(header, keyDup(key)); ++colCounter; } keyDel(key); if(useHeader == 0) fseek(fp, 0, SEEK_SET); } Key *dirKey; Key *cur; dirKey = keyDup(parentKey); keyAddName(dirKey, "#"); while(!feof(fp)) { length = getLineLength(fp); if(length == 0) break; if(elektraRealloc((void **)&lineBuffer, (length * sizeof(char))+1) < 0) { fclose(fp); elektraFree(lineBuffer); ksDel(header); keyDel(dirKey); ELEKTRA_SET_ERROR(87, parentKey, "Out of memory"); return -1; } fgets(lineBuffer, length, fp); if(elektraArrayIncName(dirKey) == -1) { elektraFree(lineBuffer); keyDel(dirKey); ksDel(header); fclose(fp); return -1; } ++nr_keys; offset = 0; colCounter = 0; char *lastIndex = "#0"; while((col = parseLine(lineBuffer, delim, offset, parentKey, lineCounter)) != NULL) { cur = getKeyByOrderNr(header, colCounter); offset += elektraStrLen(col); key = keyDup(dirKey); keyAddBaseName(key, keyBaseName(cur)); keySetString(key, col); keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); ksAppendKey(returned, key); lastIndex = (char *)keyBaseName(key); ++nr_keys; ++colCounter; } keySetString(dirKey, lastIndex); ksAppendKey(returned, keyDup(dirKey)); if(colCounter != columns) { if(fixColumnCount) { ELEKTRA_SET_ERRORF(117, parentKey, "illegal number of columns in line %lu", lineCounter); elektraFree(lineBuffer); fclose(fp); keyDel(dirKey); ksDel(header); return -1; } ELEKTRA_ADD_WARNINGF(118, parentKey, "illegal number of columns in line %lu", lineCounter); } ++lineCounter; } key = keyDup(parentKey); keySetString(key, keyBaseName(dirKey)); ksAppendKey(returned, key); keyDel(dirKey); fclose(fp); elektraFree(lineBuffer); ksDel(header); return 1; }