blob: d56b56eb2a1a9e7cd703a3c59ce6a667b0884f2c [file] [log] [blame]
Eric Paris8faf23d2011-08-03 14:02:37 -04001/* Workaround for http://bugs.python.org/issue4835 */
2#ifndef SIZEOF_SOCKET_T
3#define SIZEOF_SOCKET_T SIZEOF_INT
4#endif
5
Joshua Brindle13cd4c82008-08-19 15:30:36 -04006#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
Stephen Smalley7e09f582016-11-29 15:41:18 -050031#define BOUNDS 6
Joshua Brindle13cd4c82008-08-19 15:30:36 -040032
33struct boolean_t {
34 char *name;
35 int active;
36};
37
38static struct boolean_t **boollist = NULL;
39static int boolcnt = 0;
40
41struct 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
50static struct avc_t *avc = NULL;
51
52static sidtab_t sidtab;
53
54static 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
64static 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
163static PyObject *finish(PyObject *self __attribute__((unused)), PyObject *args) {
164 PyObject *result = 0;
165
166 if (PyArg_ParseTuple(args,(char *)":finish")) {
167 int i = 0;
rhatdan019e6fd2012-10-15 15:25:31 -0400168 if (! avc)
169 Py_RETURN_NONE;
170
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400171 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;
rhatdan019e6fd2012-10-15 15:25:31 -0400184
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400185 /* Boilerplate to return "None" */
186 Py_RETURN_NONE;
187 }
188 return result;
189}
190
191
192static int __policy_init(const char *init_path)
193{
194 FILE *fp;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400195 char path[PATH_MAX];
Stephen Smalleyc41633b2018-05-03 14:48:45 -0400196 char errormsg[PATH_MAX+1024+20];
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400197 struct sepol_policy_file *pf = NULL;
198 int rc;
199 unsigned int cnt;
200
Eric Paris933840a2012-12-04 15:23:57 -0500201 path[PATH_MAX-1] = '\0';
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400202 if (init_path) {
Eric Paris933840a2012-12-04 15:23:57 -0500203 strncpy(path, init_path, PATH_MAX-1);
Nick Kralevich64afa1a2016-12-11 09:30:16 -0800204 fp = fopen(path, "re");
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400205 if (!fp) {
206 snprintf(errormsg, sizeof(errormsg),
207 "unable to open %s: %s\n",
208 path, strerror(errno));
209 PyErr_SetString( PyExc_ValueError, errormsg);
210 return 1;
211 }
212 } else {
Stephen Smalley914e5912015-02-13 10:15:34 -0500213 const char *curpolicy = selinux_current_policy_path();
214 if (!curpolicy) {
215 /* SELinux disabled, must use -p option. */
216 snprintf(errormsg, sizeof(errormsg),
217 "You must specify the -p option with the path to the policy file.\n");
218 PyErr_SetString( PyExc_ValueError, errormsg);
219 return 1;
220 }
Nick Kralevich64afa1a2016-12-11 09:30:16 -0800221 fp = fopen(curpolicy, "re");
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400222 if (!fp) {
223 snprintf(errormsg, sizeof(errormsg),
Dan Walsh7eec00a2013-10-09 16:18:15 -0400224 "unable to open %s: %s\n",
Stephen Smalley914e5912015-02-13 10:15:34 -0500225 curpolicy,
Dan Walsh7eec00a2013-10-09 16:18:15 -0400226 strerror(errno));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400227 PyErr_SetString( PyExc_ValueError, errormsg);
228 return 1;
229 }
230 }
231
232 avc = calloc(sizeof(struct avc_t), 1);
233 if (!avc) {
234 PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
Dan Walsh74a9a522011-10-27 10:00:21 -0400235 fclose(fp);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400236 return 1;
237 }
238
239 /* Set up a policydb directly so that we can mutate it later
240 for testing what booleans might have allowed the access.
241 Otherwise, we'd just use sepol_set_policydb_from_file() here. */
242 if (sepol_policy_file_create(&pf) ||
243 sepol_policydb_create(&avc->policydb)) {
244 snprintf(errormsg, sizeof(errormsg),
245 "policydb_init failed: %s\n", strerror(errno));
246 PyErr_SetString( PyExc_RuntimeError, errormsg);
247 fclose(fp);
248 return 1;
249 }
250 sepol_policy_file_set_fp(pf, fp);
251 if (sepol_policydb_read(avc->policydb, pf)) {
252 snprintf(errormsg, sizeof(errormsg),
253 "invalid binary policy %s\n", path);
254 PyErr_SetString( PyExc_ValueError, errormsg);
255 fclose(fp);
256 return 1;
257 }
258 fclose(fp);
259 sepol_set_policydb(&avc->policydb->p);
260 avc->handle = sepol_handle_create();
Eric Paris802369f2011-07-05 00:27:41 -0400261 /* Turn off messages */
262 sepol_msg_set_callback(avc->handle, NULL, NULL);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400263
264 rc = sepol_bool_count(avc->handle,
265 avc->policydb, &cnt);
266 if (rc < 0) {
267 PyErr_SetString( PyExc_RuntimeError, "unable to get bool count\n");
268 return 1;
269 }
270
Eric Parisaa62cd62012-11-29 09:41:38 -0500271 boollist = calloc(cnt, sizeof(*boollist));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400272 if (!boollist) {
273 PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
274 return 1;
275 }
276
277 sepol_bool_iterate(avc->handle, avc->policydb,
278 load_booleans, (void *)NULL);
279
280 /* Initialize the sidtab for subsequent use by sepol_context_to_sid
281 and sepol_compute_av_reason. */
282 rc = sepol_sidtab_init(&sidtab);
283 if (rc < 0) {
284 PyErr_SetString( PyExc_RuntimeError, "unable to init sidtab\n");
285 free(boollist);
286 return 1;
287 }
288 sepol_set_sidtab(&sidtab);
289 return 0;
290}
291
292static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) {
293 int result;
Unto Stene1a74392019-05-10 16:52:08 +0300294 char *init_path = NULL;
Eric Parisd09bcb72012-11-19 12:42:38 -0500295 if (avc) {
296 PyErr_SetString( PyExc_RuntimeError, "init called multiple times");
297 return NULL;
298 }
Eric Paris2ea80c22011-06-29 00:29:21 -0400299 if (!PyArg_ParseTuple(args,(char *)"|s:policy_init",&init_path))
300 return NULL;
301 result = __policy_init(init_path);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400302 return Py_BuildValue("i", result);
303}
304
305#define RETURN(X) \
Dan Walsh756013e2013-10-09 15:11:05 -0400306 { \
307 return Py_BuildValue("iO", (X), Py_None); \
308 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400309
310static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args) {
Dan Walsh6d0f1112013-10-28 10:09:55 -0400311 char *reason_buf = NULL;
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500312 char * scon;
313 char * tcon;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400314 char *tclassstr;
315 PyObject *listObj;
316 PyObject *strObj;
317 int numlines;
318 struct boolean_t *bools;
319 unsigned int reason;
320 sepol_security_id_t ssid, tsid;
321 sepol_security_class_t tclass;
322 sepol_access_vector_t perm, av;
323 struct sepol_av_decision avd;
324 int rc;
Unto Stene1a74392019-05-10 16:52:08 +0300325 int i = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400326
327 if (!PyArg_ParseTuple(args,(char *)"sssO!:audit2why",&scon,&tcon,&tclassstr,&PyList_Type, &listObj))
328 return NULL;
329
330 /* get the number of lines passed to us */
331 numlines = PyList_Size(listObj);
332
333 /* should raise an error here. */
334 if (numlines < 0) return NULL; /* Not a list */
335
Dan Walsh756013e2013-10-09 15:11:05 -0400336 if (!avc)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400337 RETURN(NOPOLICY)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400338
339 rc = sepol_context_to_sid(scon, strlen(scon) + 1, &ssid);
Dan Walsh756013e2013-10-09 15:11:05 -0400340 if (rc < 0)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400341 RETURN(BADSCON)
Dan Walsh756013e2013-10-09 15:11:05 -0400342
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400343 rc = sepol_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
Dan Walsh756013e2013-10-09 15:11:05 -0400344 if (rc < 0)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400345 RETURN(BADTCON)
Dan Walsh756013e2013-10-09 15:11:05 -0400346
Joshua Brindledae5c272016-06-03 11:09:25 -0400347 rc = sepol_string_to_security_class(tclassstr, &tclass);
348 if (rc < 0)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400349 RETURN(BADTCLASS)
Dan Walsh756013e2013-10-09 15:11:05 -0400350
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400351 /* Convert the permission list to an AV. */
352 av = 0;
353
354 /* iterate over items of the list, grabbing strings, and parsing
355 for numbers */
Unto Stene1a74392019-05-10 16:52:08 +0300356 for (i = 0; i < numlines; i++){
Nicolas Iooss70c06e32018-08-19 15:27:24 +0200357 const char *permstr;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400358
359 /* grab the string object from the next element of the list */
360 strObj = PyList_GetItem(listObj, i); /* Can't fail */
361
362 /* make it a string */
Daniel J Walsh874bac82011-06-24 16:43:11 -0400363#if PY_MAJOR_VERSION >= 3
364 permstr = _PyUnicode_AsString( strObj );
365#else
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400366 permstr = PyString_AsString( strObj );
Daniel J Walsh874bac82011-06-24 16:43:11 -0400367#endif
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400368
Joshua Brindledae5c272016-06-03 11:09:25 -0400369 rc = sepol_string_to_av_perm(tclass, permstr, &perm);
370 if (rc < 0)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400371 RETURN(BADPERM)
Dan Walsh756013e2013-10-09 15:11:05 -0400372
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400373 av |= perm;
374 }
375
376 /* Reproduce the computation. */
Dan Walsh6d0f1112013-10-28 10:09:55 -0400377 rc = sepol_compute_av_reason_buffer(ssid, tsid, tclass, av, &avd, &reason, &reason_buf, 0);
Dan Walsh756013e2013-10-09 15:11:05 -0400378 if (rc < 0)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400379 RETURN(BADCOMPUTE)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400380
Dan Walsh756013e2013-10-09 15:11:05 -0400381 if (!reason)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400382 RETURN(ALLOW)
Dan Walsh756013e2013-10-09 15:11:05 -0400383
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400384 if (reason & SEPOL_COMPUTEAV_TE) {
385 avc->ssid = ssid;
386 avc->tsid = tsid;
387 avc->tclass = tclass;
388 avc->av = av;
389 if (check_booleans(&bools) == 0) {
390 if (av & ~avd.auditdeny) {
391 RETURN(DONTAUDIT)
392 } else {
393 RETURN(TERULE)
394 }
395 } else {
Dan Walsh756013e2013-10-09 15:11:05 -0400396 PyObject *outboollist;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400397 struct boolean_t *b = bools;
Unto Stene1a74392019-05-10 16:52:08 +0300398 int len = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400399 while (b->name) {
400 len++; b++;
401 }
402 b = bools;
Dan Walsh756013e2013-10-09 15:11:05 -0400403 outboollist = PyList_New(len);
Unto Stene1a74392019-05-10 16:52:08 +0300404 len = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400405 while(b->name) {
Dan Walsh756013e2013-10-09 15:11:05 -0400406 PyObject *bool_ = Py_BuildValue("(si)", b->name, b->active);
407 PyList_SetItem(outboollist, len++, bool_);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400408 b++;
409 }
410 free(bools);
Dan Walsh756013e2013-10-09 15:11:05 -0400411 /* 'N' steals the reference to outboollist */
412 return Py_BuildValue("iN", BOOLEAN, outboollist);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400413 }
414 }
415
416 if (reason & SEPOL_COMPUTEAV_CONS) {
Dan Walsh6d0f1112013-10-28 10:09:55 -0400417 if (reason_buf) {
418 PyObject *result = NULL;
419 result = Py_BuildValue("is", CONSTRAINT, reason_buf);
420 free(reason_buf);
421 return result;
422 }
Dan Walsh756013e2013-10-09 15:11:05 -0400423 RETURN(CONSTRAINT)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400424 }
425
Dan Walsh7504bbd2012-11-21 14:25:17 -0500426 if (reason & SEPOL_COMPUTEAV_RBAC)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400427 RETURN(RBAC)
Dan Walsh7504bbd2012-11-21 14:25:17 -0500428
Stephen Smalley7e09f582016-11-29 15:41:18 -0500429 if (reason & SEPOL_COMPUTEAV_BOUNDS)
430 RETURN(BOUNDS)
431
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400432 RETURN(BADCOMPUTE)
433}
434
435static PyMethodDef audit2whyMethods[] = {
436 {"init", init, METH_VARARGS,
437 "Initialize policy database."},
438 {"analyze", analyze, METH_VARARGS,
439 "Analyze AVC."},
440 {"finish", finish, METH_VARARGS,
441 "Finish using policy, free memory."},
442 {NULL, NULL, 0, NULL} /* Sentinel */
443};
444
Daniel J Walsh874bac82011-06-24 16:43:11 -0400445#if PY_MAJOR_VERSION >= 3
446/* Module-initialization logic specific to Python 3 */
Daniel J Walsh874bac82011-06-24 16:43:11 -0400447static struct PyModuleDef moduledef = {
448 PyModuleDef_HEAD_INIT,
449 "audit2why",
450 NULL,
Nicolas Iooss489dd592016-11-17 22:43:56 +0100451 0,
Daniel J Walsh874bac82011-06-24 16:43:11 -0400452 audit2whyMethods,
453 NULL,
454 NULL,
455 NULL,
456 NULL
457};
458
Eric Paris9b3055a2012-04-19 15:09:56 -0400459PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */
460PyMODINIT_FUNC PyInit_audit2why(void)
Daniel J Walsh874bac82011-06-24 16:43:11 -0400461#else
Eric Paris9b3055a2012-04-19 15:09:56 -0400462PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */
463PyMODINIT_FUNC initaudit2why(void)
Daniel J Walsh874bac82011-06-24 16:43:11 -0400464#endif
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400465{
Daniel J Walsh874bac82011-06-24 16:43:11 -0400466 PyObject *m;
467#if PY_MAJOR_VERSION >= 3
468 m = PyModule_Create(&moduledef);
469 if (m == NULL) {
470 return NULL;
471 }
472#else
473 m = Py_InitModule("audit2why", audit2whyMethods);
474#endif
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400475 PyModule_AddIntConstant(m,"UNKNOWN", UNKNOWN);
476 PyModule_AddIntConstant(m,"BADSCON", BADSCON);
477 PyModule_AddIntConstant(m,"BADTCON", BADTCON);
478 PyModule_AddIntConstant(m,"BADTCLASS", BADTCLASS);
479 PyModule_AddIntConstant(m,"BADPERM", BADPERM);
480 PyModule_AddIntConstant(m,"BADCOMPUTE", BADCOMPUTE);
481 PyModule_AddIntConstant(m,"NOPOLICY", NOPOLICY);
482 PyModule_AddIntConstant(m,"ALLOW", ALLOW);
483 PyModule_AddIntConstant(m,"DONTAUDIT", DONTAUDIT);
484 PyModule_AddIntConstant(m,"TERULE", TERULE);
485 PyModule_AddIntConstant(m,"BOOLEAN", BOOLEAN);
486 PyModule_AddIntConstant(m,"CONSTRAINT", CONSTRAINT);
487 PyModule_AddIntConstant(m,"RBAC", RBAC);
Stephen Smalley7e09f582016-11-29 15:41:18 -0500488 PyModule_AddIntConstant(m,"BOUNDS", BOUNDS);
Daniel J Walsh874bac82011-06-24 16:43:11 -0400489
490#if PY_MAJOR_VERSION >= 3
491 return m;
492#endif
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400493}