Re: Some ideas in SE-PostgreSQL enhancement (Re: The status of SE-PostgreSQL)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



KaiGai Kohei wrote:
> My preference is the later one:
>   TYPE_TRANSITION <subject context> <server context> : <class> <new context>;
> 
> In addition, an idea of configuration file can be considerable to set up
> the default context of database objects, though I considered it is not
> necessary in the past discussion.
> If a user want to work the database server process as an unconfined domain,
> like a legacy "disable_xxxx_trans" boolean doing, the <server context> as
> the target of TYPE_TRANSITION breaks all the correct labeling.
> 
> If we have a /etc/selinux/$POLICYTYPE/contexts/db_{sepgsql|rubix}, as follows,
> it can be used to specify the default context of special purpose database
> object such as schemas to store temporary database objects, not only the
> context of database as the root of type transition.
> ------------
> database    *             system_u:object_r:sepgsql_db_t:s0
> schema      pg_temp_*     system_u:object_r:sepgsql_temp_schema_t:s0
>   :             :            :
> ------------
> 
> The libselinux has selabel_lookup(3) interface to implement them
> for various kind of objects.

The attached patch is a proof of the concept.
It adds the forth backend of selabel_lookup(3) interface.

Under the enhancement, we should the following rules to determine what
security context is assigned on the newly created database object.

1. An explicitly specified security context by users.
   e.g)  CREATE TABLE t (a int, b text)
             SECURITY_LABEL = 'system_u:object_r:sepgsql_table_t:SystemHigh';

2. A matched entry in the configuration file which can be lookup up
   by selabel_lookup(3).
   e.g)  schema  pg_temp_*  system_u:object_r:sepgsql_temp_schema_t:s0
                 ^^^^^^^^^ --> if the new object name and type are matched.

3. The result of security_compute_av() or avc_compute_create() which can
   return the result of TYPE_TRANSITION rules.

The second step is newly suggested in this patch.
Needless to say, the determinded security context has to be checked
by the security policy.

> One concern is performance hit. If we need to open/lookup/close the file
> for each INSERT statement, its pain will be unacceptable.

This patch does not support db_tuple class, because of headach in performance
and its characteristic that database tuples have no name to identify itself.

Thanks,
-- 
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@xxxxxxxxxxxxx>
 libselinux/include/selinux/label.h   |   10 ++
 libselinux/include/selinux/selinux.h |    1 +
 libselinux/src/file_path_suffixes.h  |    1 +
 libselinux/src/label.c               |    3 +-
 libselinux/src/label_db.c            |  207 ++++++++++++++++++++++++++++++++++
 libselinux/src/label_internal.h      |    2 +
 libselinux/src/selinux_config.c      |   10 ++-
 libselinux/src/selinux_internal.h    |    1 +
 8 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/libselinux/include/selinux/label.h b/libselinux/include/selinux/label.h
index 82f4e13..e167508 100644
--- a/libselinux/include/selinux/label.h
+++ b/libselinux/include/selinux/label.h
@@ -29,6 +29,8 @@ struct selabel_handle;
 #define SELABEL_CTX_MEDIA	1
 /* x contexts */
 #define SELABEL_CTX_X		2
+/* database contexts */
+#define SELABEL_CTX_DB		3
 
 /*
  * Available options
@@ -116,6 +118,14 @@ void selabel_stats(struct selabel_handle *handle);
 #define SELABEL_X_POLYPROP	6
 #define SELABEL_X_POLYSELN	7
 
+/* Database backend */
+#define SELABEL_DB_DATABASE	1
+#define SELABEL_DB_CATALOG	2
+#define SELABEL_DB_SCHEMA	3
+#define SELABEL_DB_TABLE	4
+#define SELABEL_DB_COLUMN	5
+#define SELABEL_DB_PROCEDURE	6
+#define SELABEL_DB_SEQUENCE	7
 
 #ifdef __cplusplus
 }
diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
index fab083e..8d60b40 100644
--- a/libselinux/include/selinux/selinux.h
+++ b/libselinux/include/selinux/selinux.h
@@ -460,6 +460,7 @@ extern const char *selinux_file_context_local_path(void);
 extern const char *selinux_homedir_context_path(void);
 extern const char *selinux_media_context_path(void);
 extern const char *selinux_x_context_path(void);
+extern const char *selinux_db_context_path(void);
 extern const char *selinux_contexts_path(void);
 extern const char *selinux_securetty_types_path(void);
 extern const char *selinux_booleans_path(void);
diff --git a/libselinux/src/file_path_suffixes.h b/libselinux/src/file_path_suffixes.h
index 8d207c9..30c613d 100644
--- a/libselinux/src/file_path_suffixes.h
+++ b/libselinux/src/file_path_suffixes.h
@@ -20,3 +20,4 @@ S_(BINPOLICY, "/policy/policy")
     S_(FILE_CONTEXTS_LOCAL, "/contexts/files/file_contexts.local")
     S_(X_CONTEXTS, "/contexts/x_contexts")
     S_(COLORS, "/secolor.conf")
