lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <201006102110.DHJ56772.tQOLFSVHMFOOJF@I-love.SAKURA.ne.jp>
Date:	Thu, 10 Jun 2010 21:10:18 +0900
From:	Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>
To:	linux-security-module@...r.kernel.org
Cc:	linux-kernel@...r.kernel.org
Subject: [PATCH 3/4] TOMOYO: Add symlink's target condition support.

Symlinks are frequently abused by attackers. TOMOYO checks permissions using
dereferenced pathname. Thus, symlinks don't affect on whether the dereferenced
file is accessible or not. But symlinks do affect on how the dereferenced file
is used if the file is accessible. For example, symlink from
/var/www/html/index.txt to /var/www/html/.htpasswd will make Apache disclose
the content of /var/www/html/.htpasswd if FollowSymLinks is specified in the
configuration files.

  # echo 'demo:$apr1$Dim11...$yaZFoEyPOeO/gJe2lA0wz/' > /var/www/html/.htpasswd
  # ln -s .htpasswd /var/www/html/index.txt
  # curl http://127.0.0.1/index.txt
  demo:$apr1$Dim11...$yaZFoEyPOeO/gJe2lA0wz/

This patch allows users to check symlink's target passed to symlink()
operation. For example,

  allow_symlink /var/www/html/index.html if symlink.target="\*.html"

will allow creation of symlink /var/www/html/index.html only if the symlink's
target matches pattern "\*.html".

Signed-off-by: Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>
---
 security/tomoyo/common.c    |   49 ++++++++++++++++++++++++++++++++++++++++----
 security/tomoyo/common.h    |   10 ++++++++
 security/tomoyo/condition.c |   17 +++++++++++++--
 security/tomoyo/file.c      |   15 ++++++++++++-
 security/tomoyo/tomoyo.c    |   15 +++++++------
 5 files changed, 91 insertions(+), 15 deletions(-)

--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -20,6 +20,7 @@ static struct tomoyo_profile tomoyo_defa
 	.preference.learning_verbose = false,
 	.preference.learning_exec_realpath = true,
 	.preference.learning_exec_argv0 = true,
+	.preference.learning_symlink_target = true,
 	.preference.permissive_verbose = true
 };
 
@@ -362,6 +363,10 @@ static int tomoyo_write_profile(struct t
 			profile->preference.learning_exec_argv0 = true;
 		else if (strstr(cp, "exec.argv0=no"))
 			profile->preference.learning_exec_argv0 = false;
+		if (strstr(cp, "symlink.target=yes"))
+			profile->preference.learning_symlink_target = true;
+		else if (strstr(cp, "symlink.target=no"))
+			profile->preference.learning_symlink_target = false;
 		return 0;
 	}
 	if (profile == &tomoyo_default_profile)
@@ -422,14 +427,17 @@ static int tomoyo_read_profile(struct to
 		goto body;
 	tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
 	tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s "
-			 "max_entry=%u exec.realpath=%s exec.argv0=%s }\n",
+			 "max_entry=%u exec.realpath=%s exec.argv0=%s "
+			 "symlink.target=%s }\n",
 			 tomoyo_yesno(tomoyo_default_profile.preference.
 				      learning_verbose),
 			 tomoyo_default_profile.preference.learning_max_entry,
 			 tomoyo_yesno(tomoyo_default_profile.preference.
 				      learning_exec_realpath),
 			 tomoyo_yesno(tomoyo_default_profile.preference.
-				      learning_exec_argv0));
+				      learning_exec_argv0),
+			 tomoyo_yesno(tomoyo_default_profile.preference.
+				      learning_symlink_target));
 	tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n",
 			 tomoyo_yesno(tomoyo_default_profile.preference.
 				      permissive_verbose));
@@ -474,14 +482,16 @@ static int tomoyo_read_profile(struct to
 		    !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ "
 				      "verbose=%s max_entry=%u "
 				      "exec.realpath=%s exec.argv0=%s "
-				      "}\n", index,
+				      "symlink.target=%s }\n", index,
 				      tomoyo_yesno(profile->preference.
 						   learning_verbose),
 				      profile->preference.learning_max_entry,
 				      tomoyo_yesno(profile->preference.
 						   learning_exec_realpath),
 				      tomoyo_yesno(profile->preference.
-						   learning_exec_argv0)))
+						   learning_exec_argv0),
+				      tomoyo_yesno(profile->preference.
+						   learning_symlink_target)))
 			goto out;
 		if (profile->permissive != &tomoyo_default_profile.preference
 		    && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ "
@@ -1706,6 +1716,35 @@ static struct tomoyo_condition *tomoyo_g
 	return cond;
 }
 
