1/* Workaround for http://bugs.python.org/issue4835 */
2#ifndef SIZEOF_SOCKET_T
3#define SIZEOF_SOCKET_T SIZEOF_INT
4#endif
5
6#include <Python.h>
7#include <unistd.h>
8#include <stdlib.h>
9#include <ctype.h>
10#include <errno.h>
11#include <getopt.h>
12#include <limits.h>
13#include <sepol/sepol.h>
14#include <sepol/policydb.h>
15#include <sepol/policydb/services.h>
16#include <selinux/selinux.h>
17
18#define UNKNOWN -1
19#define BADSCON -2
20#define BADTCON -3
21#define BADTCLASS -4
22#define BADPERM -5
23#define BADCOMPUTE -6
24#define NOPOLICY -7
25#define ALLOW 0
26#define DONTAUDIT 1
27#define TERULE 2
28#define BOOLEAN 3
29#define CONSTRAINT 4
30#define RBAC 5
31#define BOUNDS 6
32
33struct boolean_t {
34	char *name;
35	int active;
36};
37
38static struct boolean_t **boollist = NULL;
39static int boolcnt = 0;
40
41struct avc_t {
42	sepol_handle_t *handle;
43	sepol_policydb_t *policydb;
44	sepol_security_id_t ssid;
45	sepol_security_id_t tsid;
46	sepol_security_class_t tclass;
47	sepol_access_vector_t av;
48};
49
50static struct avc_t *avc = NULL;
51
52static sidtab_t sidtab;
53
54static int load_booleans(const sepol_bool_t * boolean,
55			 void *arg __attribute__ ((__unused__)))
56{
57	boollist[boolcnt] = malloc(sizeof(struct boolean_t));
58	boollist[boolcnt]->name = strdup(sepol_bool_get_name(boolean));
59	boollist[boolcnt]->active = sepol_bool_get_value(boolean);
60	boolcnt++;
61	return 0;
62}
63
64static int check_booleans(struct boolean_t **bools)
65{
66	char errormsg[PATH_MAX];
67	struct sepol_av_decision avd;
68	unsigned int reason;
69	int rc;
70	int i;
71	sepol_bool_key_t *key = NULL;
72	sepol_bool_t *boolean = NULL;
73	int fcnt = 0;
74	int *foundlist = calloc(boolcnt, sizeof(int));
75	if (!foundlist) {
76		PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
77		return fcnt;
78	}
79	for (i = 0; i < boolcnt; i++) {
80		char *name = boollist[i]->name;
81		int active = boollist[i]->active;
82		rc = sepol_bool_key_create(avc->handle, name, &key);
83		if (rc < 0) {
84			PyErr_SetString( PyExc_RuntimeError,
85					 "Could not create boolean key.\n");
86			break;
87		}
88		rc = sepol_bool_query(avc->handle,
89				      avc->policydb,
90				      key, &boolean);
91
92		if (rc < 0) {
93			snprintf(errormsg, sizeof(errormsg),
94				 "Could not find boolean %s.\n", name);
95			PyErr_SetString( PyExc_RuntimeError, errormsg);
96			break;
97		}
98
99		sepol_bool_set_value(boolean, !active);
100
101		rc = sepol_bool_set(avc->handle,
102				    avc->policydb,
103				    key, boolean);
104		if (rc < 0) {
105			snprintf(errormsg, sizeof(errormsg),
106				 "Could not set boolean data %s.\n", name);
107			PyErr_SetString( PyExc_RuntimeError, errormsg);
108			break;
109		}
110
111		/* Reproduce the computation. */
112		rc = sepol_compute_av_reason(avc->ssid, avc->tsid, avc->tclass,
113					     avc->av, &avd, &reason);
114		if (rc < 0) {
115			snprintf(errormsg, sizeof(errormsg),
116				 "Error during access vector computation, skipping...");
117			PyErr_SetString( PyExc_RuntimeError, errormsg);
118
119			sepol_bool_free(boolean);
120			break;
121		} else {
122			if (!reason) {
123				foundlist[fcnt] = i;
124				fcnt++;
125			}
126			sepol_bool_set_value(boolean, active);
127			rc = sepol_bool_set(avc->handle,
128					    avc->policydb, key,
129					    boolean);
130			if (rc < 0) {
131				snprintf(errormsg, sizeof(errormsg),
132					 "Could not set boolean data %s.\n",
133					 name);
134
135				PyErr_SetString( PyExc_RuntimeError, errormsg);
136				break;
137			}
138		}
139		sepol_bool_free(boolean);
140		sepol_bool_key_free(key);
141		key = NULL;
142		boolean = NULL;
143	}
144	if (key)
145		sepol_bool_key_free(key);
146
147	if (boolean)
148		sepol_bool_free(boolean);
149
150	if (fcnt > 0) {
151		*bools = calloc(sizeof(struct boolean_t), fcnt + 1);
152		struct boolean_t *b = *bools;
153		for (i = 0; i < fcnt; i++) {
154			int ctr = foundlist[i];
155			b[i].name = strdup(boollist[ctr]->name);
156			b[i].active = !boollist[ctr]->active;
157		}
158	}
159	free(foundlist);
160	return fcnt;
161}
162
163static PyObject *finish(PyObject *self __attribute__((unused)), PyObject *args) {
164	PyObject *result = 0;
165
166	if (PyArg_ParseTuple(args,(char *)":finish")) {
167		int i = 0;
168		if (! avc)
169			Py_RETURN_NONE;
170
171		for (i = 0; i < boolcnt; i++) {
172			free(boollist[i]->name);
173			free(boollist[i]);
174		}
175		free(boollist);
176		sepol_sidtab_shutdown(&sidtab);
177		sepol_sidtab_destroy(&sidtab);
178		sepol_policydb_free(avc->policydb);
179		sepol_handle_destroy(avc->handle);
180		free(avc);
181		avc = NULL;
182		boollist = NULL;
183		boolcnt = 0;
184
185		/* Boilerplate to return "None" */
186		Py_RETURN_NONE;
187	}
188	return result;
189}
190
191
192static int __policy_init(const char *init_path)
193{
194	FILE *fp = NULL;
195	const char *curpolicy;
196	char errormsg[PATH_MAX+1024+20];
197	struct sepol_policy_file *pf = NULL;
198	int rc;
199	unsigned int cnt;
200
201	if (init_path) {
202		curpolicy = init_path;
203	} else {
204		curpolicy = selinux_current_policy_path();
205		if (!curpolicy) {
206			/* SELinux disabled, must use -p option. */
207			snprintf(errormsg, sizeof(errormsg),
208				 "You must specify the -p option with the path to the policy file.\n");
209			PyErr_SetString( PyExc_ValueError, errormsg);
210			return 1;
211		}
212	}
213
214	fp = fopen(curpolicy, "re");
215	if (!fp) {
216		snprintf(errormsg, sizeof(errormsg),
217			 "unable to open %s:  %m\n",
218			 curpolicy);
219		PyErr_SetString( PyExc_ValueError, errormsg);
220		return 1;
221	}
222
223	avc = calloc(sizeof(struct avc_t), 1);
224	if (!avc) {
225		PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
226		fclose(fp);
227		return 1;
228	}
229
230	/* Set up a policydb directly so that we can mutate it later
231	   for testing what booleans might have allowed the access.
232	   Otherwise, we'd just use sepol_set_policydb_from_file() here. */
233	if (sepol_policy_file_create(&pf) ||
234	    sepol_policydb_create(&avc->policydb)) {
235		snprintf(errormsg, sizeof(errormsg),
236			 "policydb_init failed: %m\n");
237		PyErr_SetString( PyExc_RuntimeError, errormsg);
238		goto err;
239	}
240	sepol_policy_file_set_fp(pf, fp);
241	if (sepol_policydb_read(avc->policydb, pf)) {
242		snprintf(errormsg, sizeof(errormsg),
243			 "invalid binary policy %s\n", curpolicy);
244		PyErr_SetString( PyExc_ValueError, errormsg);
245		goto err;
246	}
247	fclose(fp);
248	fp = NULL;
249	sepol_set_policydb(&avc->policydb->p);
250	avc->handle = sepol_handle_create();
251	/* Turn off messages */
252	sepol_msg_set_callback(avc->handle, NULL, NULL);
253
254	rc = sepol_bool_count(avc->handle,
255			      avc->policydb, &cnt);
256	if (rc < 0) {
257		PyErr_SetString( PyExc_RuntimeError, "unable to get bool count\n");
258		goto err;
259	}
260
261	boollist = calloc(cnt, sizeof(*boollist));
262	if (!boollist) {
263		PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
264		goto err;
265	}
266
267	sepol_bool_iterate(avc->handle, avc->policydb,
268			   load_booleans, NULL);
269
270	/* Initialize the sidtab for subsequent use by sepol_context_to_sid
271	   and sepol_compute_av_reason. */
272	rc = sepol_sidtab_init(&sidtab);
273	if (rc < 0) {
274		PyErr_SetString( PyExc_RuntimeError, "unable to init sidtab\n");
275		goto err;
276	}
277	sepol_set_sidtab(&sidtab);
278	return 0;
279
280err:
281	if (boollist)
282		free(boollist);
283	if (avc){
284		if (avc->handle)
285			sepol_handle_destroy(avc->handle);
286		if (avc->policydb)
287			sepol_policydb_free(avc->policydb);
288		free(avc);
289	}
290	if (pf)
291		sepol_policy_file_free(pf);
292	if (fp)
293		fclose(fp);
294	return 1;
295}
296
297static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) {
298  int result;
299  char *init_path = NULL;
300  if (avc) {
301	  PyErr_SetString( PyExc_RuntimeError, "init called multiple times");
302	  return NULL;
303  }
304  if (!PyArg_ParseTuple(args,(char *)"|s:policy_init",&init_path))
305    return NULL;
306  result = __policy_init(init_path);
307  return Py_BuildValue("i", result);
308}
309
310#define RETURN(X) \
311	{ \
312		return Py_BuildValue("iO", (X), Py_None);	\
313	}
314
315static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args) {
316	char *reason_buf = NULL;
317	char * scon;
318	char * tcon;
319	char *tclassstr;
320	PyObject *listObj;
321	PyObject *strObj;
322	int numlines;
323	struct boolean_t *bools;
324	unsigned int reason;
325	sepol_security_id_t ssid, tsid;
326	sepol_security_class_t tclass;
327	sepol_access_vector_t perm, av;
328	struct sepol_av_decision avd;
329	int rc;
330	int i = 0;
331
332	if (!PyArg_ParseTuple(args,(char *)"sssO!:audit2why",&scon,&tcon,&tclassstr,&PyList_Type, &listObj))
333		return NULL;
334
335	/* get the number of lines passed to us */
336	numlines = PyList_Size(listObj);
337
338	/* should raise an error here. */
339	if (numlines < 0)	return NULL; /* Not a list */
340
341	if (!avc)
342		RETURN(NOPOLICY)
343
344	rc = sepol_context_to_sid(scon, strlen(scon) + 1, &ssid);
345	if (rc < 0)
346		RETURN(BADSCON)
347
348	rc = sepol_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
349	if (rc < 0)
350		RETURN(BADTCON)
351
352	rc = sepol_string_to_security_class(tclassstr, &tclass);
353	if (rc < 0)
354		RETURN(BADTCLASS)
355
356	/* Convert the permission list to an AV. */
357	av = 0;
358
359	/* iterate over items of the list, grabbing strings, and parsing
360	   for numbers */
361	for (i = 0; i < numlines; i++){
362		const char *permstr;
363
364		/* grab the string object from the next element of the list */
365		strObj = PyList_GetItem(listObj, i); /* Can't fail */
366
367		/* make it a string */
368#if PY_MAJOR_VERSION >= 3
369		permstr = _PyUnicode_AsString( strObj );
370#else
371		permstr = PyString_AsString( strObj );
372#endif
373
374		rc = sepol_string_to_av_perm(tclass, permstr, &perm);
375		if (rc < 0)
376			RETURN(BADPERM)
377
378		av |= perm;
379	}
380
381	/* Reproduce the computation. */
382	rc = sepol_compute_av_reason_buffer(ssid, tsid, tclass, av, &avd, &reason, &reason_buf, 0);
383	if (rc < 0)
384		RETURN(BADCOMPUTE)
385
386	if (!reason)
387		RETURN(ALLOW)
388
389	if (reason & SEPOL_COMPUTEAV_TE) {
390		avc->ssid = ssid;
391		avc->tsid = tsid;
392		avc->tclass = tclass;
393		avc->av = av;
394		if (check_booleans(&bools) == 0) {
395			if (av & ~avd.auditdeny) {
396				RETURN(DONTAUDIT)
397			} else {
398				RETURN(TERULE)
399			}
400		} else {
401			PyObject *outboollist;
402			struct boolean_t *b = bools;
403			int len = 0;
404			while (b->name) {
405				len++; b++;
406			}
407			b = bools;
408			outboollist = PyList_New(len);
409			len = 0;
410			while(b->name) {
411				PyObject *bool_ = Py_BuildValue("(si)", b->name, b->active);
412				PyList_SetItem(outboollist, len++, bool_);
413				b++;
414			}
415			free(bools);
416			/* 'N' steals the reference to outboollist */
417			return Py_BuildValue("iN", BOOLEAN, outboollist);
418		}
419	}
420
421	if (reason & SEPOL_COMPUTEAV_CONS) {
422		if (reason_buf) {
423			PyObject *result = NULL;
424			result = Py_BuildValue("is", CONSTRAINT, reason_buf);
425			free(reason_buf);
426			return result;
427		}
428		RETURN(CONSTRAINT)
429	}
430
431	if (reason & SEPOL_COMPUTEAV_RBAC)
432		RETURN(RBAC)
433
434	if (reason & SEPOL_COMPUTEAV_BOUNDS)
435		RETURN(BOUNDS)
436
437        RETURN(BADCOMPUTE)
438}
439
440static PyMethodDef audit2whyMethods[] = {
441    {"init",  init, METH_VARARGS,
442     "Initialize policy database."},
443    {"analyze",  analyze, METH_VARARGS,
444     "Analyze AVC."},
445    {"finish",  finish, METH_VARARGS,
446     "Finish using policy, free memory."},
447    {NULL, NULL, 0, NULL}        /* Sentinel */
448};
449
450#if PY_MAJOR_VERSION >= 3
451/* Module-initialization logic specific to Python 3 */
452static struct PyModuleDef moduledef = {
453	PyModuleDef_HEAD_INIT,
454	"audit2why",
455	NULL,
456	0,
457	audit2whyMethods,
458	NULL,
459	NULL,
460	NULL,
461	NULL
462};
463
464PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */
465PyMODINIT_FUNC PyInit_audit2why(void)
466#else
467PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */
468PyMODINIT_FUNC initaudit2why(void)
469#endif
470{
471	PyObject *m;
472#if PY_MAJOR_VERSION >= 3
473	m = PyModule_Create(&moduledef);
474	if (m == NULL) {
475		return NULL;
476	}
477#else
478	m  = Py_InitModule("audit2why", audit2whyMethods);
479#endif
480	PyModule_AddIntConstant(m,"UNKNOWN", UNKNOWN);
481	PyModule_AddIntConstant(m,"BADSCON", BADSCON);
482	PyModule_AddIntConstant(m,"BADTCON", BADTCON);
483	PyModule_AddIntConstant(m,"BADTCLASS", BADTCLASS);
484	PyModule_AddIntConstant(m,"BADPERM", BADPERM);
485	PyModule_AddIntConstant(m,"BADCOMPUTE", BADCOMPUTE);
486	PyModule_AddIntConstant(m,"NOPOLICY", NOPOLICY);
487	PyModule_AddIntConstant(m,"ALLOW", ALLOW);
488	PyModule_AddIntConstant(m,"DONTAUDIT", DONTAUDIT);
489	PyModule_AddIntConstant(m,"TERULE", TERULE);
490	PyModule_AddIntConstant(m,"BOOLEAN", BOOLEAN);
491	PyModule_AddIntConstant(m,"CONSTRAINT", CONSTRAINT);
492	PyModule_AddIntConstant(m,"RBAC", RBAC);
493	PyModule_AddIntConstant(m,"BOUNDS", BOUNDS);
494
495#if PY_MAJOR_VERSION >= 3
496	return m;
497#endif
498}
499