[apparmor] [PATCH 7/7] Revise dconf

John Johansen john.johansen at canonical.com
Fri Feb 10 20:56:16 UTC 2017


Revise the dconf patch so that the returned paths will have the
correct permissions when taking into account stacking and delegation.

This requires doing a permission query per path, (or building the
library infrastructure to handle permission changes due to stacking
or delegation). This is done once while building the list and will
remain good until policy is changed.

Signed-off-by: John Johansen <john.johansen at canonical.com>
---
 libraries/libapparmor/include/sys/apparmor.h       |  30 +--
 libraries/libapparmor/src/Makefile.am              |   4 +
 libraries/libapparmor/src/kernel.c                 | 205 +++++++++++++++------
 libraries/libapparmor/src/tst_dconf.c              |  59 ++++++
 parser/dconf.cc                                    |  75 ++++----
 parser/dconf.h                                     |   8 +-
 parser/parser_lex.l                                |  10 +-
 parser/parser_yacc.y                               |  33 +++-
 parser/tst/equality.sh                             |  16 ++
 parser/tst/simple_tests/dconf/bad_path_01.sd       |   4 +-
 parser/tst/simple_tests/dconf/bad_path_02.sd       |   4 +-
 parser/tst/simple_tests/dconf/bad_path_03.sd       |   2 +-
 parser/tst/simple_tests/dconf/bad_path_04.sd       |   2 +-
 parser/tst/simple_tests/dconf/bad_path_05.sd       |   8 +
 parser/tst/simple_tests/dconf/bad_path_06.sd       |   8 +
 parser/tst/simple_tests/dconf/bad_path_07.sd       |   8 +
 parser/tst/simple_tests/dconf/bad_path_08.sd       |   8 +
 parser/tst/simple_tests/dconf/bad_path_09.sd       |   8 +
 parser/tst/simple_tests/dconf/ok_audit_01.sd       |   3 +-
 parser/tst/simple_tests/dconf/ok_audit_02.sd       |   8 +
 parser/tst/simple_tests/dconf/ok_dir_01.sd         |   2 +-
 parser/tst/simple_tests/dconf/ok_dir_02.sd         |   8 +
 .../tst/simple_tests/dconf/ok_escapedrechars_01.sd |   8 +
 .../tst/simple_tests/dconf/ok_escapedrechars_02.sd |   8 +
 .../tst/simple_tests/dconf/ok_escapedrechars_03.sd |   8 +
 .../tst/simple_tests/dconf/ok_escapedrechars_04.sd |   8 +
 .../tst/simple_tests/dconf/ok_escapedrechars_05.sd |   8 +
 parser/tst/simple_tests/dconf/ok_key_01.sd         |   2 +-
 28 files changed, 413 insertions(+), 142 deletions(-)
 create mode 100644 libraries/libapparmor/src/tst_dconf.c
 create mode 100644 parser/tst/simple_tests/dconf/bad_path_05.sd
 create mode 100644 parser/tst/simple_tests/dconf/bad_path_06.sd
 create mode 100644 parser/tst/simple_tests/dconf/bad_path_07.sd
 create mode 100644 parser/tst/simple_tests/dconf/bad_path_08.sd
 create mode 100644 parser/tst/simple_tests/dconf/bad_path_09.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_audit_02.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_dir_02.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_escapedrechars_01.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_escapedrechars_02.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_escapedrechars_03.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_escapedrechars_04.sd
 create mode 100644 parser/tst/simple_tests/dconf/ok_escapedrechars_05.sd

diff --git a/libraries/libapparmor/include/sys/apparmor.h b/libraries/libapparmor/include/sys/apparmor.h
index 6f79eff..e94725e 100644
--- a/libraries/libapparmor/include/sys/apparmor.h
+++ b/libraries/libapparmor/include/sys/apparmor.h
@@ -63,9 +63,9 @@ __BEGIN_DECLS
 
 
 /* Permission flags for the AA_CLASS_DCONF mediation class */
-#define AA_DCONF_READ			(1 << 2)
-#define AA_DCONF_WRITE			(1 << 1)
-#define AA_DCONF_READWRITE		((AA_DCONF_READ) | (AA_DCONF_WRITE))
+#define AA_DCONF_READ			(AA_MAY_READ)
+#define AA_DCONF_WRITE			(AA_MAY_WRITE)
+#define AA_VALID_DCONF_PERMS		((AA_DCONF_READ) | (AA_DCONF_WRITE))
 
 
 /* Prototypes for apparmor state queries */
