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 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 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 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 192 static int __policy_init(const char *init_path) 193 { 194 FILE *fp; 195 char path[PATH_MAX]; 196 char errormsg[PATH_MAX]; 197 struct sepol_policy_file *pf = NULL; 198 int rc; 199 unsigned int cnt; 200 201 path[PATH_MAX-1] = '\0'; 202 if (init_path) { 203 strncpy(path, init_path, PATH_MAX-1); 204 fp = fopen(path, "re"); 205 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 { 213 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 } 221 fp = fopen(curpolicy, "re"); 222 if (!fp) { 223 snprintf(errormsg, sizeof(errormsg), 224 "unable to open %s: %s\n", 225 curpolicy, 226 strerror(errno)); 227 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"); 235 fclose(fp); 236 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(); 261 /* Turn off messages */ 262 sepol_msg_set_callback(avc->handle, NULL, NULL); 263 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 271 boollist = calloc(cnt, sizeof(*boollist)); 272 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 292 static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) { 293 int result; 294 char *init_path=NULL; 295 if (avc) { 296 PyErr_SetString( PyExc_RuntimeError, "init called multiple times"); 297 return NULL; 298 } 299 if (!PyArg_ParseTuple(args,(char *)"|s:policy_init",&init_path)) 300 return NULL; 301 result = __policy_init(init_path); 302 return Py_BuildValue("i", result); 303 } 304 305 #define RETURN(X) \ 306 { \ 307 return Py_BuildValue("iO", (X), Py_None); \ 308 } 309 310 static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args) { 311 char *reason_buf = NULL; 312 char * scon; 313 char * tcon; 314 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; 325 int i=0; 326 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 336 if (!avc) 337 RETURN(NOPOLICY) 338 339 rc = sepol_context_to_sid(scon, strlen(scon) + 1, &ssid); 340 if (rc < 0) 341 RETURN(BADSCON) 342 343 rc = sepol_context_to_sid(tcon, strlen(tcon) + 1, &tsid); 344 if (rc < 0) 345 RETURN(BADTCON) 346 347 rc = sepol_string_to_security_class(tclassstr, &tclass); 348 if (rc < 0) 349 RETURN(BADTCLASS) 350 351 /* 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 */ 356 for (i=0; i<numlines; i++){ 357 char *permstr; 358 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 */ 363 #if PY_MAJOR_VERSION >= 3 364 permstr = _PyUnicode_AsString( strObj ); 365 #else 366 permstr = PyString_AsString( strObj ); 367 #endif 368 369 rc = sepol_string_to_av_perm(tclass, permstr, &perm); 370 if (rc < 0) 371 RETURN(BADPERM) 372 373 av |= perm; 374 } 375 376 /* Reproduce the computation. */ 377 rc = sepol_compute_av_reason_buffer(ssid, tsid, tclass, av, &avd, &reason, &reason_buf, 0); 378 if (rc < 0) 379 RETURN(BADCOMPUTE) 380 381 if (!reason) 382 RETURN(ALLOW) 383 384 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 { 396 PyObject *outboollist; 397 struct boolean_t *b = bools; 398 int len=0; 399 while (b->name) { 400 len++; b++; 401 } 402 b = bools; 403 outboollist = PyList_New(len); 404 len=0; 405 while(b->name) { 406 PyObject *bool_ = Py_BuildValue("(si)", b->name, b->active); 407 PyList_SetItem(outboollist, len++, bool_); 408 b++; 409 } 410 free(bools); 411 /* 'N' steals the reference to outboollist */ 412 return Py_BuildValue("iN", BOOLEAN, outboollist); 413 } 414 } 415 416 if (reason & SEPOL_COMPUTEAV_CONS) { 417 if (reason_buf) { 418 PyObject *result = NULL; 419 result = Py_BuildValue("is", CONSTRAINT, reason_buf); 420 free(reason_buf); 421 return result; 422 } 423 RETURN(CONSTRAINT) 424 } 425 426 if (reason & SEPOL_COMPUTEAV_RBAC) 427 RETURN(RBAC) 428 429 if (reason & SEPOL_COMPUTEAV_BOUNDS) 430 RETURN(BOUNDS) 431 432 RETURN(BADCOMPUTE) 433 } 434 435 static 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 445 #if PY_MAJOR_VERSION >= 3 446 /* Module-initialization logic specific to Python 3 */ 447 static struct PyModuleDef moduledef = { 448 PyModuleDef_HEAD_INIT, 449 "audit2why", 450 NULL, 451 0, 452 audit2whyMethods, 453 NULL, 454 NULL, 455 NULL, 456 NULL 457 }; 458 459 PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */ 460 PyMODINIT_FUNC PyInit_audit2why(void) 461 #else 462 PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */ 463 PyMODINIT_FUNC initaudit2why(void) 464 #endif 465 { 466 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 475 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); 488 PyModule_AddIntConstant(m,"BOUNDS", BOUNDS); 489 490 #if PY_MAJOR_VERSION >= 3 491 return m; 492 #endif 493 } 494