1/* Copyright (C) 2005 Red Hat, Inc. */
2
3#include <stdlib.h>
4#include "policy.h"
5#include "handle.h"
6#include "database.h"
7#include "modules.h"
8#include "debug.h"
9
10/* Powers of two only */
11#define MODE_SET    1
12#define MODE_MODIFY 2
13#define MODE_SORT   4
14
15static int clear_obsolete(semanage_handle_t * handle,
16			  record_t ** records,
17			  unsigned int nrecords,
18			  dbase_config_t * src, dbase_config_t * dst)
19{
20
21	record_key_t *key = NULL;
22	unsigned int i;
23
24	dbase_table_t *src_dtable = src->dtable;
25	dbase_table_t *dst_dtable = dst->dtable;
26	record_table_t *rtable = src_dtable->get_rtable(src->dbase);
27
28	for (i = 0; i < nrecords; i++) {
29		int exists;
30
31		if (rtable->key_extract(handle, records[i], &key) < 0)
32			goto err;
33
34		if (dst_dtable->exists(handle, dst->dbase, key, &exists) < 0)
35			goto err;
36
37		if (!exists) {
38			if (src_dtable->del(handle, src->dbase, key) < 0)
39				goto err;
40
41			rtable->free(records[i]);
42			records[i] = NULL;
43
44			/* FIXME: notice to user */
45			/* INFO(handle, "boolean %s is obsolete, unsetting configured value..."); */
46		}
47
48		rtable->key_free(key);
49	}
50
51	return STATUS_SUCCESS;
52
53      err:
54	/* FIXME: handle error */
55	rtable->key_free(key);
56	return STATUS_ERR;
57}
58
59static int load_records(semanage_handle_t * handle,
60			dbase_config_t * dst,
61			record_t ** records, unsigned int nrecords, int mode)
62{
63
64	unsigned int i;
65	record_key_t *rkey = NULL;
66
67	dbase_t *dbase = dst->dbase;
68	dbase_table_t *dtable = dst->dtable;
69	record_table_t *rtable = dtable->get_rtable(dbase);
70
71	for (i = 0; i < nrecords; i++) {
72
73		/* Possibly obsoleted */
74		if (!records[i])
75			continue;
76
77		if (rtable->key_extract(handle, records[i], &rkey) < 0)
78			goto err;
79
80		if (mode & MODE_SET &&
81		    dtable->set(handle, dbase, rkey, records[i]) < 0)
82			goto err;
83
84		else if (mode & MODE_MODIFY &&
85			 dtable->modify(handle, dbase, rkey, records[i]) < 0)
86			goto err;
87
88		rtable->key_free(rkey);
89	}
90
91	return STATUS_SUCCESS;
92
93      err:
94	/* FIXME: handle error */
95	rtable->key_free(rkey);
96	return STATUS_ERR;
97}
98
99typedef struct load_table {
100	dbase_config_t *src;
101	dbase_config_t *dst;
102	int mode;
103} load_table_t;
104
105/* This function must be called AFTER all modules are loaded.
106 * Modules could be represented as a database, in which case
107 * they should be loaded at the beginning of this function */
108
109int semanage_base_merge_components(semanage_handle_t * handle)
110{
111
112	unsigned int i, j;
113	int rc = STATUS_SUCCESS;
114
115	/* Order is important here - change things carefully.
116	 * System components first, local next. Verify runs with
117	 * mutual dependencies are ran after everything is merged */
118	load_table_t components[] = {
119
120		{semanage_user_base_dbase_local(handle),
121		 semanage_user_base_dbase_policy(handle), MODE_MODIFY},
122
123		{semanage_user_extra_dbase_local(handle),
124		 semanage_user_extra_dbase_policy(handle), MODE_MODIFY},
125
126		{semanage_port_dbase_local(handle),
127		 semanage_port_dbase_policy(handle), MODE_MODIFY},
128
129		{semanage_iface_dbase_local(handle),
130		 semanage_iface_dbase_policy(handle), MODE_MODIFY},
131
132		{semanage_bool_dbase_local(handle),
133		 semanage_bool_dbase_policy(handle), MODE_SET},
134
135		{semanage_seuser_dbase_local(handle),
136		 semanage_seuser_dbase_policy(handle), MODE_MODIFY},
137
138		{semanage_node_dbase_local(handle),
139		 semanage_node_dbase_policy(handle), MODE_MODIFY | MODE_SORT},
140
141		{semanage_ibpkey_dbase_local(handle),
142		 semanage_ibpkey_dbase_policy(handle), MODE_MODIFY},
143
144		{semanage_ibendport_dbase_local(handle),
145		 semanage_ibendport_dbase_policy(handle), MODE_MODIFY},
146	};
147	const unsigned int CCOUNT = sizeof(components) / sizeof(components[0]);
148
149	/* Merge components into policy (and validate) */
150	for (i = 0; i < CCOUNT; i++) {
151		record_t **records = NULL;
152		unsigned int nrecords = 0;
153
154		dbase_config_t *src = components[i].src;
155		dbase_config_t *dst = components[i].dst;
156		int mode = components[i].mode;
157		record_table_t *rtable = src->dtable->get_rtable(src->dbase);
158
159		/* Must invoke cache function first */
160		if (src->dtable->cache(handle, src->dbase) < 0)
161			goto err;
162		if (dst->dtable->cache(handle, dst->dbase) < 0)
163			goto err;
164
165		/* List all records */
166		if (src->dtable->list(handle, src->dbase,
167				      &records, &nrecords) < 0)
168			goto err;
169
170		/* Sort records on MODE_SORT */
171		if (mode & MODE_SORT) {
172			qsort(records, nrecords, sizeof(record_t *),
173			      (int (*)(const void *, const void *))rtable->
174			      compare2_qsort);
175		}
176
177		/* Clear obsolete ones for MODE_SET */
178		if (mode & MODE_SET &&
179		    clear_obsolete(handle, records, nrecords, src, dst) < 0) {
180			rc = STATUS_ERR;
181			goto dbase_exit;
182		}
183
184		/* Load records */
185		if (load_records(handle, dst, records, nrecords, mode) < 0) {
186
187			rc = STATUS_ERR;
188			goto dbase_exit;
189		}
190
191		/* Cleanup */
192	      dbase_exit:
193		for (j = 0; j < nrecords; j++)
194			rtable->free(records[j]);
195		free(records);
196
197		/* Abort on error */
198		if (rc < 0)
199			goto err;
200	}
201
202	return rc;
203
204      err:
205	ERR(handle, "could not merge local modifications into policy");
206	return STATUS_ERR;
207}
208
209int semanage_commit_components(semanage_handle_t * handle)
210{
211
212	int i;
213	dbase_config_t *components[] = {
214		semanage_iface_dbase_local(handle),
215		semanage_bool_dbase_local(handle),
216		semanage_user_base_dbase_local(handle),
217		semanage_user_extra_dbase_local(handle),
218		semanage_user_extra_dbase_policy(handle),
219		semanage_port_dbase_local(handle),
220		semanage_fcontext_dbase_local(handle),
221		semanage_fcontext_dbase_policy(handle),
222		semanage_seuser_dbase_local(handle),
223		semanage_seuser_dbase_policy(handle),
224		semanage_bool_dbase_active(handle),
225		semanage_node_dbase_local(handle),
226		semanage_ibpkey_dbase_local(handle),
227		semanage_ibendport_dbase_local(handle),
228	};
229	const int CCOUNT = sizeof(components) / sizeof(components[0]);
230
231	for (i = 0; i < CCOUNT; i++) {
232		/* Flush to disk */
233		if (components[i]->dtable->flush(handle, components[i]->dbase) <
234		    0)
235			goto err;
236	}
237
238	return STATUS_SUCCESS;
239
240      err:
241	ERR(handle, "could not commit local/active modifications");
242
243	for (i = 0; i < CCOUNT; i++)
244		components[i]->dtable->drop_cache(components[i]->dbase);
245	return STATUS_ERR;
246}
247