mirror of
https://github.com/SELinuxProject/selinux
synced 2025-02-28 23:40:35 +00:00
This patch uses Richard Haines fixes in libsepol to help identify which constraint is blocking access. The end goal is helping policy writers and administrators to diagnose issues with their policy.
484 lines
11 KiB
C
484 lines
11 KiB
C
/* 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
|
|
|
|
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];
|
|
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, "r");
|
|
if (!fp) {
|
|
snprintf(errormsg, sizeof(errormsg),
|
|
"unable to open %s: %s\n",
|
|
path, strerror(errno));
|
|
PyErr_SetString( PyExc_ValueError, errormsg);
|
|
return 1;
|
|
}
|
|
} else {
|
|
fp = fopen(selinux_current_policy_path(), "r");
|
|
if (!fp) {
|
|
snprintf(errormsg, sizeof(errormsg),
|
|
"unable to open %s: %s\n",
|
|
selinux_current_policy_path(),
|
|
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;
|
|
security_context_t scon;
|
|
security_context_t 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)
|
|
|
|
tclass = string_to_security_class(tclassstr);
|
|
if (!tclass)
|
|
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++){
|
|
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
|
|
|
|
perm = string_to_av_perm(tclass, permstr);
|
|
if (!perm)
|
|
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)
|
|
|
|
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 */
|
|
struct module_state {
|
|
/* empty for now */
|
|
};
|
|
static struct PyModuleDef moduledef = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"audit2why",
|
|
NULL,
|
|
sizeof(struct module_state),
|
|
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);
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
return m;
|
|
#endif
|
|
}
|