/* Workaround for http://bugs.python.org/issue4835 */
|
#ifndef SIZEOF_SOCKET_T
|
#define SIZEOF_SOCKET_T SIZEOF_INT
|
#endif
|
|
#include <Python.h>
|
#include <unistd.h>
|
#include <stdlib.h>
|
#include <ctype.h>
|
#include <errno.h>
|
#include <getopt.h>
|
#include <limits.h>
|
#include <sepol/sepol.h>
|
#include <sepol/policydb.h>
|
#include <sepol/policydb/services.h>
|
#include <selinux/selinux.h>
|
|
#define UNKNOWN -1
|
#define BADSCON -2
|
#define BADTCON -3
|
#define BADTCLASS -4
|
#define BADPERM -5
|
#define BADCOMPUTE -6
|
#define NOPOLICY -7
|
#define ALLOW 0
|
#define DONTAUDIT 1
|
#define TERULE 2
|
#define BOOLEAN 3
|
#define CONSTRAINT 4
|
#define RBAC 5
|
#define BOUNDS 6
|
|
struct boolean_t {
|
char *name;
|
int active;
|
};
|
|
static struct boolean_t **boollist = NULL;
|
static int boolcnt = 0;
|
|
struct avc_t {
|
sepol_handle_t *handle;
|
sepol_policydb_t *policydb;
|
sepol_security_id_t ssid;
|
sepol_security_id_t tsid;
|
sepol_security_class_t tclass;
|
sepol_access_vector_t av;
|
};
|
|
static struct avc_t *avc = NULL;
|
|
static sidtab_t sidtab;
|
|
static int load_booleans(const sepol_bool_t * boolean,
|
void *arg __attribute__ ((__unused__)))
|
{
|
boollist[boolcnt] = malloc(sizeof(struct boolean_t));
|
boollist[boolcnt]->name = strdup(sepol_bool_get_name(boolean));
|
boollist[boolcnt]->active = sepol_bool_get_value(boolean);
|
boolcnt++;
|
return 0;
|
}
|
|
static int check_booleans(struct boolean_t **bools)
|
{
|
char errormsg[PATH_MAX];
|
struct sepol_av_decision avd;
|
unsigned int reason;
|
int rc;
|
int i;
|
sepol_bool_key_t *key = NULL;
|
sepol_bool_t *boolean = NULL;
|
int fcnt = 0;
|
int *foundlist = calloc(boolcnt, sizeof(int));
|
if (!foundlist) {
|
PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
|
return fcnt;
|
}
|
for (i = 0; i < boolcnt; i++) {
|
char *name = boollist[i]->name;
|
int active = boollist[i]->active;
|
rc = sepol_bool_key_create(avc->handle, name, &key);
|
if (rc < 0) {
|
PyErr_SetString( PyExc_RuntimeError,
|
"Could not create boolean key.\n");
|
break;
|
}
|
rc = sepol_bool_query(avc->handle,
|
avc->policydb,
|
key, &boolean);
|
|
if (rc < 0) {
|
snprintf(errormsg, sizeof(errormsg),
|
"Could not find boolean %s.\n", name);
|
PyErr_SetString( PyExc_RuntimeError, errormsg);
|
break;
|
}
|
|
sepol_bool_set_value(boolean, !active);
|
|
rc = sepol_bool_set(avc->handle,
|
avc->policydb,
|
key, boolean);
|
if (rc < 0) {
|
snprintf(errormsg, sizeof(errormsg),
|
"Could not set boolean data %s.\n", name);
|
PyErr_SetString( PyExc_RuntimeError, errormsg);
|
break;
|
}
|
|
/* Reproduce the computation. */
|
rc = sepol_compute_av_reason(avc->ssid, avc->tsid, avc->tclass,
|
avc->av, &avd, &reason);
|
if (rc < 0) {
|
snprintf(errormsg, sizeof(errormsg),
|
"Error during access vector computation, skipping...");
|
PyErr_SetString( PyExc_RuntimeError, errormsg);
|
|
sepol_bool_free(boolean);
|
break;
|
} else {
|
if (!reason) {
|
foundlist[fcnt] = i;
|
fcnt++;
|
}
|
sepol_bool_set_value(boolean, active);
|
rc = sepol_bool_set(avc->handle,
|
avc->policydb, key,
|
boolean);
|
if (rc < 0) {
|
snprintf(errormsg, sizeof(errormsg),
|
"Could not set boolean data %s.\n",
|
name);
|
|
PyErr_SetString( PyExc_RuntimeError, errormsg);
|
break;
|
}
|
}
|
sepol_bool_free(boolean);
|
sepol_bool_key_free(key);
|
key = NULL;
|
boolean = NULL;
|
}
|
if (key)
|
sepol_bool_key_free(key);
|
|
if (boolean)
|
sepol_bool_free(boolean);
|
|
if (fcnt > 0) {
|
*bools = calloc(sizeof(struct boolean_t), fcnt + 1);
|
struct boolean_t *b = *bools;
|
for (i = 0; i < fcnt; i++) {
|
int ctr = foundlist[i];
|
b[i].name = strdup(boollist[ctr]->name);
|
b[i].active = !boollist[ctr]->active;
|
}
|
}
|
free(foundlist);
|
return fcnt;
|
}
|
|
static PyObject *finish(PyObject *self __attribute__((unused)), PyObject *args) {
|
PyObject *result = 0;
|
|
if (PyArg_ParseTuple(args,(char *)":finish")) {
|
int i = 0;
|
if (! avc)
|
Py_RETURN_NONE;
|
|
for (i = 0; i < boolcnt; i++) {
|
free(boollist[i]->name);
|
free(boollist[i]);
|
}
|
free(boollist);
|
sepol_sidtab_shutdown(&sidtab);
|
sepol_sidtab_destroy(&sidtab);
|
sepol_policydb_free(avc->policydb);
|
sepol_handle_destroy(avc->handle);
|
free(avc);
|
avc = NULL;
|
boollist = NULL;
|
boolcnt = 0;
|
|
/* Boilerplate to return "None" */
|
Py_RETURN_NONE;
|
}
|
return result;
|
}
|
|
|
static int __policy_init(const char *init_path)
|
{
|
FILE *fp;
|
char path[PATH_MAX];
|
char errormsg[PATH_MAX+1024+20];
|
struct sepol_policy_file *pf = NULL;
|
int rc;
|
unsigned int cnt;
|
|
path[PATH_MAX-1] = '\0';
|
if (init_path) {
|
strncpy(path, init_path, PATH_MAX-1);
|
fp = fopen(path, "re");
|
if (!fp) {
|
snprintf(errormsg, sizeof(errormsg),
|
"unable to open %s: %s\n",
|
path, strerror(errno));
|
PyErr_SetString( PyExc_ValueError, errormsg);
|
return 1;
|
}
|
} else {
|
const char *curpolicy = selinux_current_policy_path();
|
if (!curpolicy) {
|
/* SELinux disabled, must use -p option. */
|
snprintf(errormsg, sizeof(errormsg),
|
"You must specify the -p option with the path to the policy file.\n");
|
PyErr_SetString( PyExc_ValueError, errormsg);
|
return 1;
|
}
|
fp = fopen(curpolicy, "re");
|
if (!fp) {
|
snprintf(errormsg, sizeof(errormsg),
|
"unable to open %s: %s\n",
|
curpolicy,
|
strerror(errno));
|
PyErr_SetString( PyExc_ValueError, errormsg);
|
return 1;
|
}
|
}
|
|
avc = calloc(sizeof(struct avc_t), 1);
|
if (!avc) {
|
PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
|
fclose(fp);
|
return 1;
|
}
|
|
/* Set up a policydb directly so that we can mutate it later
|
for testing what booleans might have allowed the access.
|
Otherwise, we'd just use sepol_set_policydb_from_file() here. */
|
if (sepol_policy_file_create(&pf) ||
|
sepol_policydb_create(&avc->policydb)) {
|
snprintf(errormsg, sizeof(errormsg),
|
"policydb_init failed: %s\n", strerror(errno));
|
PyErr_SetString( PyExc_RuntimeError, errormsg);
|
fclose(fp);
|
return 1;
|
}
|
sepol_policy_file_set_fp(pf, fp);
|
if (sepol_policydb_read(avc->policydb, pf)) {
|
snprintf(errormsg, sizeof(errormsg),
|
"invalid binary policy %s\n", path);
|
PyErr_SetString( PyExc_ValueError, errormsg);
|
fclose(fp);
|
return 1;
|
}
|
fclose(fp);
|
sepol_set_policydb(&avc->policydb->p);
|
avc->handle = sepol_handle_create();
|
/* Turn off messages */
|
sepol_msg_set_callback(avc->handle, NULL, NULL);
|
|
rc = sepol_bool_count(avc->handle,
|
avc->policydb, &cnt);
|
if (rc < 0) {
|
PyErr_SetString( PyExc_RuntimeError, "unable to get bool count\n");
|
return 1;
|
}
|
|
boollist = calloc(cnt, sizeof(*boollist));
|
if (!boollist) {
|
PyErr_SetString( PyExc_MemoryError, "Out of memory\n");
|
return 1;
|
}
|
|
sepol_bool_iterate(avc->handle, avc->policydb,
|
load_booleans, (void *)NULL);
|
|
/* Initialize the sidtab for subsequent use by sepol_context_to_sid
|
and sepol_compute_av_reason. */
|
rc = sepol_sidtab_init(&sidtab);
|
if (rc < 0) {
|
PyErr_SetString( PyExc_RuntimeError, "unable to init sidtab\n");
|
free(boollist);
|
return 1;
|
}
|
sepol_set_sidtab(&sidtab);
|
return 0;
|
}
|
|
static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) {
|
int result;
|
char *init_path=NULL;
|
if (avc) {
|
PyErr_SetString( PyExc_RuntimeError, "init called multiple times");
|
return NULL;
|
}
|
if (!PyArg_ParseTuple(args,(char *)"|s:policy_init",&init_path))
|
return NULL;
|
result = __policy_init(init_path);
|
return Py_BuildValue("i", result);
|
}
|
|
#define RETURN(X) \
|
{ \
|
return Py_BuildValue("iO", (X), Py_None); \
|
}
|
|
static PyObject *analyze(PyObject *self __attribute__((unused)) , PyObject *args) {
|
char *reason_buf = NULL;
|
char * scon;
|
char * tcon;
|
char *tclassstr;
|
PyObject *listObj;
|
PyObject *strObj;
|
int numlines;
|
struct boolean_t *bools;
|
unsigned int reason;
|
sepol_security_id_t ssid, tsid;
|
sepol_security_class_t tclass;
|
sepol_access_vector_t perm, av;
|
struct sepol_av_decision avd;
|
int rc;
|
int i=0;
|
|
if (!PyArg_ParseTuple(args,(char *)"sssO!:audit2why",&scon,&tcon,&tclassstr,&PyList_Type, &listObj))
|
return NULL;
|
|
/* get the number of lines passed to us */
|
numlines = PyList_Size(listObj);
|
|
/* should raise an error here. */
|
if (numlines < 0) return NULL; /* Not a list */
|
|
if (!avc)
|
RETURN(NOPOLICY)
|
|
rc = sepol_context_to_sid(scon, strlen(scon) + 1, &ssid);
|
if (rc < 0)
|
RETURN(BADSCON)
|
|
rc = sepol_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
|
if (rc < 0)
|
RETURN(BADTCON)
|
|
rc = sepol_string_to_security_class(tclassstr, &tclass);
|
if (rc < 0)
|
RETURN(BADTCLASS)
|
|
/* Convert the permission list to an AV. */
|
av = 0;
|
|
/* iterate over items of the list, grabbing strings, and parsing
|
for numbers */
|
for (i=0; i<numlines; i++){
|
const char *permstr;
|
|
/* grab the string object from the next element of the list */
|
strObj = PyList_GetItem(listObj, i); /* Can't fail */
|
|
/* make it a string */
|
#if PY_MAJOR_VERSION >= 3
|
permstr = _PyUnicode_AsString( strObj );
|
#else
|
permstr = PyString_AsString( strObj );
|
#endif
|
|
rc = sepol_string_to_av_perm(tclass, permstr, &perm);
|
if (rc < 0)
|
RETURN(BADPERM)
|
|
av |= perm;
|
}
|
|
/* Reproduce the computation. */
|
rc = sepol_compute_av_reason_buffer(ssid, tsid, tclass, av, &avd, &reason, &reason_buf, 0);
|
if (rc < 0)
|
RETURN(BADCOMPUTE)
|
|
if (!reason)
|
RETURN(ALLOW)
|
|
if (reason & SEPOL_COMPUTEAV_TE) {
|
avc->ssid = ssid;
|
avc->tsid = tsid;
|
avc->tclass = tclass;
|
avc->av = av;
|
if (check_booleans(&bools) == 0) {
|
if (av & ~avd.auditdeny) {
|
RETURN(DONTAUDIT)
|
} else {
|
RETURN(TERULE)
|
}
|
} else {
|
PyObject *outboollist;
|
struct boolean_t *b = bools;
|
int len=0;
|
while (b->name) {
|
len++; b++;
|
}
|
b = bools;
|
outboollist = PyList_New(len);
|
len=0;
|
while(b->name) {
|
PyObject *bool_ = Py_BuildValue("(si)", b->name, b->active);
|
PyList_SetItem(outboollist, len++, bool_);
|
b++;
|
}
|
free(bools);
|
/* 'N' steals the reference to outboollist */
|
return Py_BuildValue("iN", BOOLEAN, outboollist);
|
}
|
}
|
|
if (reason & SEPOL_COMPUTEAV_CONS) {
|
if (reason_buf) {
|
PyObject *result = NULL;
|
result = Py_BuildValue("is", CONSTRAINT, reason_buf);
|
free(reason_buf);
|
return result;
|
}
|
RETURN(CONSTRAINT)
|
}
|
|
if (reason & SEPOL_COMPUTEAV_RBAC)
|
RETURN(RBAC)
|
|
if (reason & SEPOL_COMPUTEAV_BOUNDS)
|
RETURN(BOUNDS)
|
|
RETURN(BADCOMPUTE)
|
}
|
|
static PyMethodDef audit2whyMethods[] = {
|
{"init", init, METH_VARARGS,
|
"Initialize policy database."},
|
{"analyze", analyze, METH_VARARGS,
|
"Analyze AVC."},
|
{"finish", finish, METH_VARARGS,
|
"Finish using policy, free memory."},
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
};
|
|
#if PY_MAJOR_VERSION >= 3
|
/* Module-initialization logic specific to Python 3 */
|
static struct PyModuleDef moduledef = {
|
PyModuleDef_HEAD_INIT,
|
"audit2why",
|
NULL,
|
0,
|
audit2whyMethods,
|
NULL,
|
NULL,
|
NULL,
|
NULL
|
};
|
|
PyMODINIT_FUNC PyInit_audit2why(void); /* silence -Wmissing-prototypes */
|
PyMODINIT_FUNC PyInit_audit2why(void)
|
#else
|
PyMODINIT_FUNC initaudit2why(void); /* silence -Wmissing-prototypes */
|
PyMODINIT_FUNC initaudit2why(void)
|
#endif
|
{
|
PyObject *m;
|
#if PY_MAJOR_VERSION >= 3
|
m = PyModule_Create(&moduledef);
|
if (m == NULL) {
|
return NULL;
|
}
|
#else
|
m = Py_InitModule("audit2why", audit2whyMethods);
|
#endif
|
PyModule_AddIntConstant(m,"UNKNOWN", UNKNOWN);
|
PyModule_AddIntConstant(m,"BADSCON", BADSCON);
|
PyModule_AddIntConstant(m,"BADTCON", BADTCON);
|
PyModule_AddIntConstant(m,"BADTCLASS", BADTCLASS);
|
PyModule_AddIntConstant(m,"BADPERM", BADPERM);
|
PyModule_AddIntConstant(m,"BADCOMPUTE", BADCOMPUTE);
|
PyModule_AddIntConstant(m,"NOPOLICY", NOPOLICY);
|
PyModule_AddIntConstant(m,"ALLOW", ALLOW);
|
PyModule_AddIntConstant(m,"DONTAUDIT", DONTAUDIT);
|
PyModule_AddIntConstant(m,"TERULE", TERULE);
|
PyModule_AddIntConstant(m,"BOOLEAN", BOOLEAN);
|
PyModule_AddIntConstant(m,"CONSTRAINT", CONSTRAINT);
|
PyModule_AddIntConstant(m,"RBAC", RBAC);
|
PyModule_AddIntConstant(m,"BOUNDS", BOUNDS);
|
|
#if PY_MAJOR_VERSION >= 3
|
return m;
|
#endif
|
}
|