mirror of
https://github.com/SELinuxProject/selinux
synced 2025-02-05 12:21:53 +00:00
Drop the global variable policyvers. The variable is only used within checkpolicy.c and checkmodule.c, but never in any shared code. Since the variable declaration is the only content of checkpolicy.h drop it. Also set the policy version before calls to read_source_policy(), so the parser can access the requested version for checks this way. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> Acked-by: James Carter <jwcart2@gmail.com>
283 lines
6.0 KiB
C
283 lines
6.0 KiB
C
#include <assert.h>
|
|
#include <setjmp.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <sepol/debug.h>
|
|
#include <sepol/kernel_to_cil.h>
|
|
#include <sepol/kernel_to_conf.h>
|
|
#include <sepol/module_to_cil.h>
|
|
#include <sepol/policydb/policydb.h>
|
|
#include <sepol/policydb/hierarchy.h>
|
|
#include <sepol/policydb/expand.h>
|
|
#include <sepol/policydb/link.h>
|
|
|
|
#include "module_compiler.h"
|
|
#include "queue.h"
|
|
|
|
extern int policydb_validate(sepol_handle_t *handle, const policydb_t *p);
|
|
extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
|
|
|
extern int mlspol;
|
|
extern policydb_t *policydbp;
|
|
extern queue_t id_queue;
|
|
extern unsigned int policydb_errors;
|
|
|
|
extern int yynerrs;
|
|
extern FILE *yyin;
|
|
extern void init_parser(int);
|
|
extern int yyparse(void);
|
|
extern void yyrestart(FILE *);
|
|
extern int yylex_destroy(void);
|
|
extern void set_source_file(const char *name);
|
|
|
|
jmp_buf fuzzing_pre_parse_stack_state;
|
|
|
|
// Set to 1 for verbose libsepol logging
|
|
#define VERBOSE 0
|
|
|
|
static ssize_t full_write(int fd, const void *buf, size_t count)
|
|
{
|
|
ssize_t written = 0;
|
|
|
|
while (count > 0) {
|
|
ssize_t ret = write(fd, buf, count);
|
|
if (ret < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (ret == 0)
|
|
break;
|
|
|
|
written += ret;
|
|
buf = (const unsigned char *)buf + (size_t)ret;
|
|
count -= (size_t)ret;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
static int read_source_policy(policydb_t *p, const uint8_t *data, size_t size)
|
|
{
|
|
int fd, rc;
|
|
ssize_t wr;
|
|
|
|
fd = memfd_create("fuzz-input", MFD_CLOEXEC);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
wr = full_write(fd, data, size);
|
|
if (wr < 0 || (size_t)wr != size) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
fsync(fd);
|
|
|
|
yynerrs = 0;
|
|
|
|
yyin = fdopen(fd, "r");
|
|
if (!yyin) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
rewind(yyin);
|
|
|
|
set_source_file("fuzz-input");
|
|
|
|
id_queue = queue_create();
|
|
if (id_queue == NULL) {
|
|
fclose(yyin);
|
|
yylex_destroy();
|
|
return -1;
|
|
}
|
|
|
|
policydbp = p;
|
|
mlspol = p->mls;
|
|
|
|
init_parser(1);
|
|
|
|
if (!setjmp(fuzzing_pre_parse_stack_state)) {
|
|
queue_destroy(id_queue);
|
|
fclose(yyin);
|
|
yylex_destroy();
|
|
return -1;
|
|
}
|
|
|
|
rc = yyparse();
|
|
// TODO: drop global variable policydb_errors if proven to be redundant
|
|
assert(rc || !policydb_errors);
|
|
if (rc || policydb_errors) {
|
|
queue_destroy(id_queue);
|
|
fclose(yyin);
|
|
yylex_destroy();
|
|
return -1;
|
|
}
|
|
|
|
rewind(yyin);
|
|
init_parser(2);
|
|
set_source_file("fuzz-input");
|
|
yyrestart(yyin);
|
|
|
|
rc = yyparse();
|
|
assert(rc || !policydb_errors);
|
|
if (rc || policydb_errors) {
|
|
queue_destroy(id_queue);
|
|
fclose(yyin);
|
|
yylex_destroy();
|
|
return -1;
|
|
}
|
|
|
|
queue_destroy(id_queue);
|
|
fclose(yyin);
|
|
yylex_destroy();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_binary_policy(FILE *outfp, policydb_t *p)
|
|
{
|
|
struct policy_file pf;
|
|
|
|
policy_file_init(&pf);
|
|
pf.type = PF_USE_STDIO;
|
|
pf.fp = outfp;
|
|
return policydb_write(p, &pf);
|
|
}
|
|
|
|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
|
{
|
|
policydb_t parsepolicydb = {};
|
|
policydb_t kernpolicydb = {};
|
|
policydb_t *finalpolicydb;
|
|
sidtab_t sidtab = {};
|
|
FILE *devnull = NULL;
|
|
int mls, platform, policyvers;
|
|
|
|
sepol_debug(VERBOSE);
|
|
|
|
/*
|
|
* Take the first byte whether to generate a SELinux or Xen policy,
|
|
* the second byte whether to parse as MLS policy,
|
|
* and the second byte as policy version.
|
|
*/
|
|
if (size < 3)
|
|
return 0;
|
|
switch (data[0]) {
|
|
case 'S':
|
|
platform = SEPOL_TARGET_SELINUX;
|
|
break;
|
|
case 'X':
|
|
platform = SEPOL_TARGET_XEN;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
switch (data[1]) {
|
|
case '0':
|
|
mls = 0;
|
|
break;
|
|
case '1':
|
|
mls = 1;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
static_assert(0x7F - 'A' >= POLICYDB_VERSION_MAX, "Max policy version should be representable");
|
|
policyvers = data[2] - 'A';
|
|
if (policyvers < POLICYDB_VERSION_MIN || policyvers > POLICYDB_VERSION_MAX)
|
|
return 0;
|
|
data += 3;
|
|
size -= 3;
|
|
|
|
if (policydb_init(&parsepolicydb))
|
|
goto exit;
|
|
|
|
parsepolicydb.policy_type = POLICY_BASE;
|
|
parsepolicydb.mls = mls;
|
|
parsepolicydb.handle_unknown = DENY_UNKNOWN;
|
|
parsepolicydb.policyvers = policyvers;
|
|
policydb_set_target_platform(&parsepolicydb, platform);
|
|
|
|
if (read_source_policy(&parsepolicydb, data, size))
|
|
goto exit;
|
|
|
|
if (parsepolicydb.policy_type == POLICY_BASE) {
|
|
if (link_modules(NULL, &parsepolicydb, NULL, 0, VERBOSE))
|
|
goto exit;
|
|
|
|
if (policydb_init(&kernpolicydb))
|
|
goto exit;
|
|
|
|
if (expand_module(NULL, &parsepolicydb, &kernpolicydb, VERBOSE, /*check_assertions=*/0))
|
|
goto exit;
|
|
|
|
(void) check_assertions(NULL, &kernpolicydb, kernpolicydb.global->branch_list->avrules);
|
|
(void) hierarchy_check_constraints(NULL, &kernpolicydb);
|
|
|
|
kernpolicydb.policyvers = policyvers;
|
|
|
|
assert(kernpolicydb.policy_type == POLICY_KERN);
|
|
assert(kernpolicydb.handle_unknown == SEPOL_DENY_UNKNOWN);
|
|
assert(kernpolicydb.mls == mls);
|
|
assert(kernpolicydb.target_platform == platform);
|
|
|
|
finalpolicydb = &kernpolicydb;
|
|
} else {
|
|
assert(parsepolicydb.policy_type == POLICY_MOD);
|
|
assert(parsepolicydb.handle_unknown == SEPOL_DENY_UNKNOWN);
|
|
assert(parsepolicydb.mls == mls);
|
|
assert(parsepolicydb.target_platform == platform);
|
|
|
|
finalpolicydb = &parsepolicydb;
|
|
}
|
|
|
|
if (policydb_load_isids(finalpolicydb, &sidtab))
|
|
goto exit;
|
|
|
|
if (finalpolicydb->policy_type == POLICY_KERN && policydb_optimize(finalpolicydb))
|
|
goto exit;
|
|
|
|
if (policydb_sort_ocontexts(finalpolicydb))
|
|
goto exit;
|
|
|
|
if (policydb_validate(NULL, finalpolicydb))
|
|
/* never generate an invalid policy */
|
|
abort();
|
|
|
|
devnull = fopen("/dev/null", "we");
|
|
if (devnull == NULL)
|
|
goto exit;
|
|
|
|
if (write_binary_policy(devnull, finalpolicydb))
|
|
abort();
|
|
|
|
if (finalpolicydb->policy_type == POLICY_KERN && sepol_kernel_policydb_to_conf(devnull, finalpolicydb))
|
|
abort();
|
|
|
|
if (finalpolicydb->policy_type == POLICY_KERN && sepol_kernel_policydb_to_cil(devnull, finalpolicydb))
|
|
abort();
|
|
|
|
if (finalpolicydb->policy_type == POLICY_MOD && sepol_module_policydb_to_cil(devnull, finalpolicydb, /*linked=*/0))
|
|
abort();
|
|
|
|
exit:
|
|
if (devnull != NULL)
|
|
fclose(devnull);
|
|
|
|
sepol_sidtab_destroy(&sidtab);
|
|
policydb_destroy(&kernpolicydb);
|
|
policydb_destroy(&parsepolicydb);
|
|
|
|
id_queue = NULL;
|
|
policydbp = NULL;
|
|
module_compiler_reset();
|
|
|
|
/* Non-zero return values are reserved for future use. */
|
|
return 0;
|
|
}
|