From 3677186b06be92f7a5d55eb3573a3e2a877a6a96 Mon Sep 17 00:00:00 2001
From: Mike Bradeen <mbradeen@sangoma.com>
Date: Mon, 03 Oct 2022 12:54:40 -0600
Subject: [PATCH] manager: prevent file access outside of config dir

Add live_dangerously flag to manager and use this flag to
determine if a configuation file outside of AST_CONFIG_DIR
should be read.

ASTERISK-30176

Change-Id: I46b26af4047433b49ae5c8a85cb8cda806a07404
---

diff --git a/configs/samples/asterisk.conf.sample b/configs/samples/asterisk.conf.sample
index b2a4da8..0d0d2a0 100644
--- a/configs/samples/asterisk.conf.sample
+++ b/configs/samples/asterisk.conf.sample
@@ -95,10 +95,13 @@
 				;         documented in extensions.conf.sample.
 				; Default gosub.
 ;live_dangerously = no		; Enable the execution of 'dangerous' dialplan
-				; functions from external sources (AMI,
-				; etc.) These functions (such as SHELL) are
-				; considered dangerous because they can allow
-				; privilege escalation.
+				; functions and configuration file access from
+				; external sources (AMI, etc.) These functions
+				; (such as SHELL) are considered dangerous
+				; because they can allow privilege escalation.
+				; Configuration files are considered dangerous
+				; if they exist outside of the Asterisk
+				; configuration directory.
 				; Default no
 ;entityid=00:11:22:33:44:55	; Entity ID.
 				; This is in the form of a MAC address.
diff --git a/doc/UPGRADE-staging/manager_config_live_dangerously.txt b/doc/UPGRADE-staging/manager_config_live_dangerously.txt
new file mode 100644
index 0000000..56f39f9
--- /dev/null
+++ b/doc/UPGRADE-staging/manager_config_live_dangerously.txt
@@ -0,0 +1,8 @@
+Subject: AMI (Asterisk Manager Interface)
+
+Previously, GetConfig and UpdateConfig were able to access files outside of
+the Asterisk configuration directory. Now this access is put behind the
+live_dangerously configuration option in asterisk.conf, which is disabled by
+default. If access to configuration files outside of the Asterisk configuation
+directory is required via AMI, then the live_dangerously configuration option
+must be set to yes.
diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h
index 3d882ef..107f83c 100644
--- a/include/asterisk/manager.h
+++ b/include/asterisk/manager.h
@@ -350,6 +350,18 @@
  */
 void astman_send_list_complete_end(struct mansession *s);
 
+/*!
+ * \brief Enable/disable the inclusion of 'dangerous' configurations outside
+ * of the ast_config_AST_CONFIG_DIR
+ *
+ * This function can globally enable/disable the loading of configuration files
+ * outside of ast_config_AST_CONFIG_DIR.
+ *
+ * \param new_live_dangerously If true, enable the access of files outside
+ * ast_config_AST_CONFIG_DIR from astman.
+ */
+void astman_live_dangerously(int new_live_dangerously);
+
 void __attribute__((format(printf, 2, 3))) astman_append(struct mansession *s, const char *fmt, ...);
 
 /*! \brief Determine if a manager session ident is authenticated */
diff --git a/main/manager.c b/main/manager.c
index 90333a0..9d21ac3 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -1490,6 +1490,11 @@
 /*! \brief The \ref stasis_subscription for forwarding the Security topic to the AMI topic */
 static struct stasis_forward *security_topic_forwarder;
 
+/*!
+ * \brief Set to true (non-zero) to globally allow all dangerous AMI actions to run
+ */
+static int live_dangerously;
+
 #ifdef TEST_FRAMEWORK
 /*! \brief The \ref stasis_subscription for forwarding the Test topic to the AMI topic */
 static struct stasis_forward *test_suite_forwarder;
@@ -3609,6 +3614,29 @@
 	return 0;
 }
 
+void astman_live_dangerously(int new_live_dangerously)
+{
+	if (new_live_dangerously && !live_dangerously)
+	{
+		ast_log(LOG_WARNING, "Manager Configuration load protection disabled.\n");
+	}
+
+	if (!new_live_dangerously && live_dangerously)
+	{
+		ast_log(LOG_NOTICE, "Manager Configuration load protection enabled.\n");
+	}
+	live_dangerously = new_live_dangerously;
+}
+
+static int restrictedFile(const char *filename)
+{
+	if (!live_dangerously && !strncasecmp(filename, "/", 1) &&
+		 strncasecmp(filename, ast_config_AST_CONFIG_DIR, strlen(ast_config_AST_CONFIG_DIR))) {
+		return 1;
+	}
+	return 0;
+}
+
 static int action_getconfig(struct mansession *s, const struct message *m)
 {
 	struct ast_config *cfg;
@@ -3627,6 +3655,11 @@
 		return 0;
 	}
 
+	if (restrictedFile(fn)) {
+		astman_send_error(s, m, "File requires escalated priveledges");
+		return 0;
+	}
+
 	cfg = ast_config_load2(fn, "manager", config_flags);
 	if (cfg == CONFIG_STATUS_FILEMISSING) {
 		astman_send_error(s, m, "Config file not found");
@@ -3754,6 +3787,11 @@
 		return 0;
 	}
 
+	if (restrictedFile(fn)) {
+		astman_send_error(s, m, "File requires escalated priveledges");
+		return 0;
+	}
+
 	if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
 		astman_send_error(s, m, "Config file not found");
 		return 0;
@@ -4105,6 +4143,10 @@
 		astman_send_error(s, m, "Filename not specified");
 		return 0;
 	}
+	if (restrictedFile(sfn) || restrictedFile(dfn)) {
+		astman_send_error(s, m, "File requires escalated priveledges");
+		return 0;
+	}
 	if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
 		astman_send_error(s, m, "Config file not found");
 		return 0;
diff --git a/main/options.c b/main/options.c
index 07afd79..1507bc6 100644
--- a/main/options.c
+++ b/main/options.c
@@ -476,6 +476,7 @@
 	}
 	if (!ast_opt_remote) {
 		pbx_live_dangerously(live_dangerously);
+		astman_live_dangerously(live_dangerously);
 	}
 
 	option_debug += option_debug_new;