+/**
+ * tomoyo_get_symlink_condition - Get condition part for symlink requests.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+static struct tomoyo_condition *tomoyo_get_symlink_condition
+(const struct tomoyo_request_info *r)
+{
+	struct tomoyo_condition *cond;
+	char *buf;
+	int len;
+	const char *symlink;
+	const struct tomoyo_preference *pref = tomoyo_profile(r->profile)->
+		learning;
+	if (!pref->learning_symlink_target)
+		return NULL;
+	symlink = r->obj->symlink_target->name;
+	len = strlen(symlink) + 32;
+	buf = kmalloc(len, GFP_NOFS);
+	if (!buf)
+		return NULL;
+	snprintf(buf, len - 1, "if symlink.target=\"%s\"", symlink);
+	cond = tomoyo_get_condition(buf);
+	kfree(buf);
+	return cond;
+}
+
 /* Wait queue for tomoyo_query_list. */
 static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
 
@@ -1770,6 +1809,8 @@ int tomoyo_supervisor(struct tomoyo_requ
 		tomoyo_normalize_line(buffer);
 		if (r->ee)
 			cond = tomoyo_get_execute_condition(r->ee);
+		else if (r->obj && r->obj->symlink_target)
+			cond = tomoyo_get_symlink_condition(r);
 		else
 			cond = NULL;
 		tomoyo_write_domain_policy2(buffer, r->domain, cond, false);
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -196,6 +196,7 @@ enum tomoyo_conditions_index {
 	TOMOYO_EXEC_ARGC,            /* "struct linux_binprm *"->argc */
 	TOMOYO_EXEC_ENVC,            /* "struct linux_binprm *"->envc */
 	TOMOYO_EXEC_REALPATH,
+	TOMOYO_SYMLINK_TARGET,
 	TOMOYO_MAX_CONDITION_KEYWORD,
 	TOMOYO_NUMBER_UNION,
 	TOMOYO_NAME_UNION,
@@ -213,6 +214,11 @@ struct tomoyo_page_dump {
 	char *data;           /* Contents of "page". Size is PAGE_SIZE. */
 };
 
+/* Structure for attribute checks in addition to pathname checks. */
+struct tomoyo_obj_info {
+	struct tomoyo_path_info *symlink_target;
+};
+
 struct tomoyo_condition_element {
 	/*
 	 * Left hand operand. A "struct tomoyo_argv" for TOMOYO_ARGV_ENTRY, a
@@ -264,6 +270,7 @@ struct tomoyo_execve_entry;
 struct tomoyo_request_info {
 	struct tomoyo_domain_info *domain;
 	struct tomoyo_execve_entry *ee;
+	struct tomoyo_obj_info *obj;
 	u8 retry;
 	u8 profile;
 	u8 mode; /* One of tomoyo_mode_index . */
@@ -776,6 +783,7 @@ struct tomoyo_preference {
 	bool learning_verbose;
 	bool learning_exec_realpath;
 	bool learning_exec_argv0;
+	bool learning_symlink_target;
 	bool permissive_verbose;
 };
 
@@ -1004,7 +1012,7 @@ int tomoyo_path_number_perm(const u8 ope
 			    unsigned long number);
 int tomoyo_path_number3_perm(const u8 operation, struct path *path,
 			     const unsigned int mode, unsigned int dev);
-int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_path_perm(const u8 operation, struct path *path, const char *target);
 int tomoyo_path2_perm(const u8 operation, struct path *path1,
 		      struct path *path2);
 int tomoyo_find_next_domain(struct linux_binprm *bprm);
--- security-testing-2.6.orig/security/tomoyo/condition.c
+++ security-testing-2.6/security/tomoyo/condition.c
@@ -382,6 +382,7 @@ const char *tomoyo_condition_keyword[TOM
 	[TOMOYO_EXEC_ARGC]            = "exec.argc",
 	[TOMOYO_EXEC_ENVC]            = "exec.envc",
 	[TOMOYO_EXEC_REALPATH]        = "exec.realpath",
+	[TOMOYO_SYMLINK_TARGET]       = "symlink.target",
 };
 
 /* #define DEBUG_CONDITION */
@@ -473,7 +474,8 @@ struct tomoyo_condition *tomoyo_get_cond
 		condc++;
 		dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
 			word, left);
-		if (left == TOMOYO_EXEC_REALPATH) {
+		if (left == TOMOYO_EXEC_REALPATH ||
+		    left == TOMOYO_SYMLINK_TARGET) {
 			names_count++;
 			continue;
 		}
@@ -588,7 +590,8 @@ struct tomoyo_condition *tomoyo_get_cond
 		condc--;
 		dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
 			word, left);
-		if (left == TOMOYO_EXEC_REALPATH) {
+		if (left == TOMOYO_EXEC_REALPATH ||
+		    left == TOMOYO_SYMLINK_TARGET) {
 			right = TOMOYO_NAME_UNION;
 			if (!tomoyo_parse_name_union_quoted(word, names_p++))
 				goto out;
@@ -689,6 +692,7 @@ bool tomoyo_condition(struct tomoyo_requ
 	const struct tomoyo_name_union *names_p;
 	const struct tomoyo_argv_entry *argv;
 	const struct tomoyo_envp_entry *envp;
+	struct tomoyo_obj_info *obj;
 	u16 condc;
 	u16 argc;
 	u16 envc;
@@ -699,6 +703,7 @@ bool tomoyo_condition(struct tomoyo_requ
 	condc = cond->condc;
 	argc = cond->argc;
 	envc = cond->envc;
+	obj = r->obj;
 	if (r->ee)
 		bprm = r->ee->bprm;
 	if (!bprm && (argc || envc))
@@ -722,8 +727,16 @@ bool tomoyo_condition(struct tomoyo_requ
 		if (right == TOMOYO_NAME_UNION) {
 			const struct tomoyo_name_union *ptr = names_p++;
 			switch (left) {
+				struct tomoyo_path_info *symlink;
 				struct tomoyo_execve_entry *ee;
 				struct file *file;
+			case TOMOYO_SYMLINK_TARGET:
+				symlink = obj ? obj->symlink_target : NULL;
+				if (!symlink ||
+				    tomoyo_compare_name_union(symlink, ptr)
+				    != match)
+					goto out;
+				break;
 			case TOMOYO_EXEC_REALPATH:
 				ee = r->ee;
 				file = ee ? ee->bprm->file : NULL;
--- security-testing-2.6.orig/security/tomoyo/file.c
+++ security-testing-2.6/security/tomoyo/file.c
@@ -1407,14 +1407,17 @@ int tomoyo_check_open_permission(struct 
  *
  * @operation: Type of operation.
  * @path:      Pointer to "struct path".
+ * @target:    Symlink's target if @operation is TOMOYO_TYPE_SYMLINK.
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_path_perm(const u8 operation, struct path *path)
+int tomoyo_path_perm(const u8 operation, struct path *path, const char *target)
 {
 	int error = -ENOMEM;
 	struct tomoyo_path_info buf;
 	struct tomoyo_request_info r;
+	struct tomoyo_obj_info obj;
+	struct tomoyo_path_info symlink_target;
 	int idx;
 
 	if (!path->mnt)
@@ -1438,8 +1441,18 @@ int tomoyo_path_perm(const u8 operation,
 	case TOMOYO_TYPE_UMOUNT:
 		tomoyo_add_slash(&buf);
 		break;
+	case TOMOYO_TYPE_SYMLINK:
+		symlink_target.name = tomoyo_encode(target);
+		if (!symlink_target.name)
+			goto out;
+		tomoyo_fill_path_info(&symlink_target);
+		obj.symlink_target = &symlink_target;
+		r.obj = &obj;
+		break;
 	}
 	error = tomoyo_path_permission(&r, operation, &buf);
+	if (operation == TOMOYO_TYPE_SYMLINK)
+		kfree(symlink_target.name);
  out:
 	kfree(buf.name);
 	tomoyo_read_unlock(idx);
--- security-testing-2.6.orig/security/tomoyo/tomoyo.c
+++ security-testing-2.6/security/tomoyo/tomoyo.c
@@ -95,13 +95,13 @@ static int tomoyo_bprm_check_security(st
 
 static int tomoyo_path_truncate(struct path *path)
 {
-	return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
+	return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
 }
 
 static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
 {
 	struct path path = { parent->mnt, dentry };
-	return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
 }
 
 static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
@@ -115,14 +115,14 @@ static int tomoyo_path_mkdir(struct path
 static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
 {
 	struct path path = { parent->mnt, dentry };
-	return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
 }
 
 static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
 			       const char *old_name)
 {
 	struct path path = { parent->mnt, dentry };
-	return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
 }
 
 static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
@@ -177,7 +177,8 @@ static int tomoyo_file_fcntl(struct file
 			     unsigned long arg)
 {
 	if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
-		return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path);
+		return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path,
+					NULL);
 	return 0;
 }
 
@@ -216,7 +217,7 @@ static int tomoyo_path_chown(struct path
 
 static int tomoyo_path_chroot(struct path *path)
 {
-	return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
+	return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);
 }
 
 static int tomoyo_sb_mount(char *dev_name, struct path *path,
@@ -228,7 +229,7 @@ static int tomoyo_sb_mount(char *dev_nam
 static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
 {
 	struct path path = { mnt, mnt->mnt_root };
-	return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
+	return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
 }
 
 static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