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