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(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
finish(PyObject * self,PyObject * args)163 static 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
__policy_init(const char * init_path)192 static 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
280 err:
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
init(PyObject * self,PyObject * args)297 static 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
analyze(PyObject * self,PyObject * args)315 static 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
440 static 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 */
452 static 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
464 PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */
PyInit_audit2why(void)465 PyMODINIT_FUNC PyInit_audit2why(void)
466 #else
467 PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */
468 PyMODINIT_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