// elektra wildcard syntax to fnmatch syntax static char * keyNameToMatchingString (const Key * key) { uint8_t arrayCount = 0; char * name = strchr (keyName (key), '/'); if (!name) return elektraStrDup (keyName (key)); for (char * ptr = name; *ptr != '\0'; ++ptr) if (*ptr == '#') ++arrayCount; char * pattern = elektraMalloc (elektraStrLen (name) + arrayCount); char * dst = pattern; for (char * src = (name + 1); *src != '\0'; ++src) { if (*src == '_' && *(src - 1) == '/' && (*(src + 1) == '/' || *(src + 1) == '\0')) { *dst++ = '*'; } else if (*src == '#' && *(src - 1) == '/' && (*(src + 1) == '/' || *(src + 1) == '\0')) { *dst++ = '#'; *dst++ = '*'; } else { *dst++ = *src; } } *dst = '\0'; return pattern; }
/** * Creates a new KeyRegistration structure and appends it at the end of the registration list * @internal * * @param pluginState internal plugin data structure * @param key key * @param callback callback for changes * @param context context for callback * @param freeContext context needs to be freed on close * * @return pointer to created KeyRegistration structure or NULL if memory allocation failed */ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginState * pluginState, Key * key, ElektraNotificationChangeCallback callback, void * context, int freeContext) { KeyRegistration * item = elektraMalloc (sizeof *item); if (item == NULL) { return NULL; } item->next = NULL; item->lastValue = NULL; item->name = elektraStrDup (keyName (key)); item->callback = callback; item->context = context; item->sameOrBelow = 0; item->freeContext = freeContext; if (pluginState->head == NULL) { // Initialize list pluginState->head = pluginState->last = item; } else { // Make new item end of list pluginState->last->next = item; pluginState->last = item; } return item; }
/** * @retval 0 if variant did not have a result * @retval 1 on success */ static int elektraResolveSystem (char variant, ElektraResolved * handle, ElektraResolveTempfile tmpDir, Key * warningsKey) { // hardcoded path wins against variants for now if (handle->relPath[0] == '/') { /* Use absolute path */ handle->fullPath = elektraStrDup (handle->relPath); elektraResolveFinishByFilename (handle, tmpDir); return 1; } if (handle->relPath[0] == '~') { if (elektraResolveSystemPasswd (handle, warningsKey) == -1) { return -1; } elektraResolveFinishByFilename (handle, tmpDir); return 1; } switch (variant) { case 'x': return elektraResolveSystemXDG (handle, tmpDir, warningsKey); case 'b': return elektraResolveSystemBuildin (handle, tmpDir, warningsKey); // TODO: also document in doc/COMPILE.md } return -1; }
int elektraGlobMatch (Key * key, const Key * match, const char * globFlags) { char * tokenList = elektraStrDup (globFlags); char delimiter[] = ","; char * flagName = strtok (tokenList, delimiter); int flags = 0; while (flagName != NULL) { for (size_t i = 0; i < sizeof (flagMaps) / sizeof (struct GlobFlagMap); i++) { if (!strcmp (flagName, flagMaps[i].name)) { flags |= flagMaps[i].flag; } } flagName = strtok (NULL, delimiter); } free (tokenList); if (!fnmatch (keyString (match), keyName (key), flags)) { keyCopyAllMeta (key, match); return 1; } return 0; }
/** * @internal * Read placement list from plugin. * * The returned string needs to be freed. * * @param plugin Plugin * @return Space separated list of placement names */ static char * getPluginPlacementList (Plugin * plugin) { ELEKTRA_NOT_NULL (plugin); // Get placements from plugin Key * pluginInfo = keyNew ("system/elektra/modules/", KEY_END); keyAddBaseName (pluginInfo, plugin->name); KeySet * ksResult = ksNew (0, KS_END); plugin->kdbGet (plugin, ksResult, pluginInfo); Key * placementsKey = keyDup (pluginInfo); keyAddBaseName (placementsKey, "infos"); keyAddBaseName (placementsKey, "placements"); Key * placements = ksLookup (ksResult, placementsKey, 0); if (placements == NULL) { ELEKTRA_LOG_WARNING ("could not read placements from plugin"); return 0; } char * placementList = elektraStrDup (keyString (placements)); keyDel (pluginInfo); keyDel (placementsKey); ksDel (ksResult); return placementList; }
/** * @brief Builds together a format string by the plugin's configuration * * @param handle to plugin * @param first format string for key * @param second format string for value * * @return newly allocated format (to be freed with elektraFree); */ static char * getFormat (Plugin * handle, const char * first, const char * second) { char * format; Key * key = ksLookupByName (elektraPluginGetConfig (handle), "/format", 0); if (!key) { format = elektraStrDup ("%s = %s\n"); } else { const size_t maxFactor = 2; // at maximum every char is a %, %% -> %%%% const size_t newLineAtEnd = 2; const size_t userFormatSize = keyGetValueSize (key); format = elektraMalloc (userFormatSize * maxFactor + newLineAtEnd); const char * userFormat = keyString (key); int gotPercent = 0; size_t j = 0; for (size_t i = 0; i < userFormatSize; ++i, ++j) { const char c = userFormat[i]; if (gotPercent) { if (c == '%') { // escaped %% -> %%%% format[j++] = '%'; format[j++] = '%'; format[j] = '%'; } else { // single % -> %s format[j++] = 's'; format[j] = c; } gotPercent = 0; } else if (c == '%') { format[j] = c; gotPercent = 1; } else { format[j] = c; } } --j; // discard null byte that is already there ELEKTRA_ASSERT (format[j] == '\0', "should be null byte at end of string but was %c", format[j]); format[j++] = '\n'; format[j] = '\0'; } char * ret = elektraFormat (format, first, second); elektraFree (format); return ret; }
static int elektraResolveSystemXDG(resolverHandle *p, Key *warningsKey) { const char * configDir = getenv("XDG_CONFIG_DIRS"); const char *defaultDir = "/etc/xdg"; if (!configDir || !strcmp(configDir, "")) { elektraResolveSystemXDGHelper(p, defaultDir); elektraResolveFinishByFilename(p); return 1; } char *saveptr = 0; char *str = elektraStrDup(configDir); char *result = strtok_r (str, ":", &saveptr); struct stat buf; int errnoSave = errno; int success = 0; while (result) { if (result[0] != '/') { ELEKTRA_ADD_WARNINGF(100, warningsKey, "XDG_CONFIG_DIRS contains a path that is " "not absolute (violates XDG specification) and thus " "it was skipped: %s", result); result = strtok_r (0, ":", &saveptr); continue; } success = 1; // at least once we got a valid path elektraResolveSystemXDGHelper(p, result); if (stat(p->filename, &buf) == 0) { // we found a file! break; } result = strtok_r (0, ":", &saveptr); } elektraFree(str); errno = errnoSave; if (!success) { elektraResolveSystemXDGHelper(p, defaultDir); } elektraResolveFinishByFilename(p); return 1; }
/** * Creates a new ElektraError using the provided values. * The returned value will be allocated with elektraCalloc(). * * @param code The error code of the error. * @param description The description of the error. * @param severity The severity of the error. Only use ELEKTRA_ERROR_SEVERITY_FATAL, * if the error will be raised with elektraFatalError(). * @param group The group to which this error belongs. * @param module The module from which this error originates. * @return A newly allocated ElektraError (free with elektraFree()). */ ElektraError * elektraErrorCreate (ElektraErrorCode code, const char * description, ElektraErrorSeverity severity) { ElektraError * const error = elektraCalloc (sizeof (struct _ElektraError)); error->code = code; error->description = elektraStrDup (description); error->severity = severity; return error; }
static Key * prefToKey (Key * parentKey, PrefType type, const char * pref) { Key * key = keyNew (keyName (parentKey), KEY_END); keyAddBaseName (key, prefix[type]); char * localString = elektraStrDup (pref); char * cPtr = strstr (localString, ","); *cPtr = '\0'; char * sPtr = localString; ++sPtr; *sPtr++ = '\0'; char * ePtr = cPtr - 1; elektraRstrip (sPtr, &ePtr); size_t keyLen = ePtr - sPtr; char * prefKey = elektraMalloc (keyLen + 1); snprintf (prefKey, keyLen + 1, "%s", sPtr); char * tPtr = strtok (prefKey, "."); if (tPtr) keyAddBaseName (key, tPtr); while ((tPtr = strtok (NULL, ".")) != NULL) { keyAddBaseName (key, tPtr); } elektraFree (prefKey); sPtr = cPtr + 1; sPtr = elektraLskip (sPtr); ePtr = strrchr (sPtr, ')'); *ePtr-- = '\0'; elektraRstrip (sPtr, &ePtr); size_t argLen = ePtr - sPtr + 1; char * prefArg = elektraMalloc (argLen + 1); snprintf (prefArg, argLen + 1, "%s", sPtr); if (!strcmp (prefArg, "true") || !(strcmp (prefArg, "false"))) { keySetMeta (key, "type", "boolean"); keySetString (key, prefArg); } else if (prefArg[0] == '"' && prefArg[strlen (prefArg) - 1] == '"') { // TODO: else if list keySetMeta (key, "type", "string"); *prefArg = '\0'; *(prefArg + (strlen (prefArg + 1))) = '\0'; keySetString (key, (prefArg + 1)); } else { keySetMeta (key, "type", "integer"); keySetString (key, prefArg); } elektraFree (prefArg); elektraFree (localString); return key; }
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 Allocates a new string holding the name of the temporary file. * This method makes use of the "fcrypt/tmpdir" plugin configuration option. * @param conf holds the plugin configuration * @param file holds the path to the original file * @param fd will hold the file descriptor to the temporary file in case of success * @returns an allocated string holding the name of the encrypted file. Must be freed by the caller. */ static char * getTemporaryFileName (KeySet * conf, const char * file, int * fd) { // read the temporary directory to use from the plugin configuration // NOTE the string contained in tmpDir must not be modified! const char * tmpDir = NULL; Key * k = ksLookupByName (conf, ELEKTRA_FCRYPT_CONFIG_TMPDIR, 0); if (k) { tmpDir = keyString (k); } if (!tmpDir) { // check the environment; returns NULL if no match is found tmpDir = getenv ("TMPDIR"); } if (!tmpDir) { // fallback tmpDir = ELEKTRA_FCRYPT_DEFAULT_TMPDIR; } // extract the file name (base name) from the path char * fileDup = elektraStrDup (file); if (!fileDup) goto error; const char * baseName = basename (fileDup); // + 1 to add an additional '/' as path separator // + 1 to reserve space for the NULL terminator // ---------------------------------------------- // + 2 characters in total const size_t newFileAllocated = strlen (tmpDir) + strlen (baseName) + strlen (ELEKTRA_FCRYPT_TMP_FILE_SUFFIX) + 2; char * newFile = elektraMalloc (newFileAllocated); if (!newFile) goto error; snprintf (newFile, newFileAllocated, "%s/%s" ELEKTRA_FCRYPT_TMP_FILE_SUFFIX, tmpDir, baseName); *fd = mkstemp (newFile); if (*fd < 0) { elektraFree (newFile); goto error; } elektraFree (fileDup); return newFile; error: elektraFree (fileDup); return NULL; }
static void elektraResolveFinishByFilename (ElektraResolved * handle, ElektraResolveTempfile tmpDir) { size_t filenameSize = strlen (handle->fullPath); char * dir = elektraMalloc (filenameSize); char * dup = elektraStrDup (handle->fullPath); strcpy (dir, dirname (dup)); elektraFree (dup); handle->dirname = dir; switch (tmpDir) { case ELEKTRA_RESOLVER_TEMPFILE_NONE: return; case ELEKTRA_RESOLVER_TEMPFILE_SAMEDIR: elektraGenTempFilename (handle, tmpDir); return; case ELEKTRA_RESOLVER_TEMPFILE_TMPDIR: elektraGenTempFilename (handle, tmpDir); return; } }
static char * elektraResolvePasswd (Key * warningsKey) { ssize_t bufSize = sysconf (_SC_GETPW_R_SIZE_MAX); if (bufSize == -1) bufSize = 16384; // man 3 getpwuid char * buf = elektraMalloc (bufSize); if (!buf) return NULL; struct passwd pwd; struct passwd * result; int s = getpwuid_r (getuid (), &pwd, buf, bufSize, &result); if (result == NULL) { elektraFree (buf); if (s != 0) { ELEKTRA_ADD_WARNING (90, warningsKey, strerror (s)); } return NULL; } char * resolved = elektraStrDup (pwd.pw_dir); elektraFree (buf); return resolved; }
Trie* elektraTrieInsert(Trie *trie, const char *name, Backend *value) { char* p; unsigned char idx; if (name==0) name=""; idx=(unsigned char)name[0]; if (trie==NULL) { trie=elektraCalloc(sizeof(Trie)); if (!strcmp("",name)) { trie->empty_value=value; return trie; } trie->textlen[idx]=strlen(name); trie->text[idx]=elektraStrDup(name); trie->value[idx]=value; return trie; } if (!strcmp("",name)) { trie->empty_value=value; return trie; } if (trie->text[idx]) { /* there exists an entry with the same first character */ if ((p=elektraTrieStartsWith(name, trie->text[idx]))==0) { /* the name in the trie is part of the searched name --> continue search */ trie->children[idx]=elektraTrieInsert(trie->children[idx],name+trie->textlen[idx],value); } else { /* name in trie doesn't match name --> split trie */ char *newname; Trie *child; unsigned char idx2; newname=elektraStrDup(p); *p=0; /* shorten the old name in the trie */ trie->textlen[idx]=strlen(trie->text[idx]); child=trie->children[idx]; /* insert the name given as a parameter into the new trie entry */ trie->children[idx]=elektraTrieInsert(NULL, name+(p-trie->text[idx]), value); /* insert the split try into the new trie entry */ idx2 = (unsigned char) newname[0]; trie->children[idx]->text[idx2]=newname; trie->children[idx]->textlen[idx2]=strlen(newname); trie->children[idx]->value[idx2]=trie->value[idx]; trie->children[idx]->children[idx2]=child; trie->value[idx]=0; } } else { /* there doesn't exist an entry with the same first character */ trie->text[idx]=elektraStrDup(name); trie->value[idx]=(void*)value; trie->textlen[idx]=strlen(name); } return trie; }
/** * 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; } }
/** * @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; }