summaryrefslogtreecommitdiff
path: root/src/core/config_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/config_file.c')
-rw-r--r--src/core/config_file.c480
1 files changed, 480 insertions, 0 deletions
diff --git a/src/core/config_file.c b/src/core/config_file.c
new file mode 100644
index 0000000..e09fb5d
--- /dev/null
+++ b/src/core/config_file.c
@@ -0,0 +1,480 @@
+/* BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>.
+ *
+ */
+
+#include <limits.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+//json.h lib
+#include <json.h>
+#include "json_helpers.h"
+
+#include <errno.h>
+#include "base64_url.h"
+
+#include "globals.h"
+#include "utilities.h"
+
+#include "webrdp_core_api.h"
+
+enum option_type
+{
+ option_type_bool,
+ option_type_string,
+ option_type_int,
+ option_type_unsupported
+};
+
+struct option_s
+{
+ char name[128];
+ union
+ {
+ char val_string[260];
+ bool val_bool;
+ int64_t val_int;
+ };
+ enum option_type type;
+};
+
+static int64_t
+json_cfg_option_extract_int64(struct option_s *option)
+{
+ switch (option->type)
+ {
+ case option_type_int:
+ return option->val_int;
+ break;
+ case option_type_string:
+ return atoll(option->val_string);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static bool
+json_cfg_option_extract_bool(struct option_s *option)
+{
+ switch (option->type)
+ {
+ case option_type_bool:
+ return option->val_bool;
+ break;
+ case option_type_int:
+ return option->val_int;
+ break;
+ case option_type_string:
+ return atoi(option->val_string);
+ break;
+ default:
+ return false;
+ break;
+ }
+ return false;
+}
+
+static void
+handle_session_option(struct option_s *option)
+{
+ if (!strcmp(option->name, "session_time_limit"))
+ {
+ g_globals.settings.ws_session_defaults.session_time_limit
+ = json_cfg_option_extract_int64(option);
+ }
+ else if (!strcmp(option->name, "session_idle_timeout"))
+ {
+ g_globals.settings.ws_session_defaults.session_idle_timeout
+ = json_cfg_option_extract_int64(option);
+ }
+}
+
+static uint8_t
+get_log_level(const char *log_level_str)
+{
+ uint8_t lvl = wrdp_log_level_error;
+ if (!strcmp(log_level_str, "error"))
+ {
+ lvl = wrdp_log_level_error;
+ }
+ else if (!strcmp(log_level_str, "warning"))
+ {
+ lvl = wrdp_log_level_warning;
+ }
+ else if (!strcmp(log_level_str, "info"))
+ {
+ lvl = wrdp_log_level_info;
+ }
+ else if (!strcmp(log_level_str, "debug"))
+ {
+ lvl = wrdp_log_level_debug;
+ }
+ else if (!strcmp(log_level_str, "trace"))
+ {
+ lvl = wrdp_log_level_trace;
+ }
+#ifndef DEBUG
+ if (lvl > wrdp_log_level_info)
+ {
+ printf(
+ "log_level higher than info does not supported for relase"
+ " builds, downgrade to info\n");
+ lvl = wrdp_log_level_info;
+ }
+#endif /* DEBUG */
+ return lvl;
+}
+
+static void
+handle_global_option(struct option_s *option)
+{
+ if (!strcmp(option->name, "daemon"))
+ {
+ if (!g_globals.settings.daemon)
+ {
+ g_globals.settings.daemon
+ = json_cfg_option_extract_bool(option);
+ }
+ }
+ else if (!strcmp(option->name, "log_level"))
+ {
+ g_globals.settings.log_level
+ = get_log_level(option->val_string);
+ }
+ else if (!strcmp(option->name, "thread_count"))
+ {
+ g_globals.settings.thread_count
+ = json_cfg_option_extract_int64(option);
+ }
+ else if (!strcmp(option->name, "tasks_per_thread"))
+ {
+ g_globals.settings.tasks_per_thread
+ = json_cfg_option_extract_int64(option);
+ }
+ else if (!strcmp(option->name, "ws_port"))
+ {
+ g_globals.settings.ws_port
+ = json_cfg_option_extract_int64(option);
+ }
+ else if (!strcmp(option->name, "ws_socket_path"))
+ {
+ g_globals.settings.ws_socket_path = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "ctl_port"))
+ {
+ g_globals.settings.ctl_port
+ = json_cfg_option_extract_int64(option);
+ }
+ else if (!strcmp(option->name, "ctl_socket_path"))
+ {
+ g_globals.settings.ctl_socket_path = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "ctl_ssl_cafile"))
+ {
+ g_globals.settings.ctl_ssl_cafile = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "ctl_ssl_capath"))
+ {
+ g_globals.settings.ctl_ssl_capath = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "ctl_ssl_cert"))
+ {
+ g_globals.settings.ctl_ssl_cert = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "ctl_ssl_key"))
+ {
+ g_globals.settings.ctl_ssl_key = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "auth_server_url"))
+ {
+ g_globals.settings.auth_server_url = strdup(option->val_string);
+ }
+ else if (!strcmp(option->name, "secret_key_verify"))
+ {
+ size_t dec_size = 0;
+ errno = base64_url_decode((uint8_t *)(option->val_string),
+ strlen(option->val_string),
+ (uint8_t *)g_globals.settings.secret_key_verify, 66,
+ &dec_size);
+ if (errno)
+ {
+ perror("handle_global_option: base64_url_decode");
+ exit(EXIT_FAILURE);
+ }
+ if (dec_size != 64)
+ {
+ printf("Failed to decode verification secret key"
+ " (wrong decoded key length)\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ else if (!strcmp(option->name, "secret_key_sign"))
+ {
+ size_t dec_size = 0;
+ errno = base64_url_decode((uint8_t *)(option->val_string),
+ strlen(option->val_string),
+ (uint8_t *)g_globals.settings.secret_key_sign, 66,
+ &dec_size);
+ if (errno)
+ {
+ perror("handle_global_option: base64_url_decode");
+ exit(EXIT_FAILURE);
+ }
+ if (dec_size != 64)
+ {
+ printf("Failed to decode signing secret key"
+ " (wrong decoded key length)\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void
+handle_option(struct option_s *option, const char *prefix, size_t prefix_len)
+{
+ /* TODO: refactoring */
+ if (option->type == option_type_unsupported)
+ {
+ printf("Unsupported option type found in config file\n");
+ return;
+ }
+ if (prefix && prefix_len > 0)
+ {
+ size_t session_len = strlen("session");
+ if (session_len != prefix_len)
+ {
+ goto unknown_prefix;
+ }
+ if (!strncmp("session", prefix, prefix_len))
+ {
+ handle_session_option(option);
+ }
+ return;
+ unknown_prefix:
+ printf("Unknown second level option ignored\n");
+ return;
+ }
+ handle_global_option(option);
+}
+
+static void
+print_option(struct option_s *option, const char *prefix, size_t prefix_len)
+{
+ printf("Found option: ");
+ if (prefix && prefix_len > 0)
+ {
+ printf("%.*s.", (int)prefix_len, prefix);
+ }
+ printf("%s == ", option->name);
+ switch (option->type)
+ {
+ case option_type_bool:
+ {
+ if (option->val_bool)
+ printf("true");
+ else
+ printf("false");
+ printf(" (bool)\n");
+ }
+ break;
+ case option_type_int:
+ {
+ printf("%ld (int)\n", option->val_int);
+ }
+ break;
+ case option_type_string:
+ printf("%s (string)\n", option->val_string);
+ break;
+ case option_type_unsupported:
+ printf("unsupported option type\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+handle_json_option(struct json_object_element_s *json_option,
+ const char *prefix, size_t prefix_len)
+{
+ struct option_s option;
+ memset(&option, 0, sizeof(struct option_s));
+ strncpy(option.name, json_option->name->string,
+ json_option->name->string_size);
+ option.name[json_option->name->string_size] = 0;
+ switch (json_option->value->type)
+ {
+ case json_type_false:
+ {
+ option.val_bool = false;
+ option.type = option_type_bool;
+ }
+ break;
+ case json_type_true:
+ {
+ option.val_bool = true;
+ option.type = option_type_bool;
+ }
+ break;
+ case json_type_number:
+ {
+ option.val_int = json_option_extract_int64(json_option);
+ option.type = option_type_int;
+ }
+ break;
+ case json_type_string:
+ {
+ json_option_extract_string(
+ json_option, (char *)&(option.val_string));
+ option.type = option_type_string;
+ }
+ break;
+ case json_type_object:
+ {
+ struct json_object_s *jsopt
+ = (struct json_object_s *)
+ json_option->value->payload;
+ struct json_object_element_s *jsoption = jsopt->start;
+ while (jsoption)
+ {
+ handle_json_option(jsoption,
+ json_option->name->string,
+ json_option->name->string_size);
+ jsoption = jsoption->next;
+ }
+ return;
+ }
+ break;
+ default:
+ {
+ option.type = option_type_unsupported;
+ }
+ break;
+ }
+ handle_option(&option, prefix, prefix_len);
+ print_option(&option, prefix, prefix_len);
+}
+
+static void
+parse_json(char *buf, size_t buf_size)
+{
+ struct json_value_s *root = json_parse(buf, buf_size);
+ if (!root)
+ {
+ printf("Failed to parse config (not valid json ?)\n");
+ exit(EXIT_FAILURE);
+ }
+ struct json_object_s *object = (struct json_object_s *)root->payload;
+ struct json_object_element_s *option = object->start;
+ while (option)
+ {
+ handle_json_option(option, 0, 0);
+ option = option->next;
+ }
+ free(buf);
+ free(root);
+}
+
+static void
+parse_config_file(const char *path)
+{
+ char *buf = 0;
+ FILE *cfg = 0;
+ size_t size = 0;
+ {
+ struct stat st;
+ stat(path, &st);
+ size = st.st_size;
+
+ buf = (char *)malloc(size);
+ if (!buf)
+ {
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+ }
+ cfg = fopen(path, "r");
+ if (fread(buf, 1, size, cfg) != size)
+ {
+ printf("Failed to read config file\n");
+ fclose(cfg);
+ exit(EXIT_FAILURE);
+ }
+ fclose(cfg);
+ printf("\tLoaded.\n");
+ parse_json(buf, size);
+}
+
+void
+find_config_file(char *config_file_path)
+{
+ bool file_exists = false;
+ if (config_file_path[0])
+ {
+ printf("Trying to load config file from specified path: %s...",
+ config_file_path);
+ if (!is_regular_file(config_file_path))
+ {
+ printf("\tFile does not exists or not regular file\n");
+ exit(EXIT_FAILURE);
+ }
+ else
+ {
+ file_exists = true;
+ printf("\tFile exists...");
+ }
+ }
+ else
+ {
+ struct passwd *pw = getpwuid(getuid());
+ const char *homedir = pw->pw_dir;
+ snprintf(config_file_path, _POSIX_PATH_MAX - 1,
+ "%s/.config/%s/config", homedir, PROG_NAME);
+ printf("Trying to load config file from default path: %s...",
+ config_file_path);
+ if (!is_regular_file(config_file_path))
+ {
+ printf("\tFile does not exists or not regular file\n");
+ config_file_path[0] = 0;
+ snprintf(config_file_path, _POSIX_PATH_MAX - 1,
+ "/etc/%s/config", PROG_NAME);
+ }
+ else
+ {
+ file_exists = true;
+ printf("\tFile exists...");
+ }
+ if (!file_exists)
+ {
+ printf("Trying to load config file from default path: "
+ "%s...",
+ config_file_path);
+ if (!is_regular_file(config_file_path))
+ printf("\tFile does not exists or not regular "
+ "file\n");
+ else
+ {
+ file_exists = true;
+ printf("\tFile exists...");
+ }
+ }
+ if (!file_exists)
+ printf("Default config file not found and custom"
+ " config file not specififed, running with "
+ "internal "
+ "defaults.\n");
+ }
+ if (file_exists)
+ parse_config_file(config_file_path);
+}