1/*
2 * Media contexts backend for DB objects
3 *
4 * Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
5 */
6
7#include <sys/stat.h>
8#include <string.h>
9#include <stdio.h>
10#include <stdio_ext.h>
11#include <ctype.h>
12#include <errno.h>
13#include <limits.h>
14#include <fnmatch.h>
15#include "callbacks.h"
16#include "label_internal.h"
17
18/*
19 * Regular database object's security context interface
20 *
21 * It provides applications a regular security context for the given
22 * database objects. The pair of object's name and a security context
23 * are described in the specfile. In the default, it shall be stored
24 * in the /etc/selinux/$POLICYTYPE/contexts/sepgsql_contexts .
25 * (It assumes SE-PostgreSQL in the default. For other RDBMS, use the
26 * SELABEL_OPT_PATH option to specify different specfile.)
27 *
28 * Each line has the following format:
29 *   <object class> <object name/identifier> <security context>
30 *
31 * For example:
32 * ----------------------------------------
33 * #
34 * # It is an example specfile for database objects
35 * #
36 * db_database  template1           system_u:object_r:sepgsql_db_t:s0
37 *
38 * db_schema    *.pg_catalog        system_u:object_r:sepgsql_sys_schema_t:s0
39 *
40 * db_table     *.pg_catalog.*	    system_u:object_r:sepgsql_sysobj_t:s0
41 * db_column    *.pg_catalog.*.*    system_u:object_r:sepgsql_sysobj_t:s0
42 * ----------------------------------------
43 *
44 * All the characters after the '#' are dealt as comments.
45 *
46 * The first token is object class. SELABEL_DB_* declared in label.h are
47 * corresponding to a certain database object.
48 *
49 * The object name/identifier is compared to the given key.
50 * A database object can have its own namespace hierarchy.
51 * In the case of SE-PgSQL, database is the top level object, and schema
52 * is deployed just under a database. A schema can contains various kind
53 * of objects, such as tables, procedures and so on.
54 * Thus, when we lookup an expected security context for a table of
55 * "pg_class", it is necessary to assume selabel_lookup() is called with
56 * "postgres.pg_catalog.pg_class", not just a "pg_class".
57 *
58 * Wildcards ('*' or '?') are available on the patterns, so if you want
59 * to match a table within any schema, you should set '*' on the upper
60 * namespaces of the table.
61 *
62 * The structure of namespace depends on RDBMS.
63 * For example, Trusted-RUBIX has an idea of "catalog" which performs
64 * as a namespace between a database and individual schemas. In this
65 * case, a table has upper three layers.
66 */
67
68/*
69 * spec_t : It holds a pair of a key and an expected security context
70 */
71typedef struct spec {
72	struct selabel_lookup_rec lr;
73	char	       *key;
74	int		type;
75	int		matches;
76} spec_t;
77
78/*
79 * catalog_t : An array of spec_t
80 */
81typedef struct catalog {
82	unsigned int	nspec;	/* number of specs in use */
83	unsigned int	limit;	/* physical limitation of specs[] */
84	spec_t		specs[0];
85} catalog_t;
86
87/*
88 * Helper function to parse a line read from the specfile
89 */
90static int
91process_line(const char *path, char *line_buf, unsigned int line_num,
92	     catalog_t *catalog)
93{
94	spec_t	       *spec = &catalog->specs[catalog->nspec];
95	char	       *type, *key, *context, *temp;
96	int		items;
97
98	/* Cut off comments */
99	temp = strchr(line_buf, '#');
100	if (temp)
101		*temp = '\0';
102
103	/*
104	 * Every entry must have the following format
105	 *   <object class> <object name> <security context>
106	 */
107	type = key = context = temp = NULL;
108	items = sscanf(line_buf, "%ms %ms %ms %ms",
109		       &type, &key, &context, &temp);
110	if (items != 3) {
111		if (items > 0)
112			selinux_log(SELINUX_WARNING,
113				    "%s:  line %u has invalid format, skipped",
114				    path, line_num);
115		goto skip;
116	}
117
118	/*
119	 * Set up individual spec entry
120	 */
121	memset(spec, 0, sizeof(spec_t));
122
123	if (!strcmp(type, "db_database"))
124		spec->type = SELABEL_DB_DATABASE;
125	else if (!strcmp(type, "db_schema"))
126		spec->type = SELABEL_DB_SCHEMA;
127	else if (!strcmp(type, "db_table"))
128		spec->type = SELABEL_DB_TABLE;
129	else if (!strcmp(type, "db_column"))
130		spec->type = SELABEL_DB_COLUMN;
131	else if (!strcmp(type, "db_sequence"))
132		spec->type = SELABEL_DB_SEQUENCE;
133	else if (!strcmp(type, "db_view"))
134		spec->type = SELABEL_DB_VIEW;
135	else if (!strcmp(type, "db_procedure"))
136		spec->type = SELABEL_DB_PROCEDURE;
137	else if (!strcmp(type, "db_blob"))
138		spec->type = SELABEL_DB_BLOB;
139	else if (!strcmp(type, "db_tuple"))
140		spec->type = SELABEL_DB_TUPLE;
141	else if (!strcmp(type, "db_language"))
142		spec->type = SELABEL_DB_LANGUAGE;
143	else if (!strcmp(type, "db_exception"))
144		spec->type = SELABEL_DB_EXCEPTION;
145	else if (!strcmp(type, "db_datatype"))
146		spec->type = SELABEL_DB_DATATYPE;
147	else {
148		selinux_log(SELINUX_WARNING,
149			    "%s:  line %u has invalid object type %s\n",
150			    path, line_num, type);
151		goto skip;
152	}
153
154	free(type);
155	spec->key = key;
156	spec->lr.ctx_raw = context;
157
158	catalog->nspec++;
159
160	return 0;
161
162skip:
163	free(type);
164	free(key);
165	free(context);
166	free(temp);
167
168	return 0;
169}
170
171/*
172 * selabel_close() handler
173 */
174static void
175db_close(struct selabel_handle *rec)
176{
177	catalog_t      *catalog = (catalog_t *)rec->data;
178	spec_t	       *spec;
179	unsigned int	i;
180
181	for (i = 0; i < catalog->nspec; i++) {
182		spec = &catalog->specs[i];
183		free(spec->key);
184		free(spec->lr.ctx_raw);
185		free(spec->lr.ctx_trans);
186	}
187	free(catalog);
188}
189
190/*
191 * selabel_lookup() handler
192 */
193static struct selabel_lookup_rec *
194db_lookup(struct selabel_handle *rec, const char *key, int type)
195{
196	catalog_t      *catalog = (catalog_t *)rec->data;
197	spec_t	       *spec;
198	unsigned int	i;
199
200	for (i = 0; i < catalog->nspec; i++) {
201		spec = &catalog->specs[i];
202
203		if (spec->type != type)
204			continue;
205		if (!fnmatch(spec->key, key, 0)) {
206			spec->matches++;
207
208			return &spec->lr;
209		}
210	}
211
212	/* No found */
213	errno = ENOENT;
214	return NULL;
215}
216
217/*
218 * selabel_stats() handler
219 */
220static void
221db_stats(struct selabel_handle *rec)
222{
223	catalog_t      *catalog = (catalog_t *)rec->data;
224	unsigned int	i, total = 0;
225
226	for (i = 0; i < catalog->nspec; i++)
227		total += catalog->specs[i].matches;
228
229	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
230		    catalog->nspec, total);
231}
232
233/*
234 * selabel_open() handler
235 */
236static catalog_t *
237db_init(const struct selinux_opt *opts, unsigned nopts,
238			    struct selabel_handle *rec)
239{
240	catalog_t      *catalog;
241	FILE	       *filp;
242	const char     *path = NULL;
243	char	       *line_buf = NULL;
244	size_t		line_len = 0;
245	unsigned int	line_num = 0;
246	unsigned int	i;
247	struct stat sb;
248
249	/*
250	 * Initialize catalog data structure
251	 */
252	catalog = malloc(sizeof(catalog_t) + 32 * sizeof(spec_t));
253	if (!catalog)
254		return NULL;
255	catalog->limit = 32;
256	catalog->nspec = 0;
257
258	/*
259	 * Process arguments
260	 *
261	 * SELABEL_OPT_PATH:
262	 *   It allows to specify an alternative specification file instead of
263	 *   the default one. If RDBMS is not SE-PostgreSQL, it may need to
264	 *   specify an explicit specfile for database objects.
265	 */
266	while (nopts--) {
267		switch (opts[nopts].type) {
268		case SELABEL_OPT_PATH:
269			path = opts[nopts].value;
270			break;
271		}
272	}
273
274	/*
275	 * Open the specification file
276	 */
277	if (!path)
278		path = selinux_sepgsql_context_path();
279
280	if ((filp = fopen(path, "re")) == NULL) {
281		free(catalog);
282		return NULL;
283	}
284	if (fstat(fileno(filp), &sb) < 0) {
285		free(catalog);
286		fclose(filp);
287		return NULL;
288	}
289	if (!S_ISREG(sb.st_mode)) {
290		free(catalog);
291		fclose(filp);
292		errno = EINVAL;
293		return NULL;
294	}
295	rec->spec_file = strdup(path);
296	if (!rec->spec_file) {
297                free(catalog);
298                fclose(filp);
299                return NULL;
300	}
301
302	/*
303	 * Parse for each lines
304	 */
305	while (getline(&line_buf, &line_len, filp) > 0) {
306		/*
307		 * Expand catalog array, if necessary
308		 */
309		if (catalog->limit == catalog->nspec) {
310			size_t		length;
311			unsigned int	new_limit = 2 * catalog->limit;
312			catalog_t      *new_catalog;
313
314			length = sizeof(catalog_t)
315				+ new_limit * sizeof(spec_t);
316			new_catalog = realloc(catalog, length);
317			if (!new_catalog)
318				goto out_error;
319
320			catalog = new_catalog;
321			catalog->limit = new_limit;
322		}
323
324		/*
325		 * Parse a line
326		 */
327		if (process_line(path, line_buf, ++line_num, catalog) < 0)
328			goto out_error;
329	}
330
331	if (digest_add_specfile(rec->digest, filp, NULL, sb.st_size, path) < 0)
332		goto out_error;
333
334	digest_gen_hash(rec->digest);
335
336	free(line_buf);
337	fclose(filp);
338
339	return catalog;
340
341out_error:
342	free(line_buf);
343	for (i = 0; i < catalog->nspec; i++) {
344		spec_t	       *spec = &catalog->specs[i];
345
346		free(spec->key);
347		free(spec->lr.ctx_raw);
348		free(spec->lr.ctx_trans);
349	}
350	free(catalog);
351	fclose(filp);
352
353	return NULL;
354}
355
356/*
357 * Initialize selabel_handle and load the entries of specfile
358 */
359int selabel_db_init(struct selabel_handle *rec,
360		    const struct selinux_opt *opts, unsigned nopts)
361{
362	rec->func_close = &db_close;
363	rec->func_lookup = &db_lookup;
364	rec->func_stats = &db_stats;
365	rec->data = db_init(opts, nopts, rec);
366
367	return !rec->data ? -1 : 0;
368}
369