Files
xen/tools/security/secpol_tool.c
T
2005-08-11 22:12:11 +00:00

649 lines
21 KiB
C

/****************************************************************
* secpol_tool.c
*
* Copyright (C) 2005 IBM Corporation
*
* Authors:
* Reiner Sailer <sailer@watson.ibm.com>
* Stefan Berger <stefanb@watson.ibm.com>
*
* 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 of the
* License.
*
* sHype policy management tool. This code runs in a domain and
* manages the Xen security policy by interacting with the
* Xen access control module via a /proc/xen/privcmd proc-ioctl,
* which is translated into a acm_op hypercall into Xen.
*
* indent -i4 -kr -nut
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdint.h>
#include <netinet/in.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
#include <xen/acm.h>
#include <xen/acm_ops.h>
#include <xen/linux/privcmd.h>
#define PERROR(_m, _a...) \
fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \
errno, strerror(errno))
static inline int do_policycmd(int xc_handle, unsigned int cmd,
unsigned long data)
{
return ioctl(xc_handle, cmd, data);
}
static inline int do_xen_hypercall(int xc_handle,
privcmd_hypercall_t * hypercall)
{
return do_policycmd(xc_handle,
IOCTL_PRIVCMD_HYPERCALL,
(unsigned long) hypercall);
}
static inline int do_acm_op(int xc_handle, acm_op_t * op)
{
int ret = -1;
privcmd_hypercall_t hypercall;
op->interface_version = ACM_INTERFACE_VERSION;
hypercall.op = __HYPERVISOR_acm_op;
hypercall.arg[0] = (unsigned long) op;
if (mlock(op, sizeof(*op)) != 0)
{
PERROR("Could not lock memory for Xen policy hypercall");
goto out1;
}
if ((ret = do_xen_hypercall(xc_handle, &hypercall)) < 0)
{
if (errno == EACCES)
fprintf(stderr, "ACM operation failed -- need to"
" rebuild the user-space tool set?\n");
goto out2;
}
out2:(void) munlock(op, sizeof(*op));
out1:return ret;
}
/*************************** DUMPS *******************************/
void acm_dump_chinesewall_buffer(void *buf, int buflen)
{
struct acm_chwall_policy_buffer *cwbuf =
(struct acm_chwall_policy_buffer *) buf;
domaintype_t *ssids, *conflicts, *running_types, *conflict_aggregate;
int i, j;
if (htonl(cwbuf->policy_code) != ACM_CHINESE_WALL_POLICY)
{
printf("CHINESE WALL POLICY CODE not found ERROR!!\n");
return;
}
printf("\n\nChinese Wall policy:\n");
printf("====================\n");
printf("Policy version= %x.\n", ntohl(cwbuf->policy_version));
printf("Max Types = %x.\n", ntohl(cwbuf->chwall_max_types));
printf("Max Ssidrefs = %x.\n", ntohl(cwbuf->chwall_max_ssidrefs));
printf("Max ConfSets = %x.\n", ntohl(cwbuf->chwall_max_conflictsets));
printf("Ssidrefs Off = %x.\n", ntohl(cwbuf->chwall_ssid_offset));
printf("Conflicts Off = %x.\n",
ntohl(cwbuf->chwall_conflict_sets_offset));
printf("Runing T. Off = %x.\n",
ntohl(cwbuf->chwall_running_types_offset));
printf("C. Agg. Off = %x.\n",
ntohl(cwbuf->chwall_conflict_aggregate_offset));
printf("\nSSID To CHWALL-Type matrix:\n");
ssids = (domaintype_t *) (buf + ntohl(cwbuf->chwall_ssid_offset));
for (i = 0; i < ntohl(cwbuf->chwall_max_ssidrefs); i++)
{
printf("\n ssidref%2x: ", i);
for (j = 0; j < ntohl(cwbuf->chwall_max_types); j++)
printf("%02x ",
ntohs(ssids[i * ntohl(cwbuf->chwall_max_types) + j]));
}
printf("\n\nConfict Sets:\n");
conflicts =
(domaintype_t *) (buf + ntohl(cwbuf->chwall_conflict_sets_offset));
for (i = 0; i < ntohl(cwbuf->chwall_max_conflictsets); i++)
{
printf("\n c-set%2x: ", i);
for (j = 0; j < ntohl(cwbuf->chwall_max_types); j++)
printf("%02x ",
ntohs(conflicts
[i * ntohl(cwbuf->chwall_max_types) + j]));
}
printf("\n");
printf("\nRunning\nTypes: ");
if (ntohl(cwbuf->chwall_running_types_offset))
{
running_types =
(domaintype_t *) (buf +
ntohl(cwbuf->chwall_running_types_offset));
for (i = 0; i < ntohl(cwbuf->chwall_max_types); i++)
{
printf("%02x ", ntohs(running_types[i]));
}
printf("\n");
} else {
printf("Not Reported!\n");
}
printf("\nConflict\nAggregate Set: ");
if (ntohl(cwbuf->chwall_conflict_aggregate_offset))
{
conflict_aggregate =
(domaintype_t *) (buf +
ntohl(cwbuf->chwall_conflict_aggregate_offset));
for (i = 0; i < ntohl(cwbuf->chwall_max_types); i++)
{
printf("%02x ", ntohs(conflict_aggregate[i]));
}
printf("\n\n");
} else {
printf("Not Reported!\n");
}
}
void acm_dump_ste_buffer(void *buf, int buflen)
{
struct acm_ste_policy_buffer *stebuf =
(struct acm_ste_policy_buffer *) buf;
domaintype_t *ssids;
int i, j;
if (ntohl(stebuf->policy_code) != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) {
printf("SIMPLE TYPE ENFORCEMENT POLICY CODE not found ERROR!!\n");
return;
}
printf("\nSimple Type Enforcement policy:\n");
printf("===============================\n");
printf("Policy version= %x.\n", ntohl(stebuf->policy_version));
printf("Max Types = %x.\n", ntohl(stebuf->ste_max_types));
printf("Max Ssidrefs = %x.\n", ntohl(stebuf->ste_max_ssidrefs));
printf("Ssidrefs Off = %x.\n", ntohl(stebuf->ste_ssid_offset));
printf("\nSSID To STE-Type matrix:\n");
ssids = (domaintype_t *) (buf + ntohl(stebuf->ste_ssid_offset));
for (i = 0; i < ntohl(stebuf->ste_max_ssidrefs); i++)
{
printf("\n ssidref%2x: ", i);
for (j = 0; j < ntohl(stebuf->ste_max_types); j++)
printf("%02x ", ntohs(ssids[i * ntohl(stebuf->ste_max_types) + j]));
}
printf("\n\n");
}
void acm_dump_policy_buffer(void *buf, int buflen)
{
struct acm_policy_buffer *pol = (struct acm_policy_buffer *) buf;
printf("\nPolicy dump:\n");
printf("============\n");
printf("PolicyVer = %x.\n", ntohl(pol->policy_version));
printf("Magic = %x.\n", ntohl(pol->magic));
printf("Len = %x.\n", ntohl(pol->len));
printf("Primary = %s (c=%x, off=%x).\n",
ACM_POLICY_NAME(ntohl(pol->primary_policy_code)),
ntohl(pol->primary_policy_code),
ntohl(pol->primary_buffer_offset));
printf("Secondary = %s (c=%x, off=%x).\n",
ACM_POLICY_NAME(ntohl(pol->secondary_policy_code)),
ntohl(pol->secondary_policy_code),
ntohl(pol->secondary_buffer_offset));
switch (ntohl(pol->primary_policy_code))
{
case ACM_CHINESE_WALL_POLICY:
acm_dump_chinesewall_buffer(buf +
ntohl(pol->primary_buffer_offset),
ntohl(pol->len) -
ntohl(pol->primary_buffer_offset));
break;
case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY:
acm_dump_ste_buffer(buf + ntohl(pol->primary_buffer_offset),
ntohl(pol->len) -
ntohl(pol->primary_buffer_offset));
break;
case ACM_NULL_POLICY:
printf("Primary policy is NULL Policy (n/a).\n");
break;
default:
printf("UNKNOWN POLICY!\n");
}
switch (ntohl(pol->secondary_policy_code))
{
case ACM_CHINESE_WALL_POLICY:
acm_dump_chinesewall_buffer(buf +
ntohl(pol->secondary_buffer_offset),
ntohl(pol->len) -
ntohl(pol->secondary_buffer_offset));
break;
case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY:
acm_dump_ste_buffer(buf + ntohl(pol->secondary_buffer_offset),
ntohl(pol->len) -
ntohl(pol->secondary_buffer_offset));
break;
case ACM_NULL_POLICY:
printf("Secondary policy is NULL Policy (n/a).\n");
break;
default:
printf("UNKNOWN POLICY!\n");
}
}
/*************************** set policy ****************************/
int acm_domain_set_chwallpolicy(void *bufstart, int buflen)
{
#define CWALL_MAX_SSIDREFS 6
#define CWALL_MAX_TYPES 10
#define CWALL_MAX_CONFLICTSETS 2
struct acm_chwall_policy_buffer *chwall_bin_pol =
(struct acm_chwall_policy_buffer *) bufstart;
domaintype_t *ssidrefs, *conflicts;
int ret = 0;
int j;
chwall_bin_pol->chwall_max_types = htonl(CWALL_MAX_TYPES);
chwall_bin_pol->chwall_max_ssidrefs = htonl(CWALL_MAX_SSIDREFS);
chwall_bin_pol->policy_code = htonl(ACM_CHINESE_WALL_POLICY);
chwall_bin_pol->policy_version = htonl(ACM_CHWALL_VERSION);
chwall_bin_pol->chwall_ssid_offset =
htonl(sizeof(struct acm_chwall_policy_buffer));
chwall_bin_pol->chwall_max_conflictsets =
htonl(CWALL_MAX_CONFLICTSETS);
chwall_bin_pol->chwall_conflict_sets_offset =
htonl(ntohl(chwall_bin_pol->chwall_ssid_offset) +
sizeof(domaintype_t) * CWALL_MAX_SSIDREFS * CWALL_MAX_TYPES);
chwall_bin_pol->chwall_running_types_offset = 0; /* not set */
chwall_bin_pol->chwall_conflict_aggregate_offset = 0; /* not set */
ret += sizeof(struct acm_chwall_policy_buffer);
/* now push example ssids into the buffer (max_ssidrefs x max_types entries) */
/* check buffer size */
if ((buflen - ret) <
(CWALL_MAX_TYPES * CWALL_MAX_SSIDREFS * sizeof(domaintype_t)))
return -1; /* not enough space */
ssidrefs = (domaintype_t *) (bufstart +
ntohl(chwall_bin_pol->chwall_ssid_offset));
memset(ssidrefs, 0,
CWALL_MAX_TYPES * CWALL_MAX_SSIDREFS * sizeof(domaintype_t));
/* now set type j-1 for ssidref i+1 */
for (j = 0; j <= CWALL_MAX_SSIDREFS; j++)
if ((0 < j) && (j <= CWALL_MAX_TYPES))
ssidrefs[j * CWALL_MAX_TYPES + j - 1] = htons(1);
ret += CWALL_MAX_TYPES * CWALL_MAX_SSIDREFS * sizeof(domaintype_t);
if ((buflen - ret) <
(CWALL_MAX_CONFLICTSETS * CWALL_MAX_TYPES * sizeof(domaintype_t)))
return -1; /* not enough space */
/* now the chinese wall policy conflict sets */
conflicts = (domaintype_t *) (bufstart +
ntohl(chwall_bin_pol->
chwall_conflict_sets_offset));
memset((void *) conflicts, 0,
CWALL_MAX_CONFLICTSETS * CWALL_MAX_TYPES *
sizeof(domaintype_t));
/* just 1 conflict set [0]={2,3}, [1]={1,5,6} */
if (CWALL_MAX_TYPES > 3)
{
conflicts[2] = htons(1);
conflicts[3] = htons(1); /* {2,3} */
conflicts[CWALL_MAX_TYPES + 1] = htons(1);
conflicts[CWALL_MAX_TYPES + 5] = htons(1);
conflicts[CWALL_MAX_TYPES + 6] = htons(1); /* {0,5,6} */
}
ret += sizeof(domaintype_t) * CWALL_MAX_CONFLICTSETS * CWALL_MAX_TYPES;
return ret;
}
int acm_domain_set_stepolicy(void *bufstart, int buflen)
{
#define STE_MAX_SSIDREFS 6
#define STE_MAX_TYPES 5
struct acm_ste_policy_buffer *ste_bin_pol =
(struct acm_ste_policy_buffer *) bufstart;
domaintype_t *ssidrefs;
int j, ret = 0;
ste_bin_pol->ste_max_types = htonl(STE_MAX_TYPES);
ste_bin_pol->ste_max_ssidrefs = htonl(STE_MAX_SSIDREFS);
ste_bin_pol->policy_code = htonl(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY);
ste_bin_pol->policy_version = htonl(ACM_STE_VERSION);
ste_bin_pol->ste_ssid_offset =
htonl(sizeof(struct acm_ste_policy_buffer));
ret += sizeof(struct acm_ste_policy_buffer);
/* check buffer size */
if ((buflen - ret) <
(STE_MAX_TYPES * STE_MAX_SSIDREFS * sizeof(domaintype_t)))
return -1; /* not enough space */
ssidrefs =
(domaintype_t *) (bufstart + ntohl(ste_bin_pol->ste_ssid_offset));
memset(ssidrefs, 0,
STE_MAX_TYPES * STE_MAX_SSIDREFS * sizeof(domaintype_t));
/* all types 1 for ssidref 1 */
for (j = 0; j < STE_MAX_TYPES; j++)
ssidrefs[1 * STE_MAX_TYPES + j] = htons(1);
/* now set type j-1 for ssidref j */
for (j = 0; j < STE_MAX_SSIDREFS; j++)
if ((0 < j) && (j <= STE_MAX_TYPES))
ssidrefs[j * STE_MAX_TYPES + j - 1] = htons(1);
ret += STE_MAX_TYPES * STE_MAX_SSIDREFS * sizeof(domaintype_t);
return ret;
}
#define MAX_PUSH_BUFFER 16384
u8 push_buffer[MAX_PUSH_BUFFER];
int acm_domain_setpolicy(int xc_handle)
{
int ret;
struct acm_policy_buffer *bin_pol;
acm_op_t op;
/* future: read policy from file and set it */
bin_pol = (struct acm_policy_buffer *) push_buffer;
bin_pol->policy_version = htonl(ACM_POLICY_VERSION);
bin_pol->magic = htonl(ACM_MAGIC);
bin_pol->primary_policy_code = htonl(ACM_CHINESE_WALL_POLICY);
bin_pol->secondary_policy_code =
htonl(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY);
bin_pol->len = htonl(sizeof(struct acm_policy_buffer));
bin_pol->primary_buffer_offset = htonl(ntohl(bin_pol->len));
ret =
acm_domain_set_chwallpolicy(push_buffer +
ntohl(bin_pol->primary_buffer_offset),
MAX_PUSH_BUFFER -
ntohl(bin_pol->primary_buffer_offset));
if (ret < 0)
{
printf("ERROR creating chwallpolicy buffer.\n");
return -1;
}
bin_pol->len = htonl(ntohl(bin_pol->len) + ret);
bin_pol->secondary_buffer_offset = htonl(ntohl(bin_pol->len));
ret = acm_domain_set_stepolicy(push_buffer +
ntohl(bin_pol->secondary_buffer_offset),
MAX_PUSH_BUFFER -
ntohl(bin_pol->secondary_buffer_offset));
if (ret < 0)
{
printf("ERROR creating chwallpolicy buffer.\n");
return -1;
}
bin_pol->len = htonl(ntohl(bin_pol->len) + ret);
/* dump it and then push it down into xen/acm */
acm_dump_policy_buffer(push_buffer, ntohl(bin_pol->len));
op.cmd = ACM_SETPOLICY;
op.interface_version = ACM_INTERFACE_VERSION;
op.u.setpolicy.pushcache = (void *) push_buffer;
op.u.setpolicy.pushcache_size = ntohl(bin_pol->len);
ret = do_acm_op(xc_handle, &op);
if (ret)
printf("ERROR setting policy. Use 'xm dmesg' to see details.\n");
else
printf("Successfully changed policy.\n");
return ret;
}
/******************************* get policy ******************************/
#define PULL_CACHE_SIZE 8192
u8 pull_buffer[PULL_CACHE_SIZE];
int acm_domain_getpolicy(int xc_handle)
{
acm_op_t op;
int ret;
memset(pull_buffer, 0x00, sizeof(pull_buffer));
op.cmd = ACM_GETPOLICY;
op.interface_version = ACM_INTERFACE_VERSION;
op.u.getpolicy.pullcache = (void *) pull_buffer;
op.u.getpolicy.pullcache_size = sizeof(pull_buffer);
ret = do_acm_op(xc_handle, &op);
/* dump policy */
acm_dump_policy_buffer(pull_buffer, sizeof(pull_buffer));
return ret;
}
/************************ load binary policy ******************************/
int acm_domain_loadpolicy(int xc_handle, const char *filename)
{
struct stat mystat;
int ret, fd;
off_t len;
u8 *buffer;
if ((ret = stat(filename, &mystat)))
{
printf("File %s not found.\n", filename);
goto out;
}
len = mystat.st_size;
if ((buffer = malloc(len)) == NULL)
{
ret = -ENOMEM;
goto out;
}
if ((fd = open(filename, O_RDONLY)) <= 0)
{
ret = -ENOENT;
printf("File %s not found.\n", filename);
goto free_out;
}
if (len == read(fd, buffer, len))
{
acm_op_t op;
/* dump it and then push it down into xen/acm */
acm_dump_policy_buffer(buffer, len);
op.cmd = ACM_SETPOLICY;
op.interface_version = ACM_INTERFACE_VERSION;
op.u.setpolicy.pushcache = (void *) buffer;
op.u.setpolicy.pushcache_size = len;
ret = do_acm_op(xc_handle, &op);
if (ret)
printf
("ERROR setting policy. Use 'xm dmesg' to see details.\n");
else
printf("Successfully changed policy.\n");
} else {
ret = -1;
}
close(fd);
free_out:
free(buffer);
out:
return ret;
}
/************************ dump hook statistics ******************************/
void dump_ste_stats(struct acm_ste_stats_buffer *ste_stats)
{
printf("STE-Policy Security Hook Statistics:\n");
printf("ste: event_channel eval_count = %d\n",
ntohl(ste_stats->ec_eval_count));
printf("ste: event_channel denied_count = %d\n",
ntohl(ste_stats->ec_denied_count));
printf("ste: event_channel cache_hit_count = %d\n",
ntohl(ste_stats->ec_cachehit_count));
printf("ste:\n");
printf("ste: grant_table eval_count = %d\n",
ntohl(ste_stats->gt_eval_count));
printf("ste: grant_table denied_count = %d\n",
ntohl(ste_stats->gt_denied_count));
printf("ste: grant_table cache_hit_count = %d\n",
ntohl(ste_stats->gt_cachehit_count));
}
#define PULL_STATS_SIZE 8192
int acm_domain_dumpstats(int xc_handle)
{
u8 stats_buffer[PULL_STATS_SIZE];
acm_op_t op;
int ret;
struct acm_stats_buffer *stats;
memset(stats_buffer, 0x00, sizeof(stats_buffer));
op.cmd = ACM_DUMPSTATS;
op.interface_version = ACM_INTERFACE_VERSION;
op.u.dumpstats.pullcache = (void *) stats_buffer;
op.u.dumpstats.pullcache_size = sizeof(stats_buffer);
ret = do_acm_op(xc_handle, &op);
if (ret < 0)
{
printf("ERROR dumping policy stats. Use 'xm dmesg' to see details.\n");
return ret;
}
stats = (struct acm_stats_buffer *) stats_buffer;
printf("\nPolicy dump:\n");
printf("============\n");
printf("Magic = %x.\n", ntohl(stats->magic));
printf("Len = %x.\n", ntohl(stats->len));
switch (ntohl(stats->primary_policy_code))
{
case ACM_NULL_POLICY:
printf("NULL Policy: No statistics apply.\n");
break;
case ACM_CHINESE_WALL_POLICY:
printf("Chinese Wall Policy: No statistics apply.\n");
break;
case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY:
dump_ste_stats((struct acm_ste_stats_buffer *) (stats_buffer +
ntohl(stats->
primary_stats_offset)));
break;
default:
printf("UNKNOWN PRIMARY POLICY ERROR!\n");
}
switch (ntohl(stats->secondary_policy_code))
{
case ACM_NULL_POLICY:
printf("NULL Policy: No statistics apply.\n");
break;
case ACM_CHINESE_WALL_POLICY:
printf("Chinese Wall Policy: No statistics apply.\n");
break;
case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY:
dump_ste_stats((struct acm_ste_stats_buffer *) (stats_buffer +
ntohl(stats->
secondary_stats_offset)));
break;
default:
printf("UNKNOWN SECONDARY POLICY ERROR!\n");
}
return ret;
}
/***************************** main **************************************/
void usage(char *progname)
{
printf("Use: %s \n"
"\t setpolicy\n"
"\t getpolicy\n"
"\t dumpstats\n"
"\t loadpolicy <binary policy file>\n", progname);
exit(-1);
}
int main(int argc, char **argv)
{
int acm_cmd_fd, ret = 0;
if (argc < 2)
usage(argv[0]);
if ((acm_cmd_fd = open("/proc/xen/privcmd", O_RDONLY)) <= 0)
{
printf("ERROR: Could not open xen privcmd device!\n");
exit(-1);
}
if (!strcmp(argv[1], "setpolicy"))
{
if (argc != 2)
usage(argv[0]);
ret = acm_domain_setpolicy(acm_cmd_fd);
} else if (!strcmp(argv[1], "getpolicy")) {
if (argc != 2)
usage(argv[0]);
ret = acm_domain_getpolicy(acm_cmd_fd);
} else if (!strcmp(argv[1], "loadpolicy")) {
if (argc != 3)
usage(argv[0]);
ret = acm_domain_loadpolicy(acm_cmd_fd, argv[2]);
} else if (!strcmp(argv[1], "dumpstats")) {
if (argc != 2)
usage(argv[0]);
ret = acm_domain_dumpstats(acm_cmd_fd);
} else
usage(argv[0]);
close(acm_cmd_fd);
return ret;
}