• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 struct boolean_t {
33 	char *name;
34 	int active;
35 };
36 
37 static struct boolean_t **boollist = NULL;
38 static int boolcnt = 0;
39 
40 struct 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 
49 static struct avc_t *avc = NULL;
50 
51 static sidtab_t sidtab;
52 
load_booleans(const sepol_bool_t * boolean,void * arg)53 static 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 
check_booleans(struct boolean_t ** bools)63 static 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 
finish(PyObject * self,PyObject * args)162 static 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 
__policy_init(const char * init_path)191 static 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 		const char *curpolicy = selinux_current_policy_path();
213 		if (!curpolicy) {
214 			/* SELinux disabled, must use -p option. */
215 			snprintf(errormsg, sizeof(errormsg),
216 				 "You must specify the -p option with the path to the policy file.\n");
217 			PyErr_SetString( PyExc_ValueError, errormsg);
218 			return 1;
219 		}
220 		fp = fopen(curpolicy, "r");
221 		if (!fp) {
222 			snprintf(errormsg, sizeof(errormsg),
223 				 "unable to open %s:  %s\n",
224 				 curpolicy,
225 				 strerror(errno));
226 			PyErr_SetString( PyExc_ValueError, errormsg);
227 			return 1;
228 		}
229 	}
230 
231 	avc = calloc(sizeof(struct avc_t), 1);
232 	if (!avc) {
233 		PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
234 		fclose(fp);
235 		return 1;
236 	}
237 
238 	/* Set up a policydb directly so that we can mutate it later
239 	   for testing what booleans might have allowed the access.
240 	   Otherwise, we'd just use sepol_set_policydb_from_file() here. */
241 	if (sepol_policy_file_create(&pf) ||
242 	    sepol_policydb_create(&avc->policydb)) {
243 		snprintf(errormsg, sizeof(errormsg),
244 			 "policydb_init failed: %s\n", strerror(errno));
245 		PyErr_SetString( PyExc_RuntimeError, errormsg);
246 		fclose(fp);
247 		return 1;
248 	}
249 	sepol_policy_file_set_fp(pf, fp);
250 	if (sepol_policydb_read(avc->policydb, pf)) {
251 		snprintf(errormsg, sizeof(errormsg),
252 			 "invalid binary policy %s\n", path);
253 		PyErr_SetString( PyExc_ValueError, errormsg);
254 		fclose(fp);
255 		return 1;
256 	}
257 	fclose(fp);
258 	sepol_set_policydb(&avc->policydb->p);
259 	avc->handle = sepol_handle_create();
260 	/* Turn off messages */
261 	sepol_msg_set_callback(avc->handle, NULL, NULL);
262 
263 	rc = sepol_bool_count(avc->handle,
264 			      avc->policydb, &cnt);
265 	if (rc < 0) {
266 		PyErr_SetString( PyExc_RuntimeError, "unable to get bool count\n");
267 		return 1;
268 	}
269 
270 	boollist = calloc(cnt, sizeof(*boollist));
271 	if (!boollist) {
272 		PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
273 		return 1;
274 	}
275 
276 	sepol_bool_iterate(avc->handle, avc->policydb,
277 			   load_booleans, (void *)NULL);
278 
279 	/* Initialize the sidtab for subsequent use by sepol_context_to_sid
280 	   and sepol_compute_av_reason. */
281 	rc = sepol_sidtab_init(&sidtab);
282 	if (rc < 0) {
283 		PyErr_SetString( PyExc_RuntimeError, "unable to init sidtab\n");
284 		free(boollist);
285 		return 1;
286 	}
287 	sepol_set_sidtab(&sidtab);
288 	return 0;
289 }
290 
init(PyObject * self,PyObject * args)291 static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) {
292   int result;
293   char *init_path=NULL;
294   if (avc) {
295 	  PyErr_SetString( PyExc_RuntimeError, "init called multiple times");
296 	  return NULL;
297   }
298   if (!PyArg_ParseTuple(args,(char *)"|s:policy_init",&init_path))
299     return NULL;
300   result = __policy_init(init_path);
301   return Py_BuildValue("i", result);
302 }
303 
304 #define RETURN(X) \
305 	{ \
306 		return Py_BuildValue("iO", (X), Py_None);	\
307 	}
308 
analyze(PyObject * self,PyObject * args)309 static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args) {
310 	char *reason_buf = NULL;
311 	char * scon;
312 	char * tcon;
313 	char *tclassstr;
314 	PyObject *listObj;
315 	PyObject *strObj;
316 	int numlines;
317 	struct boolean_t *bools;
318 	unsigned int reason;
319 	sepol_security_id_t ssid, tsid;
320 	sepol_security_class_t tclass;
321 	sepol_access_vector_t perm, av;
322 	struct sepol_av_decision avd;
323 	int rc;
324 	int i=0;
325 
326 	if (!PyArg_ParseTuple(args,(char *)"sssO!:audit2why",&scon,&tcon,&tclassstr,&PyList_Type, &listObj))
327 		return NULL;
328 
329 	/* get the number of lines passed to us */
330 	numlines = PyList_Size(listObj);
331 
332 	/* should raise an error here. */
333 	if (numlines < 0)	return NULL; /* Not a list */
334 
335 	if (!avc)
336 		RETURN(NOPOLICY)
337 
338 	rc = sepol_context_to_sid(scon, strlen(scon) + 1, &ssid);
339 	if (rc < 0)
340 		RETURN(BADSCON)
341 
342 	rc = sepol_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
343 	if (rc < 0)
344 		RETURN(BADTCON)
345 
346 	tclass = string_to_security_class(tclassstr);
347 	if (!tclass)
348 		RETURN(BADTCLASS)
349 
350 	/* Convert the permission list to an AV. */
351 	av = 0;
352 
353 	/* iterate over items of the list, grabbing strings, and parsing
354 	   for numbers */
355 	for (i=0; i<numlines; i++){
356 		char *permstr;
357 
358 		/* grab the string object from the next element of the list */
359 		strObj = PyList_GetItem(listObj, i); /* Can't fail */
360 
361 		/* make it a string */
362 #if PY_MAJOR_VERSION >= 3
363 		permstr = _PyUnicode_AsString( strObj );
364 #else
365 		permstr = PyString_AsString( strObj );
366 #endif
367 
368 		perm = string_to_av_perm(tclass, permstr);
369 		if (!perm)
370 			RETURN(BADPERM)
371 
372 		av |= perm;
373 	}
374 
375 	/* Reproduce the computation. */
376 	rc = sepol_compute_av_reason_buffer(ssid, tsid, tclass, av, &avd, &reason, &reason_buf, 0);
377 	if (rc < 0)
378 		RETURN(BADCOMPUTE)
379 
380 	if (!reason)
381 		RETURN(ALLOW)
382 
383 	if (reason & SEPOL_COMPUTEAV_TE) {
384 		avc->ssid = ssid;
385 		avc->tsid = tsid;
386 		avc->tclass = tclass;
387 		avc->av = av;
388 		if (check_booleans(&bools) == 0) {
389 			if (av & ~avd.auditdeny) {
390 				RETURN(DONTAUDIT)
391 			} else {
392 				RETURN(TERULE)
393 			}
394 		} else {
395 			PyObject *outboollist;
396 			struct boolean_t *b = bools;
397 			int len=0;
398 			while (b->name) {
399 				len++; b++;
400 			}
401 			b = bools;
402 			outboollist = PyList_New(len);
403 			len=0;
404 			while(b->name) {
405 				PyObject *bool_ = Py_BuildValue("(si)", b->name, b->active);
406 				PyList_SetItem(outboollist, len++, bool_);
407 				b++;
408 			}
409 			free(bools);
410 			/* 'N' steals the reference to outboollist */
411 			return Py_BuildValue("iN", BOOLEAN, outboollist);
412 		}
413 	}
414 
415 	if (reason & SEPOL_COMPUTEAV_CONS) {
416 		if (reason_buf) {
417 			PyObject *result = NULL;
418 			result = Py_BuildValue("is", CONSTRAINT, reason_buf);
419 			free(reason_buf);
420 			return result;
421 		}
422 		RETURN(CONSTRAINT)
423 	}
424 
425 	if (reason & SEPOL_COMPUTEAV_RBAC)
426 		RETURN(RBAC)
427 
428         RETURN(BADCOMPUTE)
429 }
430 
431 static PyMethodDef audit2whyMethods[] = {
432     {"init",  init, METH_VARARGS,
433      "Initialize policy database."},
434     {"analyze",  analyze, METH_VARARGS,
435      "Analyze AVC."},
436     {"finish",  finish, METH_VARARGS,
437      "Finish using policy, free memory."},
438     {NULL, NULL, 0, NULL}        /* Sentinel */
439 };
440 
441 #if PY_MAJOR_VERSION >= 3
442 /* Module-initialization logic specific to Python 3 */
443 struct module_state {
444 	/* empty for now */
445 };
446 static struct PyModuleDef moduledef = {
447 	PyModuleDef_HEAD_INIT,
448 	"audit2why",
449 	NULL,
450 	sizeof(struct module_state),
451 	audit2whyMethods,
452 	NULL,
453 	NULL,
454 	NULL,
455 	NULL
456 };
457 
458 PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */
PyInit_audit2why(void)459 PyMODINIT_FUNC PyInit_audit2why(void)
460 #else
461 PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */
462 PyMODINIT_FUNC initaudit2why(void)
463 #endif
464 {
465 	PyObject *m;
466 #if PY_MAJOR_VERSION >= 3
467 	m = PyModule_Create(&moduledef);
468 	if (m == NULL) {
469 		return NULL;
470 	}
471 #else
472 	m  = Py_InitModule("audit2why", audit2whyMethods);
473 #endif
474 	PyModule_AddIntConstant(m,"UNKNOWN", UNKNOWN);
475 	PyModule_AddIntConstant(m,"BADSCON", BADSCON);
476 	PyModule_AddIntConstant(m,"BADTCON", BADTCON);
477 	PyModule_AddIntConstant(m,"BADTCLASS", BADTCLASS);
478 	PyModule_AddIntConstant(m,"BADPERM", BADPERM);
479 	PyModule_AddIntConstant(m,"BADCOMPUTE", BADCOMPUTE);
480 	PyModule_AddIntConstant(m,"NOPOLICY", NOPOLICY);
481 	PyModule_AddIntConstant(m,"ALLOW", ALLOW);
482 	PyModule_AddIntConstant(m,"DONTAUDIT", DONTAUDIT);
483 	PyModule_AddIntConstant(m,"TERULE", TERULE);
484 	PyModule_AddIntConstant(m,"BOOLEAN", BOOLEAN);
485 	PyModule_AddIntConstant(m,"CONSTRAINT", CONSTRAINT);
486 	PyModule_AddIntConstant(m,"RBAC", RBAC);
487 
488 #if PY_MAJOR_VERSION >= 3
489 	return m;
490 #endif
491 }
492