audit2why.c revision 56258807ea4b33cf3c7a1dbf1b574ab77c91f899
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	security_context_t scon;
303	security_context_t tcon;
304	char *tclassstr;
305	PyObject *listObj;
306	PyObject *strObj;
307	int numlines;
308	struct boolean_t *bools;
309	unsigned int reason;
310	sepol_security_id_t ssid, tsid;
311	sepol_security_class_t tclass;
312	sepol_access_vector_t perm, av;
313	struct sepol_av_decision avd;
314	int rc;
315	int i=0;
316
317	if (!PyArg_ParseTuple(args,(char *)"sssO!:audit2why",&scon,&tcon,&tclassstr,&PyList_Type, &listObj))
318		return NULL;
319
320	/* get the number of lines passed to us */
321	numlines = PyList_Size(listObj);
322
323	/* should raise an error here. */
324	if (numlines < 0)	return NULL; /* Not a list */
325
326	if (!avc)
327		RETURN(NOPOLICY)
328
329	rc = sepol_context_to_sid(scon, strlen(scon) + 1, &ssid);
330	if (rc < 0)
331		RETURN(BADSCON)
332
333	rc = sepol_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
334	if (rc < 0)
335		RETURN(BADTCON)
336
337	tclass = string_to_security_class(tclassstr);
338	if (!tclass)
339		RETURN(BADTCLASS)
340
341	/* Convert the permission list to an AV. */
342	av = 0;
343
344	/* iterate over items of the list, grabbing strings, and parsing
345	   for numbers */
346	for (i=0; i<numlines; i++){
347		char *permstr;
348
349		/* grab the string object from the next element of the list */
350		strObj = PyList_GetItem(listObj, i); /* Can't fail */
351
352		/* make it a string */
353#if PY_MAJOR_VERSION >= 3
354		permstr = _PyUnicode_AsString( strObj );
355#else
356		permstr = PyString_AsString( strObj );
357#endif
358
359		perm = string_to_av_perm(tclass, permstr);
360		if (!perm)
361			RETURN(BADPERM)
362
363		av |= perm;
364	}
365
366	/* Reproduce the computation. */
367	rc = sepol_compute_av_reason(ssid, tsid, tclass, av, &avd, &reason);
368	if (rc < 0)
369		RETURN(BADCOMPUTE)
370
371	if (!reason)
372		RETURN(ALLOW)
373
374	if (reason & SEPOL_COMPUTEAV_TE) {
375		avc->ssid = ssid;
376		avc->tsid = tsid;
377		avc->tclass = tclass;
378		avc->av = av;
379		if (check_booleans(&bools) == 0) {
380			if (av & ~avd.auditdeny) {
381				RETURN(DONTAUDIT)
382			} else {
383				RETURN(TERULE)
384			}
385		} else {
386			PyObject *outboollist;
387			struct boolean_t *b = bools;
388			int len=0;
389			while (b->name) {
390				len++; b++;
391			}
392			b = bools;
393			outboollist = PyList_New(len);
394			len=0;
395			while(b->name) {
396				PyObject *bool_ = Py_BuildValue("(si)", b->name, b->active);
397				PyList_SetItem(outboollist, len++, bool_);
398				b++;
399			}
400			free(bools);
401			/* 'N' steals the reference to outboollist */
402			return Py_BuildValue("iN", BOOLEAN, outboollist);
403		}
404	}
405
406	if (reason & SEPOL_COMPUTEAV_CONS) {
407		RETURN(CONSTRAINT)
408	}
409
410	if (reason & SEPOL_COMPUTEAV_RBAC)
411		RETURN(RBAC)
412
413        RETURN(BADCOMPUTE)
414}
415
416static PyMethodDef audit2whyMethods[] = {
417    {"init",  init, METH_VARARGS,
418     "Initialize policy database."},
419    {"analyze",  analyze, METH_VARARGS,
420     "Analyze AVC."},
421    {"finish",  finish, METH_VARARGS,
422     "Finish using policy, free memory."},
423    {NULL, NULL, 0, NULL}        /* Sentinel */
424};
425
426#if PY_MAJOR_VERSION >= 3
427/* Module-initialization logic specific to Python 3 */
428struct module_state {
429	/* empty for now */
430};
431static struct PyModuleDef moduledef = {
432	PyModuleDef_HEAD_INIT,
433	"audit2why",
434	NULL,
435	sizeof(struct module_state),
436	audit2whyMethods,
437	NULL,
438	NULL,
439	NULL,
440	NULL
441};
442
443PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */
444PyMODINIT_FUNC PyInit_audit2why(void)
445#else
446PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */
447PyMODINIT_FUNC initaudit2why(void)
448#endif
449{
450	PyObject *m;
451#if PY_MAJOR_VERSION >= 3
452	m = PyModule_Create(&moduledef);
453	if (m == NULL) {
454		return NULL;
455	}
456#else
457	m  = Py_InitModule("audit2why", audit2whyMethods);
458#endif
459	PyModule_AddIntConstant(m,"UNKNOWN", UNKNOWN);
460	PyModule_AddIntConstant(m,"BADSCON", BADSCON);
461	PyModule_AddIntConstant(m,"BADTCON", BADTCON);
462	PyModule_AddIntConstant(m,"BADTCLASS", BADTCLASS);
463	PyModule_AddIntConstant(m,"BADPERM", BADPERM);
464	PyModule_AddIntConstant(m,"BADCOMPUTE", BADCOMPUTE);
465	PyModule_AddIntConstant(m,"NOPOLICY", NOPOLICY);
466	PyModule_AddIntConstant(m,"ALLOW", ALLOW);
467	PyModule_AddIntConstant(m,"DONTAUDIT", DONTAUDIT);
468	PyModule_AddIntConstant(m,"TERULE", TERULE);
469	PyModule_AddIntConstant(m,"BOOLEAN", BOOLEAN);
470	PyModule_AddIntConstant(m,"CONSTRAINT", CONSTRAINT);
471	PyModule_AddIntConstant(m,"RBAC", RBAC);
472
473#if PY_MAJOR_VERSION >= 3
474	return m;
475#endif
476}
477