From: George Joseph Date: Mon, 3 Mar 2025 21:07:43 +0000 (-0700) Subject: manager.c: Check for restricted file in action_createconfig. X-Git-Tag: 21.8.0-rc1~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0e7d44428cc83fb1fab4026783f423ddf6ec870b;p=thirdparty%2Fasterisk.git manager.c: Check for restricted file in action_createconfig. 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 (cherry picked from commit 6f447132b23a3f94d9b66f47a850f98d01fc18dc) --- diff --git a/main/manager.c b/main/manager.c index b69b7ca86e..8325fb0750 100644 --- a/main/manager.c +++ b/main/manager.c @@ -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 {