@@ -143,22 +143,24 @@ typedef struct {
 	aa_label_data_ent *ents;	/* free vec of entries */
 } aa_label_data_info;
 
-typedef struct {
-	char *data;			/* free data */
-	size_t rn;			/* number of rpaths */
-	size_t rwn;			/* number of rwpaths */
-	size_t arn;			/* number of arpaths */
-	size_t arwn;			/* number of arwpaths */
-	const char **rpaths;		/* read-only paths in data */
-	const char **rwpaths;		/* read-write paths in data */
-	const char **arpaths;		/* audit read-only paths in data */
-	const char **arwpaths;		/* audit read-write paths in data */
-} aa_dconf_info;
 
 extern int aa_query_label_data(const char *label, const char *key,
 			       aa_label_data_info *out);
 extern void aa_clear_label_data(aa_label_data_info *info);
 
+
+typedef struct {
+	char *data;			/* free data */
+	size_t r_n;			/* number of r_paths */
+	size_t rw_n;			/* number of rw_paths */
+	size_t ar_n;			/* number of ar_paths */
+	size_t arw_n;			/* number of arw_paths */
+	const char **r_paths;		/* read-only paths in data */
+	const char **rw_paths;		/* read-write paths in data */
+	const char **ar_paths;		/* audit read-only paths in data */
+	const char **arw_paths;		/* audit read-write paths in data */
+} aa_dconf_info;
+
 extern int aa_query_dconf_info(const char *label, aa_dconf_info *info);
 extern void aa_clear_dconf_info(aa_dconf_info *info);
 
diff --git a/libraries/libapparmor/src/Makefile.am b/libraries/libapparmor/src/Makefile.am
index dd393a9..817510f 100644
--- a/libraries/libapparmor/src/Makefile.am
+++ b/libraries/libapparmor/src/Makefile.am
@@ -71,6 +71,10 @@ tst_kernel_SOURCES = tst_kernel.c
 tst_kernel_LDADD = .libs/libapparmor.a
 tst_kernel_LDFLAGS = -pthread
 
+tst_dconf_SOURCES = tst_dconf.c
+tst_dconf_LDADD = .libs/libapparmor.a
+tst_dconf_LDFLAGS = -pthread
+
 check_PROGRAMS = tst_aalogmisc tst_features tst_kernel
 TESTS = $(check_PROGRAMS)
 
