[svn-commits] dlee: branch dlee/record r391268 - in /team/dlee/record: include/asterisk/ ma...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Jun 10 10:22:59 CDT 2013


Author: dlee
Date: Mon Jun 10 10:22:56 2013
New Revision: 391268

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=391268
Log:
Attempt at restricting the recording directory.

Modified:
    team/dlee/record/include/asterisk/utils.h
    team/dlee/record/main/utils.c
    team/dlee/record/res/res_stasis_recording.c
    team/dlee/record/res/stasis_http/resource_channels.c
    team/dlee/record/tests/test_utils.c

Modified: team/dlee/record/include/asterisk/utils.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/include/asterisk/utils.h?view=diff&rev=391268&r1=391267&r2=391268
==============================================================================
--- team/dlee/record/include/asterisk/utils.h (original)
+++ team/dlee/record/include/asterisk/utils.h Mon Jun 10 10:22:56 2013
@@ -717,6 +717,19 @@
  * Creates a directory path, creating parent directories as needed.
  */
 int ast_mkdir(const char *path, int mode);
+
+/*!
+ * \brief Recursively create directory path, but only if it resolves within
+ * the given \a base_path.
+ *
+ * If \a base_path does not exist, it will not be created and this function
+ * returns \c EPERM.
+ *
+ * \param path The directory path to create
+ * \param mode The permissions with which to try to create the directory
+ * \return 0 on success or an error code otherwise
+ */
+int ast_safe_mkdir(const char *base_path, const char *path, int mode);
 
 #define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a]))
 

Modified: team/dlee/record/main/utils.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/main/utils.c?view=diff&rev=391268&r1=391267&r2=391268
==============================================================================
--- team/dlee/record/main/utils.c (original)
+++ team/dlee/record/main/utils.c Mon Jun 10 10:22:56 2013
@@ -2092,6 +2092,70 @@
 			return errno;
 	}
 	return 0;
+}
+
+static int safe_mkdir(const char *base_path, char *path, int mode)
+{
+	RAII_VAR(char *, absolute_path, NULL, free);
+
+	absolute_path = realpath(path, NULL);
+
+	if (absolute_path) {
+		/* Path exists, but is it in the right place? */
+		if (!ast_begins_with(absolute_path, base_path)) {
+			return EPERM;
+		}
+
+		/* It is in the right place! */
+		return 0;
+	} else {
+		char *last_slash = strrchr(path, '/');
+		int res;
+
+		if (last_slash == path) {
+			/* Parent is the root directory. */
+			res = EPERM;
+		} else if (last_slash == NULL) {
+			/* No more parents. */
+			res = EPERM;
+		} else {
+			/* Safely build the parent. */
+			*last_slash = '\0';
+			res = safe_mkdir(base_path, path, mode);
+			*last_slash = '/';
+		}
+
+		if (res != 0) {
+			/* Return failure */
+			errno = res;
+			return res;
+		}
+
+		/* Parent created, now create this path */
+		return safe_mkdir(base_path, path, mode);
+	}
+}
+
+int ast_safe_mkdir(const char *base_path, const char *path, int mode)
+{
+	RAII_VAR(char *, absolute_base_path, NULL, free);
+	RAII_VAR(char *, p, NULL, ast_free);
+
+	if (base_path == NULL || path == NULL) {
+		return EFAULT;
+	}
+
+	p = ast_strdup(path);
+	if (p == NULL) {
+		return ENOMEM;
+	}
+
+	absolute_base_path = realpath(base_path, NULL);
+	if (absolute_base_path == NULL) {
+		return ENOENT;
+	}
+
+	return safe_mkdir(absolute_base_path, p, mode);
 }
 
 int ast_utils_init(void)

Modified: team/dlee/record/res/res_stasis_recording.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/res/res_stasis_recording.c?view=diff&rev=391268&r1=391267&r2=391268
==============================================================================
--- team/dlee/record/res/res_stasis_recording.c (original)
+++ team/dlee/record/res/res_stasis_recording.c Mon Jun 10 10:22:56 2013
@@ -135,19 +135,19 @@
 		return STASIS_APP_RECORDING_TERMINATE_NONE;
 	}
 
