From e87c13dee0cae879c64dece0417ca1e224b5f4e1 Mon Sep 17 00:00:00 2001 From: Jussi Laakkonen Date: Wed, 19 May 2021 17:50:23 +0300 Subject: [PATCH] [firejail] Implement template support. OMP#JOLLA-88 JB#54034 Add two patches which change profile loading to be delayed until all arguments are parsed. This is due to the template key replacement, since templates are to be given as arguments as well. Each line in the profile line is checked and if one template is found they are replaced with the corresponding value. If no changes are required the profile line is untouched. --- ...s-to-a-list-when-processing-argument.patch | 159 ++++ ...te-addition-for-replacing-keys-in-pr.patch | 697 ++++++++++++++++++ rpm/firejail.spec | 2 + 3 files changed, 858 insertions(+) create mode 100644 rpm/0011-Add-profile-files-to-a-list-when-processing-argument.patch create mode 100644 rpm/0012-Implement-template-addition-for-replacing-keys-in-pr.patch diff --git a/rpm/0011-Add-profile-files-to-a-list-when-processing-argument.patch b/rpm/0011-Add-profile-files-to-a-list-when-processing-argument.patch new file mode 100644 index 00000000..78c06018 --- /dev/null +++ b/rpm/0011-Add-profile-files-to-a-list-when-processing-argument.patch @@ -0,0 +1,159 @@ +From 11b8108544b4a0d535ce4bb7c6c098b8d6070f88 Mon Sep 17 00:00:00 2001 +From: Jussi Laakkonen +Date: Mon, 17 May 2021 17:33:30 +0300 +Subject: [PATCH] Add profile files to a list when processing arguments + +Do not read the profile files when processing arguments. Instead prepend +them to a list that is processed after the arguments are processed in +order to be able to replace all template keys in the profile file lines. +The list is reversed before processing to have the profile files read in +the given order. Each item in the list is free'd after it has been +processed. +--- + src/firejail/firejail.h | 1 + + src/firejail/main.c | 3 ++ + src/firejail/profile.c | 81 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 85 insertions(+) + +diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h +index e4eebe62..5ac874e0 100644 +--- a/src/firejail/firejail.h ++++ b/src/firejail/firejail.h +@@ -454,6 +454,7 @@ char *profile_list_slice(char *pos, char **ppos); + char *profile_list_normalize(char *list); + char *profile_list_compress(char *list); + void profile_list_augment(char **list, const char *items); ++void profile_read_file_list(); + + // list.c + void list(void); +diff --git a/src/firejail/main.c b/src/firejail/main.c +index 4a367d7e..88b99de7 100644 +--- a/src/firejail/main.c ++++ b/src/firejail/main.c +@@ -2732,6 +2732,9 @@ int main(int argc, char **argv, char **envp) { + break; + } + } ++ ++ profile_read_file_list(); ++ + EUID_ASSERT(); + + // exit chroot, overlay and appimage sandboxes when caps are explicitly specified on command line +diff --git a/src/firejail/profile.c b/src/firejail/profile.c +index c57d8d26..9de72826 100644 +--- a/src/firejail/profile.c ++++ b/src/firejail/profile.c +@@ -26,6 +26,18 @@ extern char *xephyr_screen; + + #define MAX_READ 8192 // line buffer for profile files + ++typedef struct profile_file_name_t { ++ char *fname; ++ struct profile_file_name_t *next; ++} ProfileFileName; ++ ++// This is initially set to make profile_read() to add the profile filename ++// to a list of profiles that are to be read after arguments have been ++// processed and templates are set in order to replace any template key ++// existing in the profile lines. ++static int read_profiles = 0; ++static ProfileFileName *profile_file_name_list = NULL; ++ + // find and read the profile specified by name from dir directory + // return 1 if a profile was found + static int profile_find(const char *name, const char *dir, int add_ext) { +@@ -1643,6 +1655,27 @@ void profile_add(char *str) { + ptr->next = prf; + } + ++// Prepends entries to profile_file_name_list for later reading of the files ++// List is reversed when the file list is processed to provide correct order ++void add_to_profile_file_name_list(const char *fname) ++{ ++ ProfileFileName *pfn; ++ ++ if (!fname || !*fname) ++ return; ++ ++ if (arg_debug) ++ printf("Add profile \"%s\" to list\n", fname); ++ ++ pfn = malloc(sizeof(ProfileFileName)); ++ if (!pfn) ++ errExit("malloc"); ++ ++ pfn->fname = strdup(fname); ++ pfn->next = profile_file_name_list; ++ profile_file_name_list = pfn; ++} ++ + // read a profile file + static int include_level = 0; + void profile_read(const char *fname) { +@@ -1691,6 +1724,11 @@ void profile_read(const char *fname) { + } + } + ++ if (!read_profiles) { ++ add_to_profile_file_name_list(fname); ++ return; ++ } ++ + // open profile file: + FILE *fp = fopen(fname, "r"); + if (fp == NULL) { +@@ -1779,6 +1817,49 @@ void profile_read(const char *fname) { + fclose(fp); + } + ++static ProfileFileName *reverse_read_file_list(ProfileFileName *head) ++{ ++ ProfileFileName *curr = head; ++ ProfileFileName *prev = NULL; ++ ProfileFileName *next = NULL; ++ ++ while (curr) { ++ next = curr->next; ++ curr->next = prev; ++ prev = curr; ++ curr = next; ++ } ++ ++ return prev; ++} ++ ++void profile_read_file_list() ++{ ++ ProfileFileName *iter; ++ ProfileFileName *temp; ++ ++ read_profiles = 1; ++ ++ // Profile files are prepended to the list, reverse the list to ++ // read profile files in given order. Get the beginning of the ++ // reverse list and free each element as they are processed. ++ iter = reverse_read_file_list(profile_file_name_list); ++ while (iter) { ++ if (arg_debug) ++ printf("Read profile \"%s\"\n", iter->fname); ++ ++ profile_read(iter->fname); ++ ++ temp = iter; ++ iter = iter->next; ++ ++ free(temp->fname); ++ free(temp); ++ } ++ ++ profile_file_name_list = NULL; ++} ++ + char *profile_list_slice(char *pos, char **ppos) + { + /* Extract token from comma separated list. +-- +2.20.1 + diff --git a/rpm/0012-Implement-template-addition-for-replacing-keys-in-pr.patch b/rpm/0012-Implement-template-addition-for-replacing-keys-in-pr.patch new file mode 100644 index 00000000..07cbd466 --- /dev/null +++ b/rpm/0012-Implement-template-addition-for-replacing-keys-in-pr.patch @@ -0,0 +1,697 @@ +From 8aceebe6fe1354e9b85ee3effcfabeda34e7f770 Mon Sep 17 00:00:00 2001 +From: Jussi Laakkonen +Date: Fri, 7 May 2021 18:29:29 +0300 +Subject: [PATCH] Implement template addition for replacing keys in profile + files + +Implement template addition to pass templates as key value pairs as cmd +line parameters to replace the keys in read profile file lines to allow +more customization and flexibility. This adds a new file called +template.c which contains all the functionality. + +Motivation for this is to have the possibility to create profile files +where a specific key exists that can be customized per application that +us being run with firejail. For example, application name can be used in +a D-Bus name that is requested to be owned to avoid collision with other +applications requiring the same base name. This can be passed directly +then to firejail as a cmd line parameter when starting the application. + +All templates are to be given via cmd line parameters in format: + --template=KEY:VALUE + +The keys and values are stored in a single linked list within template.c, +which is free'd when the keys in all read profile file (including +included profiles) lines have been replaced. + +Each key can exist only once, existing hardcoded macros in addition to +XDG cannot be overwritten. In any of these is violated firejail exits. +Key cannot start with a digit and must contain alphanumeric chars only. + +Each value must conform to following rules: + - length is < 255 (D-Bus name length) + - can contain alphabetical (a-zA-Z), integer (0-9) and '_./' chars but + no '..' + +In order to use the same DBUS_MAX_NAME_LENGTH it is moved from dbus.c to +firejail.h. + +When processing the profile file lines the template keys are expected to +be written as other macros, ${TEMPLATE_KEY}. Template cannot be in the +beginning of the line. If the read line contains other internal macros +they are not replaced as they are processed later with more strict and +specific checks. It is known that using strtok_r() and doing the +tokenization in two steps, first by '$' and then by '{}' invalid +definitions such as ${{TEMPLATE_KEY2}} will pass the checks. The process +of replacing the keys can be described as follows to ease the +understanding of the code: + +1. "whitelist ${HOME}/${key1}/path/to/${key2}.somewhere" tokens are: + a: whitelist + b: {HOME}/ + c: {key1}/path/to/ + d: {key2}.somewhere +2. Keys in the first token 'a' are ignored, it is the start of new str +3. Tokens 'b', 'c' and 'd' are passed to process_key_value +4. Each of the template keys are replaced with corresponding values, as +${HOME} is internal macro it is not replaced but added as is. Only the +first items in tokens, 'key1' and 'key2' are considered as proper keys to +have the values replaced, the remains are just added to the str. +5. Resulting string would be then: + "whitelist ${HOME}/value1/path/to/value2.somewhere" + +In order to avoid unnecessary duplication of each read profile line the +line is first checked to have at least one template key. If the template +key is not found firejail will exit with an error. +--- + src/firejail/dbus.c | 4 +- + src/firejail/firejail.h | 8 + + src/firejail/main.c | 11 + + src/firejail/profile.c | 34 +++ + src/firejail/template.c | 490 ++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 545 insertions(+), 2 deletions(-) + create mode 100644 src/firejail/template.c + +diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c +index 3cf75ed8..8b5e52f4 100644 +--- a/src/firejail/dbus.c ++++ b/src/firejail/dbus.c +@@ -41,7 +41,7 @@ + #define DBUS_USER_DIR_FORMAT RUN_FIREJAIL_DBUS_DIR "/%d" + #define DBUS_USER_PROXY_SOCKET_FORMAT DBUS_USER_DIR_FORMAT "/%d-user" + #define DBUS_SYSTEM_PROXY_SOCKET_FORMAT DBUS_USER_DIR_FORMAT "/%d-system" +-#define DBUS_MAX_NAME_LENGTH 255 ++// moved to firejail.h - #define DBUS_MAX_NAME_LENGTH 255 + // moved to include/common.h - #define XDG_DBUS_PROXY_PATH "/usr/bin/xdg-dbus-proxy" + + static pid_t dbus_proxy_pid = 0; +@@ -561,4 +561,4 @@ void dbus_apply_policy(void) { + + fwarning("An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.\n"); + } +-#endif // HAVE_DBUSPROXY +\ No newline at end of file ++#endif // HAVE_DBUSPROXY +diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h +index 5ac874e0..fdf49873 100644 +--- a/src/firejail/firejail.h ++++ b/src/firejail/firejail.h +@@ -863,6 +863,8 @@ void set_x11_run_file(pid_t pid, int display); + void set_profile_run_file(pid_t pid, const char *fname); + + // dbus.c ++#define DBUS_MAX_NAME_LENGTH 255 ++ + int dbus_check_name(const char *name); + int dbus_check_call_rule(const char *name); + void dbus_check_profile(void); +@@ -881,4 +883,10 @@ void dhcp_start(void); + // selinux.c + void selinux_relabel_path(const char *path, const char *inside_path); + ++// template.c ++void check_template(char *arg); ++int template_requires_expansion(char *arg); ++char *template_replace_keys(char *arg); ++void template_print_all(); ++void template_cleanup(); + #endif +diff --git a/src/firejail/main.c b/src/firejail/main.c +index 88b99de7..e02e96c4 100644 +--- a/src/firejail/main.c ++++ b/src/firejail/main.c +@@ -2598,6 +2598,11 @@ int main(int argc, char **argv, char **envp) { + exit_err_feature("networking"); + } + #endif ++ else if (strncmp(argv[i], "--template=", 11) == 0) { ++ char *arg = strdup(argv[i] + 11); // Parse key in check_template() ++ check_template(arg); ++ free(arg); ++ } + //************************************* + // command + //************************************* +@@ -2733,6 +2738,9 @@ int main(int argc, char **argv, char **envp) { + } + } + ++ // Prints templates only if arg_debug is set ++ template_print_all(); ++ + profile_read_file_list(); + + EUID_ASSERT(); +@@ -2852,6 +2860,9 @@ int main(int argc, char **argv, char **envp) { + } + EUID_ASSERT(); + ++ // Templates are no longer needed as profile files are read ++ template_cleanup(); ++ + // block X11 sockets + if (arg_x11_block) + x11_block(); +diff --git a/src/firejail/profile.c b/src/firejail/profile.c +index d4bbef97..dc8553a6 100644 +--- a/src/firejail/profile.c ++++ b/src/firejail/profile.c +@@ -1784,6 +1784,40 @@ void profile_read(const char *fname) { + msg_printed = 1; + } + ++ // Replace all template keys on line if at least one non- ++ // hardcoded or not internally used is found. Since templates ++ // can be used anywhere process the keys before include. ++ char *ptr_expanded; ++ ++ switch (template_requires_expansion(ptr)) { ++ case -EINVAL: ++ fprintf(stderr, "Ignoring line \"%s\", as it " ++ "contains invalid template keys\n", ++ ptr); ++ free(ptr); ++ continue; ++ case 0: ++ break; ++ case 1: ++ ptr_expanded = template_replace_keys(ptr); ++ if (ptr_expanded == NULL) { ++ fprintf(stderr, "Ignoring line \"%s\"\n", ptr); ++ free(ptr); ++ continue; ++ } ++ ++ if (arg_debug) ++ printf("template keys expanded: \"%s\"\n", ++ ptr_expanded); ++ ++ free(ptr); ++ ptr = ptr_expanded; ++ ++ break; ++ default: ++ break; ++ } ++ + // process include + if (strncmp(ptr, "include ", 8) == 0 && !is_in_ignore_list(ptr)) { + include_level++; +diff --git a/src/firejail/template.c b/src/firejail/template.c +new file mode 100644 +index 00000000..2159f33f +--- /dev/null ++++ b/src/firejail/template.c +@@ -0,0 +1,490 @@ ++/* ++ * Copyright (C) 2021 Jolla Ltd. ++ * Copyright (C) 2021 Open Mobile Platform ++ * ++ * This file is part of firejail project ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++*/ ++ ++#include "firejail.h" ++#include ++#include ++#include ++#include ++#include ++ ++#define TEMPLATE_KEY_VALUE_DELIM ":" ++#define TEMPLATE_KEY_MACRO_DELIM "$" ++#define TEMPLATE_KEY_MACRO_SUB_DELIMS "{}" ++ ++typedef struct template_t { ++ char *key; ++ char *value; ++ struct template_t *next; ++} Template; ++ ++typedef enum { ++ STR_CHECK_ALNUM = 0, ++ STR_CHECK_DBUS_COMPAT ++} StrCheckType; ++ ++Template *tmpl_list = NULL; ++ ++static Template *template_new(const char *key, const char *value) ++{ ++ Template *tmpl; ++ ++ if (!key || !*key || !value || !*value) ++ return NULL; ++ ++ tmpl = malloc(sizeof(Template)); ++ if (!tmpl) ++ errExit("malloc"); ++ ++ tmpl->key = strdup(key); ++ tmpl->value = strdup(value); ++ tmpl->next = NULL; ++ ++ if (arg_debug) ++ fprintf(stdout, "Create template key \"%s\" value \"%s\"\n", ++ key, value); ++ ++ return tmpl; ++} ++ ++static void template_free(Template *tmpl) ++{ ++ if (!tmpl) ++ return; ++ ++ if (arg_debug) ++ fprintf(stdout, "free %p key \"%s\" value \"%s\"\n", tmpl, ++ tmpl->key, tmpl->value); ++ ++ free(tmpl->key); ++ free(tmpl->value); ++ free(tmpl); ++} ++ ++/* ++ * Get template with matching key, if list is empty or key is not found ++ * -ENOKEY is set to err. With empty key -EINVAL is set. ++ */ ++static Template* template_get(const char *key, int *err) ++{ ++ Template *iter; ++ ++ if (!key || !*key) { ++ *err = -EINVAL; ++ return NULL; ++ } ++ ++ iter = tmpl_list; ++ while (iter) { ++ if (!strcmp(key, iter->key)) ++ return iter; ++ ++ iter = iter->next; ++ } ++ ++ *err = -ENOKEY; ++ return NULL; ++} ++ ++/* Return value for a key, err is set by template_get() */ ++static const char* template_get_value(const char *key, int *err) ++{ ++ Template *tmpl; ++ ++ tmpl = template_get(key, err); ++ if (!tmpl) ++ return NULL; ++ ++ return tmpl->value; ++} ++ ++/* ++ * Prepend template to the list. If the key already exists -EALREADY is ++ * reported back and caller must free the Template. ++ */ ++static int template_add(Template *tmpl) ++{ ++ int err; ++ ++ if (!tmpl) ++ return -EINVAL; ++ ++ if (tmpl_list && template_get(tmpl->key, &err)) ++ return -EALREADY; ++ ++ tmpl->next = tmpl_list; ++ tmpl_list = tmpl; ++ ++ return 0; ++} ++ ++/* Free all the Templates in the list */ ++void template_cleanup() ++{ ++ Template *iter; ++ Template *curr; ++ ++ iter = tmpl_list; ++ while (iter) { ++ curr = iter; ++ iter = iter->next; ++ template_free(curr); ++ } ++ ++ tmpl_list = NULL; ++} ++ ++/* For debugging, traverses Template list and prints out keys and values */ ++void template_print_all() ++{ ++ Template *iter; ++ ++ if (!arg_debug) ++ return; ++ ++ iter = tmpl_list; ++ while (iter) { ++ fprintf(stdout, "template key \"%s\" value \"%s\"\n", ++ iter->key, iter->value); ++ iter = iter->next; ++ } ++} ++ ++/* Check if the string is valid for the given type */ ++static int is_valid_str(const char *str, StrCheckType type) ++{ ++ int i; ++ ++ if (!str || !*str) ++ return 0; ++ ++ //Keys must start with an alphabetic char and the values must not ++ // exceed D-Bus limit. ++ switch (type) { ++ case STR_CHECK_ALNUM: ++ if (!isalpha(*str)) ++ return 0; ++ ++ break; ++ case STR_CHECK_DBUS_COMPAT: ++ if (strlen(str) > DBUS_MAX_NAME_LENGTH) ++ return 0; ++ ++ if (strstr(str, "..")) ++ return 0; ++ ++ break; ++ } ++ ++ for (i = 1; str[i] != '\0'; i++) { ++ if (iscntrl(str[i])) ++ return 0; ++ ++ switch (type) { ++ case STR_CHECK_ALNUM: ++ if (!isalnum(str[i])) ++ return 0; ++ ++ break; ++ case STR_CHECK_DBUS_COMPAT: ++ // Allow '_./' in str ++ if (!isalnum(str[i]) && str[i] != '_' && ++ str[i] != '.' && str[i] != '/') ++ return 0; ++ ++ break; ++ } ++ } ++ ++ return 1; ++} ++ ++/* TODO There should be a function in macro.c to check if a key is internal */ ++const char *internal_keys[] = { "HOME", "CFG", "RUNUSER", "PATH", "PRIVILEGED", ++ NULL }; ++ ++/* Check if the key is in internal key list or it has a hardcoded macro. */ ++static int is_internal_macro(const char *key) ++{ ++ char *macro; ++ int i; ++ ++ for (i = 0; internal_keys[i]; i++) { ++ if (!strcmp(key, internal_keys[i])) ++ return 1; ++ } ++ ++ if (asprintf(¯o, "${%s}", key) == -1) ++ errExit("asprintf"); ++ ++ i = macro_id(macro); ++ free(macro); ++ ++ if (i != -1) ++ return 1; ++ ++ return 0; ++} ++ ++/* ++ *Check the Template argument to have KEY:VALUE in valid format. A valid ++ * Template is added to template list. In case of invalid key, value, internal ++ * macro or existing key firejail is called to exit ++ */ ++void check_template(char *arg) { ++ Template *tmpl; ++ const char *key; ++ const char *value; ++ const char *delim = TEMPLATE_KEY_VALUE_DELIM; ++ char *saveptr; ++ int err; ++ ++ /* Only alphanumeric chars in template key. */ ++ key = strtok_r(arg, delim, &saveptr); ++ if (!is_valid_str(key, STR_CHECK_ALNUM)) { ++ fprintf(stderr, "Error invalid template key \"%s\"\n", key); ++ exit(1); ++ } ++ ++ /* Only a-zA-Z0-9_ /*/ ++ value = strtok_r(NULL, delim, &saveptr); ++ if (!is_valid_str(value, STR_CHECK_DBUS_COMPAT)) { ++ fprintf(stderr, "Error invalid template value in \"%s:%s\"\n", ++ key, value); ++ exit(1); ++ } ++ ++ /* Hardcoded macro or XDG value is not allowed to be overridden. */ ++ if (is_internal_macro(key)) { ++ fprintf(stderr, "Error override of \"${%s}\" is not permitted\n", ++ key); ++ exit(1); ++ } ++ ++ /* Returns either a Template or exits firejail */ ++ tmpl = template_new(key, value); ++ ++ err = template_add(tmpl); ++ switch (err) { ++ case 0: ++ return; ++ case -EINVAL: ++ fprintf(stderr, "Error invalid template key \"%s\" " ++ "value \"%s\"\n", key, value); ++ break; ++ case -EALREADY: ++ fprintf(stderr, "Error template key \"%s\" already exists\n", ++ key); ++ break; ++ } ++ ++ template_free(tmpl); ++ exit(1); ++} ++ ++/* ++ * Check if the argument contains template keys that should be expanded. Will ++ * return 1 only when there is at least one template key found. If an unknown ++ * template exists -EINVAL is returned. If there is no '$' or the macros are ++ * internal only 0 is returned as there is nothing to expand. ++ */ ++int template_requires_expansion(char *arg) ++{ ++ char *ptr; ++ ++ if (!arg || !*arg) ++ return 0; ++ ++ ptr = strchr(arg, '$'); ++ if (!ptr) ++ return 0; ++ ++ while (*ptr) { ++ if (*ptr == '$' && *(ptr+1) == '{') { ++ char buf[DBUS_MAX_NAME_LENGTH] = { 0 }; ++ int err; ++ int i; ++ ++ // Copy template key name only ++ for (i = 0, ptr += 2; *ptr && *ptr != '}' && ++ i < DBUS_MAX_NAME_LENGTH; ++ ptr++, i++) ++ buf[i] = *ptr; ++ ++ if (is_internal_macro(buf)) ++ continue; ++ ++ // Invalid line if '${}' used but no valid template key ++ if (!template_get(buf, &err)) ++ return -EINVAL; ++ ++ // At least one template key, needs template expansion ++ return 1; ++ } ++ ++ptr; ++ } ++ ++ return 0; ++} ++ ++/* Concatenate str1 and str2 by reallocating str1 to fit both. */ ++static char* append_to_string(char *str1, const char *str2) ++{ ++ size_t len; ++ ++ if (!str2) ++ return str1; ++ ++ if (!str1) ++ return strdup(str2); ++ ++ len = strlen(str2); ++ str1 = realloc(str1, strlen(str1) + len + 1); ++ if (!str1) ++ return NULL; ++ ++ return strncat(str1, str2, len); ++} ++ ++/* ++ * Replace the key with corresponding value in the str_in token, this is called ++ * only from template_replace_keys() to process the str_in between '{' and '}' ++ * since the line is tokenized first using '$'. With strtok_r() the '{' and '}' ++ * are replaced using as delimiters and only the first part of the str_in is ++ * the actual template key, which is replaced, rest is appended to the ++ * container. If the key is an internal macro it is added to container as ++ * '${MACRO_NAME}'. ++ */ ++static char *process_key_value(char *container, char *str_in, int *err) ++{ ++ char *str; ++ char *token; ++ char *saveptr; ++ const char *delim = TEMPLATE_KEY_MACRO_SUB_DELIMS; ++ const char *value; ++ ++ *err = 0; ++ ++ for (str = str_in; ; str = NULL) { ++ token = strtok_r(str, delim, &saveptr); ++ if (!token) ++ break; ++ ++ if (is_internal_macro(token)) { ++ char *macro; ++ ++ if (asprintf(¯o, "${%s}", token) == -1) ++ errExit("asprintf"); ++ ++ container = append_to_string(container, macro); ++ free(macro); ++ ++ if (!container) ++ goto err; ++ ++ continue; ++ } ++ ++ // Only the first iteration is the template key to be expanded ++ // and everything after the first token is added to the end. ++ value = str ? template_get_value(token, err) : token; ++ if (!value) ++ goto err; ++ ++ container = append_to_string(container, value); ++ if (!container) ++ goto err; ++ } ++ ++ return container; ++ ++err: ++ if (container) ++ free(container); ++ else ++ *err = -EINVAL; ++ ++ return NULL; ++} ++ ++/* ++ * Allocates new string with all template keus replaced with the values. ++ * Returns NULL if there is a nonexistent key, allocation fails or if arg ++ * begins with $. If arg does not contain $ it is only duplicated. Calls ++ * process_key_value to replace the template keys with corresponding values. ++ */ ++char *template_replace_keys(char *arg) ++{ ++ char *new_string = NULL; ++ char *str; ++ char *token; ++ char *saveptr; ++ const char *delim = TEMPLATE_KEY_MACRO_DELIM; ++ int err = 0; ++ ++ if (!arg || !*arg) ++ return NULL; ++ ++ if (!strchr(arg, '$')) ++ return strdup(arg); ++ ++ // Templates must not be given at the beginning of the line ++ if (*arg == '$') { ++ fprintf(stderr, "Error line \"%s\" starts with \"$\"\n", arg); ++ return NULL; ++ } ++ ++ for (str = arg; ; str = NULL) { ++ token = strtok_r(str, delim, &saveptr); ++ if (!token) ++ break; ++ ++ /* ++ * Process template values starting from the second token as ++ * templates cannot be used at the beginning of the arg ++ * because only hardcoded macros should be as first. ++ */ ++ if (!str) { ++ // Valid token must begin with '{' and to have '}' ++ if (*token != '{' && !strchr(token+1, '}')) { ++ if (new_string) ++ free(new_string); ++ ++ fprintf(stderr, "Unterminated macro/template " ++ "key on line \"%s\"\n", ++ arg); ++ return NULL; ++ } ++ ++ new_string = process_key_value(new_string, token, &err); ++ } else { ++ new_string = append_to_string(new_string, token); ++ } ++ ++ if (!new_string) { ++ fprintf(stderr, "Error invalid line \"%s\" (err %s)\n", ++ arg, strerror(err)); ++ return NULL; ++ } ++ } ++ ++ return new_string; ++} +-- +2.20.1 + diff --git a/rpm/firejail.spec b/rpm/firejail.spec index db9708dd..23577138 100644 --- a/rpm/firejail.spec +++ b/rpm/firejail.spec @@ -16,6 +16,8 @@ Patch8: 0008-Allow-changing-protocol-list-after-initial-set.patch # Sailfish OS patches Patch9: 0009-Preserve-process-effective-group-for-privileged-grou.patch Patch10: 0010-Implement-Sailfish-OS-specific-privileged-data-optio.patch +Patch11: 0011-Add-profile-files-to-a-list-when-processing-argument.patch +Patch12: 0012-Implement-template-addition-for-replacing-keys-in-pr.patch URL: https://github.com/sailfishos/firejail