diff --git a/libraries/libapparmor/src/kernel.c b/libraries/libapparmor/src/kernel.c
index 7aa665d..2b94689 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -1119,8 +1119,8 @@ static ssize_t query_dconf_setup(char **query, const char *label, size_t label_l
 		return -1;
 	memcpy(*query + AA_QUERY_CMD_LABEL_SIZE, label, label_len);
 	/* null separator */
-	*query[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
-	*query[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_DCONF;
+	(*query)[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
+	(*query)[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_DCONF;
 	memcpy(*query + AA_QUERY_CMD_LABEL_SIZE + label_len + 2, path, path_len);
 
 	return size;
@@ -1321,6 +1321,35 @@ void aa_clear_label_data(aa_label_data_info *info)
 	memset(info, 0, sizeof(*info));
 }
 
+static uint32_t unpack32(const char *p)
+{
+	uint32_t tmp;
+	memcpy(&tmp, p, sizeof(tmp));
+	return le32toh(tmp);
+}
+
+static int strcmp_cb(const void *p1, const void *p2)
+{
+           return strcmp(* (char * const *) p1, * (char * const *) p2);
+}
+
+static int unique(const char **vec, size_t n)
+{
+	size_t i, dups = 0, pos = 0;
+
+	for (i = 1; i < n; i++) {
+		if (strcmp(vec[pos], vec[i]) == 0) {
+			dups++;
+			continue;
+		}
+		pos++;
+		if (dups)
+			vec[pos] = vec[i];
+	}
+
+	return dups;
+}
+
 /**
  * aa_query_dconf_info - query dconf info associated with @label
  * @label: label of the confinement context
@@ -1332,72 +1361,128 @@ void aa_clear_label_data(aa_label_data_info *info)
  */
 int aa_query_dconf_info(const char *label, aa_dconf_info *info)
 {
-	aa_label_data_info data_info;
+	aa_label_data_info raw;
 	const char *p;
-	uint32_t tmp;
-	int i;
+	int i, n, res = -1;
 
 	memset(info, 0, sizeof (*info));
-
-	if (aa_query_label_data(label, "dconf", &data_info))
-		return -1;
-
-	if (data_info.n < 1)
+	if (aa_query_label_data(label, "dconf", &raw))
 		return -1;
 
-	info->data = data_info.data;
-	p = data_info.ents[0].entry;
-
-	memcpy(&tmp, p, sizeof(tmp));
-	p += sizeof(tmp);
-	info->rn = le32toh(tmp);
-	memcpy(&tmp, p, sizeof(tmp));
-	p += sizeof(tmp);
-	info->rwn = le32toh(tmp);
-	memcpy(&tmp, p, sizeof(tmp));
-	p += sizeof(tmp);
-	info->arn = le32toh(tmp);
-	memcpy(&tmp, p, sizeof(tmp));
-	p += sizeof(tmp);
-	info->arwn = le32toh(tmp);
-
-	info->rpaths = malloc(info->rn * sizeof(*info->rpaths));
-	info->rwpaths = malloc(info->rwn * sizeof(*info->rwpaths));
-	info->arpaths = malloc(info->arn * sizeof(*info->arpaths));
-	info->arwpaths = malloc(info->arwn * sizeof(*info->arwpaths));
-
-	for (i = 0; i < info->rn; i++) {
-		memcpy(&tmp, p, sizeof(tmp));
-		p += sizeof(tmp);
-		info->rpaths[i] = p;
-		p += le32toh(tmp);
+	/* Find upper bound on how many paths there are, one data ent per
+	 * profile, each profile can have multiple paths and some maybe
+	 * the same.
+	 *
+	 * Outer loop, for each profile in label
+	 */
+	for (n = i = 0; i < raw.n; i++) {
+		/* find dconf ents within data blob.
+		 * <uint32_t><string><\0>
+		 */
+		p = raw.ents[i].entry;
+		while (p < raw.ents[i].entry + raw.ents[i].size) {
+			uint32_t size = unpack32(p);
+			p += size + sizeof(size);
+			/* must be null terminated */
+			if (*(p-1) != 0)
+				goto eproto;
+			n++;
+		}
+		/* verify set of strings is exactly the size of the data */
+		if (p - raw.ents[i].entry != raw.ents[i].size)
+			goto erange;
 	}
 
-	for (i = 0; i < info->rwn; i++) {
-		memcpy(&tmp, p, sizeof(tmp));
-		p += sizeof(tmp);
-		info->rwpaths[i] = p;
-		p += le32toh(tmp);
+	/* setups work array for paths */
+	{
+		const char *vec[n];
+		aa_perms_t perms[n];
+		int j;
+
+		for (i = j = 0; i < raw.n; i++) {
+			p = raw.ents[i].entry;
+			while (p < raw.ents[i].entry + raw.ents[i].size) {
+				uint32_t size = unpack32(p);
+				vec[j++] = p + sizeof(size);
+				p += size + sizeof(size);
+			}
+		}
+		qsort(vec, n, sizeof(const char *), strcmp_cb);
+		n -= unique(vec, n);
+		for (i = 0; i < n; i++) {
+			/* do a query to get the compound label
+			 * perms permissions for an entry
+			 */
+			if (aa_query_dconf_raw(label, strlen(label), vec[i],
+					       strlen(vec[i]), &perms[i]))
+					goto out;
+			perms[i].allow &= ~perms[i].deny;
+			if (perms[i].allow & AA_MAY_WRITE) {
+				if (perms[i].audit & AA_MAY_WRITE)
+					info->arw_n++;
+				else
+					info->rw_n++;
+			} else if (perms[i].allow & AA_MAY_READ) {
+				if (perms[i].audit & AA_MAY_READ)
+					info->ar_n++;
+				else
+					info->r_n++;
+			} else
+				goto eproto;
+		}
+		
+		/* allocate vecs that point into data */
+		info->r_paths = malloc(info->r_n * sizeof(const char *));
+		if (!info->r_paths)
+			goto nomem;
+		info->rw_paths = malloc(info->rw_n * sizeof(const char *));
+		if (!info->rw_paths)
+			goto nomem;
+		info->ar_paths = malloc(info->ar_n * sizeof(const char *));
+		if (!info->ar_paths)
+			goto nomem;
+		info->arw_paths = malloc(info->arw_n * sizeof(const char *));
+		if (!info->arw_paths)
+			goto nomem;
+
+		/* now setup the data arrays */
+		size_t r_n = 0, rw_n = 0, ar_n = 0, arw_n = 0;
+		for (i = 0; i < n; i++) {
+			if (perms[i].allow & AA_MAY_WRITE) {
+				if (perms[i].audit & AA_MAY_WRITE)
+					info->arw_paths[arw_n++] = vec[i];
+				else
+					info->rw_paths[rw_n++] = vec[i];
+			} else if (perms[i].allow & AA_MAY_READ) {
+				if (perms[i].audit & AA_MAY_READ)
+					info->ar_paths[ar_n++] = vec[i];
+				else
+					info->r_paths[r_n++] = vec[i];
+			}
+		}
 	}
 
-	for (i = 0; i < info->arn; i++) {
-		memcpy(&tmp, p, sizeof(tmp));
-		p += sizeof(tmp);
-		info->arpaths[i] = p;
-		p += le32toh(tmp);
-	}
+	/* transfer raw data blob */
+	info->data = raw.data;
+	raw.data = NULL;
+	res = 0;
+out:
+	aa_clear_label_data(&raw);
 
-	for (i = 0; i < info->arwn; i++) {
-		memcpy(&tmp, p, sizeof(tmp));
-		p += sizeof(tmp);
-		info->arwpaths[i] = p;
-		p += le32toh(tmp);
-	}
+	return res;
 
-	data_info.data = NULL;
-	aa_clear_label_data(&data_info);
+eproto:
+	errno = EPROTO;
+	goto out;
 
-	return 0;
+erange:
+	errno = ERANGE;
+	goto out;
+
+nomem:
+	aa_clear_dconf_info(info);
+	errno = ENOMEM;
+	goto out;
 }
 
 /**
@@ -1408,10 +1493,10 @@ int aa_query_dconf_info(const char *label, aa_dconf_info *info)
  */
 void aa_clear_dconf_info(aa_dconf_info *info)
 {
-	free(info->arwpaths);
-	free(info->arpaths);
-	free(info->rwpaths);
-	free(info->rpaths);
+	free(info->r_paths);
+	free(info->ar_paths);
+	free(info->rw_paths);
+	free(info->arw_paths);
 	free(info->data);
 
 	memset(info, 0, sizeof(*info));
diff --git a/libraries/libapparmor/src/tst_dconf.c b/libraries/libapparmor/src/tst_dconf.c
new file mode 100644
index 0000000..97c332c
--- /dev/null
+++ b/libraries/libapparmor/src/tst_dconf.c
@@ -0,0 +1,59 @@
+/*
+ *   Copyright (c) 2017
+ *   Canonical, Ltd. (All rights reserved)
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, contact Novell, Inc. or Canonical
+ *   Ltd.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "kernel.c"
+
+void print_strings(const char *msg, const char **paths, size_t n)
+{
+	size_t i;
+
+	printf("%s: %lu entries\n", msg, n);
+	for (i = 0; i < n; i++)
+		printf("\t%s\n", paths[i]);
+}
+
+void usage(const char *prog) {
+	fprintf(stderr, "Usage\n  %s <profile>\n", prog);
+}
+
+int main(int argc, char **argv)
+{
+	aa_dconf_info info;
+	int retval;
+
+	if (argc < 2) {
+		usage(argv[0]);
+		exit(1);
+	}
+
+	retval = aa_query_dconf_info(argv[1], &info);
+	if (retval == -1) {
+		fprintf(stderr, "query failed: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	print_strings("read paths", info.r_paths, info.r_n);
+	print_strings("audit read paths", info.ar_paths, info.ar_n);
+	print_strings("read/write paths", info.rw_paths, info.rw_n);
+	print_strings("audit read/write paths", info.arw_paths, info.arw_n);
+
+	return 0;
+}
diff --git a/parser/dconf.cc b/parser/dconf.cc
index 669bf80..a673791 100644
--- a/parser/dconf.cc
+++ b/parser/dconf.cc
@@ -24,32 +24,12 @@
 #include <sstream>
 #include <iomanip>
 
-dconfDataEnt::~dconfDataEnt()
-{
-}
 
 void dconfDataEnt::serialize(std::ostringstream &buf)
 {
 	ostringstream tmp;
 
-	sd_write32(tmp, rpaths.size());
-	sd_write32(tmp, rwpaths.size());
-	sd_write32(tmp, arpaths.size());
-	sd_write32(tmp, arwpaths.size());
-
-	for (set<std::string>::iterator i = rpaths.begin(); i != rpaths.end(); i++) {
-		sd_write32(tmp, i->size() + 1);
-		tmp.write(i->c_str(), i->size() + 1);
-	}
-	for (set<std::string>::iterator i = rwpaths.begin(); i != rwpaths.end(); i++) {
-		sd_write32(tmp, i->size() + 1);
-		tmp.write(i->c_str(), i->size() + 1);
-	}
-	for (set<std::string>::iterator i = arpaths.begin(); i != arpaths.end(); i++) {
-		sd_write32(tmp, i->size() + 1);
-		tmp.write(i->c_str(), i->size() + 1);
-	}
-	for (set<std::string>::iterator i = arwpaths.begin(); i != arwpaths.end(); i++) {
+	for (set<std::string>::iterator i = paths.begin(); i != paths.end(); i++) {
 		sd_write32(tmp, i->size() + 1);
 		tmp.write(i->c_str(), i->size() + 1);
 	}
@@ -75,7 +55,7 @@ std::ostream &dconf_rule::dump(std::ostream &os)
 
 	os << "dconf (";
 
-	switch (mode & AA_DCONF_READWRITE) {
+	switch (mode & AA_VALID_DCONF_PERMS) {
 	case AA_DCONF_READ:
 		os << "read";
 		break;
@@ -90,6 +70,18 @@ std::ostream &dconf_rule::dump(std::ostream &os)
 
 int dconf_rule::expand_variables(void)
 {
+	const char *pstr;
+	int error = expand_entry_variables(&path);
+	if (error)
+		return error;
+	/* do additional semantic checks. TODO these should have their own hook */
+	if (path[0] != '/')
+		return -1;
+	for (pstr = path; *pstr; pstr++) {
+		if (*pstr == '/' && *(pstr + 1) == '/')
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -108,37 +100,40 @@ void dconf_rule::gen_data(Profile &prof)
 			return;
 	}
 
-	if (mode & AA_DCONF_WRITE) {
-		if (audit)
-			dconf->arwpaths.insert(path);
-		else
-			dconf->rwpaths.insert(path);
-	} else {
-		if (audit)
-			dconf->arpaths.insert(path);
-		else
-			dconf->rpaths.insert(path);
-	}
+	dconf->paths.insert(path);
 }
 
 int dconf_rule::gen_policy_re(Profile &prof)
 {
+	std::string buf;
 	std::ostringstream rule;
+	pattern_t ptype;
+	int pos;
 
-	if ((mode & AA_DCONF_READWRITE) == 0)
+	/* don't generate policy for rules without permissions */
+	if ((mode & AA_VALID_DCONF_PERMS) == 0)
 		return RULE_OK;
 
-	rule << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DCONF;
+	ptype = convert_aaregex_to_pcre(path, 0, glob_default, buf, &pos);
+	if (ptype == ePatternInvalid)
+		return RULE_ERROR;
+	else if (ptype != ePatternBasic) {
+		cerr << "pattern matching not supported in dconf rules";
+		return RULE_ERROR;
+	}
 
-	if (path[strlen(path) - 1] == '/')
-		rule << path << "[^\\x00]*";
+	rule << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DCONF;
+	
+	/* dconf directories are converted to tail globs for the dfa */
+	if (buf[buf.length() - 1] == '/')
+		rule << buf << "[^\\x00]*";
 	else
-		rule << path;
+		rule << buf;
 
 	if (!prof.policy.rules->add_rule(rule.str().c_str(),
 					 0,
-					 mode & AA_DCONF_READWRITE,
-					 audit ? mode & AA_DCONF_READWRITE : 0,
+					 mode & AA_VALID_DCONF_PERMS,
+					 audit ? mode & AA_VALID_DCONF_PERMS : 0,
 					 dfaflags))
 		return RULE_ERROR;
 
diff --git a/parser/dconf.h b/parser/dconf.h
index 0477f06..c8c72c5 100644
--- a/parser/dconf.h
+++ b/parser/dconf.h
@@ -24,12 +24,8 @@
 
 class dconfDataEnt: public DataEnt {
 public:
-	set<std::string> rpaths;
-	set<std::string> rwpaths;
-	set<std::string> arpaths;
-	set<std::string> arwpaths;
-
-	virtual ~dconfDataEnt();
+	set<std::string> paths;
+	virtual ~dconfDataEnt() { };
 
 	void serialize(std::ostringstream &buf);
 };
diff --git a/parser/parser_lex.l b/parser/parser_lex.l
index 82cd84c..7b95d97 100644
--- a/parser/parser_lex.l
+++ b/parser/parser_lex.l
@@ -215,6 +215,7 @@ ID_NOEQ		{ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
 IDS_NOEQ	{LEADING_ID_CHARS_NOEQ}{ID_NOEQ}*
 ALLOWED_QUOTED_ID 	[^\0"]|\\\"
 QUOTED_ID	\"{ALLOWED_QUOTED_ID}*\"
+ID_CHARS_NOPAREN	[^ \t\n"!,(]
 
 IP		{NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER}
 
@@ -229,9 +230,6 @@ BOOL_VARIABLE	$(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
 LABEL		(\/|{SET_VARIABLE}{POST_VAR_ID}|{COLON}|{AMPERSAND}){ID}*
 QUOTED_LABEL	\"(\/|{SET_VAR_PREFIX}|{COLON}|{AMPERSAND})([^\0"]|\\\")*\"
 
-DPATHCOMP	[^[:space:]/]+
-DPATHNAME	{SLASH}({DPATHCOMP}{SLASH})*{DPATHCOMP}?
-
 OPEN_PAREN 	\(
 CLOSE_PAREN	\)
 COMMA		\,
@@ -508,7 +506,7 @@ LT_EQUAL	<=
 	tracedby	{ RETURN_TOKEN(TOK_TRACEDBY); }
 }
 
-<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<DCONF_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
 	read		{ RETURN_TOKEN(TOK_READ); }
 	write		{ RETURN_TOKEN(TOK_WRITE); }
 	{OPEN_PAREN}	{
@@ -521,9 +519,7 @@ LT_EQUAL	<=
 }
 
 <DCONF_MODE>{
-	r	{ RETURN_TOKEN(TOK_READ); }
-	rw	{ RETURN_TOKEN(TOK_READWRITE); }
-	{DPATHNAME}	{
+	({ID_CHARS_NOPAREN}{ID}*|{QUOTED_ID}) {
 		yylval.id = processid(yytext, yyleng);
 		RETURN_TOKEN(TOK_ID);
 	}
diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y
index 75e9387..16245d6 100644
--- a/parser/parser_yacc.y
+++ b/parser/parser_yacc.y
@@ -149,7 +149,6 @@ void add_local_entry(Profile *prof);
 %token TOK_BIND
 %token TOK_READ
 %token TOK_WRITE
-%token TOK_READWRITE
 %token TOK_EAVESDROP
 %token TOK_PEER
 %token TOK_TRACE
@@ -1325,17 +1324,41 @@ dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE
 	}
 
 dconf_perm: TOK_READ { $$ = AA_DCONF_READ; }
-	| TOK_READWRITE { $$ = AA_DCONF_READWRITE; }
+	| TOK_WRITE { $$ = AA_DCONF_WRITE; }
+	| TOK_VALUE
+	{
+		if (!$1)
+			$$ = 0;
+		else if (strcmp($1, "read") == 0)
+			$$ = AA_DCONF_READ;
+		else if (strcmp($1, "write") == 0)
+			$$ = AA_DCONF_WRITE;
+		else
+			parse_X_mode("dconf", AA_DCONF_READ | AA_DCONF_WRITE,
+				     $1, &$$, 1);
+		free($1);
+	}
+	| TOK_MODE
+	{
+		parse_X_mode("dconf", AA_DCONF_READ | AA_DCONF_WRITE, $1, &$$,
+			     1);
+		free($1);
+	}
 
 dconf_perms: { /* nothing */ $$ = 0; }
 	| dconf_perms dconf_perm { $$ = $1 | $2; }
+	| dconf_perms TOK_COMMA dconf_perm { $$ = $1 | $3; }
 
-opt_dconf_perms: { /* nothing */ $$ = AA_DCONF_READWRITE; /* default read-write */ }
+opt_dconf_perms: { /* nothing */ $$ = AA_VALID_DCONF_PERMS; }
 	| dconf_perms dconf_perm { $$ = $1 | $2; }
+	| TOK_OPENPAREN dconf_perms TOK_CLOSEPAREN { $$ = $2; }
 
-dconf_rule: TOK_DCONF TOK_ID opt_dconf_perms TOK_END_OF_RULE
+dconf_rule: TOK_DCONF opt_dconf_perms opt_id TOK_END_OF_RULE
 	{
-		dconf_rule *ent = new dconf_rule($2, $3);
+		dconf_rule *ent;
+		if ((($2) & AA_DCONF_WRITE) && !(($2) & AA_DCONF_READ))
+			yyerror(_("dconf write permission must be accompanied by read permission"));
+		ent = new dconf_rule($3 ? $3 : strdup("/"), $2);
 		if (!ent) {
 			yyerror(_("Memory allocation error."));
 		}
diff --git a/parser/tst/equality.sh b/parser/tst/equality.sh
index 029eec4..c2ac80b 100755
--- a/parser/tst/equality.sh
+++ b/parser/tst/equality.sh
@@ -265,6 +265,22 @@ verify_binary_equality "dbus minimization found in dbus abstractions" \
                    peer=(name=org.freedesktop.DBus),
 	      dbus send bus=session, }"
 
+verify_binary_equality "dconf read" \
+	"/t { dconf r /, }" \
+	"/t { dconf read /, }"
+
+verify_binary_equality "dconf read-write" \
+	"/t { dconf /, }" \
+	"/t { dconf rw /, }" \
+	"/t { dconf wr /, }" \
+	"/t { dconf (read write) /, }" \
+	"/t { dconf (write read) /, }" \
+	"/t { dconf (read, write) /, }"
+
+verify_binary_equality "dconf missing options" \
+	"/t { dconf /, }" \
+	"/t { dconf, }"
+
 # Rules compatible with audit, deny, and audit deny
 # note: change_profile does not support audit/allow/deny atm
 for rule in "capability" "capability mac_admin" \
diff --git a/parser/tst/simple_tests/dconf/bad_path_01.sd b/parser/tst/simple_tests/dconf/bad_path_01.sd
index c78d407..ee703df 100644
--- a/parser/tst/simple_tests/dconf/bad_path_01.sd
+++ b/parser/tst/simple_tests/dconf/bad_path_01.sd
@@ -1,8 +1,8 @@
 #
-#=DESCRIPTION dconf rule missing path
+#=DESCRIPTION dconf rule path doesn't start with /
 #=EXRESULT FAIL
 #
 
 profile bad_path {
-  dconf r,
+  dconf r abc,
 }
diff --git a/parser/tst/simple_tests/dconf/bad_path_02.sd b/parser/tst/simple_tests/dconf/bad_path_02.sd
index e0ccc03..2e42492 100644
--- a/parser/tst/simple_tests/dconf/bad_path_02.sd
+++ b/parser/tst/simple_tests/dconf/bad_path_02.sd
@@ -1,8 +1,8 @@
 #
-#=DESCRIPTION dconf rule missing leading slash
+#=DESCRIPTION dconf  trailing perm
 #=EXRESULT FAIL
 #
 
 profile bad_path {
-  dconf a/b/c r,
+  dconf /a/b/c r,
 }
diff --git a/parser/tst/simple_tests/dconf/bad_path_03.sd b/parser/tst/simple_tests/dconf/bad_path_03.sd
index d9c93c9..daf746a 100644
--- a/parser/tst/simple_tests/dconf/bad_path_03.sd
+++ b/parser/tst/simple_tests/dconf/bad_path_03.sd
@@ -4,5 +4,5 @@
 #
 
 profile bad_path {
-  dconf /a//b r,
+  dconf r /a//b,
 }
diff --git a/parser/tst/simple_tests/dconf/bad_path_04.sd b/parser/tst/simple_tests/dconf/bad_path_04.sd
index a870c12..d9e086b 100644
--- a/parser/tst/simple_tests/dconf/bad_path_04.sd
+++ b/parser/tst/simple_tests/dconf/bad_path_04.sd
@@ -4,5 +4,5 @@
 #
 
 profile bad_path {
-  dconf /a /b r,
+  dconf r /a /b,
 }
diff --git a/parser/tst/simple_tests/dconf/bad_path_05.sd b/parser/tst/simple_tests/dconf/bad_path_05.sd
new file mode 100644
index 0000000..38b61d2
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_05.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with tail glob
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+  dconf r /a/b**,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_06.sd b/parser/tst/simple_tests/dconf/bad_path_06.sd
new file mode 100644
index 0000000..d73aa0c
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_06.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with alternation
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+  dconf r /a/b{c,d},
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_07.sd b/parser/tst/simple_tests/dconf/bad_path_07.sd
new file mode 100644
index 0000000..114fc61
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_07.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with ?
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+  dconf r /a/b?,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_08.sd b/parser/tst/simple_tests/dconf/bad_path_08.sd
new file mode 100644
index 0000000..925f2c7
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_08.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with *
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+  dconf r /a*/b,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_09.sd b/parser/tst/simple_tests/dconf/bad_path_09.sd
new file mode 100644
index 0000000..3f5b274
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_09.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with non tail **
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+  dconf r /a**/b,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_audit_01.sd b/parser/tst/simple_tests/dconf/ok_audit_01.sd
index d2e63f2..6b2087c 100644
--- a/parser/tst/simple_tests/dconf/ok_audit_01.sd
+++ b/parser/tst/simple_tests/dconf/ok_audit_01.sd
@@ -4,6 +4,5 @@
 #
 
 profile ok_audit {
-  audit dconf /a/b/c r,
-  audit dconf /d/e/f rw,
+  audit dconf r /a/b/c,
 }
diff --git a/parser/tst/simple_tests/dconf/ok_audit_02.sd b/parser/tst/simple_tests/dconf/ok_audit_02.sd
new file mode 100644
index 0000000..786557d
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_audit_02.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with audit
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  audit dconf rw /d/e/f,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_dir_01.sd b/parser/tst/simple_tests/dconf/ok_dir_01.sd
index aa1f487..29c987a 100644
--- a/parser/tst/simple_tests/dconf/ok_dir_01.sd
+++ b/parser/tst/simple_tests/dconf/ok_dir_01.sd
@@ -4,5 +4,5 @@
 #
 
 profile ok_audit {
-  dconf /a/b/c/ r,
+  dconf r /a/b/c/,
 }
diff --git a/parser/tst/simple_tests/dconf/ok_dir_02.sd b/parser/tst/simple_tests/dconf/ok_dir_02.sd
new file mode 100644
index 0000000..52dbe5f
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_dir_02.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule for dir
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  dconf rw /a/b/c/,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_escapedrechars_01.sd b/parser/tst/simple_tests/dconf/ok_escapedrechars_01.sd
new file mode 100644
index 0000000..78dbe64
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_escapedrechars_01.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with \*
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  dconf r /a/\*b/c,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_escapedrechars_02.sd b/parser/tst/simple_tests/dconf/ok_escapedrechars_02.sd
new file mode 100644
index 0000000..1c0604b
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_escapedrechars_02.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with \*\*
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  dconf r /a/\*\*b/c,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_escapedrechars_03.sd b/parser/tst/simple_tests/dconf/ok_escapedrechars_03.sd
new file mode 100644
index 0000000..044b6aa
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_escapedrechars_03.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with tail \*\*
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  dconf r /a/b/c\*\*,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_escapedrechars_04.sd b/parser/tst/simple_tests/dconf/ok_escapedrechars_04.sd
new file mode 100644
index 0000000..603b6ed
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_escapedrechars_04.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with tail ?
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  dconf r /a/b/c\?,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_escapedrechars_05.sd b/parser/tst/simple_tests/dconf/ok_escapedrechars_05.sd
new file mode 100644
index 0000000..e9c25e0
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_escapedrechars_05.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with tail \{
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+  dconf r /a/b/c\{c,d\},
+}
diff --git a/parser/tst/simple_tests/dconf/ok_key_01.sd b/parser/tst/simple_tests/dconf/ok_key_01.sd
index 5ec92fa..b6c6f3f 100644
--- a/parser/tst/simple_tests/dconf/ok_key_01.sd
+++ b/parser/tst/simple_tests/dconf/ok_key_01.sd
@@ -4,5 +4,5 @@
 #
 
 profile ok_audit {
-  dconf /a/b/c r,
+  dconf r /a/b/c,
 }
-- 
2.9.3





More information about the AppArmor mailing list