xref: /aosp_15_r20/external/selinux/libselinux/src/label_db.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1 /*
2  * Media contexts backend for DB objects
3  *
4  * Author: KaiGai Kohei <[email protected]>
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  */
71 typedef 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  */
81 typedef 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  */
90 static int
process_line(const char * path,char * line_buf,unsigned int line_num,catalog_t * catalog)91 process_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 
162 skip:
163 	free(type);
164 	free(key);
165 	free(context);
166 	free(temp);
167 
168 	return 0;
169 }
170 
171 /*
172  * selabel_close() handler
173  */
174 static void
db_close(struct selabel_handle * rec)175 db_close(struct selabel_handle *rec)
176 {
177 	catalog_t      *catalog = (catalog_t *)rec->data;
178 	spec_t	       *spec;
179 	unsigned int	i;
180 
181 	if (!catalog)
182 		return;
183 
184 	for (i = 0; i < catalog->nspec; i++) {
185 		spec = &catalog->specs[i];
186 		free(spec->key);
187 		free(spec->lr.ctx_raw);
188 		free(spec->lr.ctx_trans);
189 	}
190 	free(catalog);
191 }
192 
193 /*
194  * selabel_lookup() handler
195  */
196 static struct selabel_lookup_rec *
db_lookup(struct selabel_handle * rec,const char * key,int type)197 db_lookup(struct selabel_handle *rec, const char *key, int type)
198 {
199 	catalog_t      *catalog = (catalog_t *)rec->data;
200 	spec_t	       *spec;
201 	unsigned int	i;
202 
203 	for (i = 0; i < catalog->nspec; i++) {
204 		spec = &catalog->specs[i];
205 
206 		if (spec->type != type)
207 			continue;
208 		if (!fnmatch(spec->key, key, 0)) {
209 			spec->matches++;
210 
211 			return &spec->lr;
212 		}
213 	}
214 
215 	/* No found */
216 	errno = ENOENT;
217 	return NULL;
218 }
219 
220 /*
221  * selabel_stats() handler
222  */
223 static void
db_stats(struct selabel_handle * rec)224 db_stats(struct selabel_handle *rec)
225 {
226 	catalog_t      *catalog = (catalog_t *)rec->data;
227 	unsigned int	i, total = 0;
228 
229 	for (i = 0; i < catalog->nspec; i++)
230 		total += catalog->specs[i].matches;
231 
232 	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
233 		    catalog->nspec, total);
234 }
235 
236 /*
237  * selabel_open() handler
238  */
239 static catalog_t *
db_init(const struct selinux_opt * opts,unsigned nopts,struct selabel_handle * rec)240 db_init(const struct selinux_opt *opts, unsigned nopts,
241 			    struct selabel_handle *rec)
242 {
243 	catalog_t      *catalog;
244 	FILE	       *filp;
245 	const char     *path = NULL;
246 	char	       *line_buf = NULL;
247 	size_t		line_len = 0;
248 	unsigned int	line_num = 0;
249 	unsigned int	i;
250 	struct stat sb;
251 
252 	/*
253 	 * Initialize catalog data structure
254 	 */
255 	catalog = malloc(sizeof(catalog_t) + 32 * sizeof(spec_t));
256 	if (!catalog)
257 		return NULL;
258 	catalog->limit = 32;
259 	catalog->nspec = 0;
260 
261 	/*
262 	 * Process arguments
263 	 *
264 	 * SELABEL_OPT_PATH:
265 	 *   It allows to specify an alternative specification file instead of
266 	 *   the default one. If RDBMS is not SE-PostgreSQL, it may need to
267 	 *   specify an explicit specfile for database objects.
268 	 */
269 	while (nopts) {
270 		nopts--;
271 		switch (opts[nopts].type) {
272 		case SELABEL_OPT_PATH:
273 			path = opts[nopts].value;
274 			break;
275 		case SELABEL_OPT_UNUSED:
276 		case SELABEL_OPT_VALIDATE:
277 		case SELABEL_OPT_DIGEST:
278 			break;
279 		default:
280 			free(catalog);
281 			errno = EINVAL;
282 			return NULL;
283 		}
284 	}
285 
286 	/*
287 	 * Open the specification file
288 	 */
289 	if (!path)
290 		path = selinux_sepgsql_context_path();
291 
292 	if ((filp = fopen(path, "re")) == NULL) {
293 		free(catalog);
294 		return NULL;
295 	}
296 	if (fstat(fileno(filp), &sb) < 0) {
297 		free(catalog);
298 		fclose(filp);
299 		return NULL;
300 	}
301 	if (!S_ISREG(sb.st_mode)) {
302 		free(catalog);
303 		fclose(filp);
304 		errno = EINVAL;
305 		return NULL;
306 	}
307 	rec->spec_file = strdup(path);
308 	if (!rec->spec_file) {
309                 free(catalog);
310                 fclose(filp);
311                 return NULL;
312 	}
313 
314 	/*
315 	 * Parse for each lines
316 	 */
317 	while (getline(&line_buf, &line_len, filp) > 0) {
318 		/*
319 		 * Expand catalog array, if necessary
320 		 */
321 		if (catalog->limit == catalog->nspec) {
322 			size_t		length;
323 			unsigned int	new_limit = 2 * catalog->limit;
324 			catalog_t      *new_catalog;
325 
326 			length = sizeof(catalog_t)
327 				+ new_limit * sizeof(spec_t);
328 			new_catalog = realloc(catalog, length);
329 			if (!new_catalog)
330 				goto out_error;
331 
332 			catalog = new_catalog;
333 			catalog->limit = new_limit;
334 		}
335 
336 		/*
337 		 * Parse a line
338 		 */
339 		if (process_line(path, line_buf, ++line_num, catalog) < 0)
340 			goto out_error;
341 	}
342 
343 	if (digest_add_specfile(rec->digest, filp, NULL, sb.st_size, path) < 0)
344 		goto out_error;
345 
346 	digest_gen_hash(rec->digest);
347 
348 	free(line_buf);
349 	fclose(filp);
350 
351 	return catalog;
352 
353 out_error:
354 	free(line_buf);
355 	for (i = 0; i < catalog->nspec; i++) {
356 		spec_t	       *spec = &catalog->specs[i];
357 
358 		free(spec->key);
359 		free(spec->lr.ctx_raw);
360 		free(spec->lr.ctx_trans);
361 	}
362 	free(catalog);
363 	fclose(filp);
364 
365 	return NULL;
366 }
367 
368 /*
369  * Initialize selabel_handle and load the entries of specfile
370  */
selabel_db_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)371 int selabel_db_init(struct selabel_handle *rec,
372 		    const struct selinux_opt *opts, unsigned nopts)
373 {
374 	rec->func_close = &db_close;
375 	rec->func_lookup = &db_lookup;
376 	rec->func_stats = &db_stats;
377 	rec->data = db_init(opts, nopts, rec);
378 
379 	return !rec->data ? -1 : 0;
380 }
381