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