selinux/checkpolicy/checkmodule.c
Gary Tierney 4984a75f38 checkmodule: add support for specifying module policy version
Currently checkpolicy can produce binary policies for earlier policy versions
to provide support for building policies on one machine and loading/analyzing
them on another machine with an earlier version of the kernel or libsepol,
respectively. However, checkmodule was lacking this capability.

This commit adds an identical `-c` flag that can be passed to checkmodule that
will build a modular policy file of the specified version.

Signed-off-by: Gary Tierney <gary.tierney@fastmail.com>
2019-04-19 13:10:44 -04:00

360 lines
8.7 KiB
C

/*
* Authors: Joshua Brindle <jbrindle@tresys.com>
* Karl MacMillan <kmacmillan@tresys.com>
* Jason Tang <jtang@tresys.com>
*
*
* Copyright (C) 2004-5 Tresys Technology, LLC
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*/
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include <libgen.h>
#include <sepol/module_to_cil.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/services.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/flask.h>
#include <sepol/policydb/hierarchy.h>
#include <sepol/policydb/expand.h>
#include <sepol/policydb/link.h>
#include <sepol/policydb/sidtab.h>
#include "queue.h"
#include "checkpolicy.h"
#include "parse_util.h"
extern char *optarg;
extern int optind;
static sidtab_t sidtab;
extern int mlspol;
static int handle_unknown = SEPOL_DENY_UNKNOWN;
static const char *txtfile = "policy.conf";
static const char *binfile = "policy";
unsigned int policy_type = POLICY_BASE;
unsigned int policyvers = MOD_POLICYDB_VERSION_MAX;
static int read_binary_policy(policydb_t * p, const char *file, const char *progname)
{
int fd;
struct stat sb;
void *map;
struct policy_file f, *fp;
fd = open(file, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can't open '%s': %s\n",
file, strerror(errno));
return -1;
}
if (fstat(fd, &sb) < 0) {
fprintf(stderr, "Can't stat '%s': %s\n",
file, strerror(errno));
close(fd);
return -1;
}
map =
mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);
if (map == MAP_FAILED) {
fprintf(stderr, "Can't map '%s': %s\n", file, strerror(errno));
return -1;
}
policy_file_init(&f);
f.type = PF_USE_MEMORY;
f.data = map;
f.len = sb.st_size;
fp = &f;
if (policydb_init(p)) {
fprintf(stderr, "%s: policydb_init: Out of memory!\n",
progname);
return -1;
}
if (policydb_read(p, fp, 1)) {
fprintf(stderr,
"%s: error(s) encountered while parsing configuration\n",
progname);
return -1;
}
/* Check Policy Consistency */
if (p->mls) {
if (!mlspol) {
fprintf(stderr, "%s: MLS policy, but non-MLS"
" is specified\n", progname);
return -1;
}
} else {
if (mlspol) {
fprintf(stderr, "%s: non-MLS policy, but MLS"
" is specified\n", progname);
return -1;
}
}
return 0;
}
static int write_binary_policy(policydb_t * p, FILE *outfp)
{
struct policy_file pf;
p->policy_type = policy_type;
p->policyvers = policyvers;
p->handle_unknown = handle_unknown;
policy_file_init(&pf);
pf.type = PF_USE_STDIO;
pf.fp = outfp;
return policydb_write(p, &pf);
}
static __attribute__((__noreturn__)) void usage(const char *progname)
{
printf("usage: %s [-h] [-V] [-b] [-C] [-U handle_unknown] [-m] [-M] [-o FILE] [INPUT]\n", progname);
printf("Build base and policy modules.\n");
printf("Options:\n");
printf(" INPUT build module from INPUT (else read from \"%s\")\n",
txtfile);
printf(" -V show policy versions created by this program\n");
printf(" -b treat input as a binary policy file\n");
printf(" -C output CIL policy instead of binary policy\n");
printf(" -h print usage\n");
printf(" -U OPTION How to handle unknown classes and permissions\n");
printf(" deny: Deny unknown kernel checks\n");
printf(" reject: Reject loading of policy with unknowns\n");
printf(" allow: Allow unknown kernel checks\n");
printf(" -m build a policy module instead of a base module\n");
printf(" -M enable MLS policy\n");
printf(" -o FILE write module to FILE (else just check syntax)\n");
printf(" -c VERSION build a policy module targeting a modular policy version (%d-%d)\n",
MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX);
exit(1);
}
int main(int argc, char **argv)
{
const char *file = txtfile, *outfile = NULL;
unsigned int binary = 0, cil = 0;
int ch;
int show_version = 0;
policydb_t modpolicydb;
struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"output", required_argument, NULL, 'o'},
{"binary", no_argument, NULL, 'b'},
{"version", no_argument, NULL, 'V'},
{"handle-unknown", required_argument, NULL, 'U'},
{"mls", no_argument, NULL, 'M'},
{"cil", no_argument, NULL, 'C'},
{NULL, 0, NULL, 0}
};
while ((ch = getopt_long(argc, argv, "ho:bVU:mMCc:", long_options, NULL)) != -1) {
switch (ch) {
case 'h':
usage(argv[0]);
break;
case 'o':
outfile = optarg;
break;
case 'b':
binary = 1;
file = binfile;
break;
case 'V':
show_version = 1;
break;
case 'U':
if (!strcasecmp(optarg, "deny")) {
handle_unknown = DENY_UNKNOWN;
break;
}
if (!strcasecmp(optarg, "reject")) {
handle_unknown = REJECT_UNKNOWN;
break;
}
if (!strcasecmp(optarg, "allow")) {
handle_unknown = ALLOW_UNKNOWN;
break;
}
usage(argv[0]);
case 'm':
policy_type = POLICY_MOD;
break;
case 'M':
mlspol = 1;
break;
case 'C':
cil = 1;
break;
case 'c': {
long int n;
errno = 0;
n = strtol(optarg, NULL, 10);
if (errno) {
fprintf(stderr,
"Invalid policyvers specified: %s\n",
optarg);
usage(argv[0]);
}
if (n < MOD_POLICYDB_VERSION_MIN
|| n > MOD_POLICYDB_VERSION_MAX) {
fprintf(stderr,
"policyvers value %ld not in range %d-%d\n",
n, MOD_POLICYDB_VERSION_MIN,
MOD_POLICYDB_VERSION_MAX);
usage(argv[0]);
}
policyvers = n;
break;
}
default:
usage(argv[0]);
}
}
if (show_version) {
printf("Module versions %d-%d\n",
MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX);
exit(0);
}
if (handle_unknown && (policy_type != POLICY_BASE)) {
fprintf(stderr, "%s: Handling of unknown classes and permissions is only valid in the base module.\n", argv[0]);
exit(1);
}
if (binary && (policy_type != POLICY_BASE)) {
fprintf(stderr, "%s: -b and -m are incompatible with each other.\n", argv[0]);
exit(1);
}
if (optind != argc) {
file = argv[optind++];
if (optind != argc)
usage(argv[0]);
}
/* Set policydb and sidtab used by libsepol service functions
to my structures, so that I can directly populate and
manipulate them. */
sepol_set_policydb(&modpolicydb);
sepol_set_sidtab(&sidtab);
if (binary) {
if (read_binary_policy(&modpolicydb, file, argv[0]) == -1) {
exit(1);
}
} else {
if (policydb_init(&modpolicydb)) {
fprintf(stderr, "%s: out of memory!\n", argv[0]);
return -1;
}
modpolicydb.policy_type = policy_type;
modpolicydb.mls = mlspol;
modpolicydb.handle_unknown = handle_unknown;
if (read_source_policy(&modpolicydb, file, argv[0]) == -1) {
exit(1);
}
if (hierarchy_check_constraints(NULL, &modpolicydb)) {
return -1;
}
}
if (policy_type != POLICY_BASE && outfile) {
char *mod_name = modpolicydb.name;
char *out_path = strdup(outfile);
if (out_path == NULL) {
fprintf(stderr, "%s: out of memory\n", argv[0]);
exit(1);
}
char *out_name = basename(out_path);
char *separator = strrchr(out_name, '.');
if (separator) {
*separator = '\0';
}
if (strcmp(mod_name, out_name) != 0) {
fprintf(stderr, "%s: Module name %s is different than the output base filename %s\n", argv[0], mod_name, out_name);
exit(1);
}
free(out_path);
}
if (modpolicydb.policy_type == POLICY_BASE && !cil) {
/* Verify that we can successfully expand the base module. */
policydb_t kernpolicydb;
if (policydb_init(&kernpolicydb)) {
fprintf(stderr, "%s: policydb_init failed\n", argv[0]);
exit(1);
}
if (link_modules(NULL, &modpolicydb, NULL, 0, 0)) {
fprintf(stderr, "%s: link modules failed\n", argv[0]);
exit(1);
}
if (expand_module(NULL, &modpolicydb, &kernpolicydb, 0, 1)) {
fprintf(stderr, "%s: expand module failed\n", argv[0]);
exit(1);
}
policydb_destroy(&kernpolicydb);
}
if (policydb_load_isids(&modpolicydb, &sidtab))
exit(1);
sepol_sidtab_destroy(&sidtab);
if (outfile) {
FILE *outfp = fopen(outfile, "w");
if (!outfp) {
perror(outfile);
exit(1);
}
if (!cil) {
if (write_binary_policy(&modpolicydb, outfp) != 0) {
fprintf(stderr, "%s: error writing %s\n", argv[0], outfile);
exit(1);
}
} else {
if (sepol_module_policydb_to_cil(outfp, &modpolicydb, 0) != 0) {
fprintf(stderr, "%s: error writing %s\n", argv[0], outfile);
exit(1);
}
}
fclose(outfp);
} else if (cil) {
fprintf(stderr, "%s: No file to write CIL was specified\n", argv[0]);
exit(1);
}
policydb_destroy(&modpolicydb);
return 0;
}
/* FLASK */