1/* Copyright (C) 2005 Red Hat, Inc. */
2
3/* Object: dbase_policydb_t (Policy)
4 * Implements: dbase_t (Database)
5 */
6
7struct dbase_policydb;
8typedef struct dbase_policydb dbase_t;
9#define DBASE_DEFINED
10
11#include <stdlib.h>
12#include <stddef.h>
13#include <string.h>
14#include <stdio.h>
15#include <stdio_ext.h>
16#include <errno.h>
17
18#include <sepol/policydb.h>
19
20#include "database_policydb.h"
21#include "semanage_store.h"
22#include "handle.h"
23#include "debug.h"
24
25/* POLICYDB dbase */
26struct dbase_policydb {
27
28        /* Backing path for read-only[0] and transaction[1] */
29        const char *path[2];
30
31	/* Base record table */
32	record_table_t *rtable;
33
34	/* Policy extensions */
35	record_policydb_table_t *rptable;
36
37	sepol_policydb_t *policydb;
38
39	int cache_serial;
40	int modified;
41	int attached;
42};
43
44static void dbase_policydb_drop_cache(dbase_policydb_t * dbase)
45{
46
47	if (dbase->cache_serial >= 0) {
48		sepol_policydb_free(dbase->policydb);
49		dbase->cache_serial = -1;
50		dbase->modified = 0;
51	}
52}
53
54static int dbase_policydb_set_serial(semanage_handle_t * handle,
55				     dbase_policydb_t * dbase)
56{
57
58	int cache_serial = handle->funcs->get_serial(handle);
59	if (cache_serial < 0) {
60		ERR(handle, "could not update cache serial");
61		return STATUS_ERR;
62	}
63
64	dbase->cache_serial = cache_serial;
65	return STATUS_SUCCESS;
66}
67
68static int dbase_policydb_needs_resync(semanage_handle_t * handle,
69				       dbase_policydb_t * dbase)
70{
71
72	int cache_serial;
73
74	if (dbase->cache_serial < 0)
75		return 1;
76
77	cache_serial = handle->funcs->get_serial(handle);
78	if (cache_serial < 0)
79		return 1;
80
81	if (cache_serial != dbase->cache_serial) {
82		dbase_policydb_drop_cache(dbase);
83		dbase->cache_serial = -1;
84		return 1;
85	}
86	return 0;
87}
88
89static int dbase_policydb_cache(semanage_handle_t * handle,
90				dbase_policydb_t * dbase)
91{
92
93	FILE *fp = NULL;
94	sepol_policydb_t *policydb = NULL;
95	sepol_policy_file_t *pf = NULL;
96	const char *fname = NULL;
97
98	/* Check if cache is needed */
99	if (dbase->attached)
100		return STATUS_SUCCESS;
101
102	if (!dbase_policydb_needs_resync(handle, dbase))
103		return STATUS_SUCCESS;
104
105	fname = dbase->path[handle->is_in_transaction];
106
107	if (sepol_policydb_create(&policydb) < 0) {
108		ERR(handle, "could not create policydb object");
109		goto err;
110	}
111
112	/* Try opening file
113	 * ENOENT is not fatal - we just create an empty policydb */
114	fp = fopen(fname, "rb");
115	if (fp == NULL && errno != ENOENT) {
116		ERR(handle, "could not open %s for reading: %s",
117		    fname, strerror(errno));
118		goto err;
119	}
120
121	/* If the file was opened successfully, read a policydb */
122	if (fp != NULL) {
123		__fsetlocking(fp, FSETLOCKING_BYCALLER);
124		if (sepol_policy_file_create(&pf) < 0) {
125			ERR(handle, "could not create policy file object");
126			goto err;
127		}
128
129		sepol_policy_file_set_fp(pf, fp);
130		sepol_policy_file_set_handle(pf, handle->sepolh);
131
132		if (sepol_policydb_read(policydb, pf) < 0)
133			goto err;
134
135		sepol_policy_file_free(pf);
136		fclose(fp);
137		fp = NULL;
138	}
139
140	/* Update cache serial */
141	if (dbase_policydb_set_serial(handle, dbase) < 0)
142		goto err;
143
144	/* Update the database policydb */
145	dbase->policydb = policydb;
146	return STATUS_SUCCESS;
147
148      err:
149	ERR(handle, "could not cache policy database");
150	if (fp)
151		fclose(fp);
152	sepol_policydb_free(policydb);
153	sepol_policy_file_free(pf);
154	return STATUS_ERR;
155}
156
157static int dbase_policydb_flush(semanage_handle_t * handle
158				__attribute__ ((unused)),
159				dbase_policydb_t * dbase)
160{
161
162	if (!dbase->modified)
163		return STATUS_SUCCESS;
164
165	dbase->modified = 0;
166
167	/* Stub */
168	return STATUS_ERR;
169}
170
171/* Check if modified */
172static int dbase_policydb_is_modified(dbase_policydb_t * dbase)
173{
174
175	return dbase->modified;
176}
177
178int dbase_policydb_init(semanage_handle_t * handle,
179			const char *path_ro,
180			const char *path_rw,
181			record_table_t * rtable,
182			record_policydb_table_t * rptable,
183			dbase_policydb_t ** dbase)
184{
185
186	dbase_policydb_t *tmp_dbase =
187	    (dbase_policydb_t *) malloc(sizeof(dbase_policydb_t));
188
189	if (!tmp_dbase)
190		goto omem;
191
192	tmp_dbase->path[0] = path_ro;
193	tmp_dbase->path[1] = path_rw;
194	tmp_dbase->rtable = rtable;
195	tmp_dbase->rptable = rptable;
196	tmp_dbase->policydb = NULL;
197	tmp_dbase->cache_serial = -1;
198	tmp_dbase->modified = 0;
199	tmp_dbase->attached = 0;
200	*dbase = tmp_dbase;
201
202	return STATUS_SUCCESS;
203
204      omem:
205	ERR(handle, "out of memory, could not initialize policy database");
206	free(tmp_dbase);
207
208	return STATUS_ERR;
209}
210
211/* Release dbase resources */
212void dbase_policydb_release(dbase_policydb_t * dbase)
213{
214
215	dbase_policydb_drop_cache(dbase);
216	free(dbase);
217}
218
219/* Attach to a shared policydb.
220 * This implies drop_cache(),
221 * and prevents flush() and drop_cache()
222 * until detached. */
223void dbase_policydb_attach(dbase_policydb_t * dbase,
224			   sepol_policydb_t * policydb)
225{
226
227	dbase->attached = 1;
228	dbase_policydb_drop_cache(dbase);
229	dbase->policydb = policydb;
230}
231
232/* Detach from a shared policdb.
233 * This implies drop_cache. */
234void dbase_policydb_detach(dbase_policydb_t * dbase)
235{
236
237	dbase->attached = 0;
238	dbase->modified = 0;
239}
240
241static int dbase_policydb_add(semanage_handle_t * handle,
242			      dbase_policydb_t * dbase,
243			      const record_key_t * key, const record_t * data)
244{
245
246	if (dbase->rptable->add(handle->sepolh, dbase->policydb, key, data) < 0)
247		goto err;
248
249	dbase->modified = 1;
250	return STATUS_SUCCESS;
251
252      err:
253	ERR(handle, "could not add record to the database");
254	return STATUS_ERR;
255}
256
257static int dbase_policydb_set(semanage_handle_t * handle,
258			      dbase_policydb_t * dbase,
259			      const record_key_t * key, const record_t * data)
260{
261
262	if (dbase->rptable->set(handle->sepolh, dbase->policydb, key, data) < 0)
263		goto err;
264
265	dbase->modified = 1;
266	return STATUS_SUCCESS;
267
268      err:
269	ERR(handle, "could not set record value");
270	return STATUS_ERR;
271}
272
273static int dbase_policydb_modify(semanage_handle_t * handle,
274				 dbase_policydb_t * dbase,
275				 const record_key_t * key,
276				 const record_t * data)
277{
278
279	if (dbase->rptable->modify(handle->sepolh,
280				   dbase->policydb, key, data) < 0)
281		goto err;
282
283	dbase->modified = 1;
284	return STATUS_SUCCESS;
285
286      err:
287	ERR(handle, "could not modify record value");
288	return STATUS_ERR;
289}
290
291static int dbase_policydb_del(semanage_handle_t * handle
292				__attribute__ ((unused)),
293			      dbase_policydb_t * dbase
294				__attribute__ ((unused)),
295			      const record_key_t * key
296				__attribute__ ((unused)))
297{
298
299	/* Stub */
300	return STATUS_ERR;
301}
302
303static int dbase_policydb_clear(semanage_handle_t * handle
304				__attribute__ ((unused)),
305				dbase_policydb_t * dbase
306				__attribute__ ((unused)))
307{
308
309	/* Stub */
310	return STATUS_ERR;
311}
312
313static int dbase_policydb_query(semanage_handle_t * handle,
314				dbase_policydb_t * dbase,
315				const record_key_t * key, record_t ** response)
316{
317
318	if (dbase->rptable->query(handle->sepolh,
319				  dbase->policydb, key, response) < 0)
320		goto err;
321
322	return STATUS_SUCCESS;
323
324      err:
325	ERR(handle, "could not query record value");
326	return STATUS_ERR;
327}
328
329static int dbase_policydb_exists(semanage_handle_t * handle,
330				 dbase_policydb_t * dbase,
331				 const record_key_t * key, int *response)
332{
333
334	if (dbase->rptable->exists(handle->sepolh,
335				   dbase->policydb, key, response) < 0)
336		goto err;
337
338	return STATUS_SUCCESS;
339
340      err:
341	ERR(handle, "could not check if record exists");
342	return STATUS_ERR;
343}
344
345static int dbase_policydb_count(semanage_handle_t * handle,
346				dbase_policydb_t * dbase,
347				unsigned int *response)
348{
349
350	if (dbase->rptable->count(handle->sepolh,
351				  dbase->policydb, response) < 0)
352		goto err;
353
354	return STATUS_SUCCESS;
355
356      err:
357	ERR(handle, "could not count the database records");
358	return STATUS_ERR;
359}
360
361static int dbase_policydb_iterate(semanage_handle_t * handle,
362				  dbase_policydb_t * dbase,
363				  int (*fn) (const record_t * record,
364					     void *fn_arg), void *arg)
365{
366
367	if (dbase->rptable->iterate(handle->sepolh,
368				    dbase->policydb, fn, arg) < 0)
369		goto err;
370
371	return STATUS_SUCCESS;
372
373      err:
374	ERR(handle, "could not iterate over records");
375	return STATUS_ERR;
376}
377
378struct list_handler_arg {
379	semanage_handle_t *handle;
380	record_table_t *rtable;
381	record_t **records;
382	int pos;
383};
384
385static int list_handler(const record_t * record, void *varg)
386{
387
388	struct list_handler_arg *arg = (struct list_handler_arg *)varg;
389
390	if (arg->rtable->clone(arg->handle, record, &arg->records[arg->pos]) <
391	    0)
392		return -1;
393	arg->pos++;
394	return 0;
395}
396
397static int dbase_policydb_list(semanage_handle_t * handle,
398			       dbase_t * dbase,
399			       record_t *** records, unsigned int *count)
400{
401
402	record_t **tmp_records = NULL;
403	unsigned int tmp_count;
404	struct list_handler_arg list_arg;
405	list_arg.pos = 0;
406	list_arg.rtable = dbase->rtable;
407	list_arg.handle = handle;
408
409	if (dbase->rptable->count(handle->sepolh,
410				  dbase->policydb, &tmp_count) < 0)
411		goto err;
412
413	if (tmp_count > 0) {
414		tmp_records = (record_t **)
415		    calloc(tmp_count, sizeof(record_t *));
416
417		if (tmp_records == NULL)
418			goto omem;
419
420		list_arg.records = tmp_records;
421
422		if (dbase->rptable->iterate(handle->sepolh,
423					    dbase->policydb, list_handler,
424					    &list_arg) < 0) {
425			ERR(handle, "list handler could not extract record");
426			goto err;
427		}
428	}
429
430	*records = tmp_records;
431	*count = tmp_count;
432	return STATUS_SUCCESS;
433
434      omem:
435	ERR(handle, "out of memory");
436
437      err:
438	if (tmp_records) {
439		for (; list_arg.pos >= 0; list_arg.pos--)
440			dbase->rtable->free(tmp_records[list_arg.pos]);
441		free(tmp_records);
442	}
443	ERR(handle, "could not list records");
444	return STATUS_ERR;
445}
446
447static record_table_t *dbase_policydb_get_rtable(dbase_policydb_t * dbase)
448{
449
450	return dbase->rtable;
451}
452
453/* POLICYDB dbase - method table implementation */
454dbase_table_t SEMANAGE_POLICYDB_DTABLE = {
455
456	/* Cache/Transactions */
457	.cache = dbase_policydb_cache,
458	.drop_cache = dbase_policydb_drop_cache,
459	.flush = dbase_policydb_flush,
460	.is_modified = dbase_policydb_is_modified,
461
462	/* Database Functionality */
463	.iterate = dbase_policydb_iterate,
464	.exists = dbase_policydb_exists,
465	.list = dbase_policydb_list,
466	.add = dbase_policydb_add,
467	.set = dbase_policydb_set,
468	.del = dbase_policydb_del,
469	.clear = dbase_policydb_clear,
470	.modify = dbase_policydb_modify,
471	.query = dbase_policydb_query,
472	.count = dbase_policydb_count,
473
474	/* Polymorphism */
475	.get_rtable = dbase_policydb_get_rtable
476};
477