-	if (strcmp(str, "none") == 0) {
+	if (strcasecmp(str, "none") == 0) {
 		return STASIS_APP_RECORDING_TERMINATE_NONE;
 	}
 
-	if (strcmp(str, "any") == 0) {
+	if (strcasecmp(str, "any") == 0) {
 		return STASIS_APP_RECORDING_TERMINATE_ANY;
 	}
 
-	if (strcmp(str, "#") == 0) {
+	if (strcasecmp(str, "#") == 0) {
 		return '#';
 	}
 
-	if (strcmp(str, "*") == 0) {
+	if (strcasecmp(str, "*") == 0) {
 		return '*';
 	}
 
@@ -162,15 +162,15 @@
 		return STASIS_APP_RECORDING_IF_EXISTS_FAIL;
 	}
 
-	if (strcmp(str, "fail") == 0) {
+	if (strcasecmp(str, "fail") == 0) {
 		return STASIS_APP_RECORDING_IF_EXISTS_FAIL;
 	}
 
-	if (strcmp(str, "overwrite") == 0) {
+	if (strcasecmp(str, "overwrite") == 0) {
 		return STASIS_APP_RECORDING_IF_EXISTS_OVERWRITE;
 	}
 
-	if (strcmp(str, "append") == 0) {
+	if (strcasecmp(str, "append") == 0) {
 		return STASIS_APP_RECORDING_IF_EXISTS_APPEND;
 	}
 
@@ -453,12 +453,8 @@
 		return NULL;
 	}
 
-	if (options->name[0] == '/') {
-		recording->absolute_name = ast_strdup(options->name);
-	} else {
-		ast_asprintf(&recording->absolute_name, "%s/%s",
-			ast_config_AST_RECORDING_DIR, options->name);
-	}
+	ast_asprintf(&recording->absolute_name, "%s/%s",
+		ast_config_AST_RECORDING_DIR, options->name);
 
 	if (recording->absolute_name == NULL) {
 		errno = ENOMEM;
@@ -467,7 +463,8 @@
 
 	if ((last_slash = strrchr(recording->absolute_name, '/'))) {
 		*last_slash = '\0';
-		if (ast_mkdir(recording->absolute_name, 0777) != 0) {
+		if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
+				recording->absolute_name, 0777) != 0) {
 			/* errno set by ast_mkdir */
 			return NULL;
 		}

Modified: team/dlee/record/res/stasis_http/resource_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/res/stasis_http/resource_channels.c?view=diff&rev=391268&r1=391267&r2=391268
==============================================================================
--- team/dlee/record/res/stasis_http/resource_channels.c (original)
+++ team/dlee/record/res/stasis_http/resource_channels.c Mon Jun 10 10:22:56 2013
@@ -295,7 +295,15 @@
 				response, 500, "Internal Server Error",
 				"Out of memory");
 			break;
+		case EPERM:
+			stasis_http_response_error(
+				response, 400, "Bad Request",
+				"Recording name invalid");
+			break;
 		default:
+			ast_log(LOG_WARNING,
+				"Unrecognized recording error: %s\n",
+				strerror(errno));
 			stasis_http_response_error(
 				response, 500, "Internal Server Error",
 				"Internal Server Error");

Modified: team/dlee/record/tests/test_utils.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/tests/test_utils.c?view=diff&rev=391268&r1=391267&r2=391268
==============================================================================
--- team/dlee/record/tests/test_utils.c (original)
+++ team/dlee/record/tests/test_utils.c Mon Jun 10 10:22:56 2013
@@ -42,6 +42,8 @@
 #include "asterisk/channel.h"
 #include "asterisk/module.h"
 
+#include <sys/stat.h>
+
 AST_TEST_DEFINE(uri_encode_decode_test)
 {
 	int res = AST_TEST_PASS;
@@ -421,6 +423,93 @@
 	return res;
 }
 
+AST_TEST_DEFINE(safe_mkdir_test)
+{
+	char base_path[] = "/tmp/safe_mkdir.XXXXXX";
+	char path[80] = {};
+	int res;
+	struct stat actual;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/main/utils/";
+		info->summary = "Safe mkdir test";
+		info->description =
+			"This test ensures that ast_safe_mkdir does what it is "
+			"supposed to";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (mkdtemp(base_path) == NULL) {
+		ast_test_status_update(test, "Failed to create tmpdir for test\n");
+		return AST_TEST_FAIL;
+	}
+
+	snprintf(path, sizeof(path), "%s/should_work", base_path);
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 == res);
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, S_ISDIR(actual.st_mode));
+
+	snprintf(path, sizeof(path), "%s/should/also/work", base_path);
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 == res);
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, S_ISDIR(actual.st_mode));
+
+	snprintf(path, sizeof(path), "%s/even/this/../should/work", base_path);
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 == res);
+	snprintf(path, sizeof(path), "%s/even/should/work", base_path);
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, S_ISDIR(actual.st_mode));
+
+	snprintf(path, sizeof(path),
+		"%s/surprisingly/this/should//////////////////work", base_path);
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 == res);
+	snprintf(path, sizeof(path),
+		"%s/surprisingly/this/should/work", base_path);
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, S_ISDIR(actual.st_mode));
+
+	snprintf(path, sizeof(path), "/should_not_work");
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 != res);
+	ast_test_validate(test, EPERM == errno);
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 != res);
+	ast_test_validate(test, ENOENT == errno);
+
+	snprintf(path, sizeof(path), "%s/../nor_should_this", base_path);
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 != res);
+	ast_test_validate(test, EPERM == errno);
+	strncpy(path, "/tmp/nor_should_this", sizeof(path));
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 != res);
+	ast_test_validate(test, ENOENT == errno);
+
+	snprintf(path, sizeof(path),
+		"%s/this/especially/should/not/../../../../work", base_path);
+	res = ast_safe_mkdir(base_path, path, 0777);
+	ast_test_validate(test, 0 != res);
+	ast_test_validate(test, EPERM == errno);
+	strncpy(path, "/tmp/work", sizeof(path));
+	res = stat(path, &actual);
+	ast_test_validate(test, 0 != res);
+	ast_test_validate(test, ENOENT == errno);
+
+	return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(uri_encode_decode_test);
@@ -431,6 +520,7 @@
 	AST_TEST_UNREGISTER(crypto_loaded_test);
 	AST_TEST_UNREGISTER(adsi_loaded_test);
 	AST_TEST_UNREGISTER(agi_loaded_test);
+	AST_TEST_UNREGISTER(safe_mkdir_test);
 	return 0;
 }
 
@@ -444,6 +534,7 @@
 	AST_TEST_REGISTER(crypto_loaded_test);
 	AST_TEST_REGISTER(adsi_loaded_test);
 	AST_TEST_REGISTER(agi_loaded_test);
+	AST_TEST_REGISTER(safe_mkdir_test);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 




More information about the svn-commits mailing list