/* 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
}