]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
manager.c: Check for restricted file in action_createconfig.
authorGeorge Joseph <gjoseph@sangoma.com>
Mon, 3 Mar 2025 21:07:43 +0000 (14:07 -0700)
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Thu, 6 Mar 2025 15:04:18 +0000 (15:04 +0000)
The `CreateConfig` manager action now ensures that a config file can
only be created in the AST_CONFIG_DIR unless `live_dangerously` is set.

Resolves: #1122

main/manager.c

index b69b7ca86e67769317f83d0cb85b8b1b730dadc3..8325fb0750d24db8093ef7e77889714cb68a88e5 100644 (file)
@@ -3097,11 +3097,67 @@ static int action_createconfig(struct mansession *s, const struct message *m)
 {
        int fd;
        const char *fn = astman_get_header(m, "Filename");
-       struct ast_str *filepath = ast_str_alloca(PATH_MAX);
-       ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
-       ast_str_append(&filepath, 0, "%s", fn);
+       char *stripped_filename;
+       RAII_VAR(char *, filepath, NULL, ast_free);
+       RAII_VAR(char *, real_dir, NULL, ast_std_free);
+       RAII_VAR(char *, real_path, NULL, ast_free);
+       char *filename;
+
+       if (ast_strlen_zero(fn)) {
+               astman_send_error(s, m, "Filename not specified");
+               return 0;
+       }
+
+       stripped_filename = ast_strip(ast_strdupa(fn));
+
+       /* If the file name is relative, prepend ast_config_AST_CONFIG_DIR */
+       if (stripped_filename[0] != '/') {
+               if (ast_asprintf(&filepath, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
+                       return -1;
+               }
+       } else {
+               filepath = ast_strdup(stripped_filename);
+       }
+
+       /*
+        * We can't call is_restricted_file() here because it uses realpath() and...
+        *
+        * realpath() and other functions that canonicalize paths won't work with
+        * a filename that doesn't exist, so we need to separate the directory
+        * from the filename and canonicalize the directory first.  We have to do
+        * the separation manually because dirname() and basename() aren't all
+        * that friendly to multi-threaded programs and there are different
+        * versions of basename for glibc and POSIX.
+        */
+
+       filename = strrchr(filepath, '/');
+       if (!filename) {
+               astman_send_error(s, m, "Filename is invalid");
+               return 0;
+       }
+       *filename = '\0';
+       filename++;
+
+       /* filepath just has the directory now so canonicalize it. */
+       real_dir = realpath(filepath, NULL);
+       if (ast_strlen_zero(real_dir)) {
+               astman_send_error(s, m, strerror(errno));
+               return 0;
+       }
+
+       /* Check if the directory is restricted. */
+       if (!live_dangerously && !ast_begins_with(real_dir, ast_config_AST_CONFIG_DIR)) {
+               astman_send_error(s, m, "File requires escalated privileges");
+               return 0;
+       }
+
+       /* Create the final file path. */
+       if (ast_asprintf(&real_path, "%s/%s", real_dir, filename) == -1) {
+               astman_send_error(s, m, strerror(errno));
+               return -1;
+       }
 
-       if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
+       if ((fd = open(real_path, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
                close(fd);
                astman_send_ack(s, m, "New configuration file created successfully");
        } else {