+    S_(DB_CONTEXTS, "/contexts/db_contexts")
diff --git a/libselinux/src/label.c b/libselinux/src/label.c
index f7418d6..1a7b13c 100644
--- a/libselinux/src/label.c
+++ b/libselinux/src/label.c
@@ -20,7 +20,8 @@ typedef int (*selabel_initfunc)(struct selabel_handle *rec,
 static selabel_initfunc initfuncs[] = {
 	&selabel_file_init,
 	&selabel_media_init,
-	&selabel_x_init
+	&selabel_x_init,
+	&selabel_db_init,
 };
 
 /*
diff --git a/libselinux/src/label_db.c b/libselinux/src/label_db.c
index e69de29..974770b 100644
--- a/libselinux/src/label_db.c
+++ b/libselinux/src/label_db.c
@@ -0,0 +1,207 @@
+/*
+ * Media contexts backend for Database contexts
+ *
+ * Author: KaiGai Kohei <kaigai@xxxxxxxxxxxxx>
+ */
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+struct dbctx_entry {
+	struct dbctx_entry *next;
+
+	int type;
+	struct selabel_lookup_rec lr;
+	char *key;
+
+	int matches;
+};
+
+static void selabel_db_close(struct selabel_handle *rec)
+{
+	struct dbctx_entry *entry = rec->data;
+	struct dbctx_entry *next;
+
+	while (entry != NULL) {
+		next = entry->next;
+		free(entry->key);
+		free(entry->lr.ctx_raw);
+		free(entry->lr.ctx_trans);
+		entry = next;
+	}
+}
+
+static struct selabel_lookup_rec *
+selabel_db_lookup(struct selabel_handle *rec, const char *key, int type)
+{
+	struct dbctx_entry *entry = rec->data;
+
+	while (entry != NULL) {
+		if (entry->type == type &&
+		    !fnmatch(entry->key, key, 0)) {
+			entry->matches++;
+                        return &entry->lr;
+		}
+		entry = entry->next;
+	}
+
+	return NULL;
+}
+
+static int process_line(struct selabel_handle *rec, struct dbctx_entry **tail,
+			char *line_buf, const char *path, unsigned lineno)
+{
+	struct dbctx_entry *entry;
+	char *type, *key, *context;
+	int items;
+
+	/* Skip empty lines */
+	while (isspace(*line_buf))
+		line_buf++;
+	if (*line_buf == '#' || *line_buf == '\0')
+		return 0;
+
+	/* Read <type> <key> <context> pair */
+	entry = malloc(sizeof(struct dbctx_entry));
+	if (!entry)
+		return -1;
+	memset(entry, 0, sizeof(struct dbctx_entry));
+
+	items = sscanf(line_buf, "%as %as %as", &type, &key, &context);
+	if (items < 3) {
+		selinux_log(SELINUX_WARNING,
+			    "%s:  line %d is missing fields, skipping\n",
+			    path, lineno);
+		free(entry);
+		if (items > 0)
+			free(type);
+		if (items > 1)
+			free(key);
+		return 0;
+	}
+
+	if (!strcmp(type, "database"))
+		entry->type = SELABEL_DB_DATABASE;
+	else if (!strcmp(type, "catalog"))
+		entry->type = SELABEL_DB_CATALOG;
+	else if (!strcmp(type, "schema"))
+		entry->type = SELABEL_DB_SCHEMA;
+	else if (!strcmp(type, "table"))
+		entry->type = SELABEL_DB_TABLE;
+	else if (!strcmp(type, "column"))
+		entry->type = SELABEL_DB_COLUMN;
+	else if (!strcmp(type, "procedure"))
+		entry->type = SELABEL_DB_PROCEDURE;
+	else if (!strcmp(type, "sequence"))
+		entry->type = SELABEL_DB_SEQUENCE;
+	else {
+		selinux_log(SELINUX_WARNING,
+			    "%s:  line %d has invalid object type %s\n",
+			    path, lineno, type);
+		free(type);
+		free(key);
+		free(context);
+		return 0;
+	}
+	entry->next = NULL;
+	entry->key = key;
+	entry->lr.ctx_raw = context;
+	free(type);
+
+	if (*tail == NULL)
+		rec->data = entry;
+	else
+		(*tail)->next = entry;
+	*tail = entry;
+
+	return 0;
+}
+
+static int selabel_db_open(struct selabel_handle *rec,
+			   struct selinux_opt *opts, unsigned nopts)
+{
+	FILE *filp;
+	const char *path = NULL;
+	char *line_buf = NULL;
+	size_t line_len = 0;
+	struct dbctx_entry *tail = NULL;
+	struct stat sb;
+	int status = -1;
+	int lineno = 0;
+
+	/* Process arguments */
+	while (nopts--) {
+		switch (opts[nopts].type) {
+		case SELABEL_OPT_PATH:
+			path = opts[nopts].value;
+			break;
+		}
+
+	}
+
+	/* Open the specification file */
+	if (!path)
+		path = selinux_db_context_path();
+	if ((filp = fopen(path, "r")) == NULL)
+		return -1;
+	__fsetlocking(filp, FSETLOCKING_BYCALLER);
+
+	if (fstat(fileno(filp), &sb) < 0)
+		goto out;
+	if (!S_ISREG(sb.st_mode)) {
+		errno = EINVAL;
+		goto out;
+	}
+
+	/* Read specfile */
+	while (getline(&line_buf, &line_len, filp) > 0)
+	{
+		lineno++;
+
+		if (process_line(rec, &tail, line_buf, path, lineno))
+		{
+			/* release all the list */
+			selabel_db_close(rec);
+			free(line_buf);
+			goto out;
+		}
+	}
+	free(line_buf);
+
+	status = 0;
+out:
+	fclose(filp);
+	return status;
+}
+
+static void selabel_db_stats(struct selabel_handle *rec)
+{
+	struct dbctx_entry *entry;
+	int count =0, total = 0;
+
+	entry = rec->data;
+	while (entry != NULL) {
+		count++;
+		total += entry->matches;
+	}
+
+	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
+		    count, total);
+}
+
+int selabel_db_init(struct selabel_handle *rec,
+		    struct selinux_opt *opts, unsigned nopts)
+{
+	rec->data = NULL;
+	rec->func_close = &selabel_db_close;
+	rec->func_lookup = &selabel_db_lookup;
+	rec->func_stats = &selabel_db_stats;
+
+	return selabel_db_open(rec, opts, nopts);
+}
diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h
index 27a1f06..0f3a3d9 100644
--- a/libselinux/src/label_internal.h
+++ b/libselinux/src/label_internal.h
@@ -23,6 +23,8 @@ int selabel_media_init(struct selabel_handle *rec, struct selinux_opt *opts,
 		      unsigned nopts) hidden;
 int selabel_x_init(struct selabel_handle *rec, struct selinux_opt *opts,
 		   unsigned nopts) hidden;
