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	const unsigned int CCOUNT = sizeof(components) / sizeof(components[0]);
142
143	/* Merge components into policy (and validate) */
144	for (i = 0; i < CCOUNT; i++) {
145
146		record_t **records = NULL;
147		unsigned int nrecords = 0;
148
149		dbase_config_t *src = components[i].src;
150		dbase_config_t *dst = components[i].dst;
151		int mode = components[i].mode;
152		record_table_t *rtable = src->dtable->get_rtable(src->dbase);
153
154		/* Must invoke cache function first */
155		if (src->dtable->cache(handle, src->dbase) < 0)
156			goto err;
157		if (dst->dtable->cache(handle, dst->dbase) < 0)
158			goto err;
159
160		/* List all records */
161		if (src->dtable->list(handle, src->dbase,
162				      &records, &nrecords) < 0)
163			goto err;
164
165		/* Sort records on MODE_SORT */
166		if (mode & MODE_SORT) {
167			qsort(records, nrecords, sizeof(record_t *),
168			      (int (*)(const void *, const void *))rtable->
169			      compare2_qsort);
170		}
171
172		/* Clear obsolete ones for MODE_SET */
173		if (mode & MODE_SET &&
174		    clear_obsolete(handle, records, nrecords, src, dst) < 0) {
175			rc = STATUS_ERR;
176			goto dbase_exit;
177		}
178
179		/* Load records */
180		if (load_records(handle, dst, records, nrecords, mode) < 0) {
181
182			rc = STATUS_ERR;
183			goto dbase_exit;
184		}
185
186		/* Cleanup */
187	      dbase_exit:
188		for (j = 0; j < nrecords; j++)
189			rtable->free(records[j]);
190		free(records);
191
192		/* Abort on error */
193		if (rc < 0)
194			goto err;
195	}
196
197	return rc;
198
199      err:
200	ERR(handle, "could not merge local modifications into policy");
201	return STATUS_ERR;
202}
203
204int semanage_commit_components(semanage_handle_t * handle)
205{
206
207	int i;
208	dbase_config_t *components[] = {
209		semanage_iface_dbase_local(handle),
210		semanage_bool_dbase_local(handle),
211		semanage_user_base_dbase_local(handle),
212		semanage_user_extra_dbase_local(handle),
213		semanage_user_extra_dbase_policy(handle),
214		semanage_port_dbase_local(handle),
215		semanage_fcontext_dbase_local(handle),
216		semanage_fcontext_dbase_policy(handle),
217		semanage_seuser_dbase_local(handle),
218		semanage_seuser_dbase_policy(handle),
219		semanage_bool_dbase_active(handle),
220		semanage_node_dbase_local(handle),
221	};
222	const int CCOUNT = sizeof(components) / sizeof(components[0]);
223
224	for (i = 0; i < CCOUNT; i++) {
225		/* Flush to disk */
226		if (components[i]->dtable->flush(handle, components[i]->dbase) <
227		    0)
228			goto err;
229	}
230
231	return STATUS_SUCCESS;
232
233      err:
234	ERR(handle, "could not commit local/active modifications");
235
236	for (i = 0; i < CCOUNT; i++)
237		components[i]->dtable->drop_cache(components[i]->dbase);
238	return STATUS_ERR;
239}
240