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