+int selabel_db_init(struct selabel_handle *rec, struct selinux_opt *opts,
+		    unsigned nopts) hidden;
 
 /*
  * Labeling internal structures
diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c
index dec5426..8870dc8 100644
--- a/libselinux/src/selinux_config.c
+++ b/libselinux/src/selinux_config.c
@@ -40,7 +40,8 @@
 #define SECURETTY_TYPES   18
 #define X_CONTEXTS        19
 #define COLORS            20
-#define NEL               21
+#define DB_CONTEXTS       21
+#define NEL               22
 
 /* New layout is relative to SELINUXDIR/policytype. */
 static char *file_paths[NEL];
@@ -391,3 +392,10 @@ const char *selinux_x_context_path()
 }
 
 hidden_def(selinux_x_context_path)
+
+const char *selinux_db_context_path()
+{
+	return get_path(DB_CONTEXTS);
+}
+
+hidden_def(selinux_db_context_path)
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
index 8b4c6d4..d0e5191 100644
--- a/libselinux/src/selinux_internal.h
+++ b/libselinux/src/selinux_internal.h
@@ -66,6 +66,7 @@ hidden_proto(selinux_mkload_policy)
     hidden_proto(selinux_customizable_types_path)
     hidden_proto(selinux_media_context_path)
     hidden_proto(selinux_x_context_path)
+    hidden_proto(selinux_db_context_path)
     hidden_proto(selinux_path)
     hidden_proto(selinux_check_passwd_access)
     hidden_proto(selinux_check_securetty_context)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <selinux/selinux.h>
#include <selinux/label.h>

int main(int argc, char *argv[])
{
    security_context_t context;
    struct selabel_handle *hnd;
    const char *typename;
    const char *keyword;
    int type;

    if (!argv[1] || !argv[2]) {
	fprintf(stderr, "usage: %s <type> <key>\n", argv[0]);
	return 1;
    }

    typename = argv[1];
    keyword = argv[2];

    if (!strcmp(typename, "database"))
	type = SELABEL_DB_DATABASE;
    else if (!strcmp(typename, "catalog"))
	type = SELABEL_DB_CATALOG;
    else if (!strcmp(typename, "schema"))
	type = SELABEL_DB_SCHEMA;
    else if (!strcmp(typename, "table"))
	type = SELABEL_DB_TABLE;
    else if (!strcmp(typename, "column"))
	type = SELABEL_DB_COLUMN;
    else if (!strcmp(typename, "procedure"))
	type = SELABEL_DB_PROCEDURE;
    else if (!strcmp(typename, "sequence"))
	type = SELABEL_DB_SEQUENCE;
    else {
	fprintf(stderr, "invalid object type: %s\n", argv[1]);
	return 1;
    }

    hnd = selabel_open(SELABEL_CTX_DB, NULL, 0);
    if (!hnd) {
	fprintf(stderr, "selabel_open failed : %s\n", strerror(errno));
	return 1;
    }

    if (selabel_lookup(hnd, &context, keyword, type) < 0)
	printf("No valid context for (%s,%s)\n", typename, keyword);
    else
	printf("lookup: %s for (%s,%s)\n", context, typename, keyword);

    selabel_close(hnd);
}

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux