/* Author: Karl MacMillan <kmacmillan@tresys.com>
|
* Jason Tang <jtang@tresys.com>
|
* Chris PeBenito <cpebenito@tresys.com>
|
*
|
* Copyright (C) 2004-2005 Tresys Technology, LLC
|
*
|
* This library is free software; you can redistribute it and/or
|
* modify it under the terms of the GNU Lesser General Public
|
* License as published by the Free Software Foundation; either
|
* version 2.1 of the License, or (at your option) any later version.
|
*
|
* This library is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* Lesser General Public License for more details.
|
*
|
* You should have received a copy of the GNU Lesser General Public
|
* License along with this library; if not, write to the Free Software
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
*/
|
|
#include "policydb_internal.h"
|
#include "module_internal.h"
|
#include <sepol/policydb/link.h>
|
#include <sepol/policydb/expand.h>
|
#include <sepol/policydb/module.h>
|
#include "debug.h"
|
#include "private.h"
|
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <limits.h>
|
#include <inttypes.h>
|
|
#define SEPOL_PACKAGE_SECTION_FC 0xf97cff90
|
#define SEPOL_PACKAGE_SECTION_SEUSER 0x97cff91
|
#define SEPOL_PACKAGE_SECTION_USER_EXTRA 0x97cff92
|
#define SEPOL_PACKAGE_SECTION_NETFILTER 0x97cff93
|
|
static int policy_file_seek(struct policy_file *fp, size_t offset)
|
{
|
switch (fp->type) {
|
case PF_USE_STDIO:
|
if (offset > LONG_MAX) {
|
errno = EFAULT;
|
return -1;
|
}
|
return fseek(fp->fp, (long)offset, SEEK_SET);
|
case PF_USE_MEMORY:
|
if (offset > fp->size) {
|
errno = EFAULT;
|
return -1;
|
}
|
fp->data -= fp->size - fp->len;
|
fp->data += offset;
|
fp->len = fp->size - offset;
|
return 0;
|
default:
|
return 0;
|
}
|
}
|
|
static int policy_file_length(struct policy_file *fp, size_t *out)
|
{
|
long prev_offset, end_offset;
|
int rc;
|
switch (fp->type) {
|
case PF_USE_STDIO:
|
prev_offset = ftell(fp->fp);
|
if (prev_offset < 0)
|
return prev_offset;
|
rc = fseek(fp->fp, 0L, SEEK_END);
|
if (rc < 0)
|
return rc;
|
end_offset = ftell(fp->fp);
|
if (end_offset < 0)
|
return end_offset;
|
rc = fseek(fp->fp, prev_offset, SEEK_SET);
|
if (rc < 0)
|
return rc;
|
*out = end_offset;
|
break;
|
case PF_USE_MEMORY:
|
*out = fp->size;
|
break;;
|
default:
|
*out = 0;
|
break;
|
}
|
return 0;
|
}
|
|
static int module_package_init(sepol_module_package_t * p)
|
{
|
memset(p, 0, sizeof(sepol_module_package_t));
|
if (sepol_policydb_create(&p->policy))
|
return -1;
|
|
p->version = 1;
|
return 0;
|
}
|
|
static int set_char(char **field, char *data, size_t len)
|
{
|
if (*field) {
|
free(*field);
|
*field = NULL;
|
}
|
if (len) {
|
*field = malloc(len);
|
if (!*field)
|
return -1;
|
memcpy(*field, data, len);
|
}
|
return 0;
|
}
|
|
int sepol_module_package_create(sepol_module_package_t ** p)
|
{
|
int rc;
|
|
*p = calloc(1, sizeof(sepol_module_package_t));
|
if (!(*p))
|
return -1;
|
|
rc = module_package_init(*p);
|
if (rc < 0)
|
free(*p);
|
|
return rc;
|
}
|
|
hidden_def(sepol_module_package_create)
|
|
/* Deallocates all memory associated with a module package, including
|
* the pointer itself. Does nothing if p is NULL.
|
*/
|
void sepol_module_package_free(sepol_module_package_t * p)
|
{
|
if (p == NULL)
|
return;
|
|
sepol_policydb_free(p->policy);
|
free(p->file_contexts);
|
free(p->seusers);
|
free(p->user_extra);
|
free(p->netfilter_contexts);
|
free(p);
|
}
|
|
hidden_def(sepol_module_package_free)
|
|
char *sepol_module_package_get_file_contexts(sepol_module_package_t * p)
|
{
|
return p->file_contexts;
|
}
|
|
size_t sepol_module_package_get_file_contexts_len(sepol_module_package_t * p)
|
{
|
return p->file_contexts_len;
|
}
|
|
char *sepol_module_package_get_seusers(sepol_module_package_t * p)
|
{
|
return p->seusers;
|
}
|
|
size_t sepol_module_package_get_seusers_len(sepol_module_package_t * p)
|
{
|
return p->seusers_len;
|
}
|
|
char *sepol_module_package_get_user_extra(sepol_module_package_t * p)
|
{
|
return p->user_extra;
|
}
|
|
size_t sepol_module_package_get_user_extra_len(sepol_module_package_t * p)
|
{
|
return p->user_extra_len;
|
}
|
|
char *sepol_module_package_get_netfilter_contexts(sepol_module_package_t * p)
|
{
|
return p->netfilter_contexts;
|
}
|
|
size_t sepol_module_package_get_netfilter_contexts_len(sepol_module_package_t *
|
p)
|
{
|
return p->netfilter_contexts_len;
|
}
|
|
int sepol_module_package_set_file_contexts(sepol_module_package_t * p,
|
char *data, size_t len)
|
{
|
if (set_char(&p->file_contexts, data, len))
|
return -1;
|
|
p->file_contexts_len = len;
|
return 0;
|
}
|
|
int sepol_module_package_set_seusers(sepol_module_package_t * p,
|
char *data, size_t len)
|
{
|
if (set_char(&p->seusers, data, len))
|
return -1;
|
|
p->seusers_len = len;
|
return 0;
|
}
|
|
int sepol_module_package_set_user_extra(sepol_module_package_t * p,
|
char *data, size_t len)
|
{
|
if (set_char(&p->user_extra, data, len))
|
return -1;
|
|
p->user_extra_len = len;
|
return 0;
|
}
|
|
int sepol_module_package_set_netfilter_contexts(sepol_module_package_t * p,
|
char *data, size_t len)
|
{
|
if (set_char(&p->netfilter_contexts, data, len))
|
return -1;
|
|
p->netfilter_contexts_len = len;
|
return 0;
|
}
|
|
sepol_policydb_t *sepol_module_package_get_policy(sepol_module_package_t * p)
|
{
|
return p->policy;
|
}
|
|
/* Append each of the file contexts from each module to the base
|
* policy's file context. 'base_context' will be reallocated to a
|
* larger size (and thus it is an in/out reference
|
* variable). 'base_fc_len' is the length of base's file context; it
|
* too is a reference variable. Return 0 on success, -1 if out of
|
* memory. */
|
static int link_file_contexts(sepol_module_package_t * base,
|
sepol_module_package_t ** modules,
|
int num_modules)
|
{
|
size_t fc_len;
|
int i;
|
char *s;
|
|
fc_len = base->file_contexts_len;
|
for (i = 0; i < num_modules; i++) {
|
fc_len += modules[i]->file_contexts_len;
|
}
|
|
if ((s = (char *)realloc(base->file_contexts, fc_len)) == NULL) {
|
return -1;
|
}
|
base->file_contexts = s;
|
for (i = 0; i < num_modules; i++) {
|
memcpy(base->file_contexts + base->file_contexts_len,
|
modules[i]->file_contexts,
|
modules[i]->file_contexts_len);
|
base->file_contexts_len += modules[i]->file_contexts_len;
|
}
|
return 0;
|
}
|
|
/* Append each of the netfilter contexts from each module to the base
|
* policy's netfilter context. 'base_context' will be reallocated to a
|
* larger size (and thus it is an in/out reference
|
* variable). 'base_nc_len' is the length of base's netfilter contexts; it
|
* too is a reference variable. Return 0 on success, -1 if out of
|
* memory. */
|
static int link_netfilter_contexts(sepol_module_package_t * base,
|
sepol_module_package_t ** modules,
|
int num_modules)
|
{
|
size_t base_nc_len;
|
int i;
|
char *base_context;
|
|
base_nc_len = base->netfilter_contexts_len;
|
for (i = 0; i < num_modules; i++) {
|
base_nc_len += modules[i]->netfilter_contexts_len;
|
}
|
|
if ((base_context =
|
(char *)realloc(base->netfilter_contexts, base_nc_len)) == NULL) {
|
return -1;
|
}
|
base->netfilter_contexts = base_context;
|
for (i = 0; i < num_modules; i++) {
|
memcpy(base->netfilter_contexts + base->netfilter_contexts_len,
|
modules[i]->netfilter_contexts,
|
modules[i]->netfilter_contexts_len);
|
base->netfilter_contexts_len +=
|
modules[i]->netfilter_contexts_len;
|
}
|
return 0;
|
}
|
|
/* Links the module packages into the base. Returns 0 on success, -1
|
* if a requirement was not met, or -2 for all other errors. */
|
int sepol_link_packages(sepol_handle_t * handle,
|
sepol_module_package_t * base,
|
sepol_module_package_t ** modules, int num_modules,
|
int verbose)
|
{
|
policydb_t **mod_pols = NULL;
|
int i, retval;
|
|
if ((mod_pols = calloc(num_modules, sizeof(*mod_pols))) == NULL) {
|
ERR(handle, "Out of memory!");
|
return -2;
|
}
|
for (i = 0; i < num_modules; i++) {
|
mod_pols[i] = &modules[i]->policy->p;
|
}
|
|
retval = link_modules(handle, &base->policy->p, mod_pols, num_modules,
|
verbose);
|
free(mod_pols);
|
if (retval == -3) {
|
return -1;
|
} else if (retval < 0) {
|
return -2;
|
}
|
|
if (link_file_contexts(base, modules, num_modules) == -1) {
|
ERR(handle, "Out of memory!");
|
return -2;
|
}
|
|
if (link_netfilter_contexts(base, modules, num_modules) == -1) {
|
ERR(handle, "Out of memory!");
|
return -2;
|
}
|
|
return 0;
|
}
|
|
/* buf must be large enough - no checks are performed */
|
#define _read_helper_bufsize BUFSIZ
|
static int read_helper(char *buf, struct policy_file *file, uint32_t bytes)
|
{
|
uint32_t offset, nel, read_len;
|
int rc;
|
|
offset = 0;
|
nel = bytes;
|
|
while (nel) {
|
if (nel < _read_helper_bufsize)
|
read_len = nel;
|
else
|
read_len = _read_helper_bufsize;
|
rc = next_entry(&buf[offset], file, read_len);
|
if (rc < 0)
|
return -1;
|
offset += read_len;
|
nel -= read_len;
|
}
|
return 0;
|
}
|
|
#define MAXSECTIONS 100
|
|
/* Get the section offsets from a package file, offsets will be malloc'd to
|
* the appropriate size and the caller must free() them */
|
static int module_package_read_offsets(sepol_module_package_t * mod,
|
struct policy_file *file,
|
size_t ** offsets, uint32_t * sections)
|
{
|
uint32_t *buf = NULL, nsec;
|
unsigned i;
|
size_t *off = NULL;
|
int rc;
|
|
buf = malloc(sizeof(uint32_t)*3);
|
if (!buf) {
|
ERR(file->handle, "out of memory");
|
goto err;
|
}
|
|
rc = next_entry(buf, file, sizeof(uint32_t) * 3);
|
if (rc < 0) {
|
ERR(file->handle, "module package header truncated");
|
goto err;
|
}
|
if (le32_to_cpu(buf[0]) != SEPOL_MODULE_PACKAGE_MAGIC) {
|
ERR(file->handle,
|
"wrong magic number for module package: expected %#08x, got %#08x",
|
SEPOL_MODULE_PACKAGE_MAGIC, le32_to_cpu(buf[0]));
|
goto err;
|
}
|
|
mod->version = le32_to_cpu(buf[1]);
|
nsec = *sections = le32_to_cpu(buf[2]);
|
|
if (nsec > MAXSECTIONS) {
|
ERR(file->handle, "too many sections (%u) in module package",
|
nsec);
|
goto err;
|
}
|
|
off = (size_t *) malloc((nsec + 1) * sizeof(size_t));
|
if (!off) {
|
ERR(file->handle, "out of memory");
|
goto err;
|
}
|
|
free(buf);
|
buf = malloc(sizeof(uint32_t) * nsec);
|
if (!buf) {
|
ERR(file->handle, "out of memory");
|
goto err;
|
}
|
rc = next_entry(buf, file, sizeof(uint32_t) * nsec);
|
if (rc < 0) {
|
ERR(file->handle, "module package offset array truncated");
|
goto err;
|
}
|
|
for (i = 0; i < nsec; i++) {
|
off[i] = le32_to_cpu(buf[i]);
|
if (i && off[i] < off[i - 1]) {
|
ERR(file->handle, "offsets are not increasing (at %u, "
|
"offset %zu -> %zu", i, off[i - 1],
|
off[i]);
|
goto err;
|
}
|
}
|
|
rc = policy_file_length(file, &off[nsec]);
|
if (rc < 0)
|
goto err;
|
|
if (nsec && off[nsec] < off[nsec-1]) {
|
ERR(file->handle, "offset greater than file size (at %u, "
|
"offset %zu -> %zu", nsec, off[nsec - 1],
|
off[nsec]);
|
goto err;
|
}
|
*offsets = off;
|
free(buf);
|
return 0;
|
|
err:
|
free(buf);
|
free(off);
|
return -1;
|
}
|
|
/* Flags for which sections have been seen during parsing of module package. */
|
#define SEEN_MOD 1
|
#define SEEN_FC 2
|
#define SEEN_SEUSER 4
|
#define SEEN_USER_EXTRA 8
|
#define SEEN_NETFILTER 16
|
|
int sepol_module_package_read(sepol_module_package_t * mod,
|
struct sepol_policy_file *spf, int verbose)
|
{
|
struct policy_file *file = &spf->pf;
|
uint32_t buf[1], nsec;
|
size_t *offsets, len;
|
int rc;
|
unsigned i, seen = 0;
|
|
if (module_package_read_offsets(mod, file, &offsets, &nsec))
|
return -1;
|
|
/* we know the section offsets, seek to them and read in the data */
|
|
for (i = 0; i < nsec; i++) {
|
|
if (policy_file_seek(file, offsets[i])) {
|
ERR(file->handle, "error seeking to offset %zu for "
|
"module package section %u", offsets[i], i);
|
goto cleanup;
|
}
|
|
len = offsets[i + 1] - offsets[i];
|
|
if (len < sizeof(uint32_t)) {
|
ERR(file->handle, "module package section %u "
|
"has too small length %zu", i, len);
|
goto cleanup;
|
}
|
|
/* read the magic number, so that we know which function to call */
|
rc = next_entry(buf, file, sizeof(uint32_t));
|
if (rc < 0) {
|
ERR(file->handle,
|
"module package section %u truncated, lacks magic number",
|
i);
|
goto cleanup;
|
}
|
|
switch (le32_to_cpu(buf[0])) {
|
case SEPOL_PACKAGE_SECTION_FC:
|
if (seen & SEEN_FC) {
|
ERR(file->handle,
|
"found multiple file contexts sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
mod->file_contexts_len = len - sizeof(uint32_t);
|
mod->file_contexts =
|
(char *)malloc(mod->file_contexts_len);
|
if (!mod->file_contexts) {
|
ERR(file->handle, "out of memory");
|
goto cleanup;
|
}
|
if (read_helper
|
(mod->file_contexts, file,
|
mod->file_contexts_len)) {
|
ERR(file->handle,
|
"invalid file contexts section at section %u",
|
i);
|
free(mod->file_contexts);
|
mod->file_contexts = NULL;
|
goto cleanup;
|
}
|
seen |= SEEN_FC;
|
break;
|
case SEPOL_PACKAGE_SECTION_SEUSER:
|
if (seen & SEEN_SEUSER) {
|
ERR(file->handle,
|
"found multiple seuser sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
mod->seusers_len = len - sizeof(uint32_t);
|
mod->seusers = (char *)malloc(mod->seusers_len);
|
if (!mod->seusers) {
|
ERR(file->handle, "out of memory");
|
goto cleanup;
|
}
|
if (read_helper(mod->seusers, file, mod->seusers_len)) {
|
ERR(file->handle,
|
"invalid seuser section at section %u", i);
|
free(mod->seusers);
|
mod->seusers = NULL;
|
goto cleanup;
|
}
|
seen |= SEEN_SEUSER;
|
break;
|
case SEPOL_PACKAGE_SECTION_USER_EXTRA:
|
if (seen & SEEN_USER_EXTRA) {
|
ERR(file->handle,
|
"found multiple user_extra sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
mod->user_extra_len = len - sizeof(uint32_t);
|
mod->user_extra = (char *)malloc(mod->user_extra_len);
|
if (!mod->user_extra) {
|
ERR(file->handle, "out of memory");
|
goto cleanup;
|
}
|
if (read_helper
|
(mod->user_extra, file, mod->user_extra_len)) {
|
ERR(file->handle,
|
"invalid user_extra section at section %u",
|
i);
|
free(mod->user_extra);
|
mod->user_extra = NULL;
|
goto cleanup;
|
}
|
seen |= SEEN_USER_EXTRA;
|
break;
|
case SEPOL_PACKAGE_SECTION_NETFILTER:
|
if (seen & SEEN_NETFILTER) {
|
ERR(file->handle,
|
"found multiple netfilter contexts sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
mod->netfilter_contexts_len = len - sizeof(uint32_t);
|
mod->netfilter_contexts =
|
(char *)malloc(mod->netfilter_contexts_len);
|
if (!mod->netfilter_contexts) {
|
ERR(file->handle, "out of memory");
|
goto cleanup;
|
}
|
if (read_helper
|
(mod->netfilter_contexts, file,
|
mod->netfilter_contexts_len)) {
|
ERR(file->handle,
|
"invalid netfilter contexts section at section %u",
|
i);
|
free(mod->netfilter_contexts);
|
mod->netfilter_contexts = NULL;
|
goto cleanup;
|
}
|
seen |= SEEN_NETFILTER;
|
break;
|
case POLICYDB_MOD_MAGIC:
|
if (seen & SEEN_MOD) {
|
ERR(file->handle,
|
"found multiple module sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
/* seek back to where the magic number was */
|
if (policy_file_seek(file, offsets[i]))
|
goto cleanup;
|
|
rc = policydb_read(&mod->policy->p, file, verbose);
|
if (rc < 0) {
|
ERR(file->handle,
|
"invalid module in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
seen |= SEEN_MOD;
|
break;
|
default:
|
/* unknown section, ignore */
|
ERR(file->handle,
|
"unknown magic number at section %u, offset: %zx, number: %x ",
|
i, offsets[i], le32_to_cpu(buf[0]));
|
break;
|
}
|
}
|
|
if ((seen & SEEN_MOD) == 0) {
|
ERR(file->handle, "missing module in module package");
|
goto cleanup;
|
}
|
|
free(offsets);
|
return 0;
|
|
cleanup:
|
free(offsets);
|
return -1;
|
}
|
|
int sepol_module_package_info(struct sepol_policy_file *spf, int *type,
|
char **name, char **version)
|
{
|
struct policy_file *file = &spf->pf;
|
sepol_module_package_t *mod = NULL;
|
uint32_t buf[5], len, nsec;
|
size_t *offsets = NULL;
|
unsigned i, seen = 0;
|
char *id;
|
int rc;
|
|
if (sepol_module_package_create(&mod))
|
return -1;
|
|
if (module_package_read_offsets(mod, file, &offsets, &nsec)) {
|
goto cleanup;
|
}
|
|
for (i = 0; i < nsec; i++) {
|
|
if (policy_file_seek(file, offsets[i])) {
|
ERR(file->handle, "error seeking to offset "
|
"%zu for module package section %u", offsets[i], i);
|
goto cleanup;
|
}
|
|
len = offsets[i + 1] - offsets[i];
|
|
if (len < sizeof(uint32_t)) {
|
ERR(file->handle,
|
"module package section %u has too small length %u",
|
i, len);
|
goto cleanup;
|
}
|
|
/* read the magic number, so that we know which function to call */
|
rc = next_entry(buf, file, sizeof(uint32_t) * 2);
|
if (rc < 0) {
|
ERR(file->handle,
|
"module package section %u truncated, lacks magic number",
|
i);
|
goto cleanup;
|
}
|
|
switch (le32_to_cpu(buf[0])) {
|
case SEPOL_PACKAGE_SECTION_FC:
|
/* skip file contexts */
|
if (seen & SEEN_FC) {
|
ERR(file->handle,
|
"found multiple file contexts sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
seen |= SEEN_FC;
|
break;
|
case SEPOL_PACKAGE_SECTION_SEUSER:
|
/* skip seuser */
|
if (seen & SEEN_SEUSER) {
|
ERR(file->handle,
|
"found seuser sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
seen |= SEEN_SEUSER;
|
break;
|
case SEPOL_PACKAGE_SECTION_USER_EXTRA:
|
/* skip user_extra */
|
if (seen & SEEN_USER_EXTRA) {
|
ERR(file->handle,
|
"found user_extra sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
seen |= SEEN_USER_EXTRA;
|
break;
|
case SEPOL_PACKAGE_SECTION_NETFILTER:
|
/* skip netfilter contexts */
|
if (seen & SEEN_NETFILTER) {
|
ERR(file->handle,
|
"found multiple netfilter contexts sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
seen |= SEEN_NETFILTER;
|
break;
|
case POLICYDB_MOD_MAGIC:
|
if (seen & SEEN_MOD) {
|
ERR(file->handle,
|
"found multiple module sections in module package (at section %u)",
|
i);
|
goto cleanup;
|
}
|
len = le32_to_cpu(buf[1]);
|
if (len != strlen(POLICYDB_MOD_STRING)) {
|
ERR(file->handle,
|
"module string length is wrong (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
/* skip id */
|
id = malloc(len + 1);
|
if (!id) {
|
ERR(file->handle,
|
"out of memory (at section %u)",
|
i);
|
goto cleanup;
|
}
|
rc = next_entry(id, file, len);
|
free(id);
|
if (rc < 0) {
|
ERR(file->handle,
|
"cannot get module string (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
rc = next_entry(buf, file, sizeof(uint32_t) * 5);
|
if (rc < 0) {
|
ERR(file->handle,
|
"cannot get module header (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
*type = le32_to_cpu(buf[0]);
|
/* if base - we're done */
|
if (*type == POLICY_BASE) {
|
*name = NULL;
|
*version = NULL;
|
seen |= SEEN_MOD;
|
break;
|
} else if (*type != POLICY_MOD) {
|
ERR(file->handle,
|
"module has invalid type %d (at section %u)",
|
*type, i);
|
goto cleanup;
|
}
|
|
/* read the name and version */
|
rc = next_entry(buf, file, sizeof(uint32_t));
|
if (rc < 0) {
|
ERR(file->handle,
|
"cannot get module name len (at section %u)",
|
i);
|
goto cleanup;
|
}
|
|
len = le32_to_cpu(buf[0]);
|
if (str_read(name, file, len)) {
|
ERR(file->handle, "%s", strerror(errno));
|
goto cleanup;
|
}
|
|
rc = next_entry(buf, file, sizeof(uint32_t));
|
if (rc < 0) {
|
ERR(file->handle,
|
"cannot get module version len (at section %u)",
|
i);
|
goto cleanup;
|
}
|
len = le32_to_cpu(buf[0]);
|
if (str_read(version, file, len)) {
|
ERR(file->handle, "%s", strerror(errno));
|
goto cleanup;
|
}
|
seen |= SEEN_MOD;
|
break;
|
default:
|
break;
|
}
|
|
}
|
|
if ((seen & SEEN_MOD) == 0) {
|
ERR(file->handle, "missing module in module package");
|
goto cleanup;
|
}
|
|
sepol_module_package_free(mod);
|
free(offsets);
|
return 0;
|
|
cleanup:
|
sepol_module_package_free(mod);
|
free(offsets);
|
return -1;
|
}
|
|
static int write_helper(char *data, size_t len, struct policy_file *file)
|
{
|
int idx = 0;
|
size_t len2;
|
|
while (len) {
|
if (len > BUFSIZ)
|
len2 = BUFSIZ;
|
else
|
len2 = len;
|
|
if (put_entry(&data[idx], 1, len2, file) != len2) {
|
return -1;
|
}
|
len -= len2;
|
idx += len2;
|
}
|
return 0;
|
}
|
|
int sepol_module_package_write(sepol_module_package_t * p,
|
struct sepol_policy_file *spf)
|
{
|
struct policy_file *file = &spf->pf;
|
policy_file_t polfile;
|
uint32_t buf[5], offsets[5], len, nsec = 0;
|
int i;
|
|
if (p->policy) {
|
/* compute policy length */
|
policy_file_init(&polfile);
|
polfile.type = PF_LEN;
|
polfile.handle = file->handle;
|
if (policydb_write(&p->policy->p, &polfile))
|
return -1;
|
len = polfile.len;
|
if (!polfile.len)
|
return -1;
|
nsec++;
|
|
} else {
|
/* We don't support writing a package without a module at this point */
|
return -1;
|
}
|
|
/* seusers and user_extra only supported in base at the moment */
|
if ((p->seusers || p->user_extra)
|
&& (p->policy->p.policy_type != SEPOL_POLICY_BASE)) {
|
ERR(file->handle,
|
"seuser and user_extra sections only supported in base");
|
return -1;
|
}
|
|
if (p->file_contexts)
|
nsec++;
|
|
if (p->seusers)
|
nsec++;
|
|
if (p->user_extra)
|
nsec++;
|
|
if (p->netfilter_contexts)
|
nsec++;
|
|
buf[0] = cpu_to_le32(SEPOL_MODULE_PACKAGE_MAGIC);
|
buf[1] = cpu_to_le32(p->version);
|
buf[2] = cpu_to_le32(nsec);
|
if (put_entry(buf, sizeof(uint32_t), 3, file) != 3)
|
return -1;
|
|
/* calculate offsets */
|
offsets[0] = (nsec + 3) * sizeof(uint32_t);
|
buf[0] = cpu_to_le32(offsets[0]);
|
|
i = 1;
|
if (p->file_contexts) {
|
offsets[i] = offsets[i - 1] + len;
|
buf[i] = cpu_to_le32(offsets[i]);
|
/* add a uint32_t to compensate for the magic number */
|
len = p->file_contexts_len + sizeof(uint32_t);
|
i++;
|
}
|
if (p->seusers) {
|
offsets[i] = offsets[i - 1] + len;
|
buf[i] = cpu_to_le32(offsets[i]);
|
len = p->seusers_len + sizeof(uint32_t);
|
i++;
|
}
|
if (p->user_extra) {
|
offsets[i] = offsets[i - 1] + len;
|
buf[i] = cpu_to_le32(offsets[i]);
|
len = p->user_extra_len + sizeof(uint32_t);
|
i++;
|
}
|
if (p->netfilter_contexts) {
|
offsets[i] = offsets[i - 1] + len;
|
buf[i] = cpu_to_le32(offsets[i]);
|
len = p->netfilter_contexts_len + sizeof(uint32_t);
|
i++;
|
}
|
if (put_entry(buf, sizeof(uint32_t), nsec, file) != nsec)
|
return -1;
|
|
/* write sections */
|
|
if (policydb_write(&p->policy->p, file))
|
return -1;
|
|
if (p->file_contexts) {
|
buf[0] = cpu_to_le32(SEPOL_PACKAGE_SECTION_FC);
|
if (put_entry(buf, sizeof(uint32_t), 1, file) != 1)
|
return -1;
|
if (write_helper(p->file_contexts, p->file_contexts_len, file))
|
return -1;
|
}
|
if (p->seusers) {
|
buf[0] = cpu_to_le32(SEPOL_PACKAGE_SECTION_SEUSER);
|
if (put_entry(buf, sizeof(uint32_t), 1, file) != 1)
|
return -1;
|
if (write_helper(p->seusers, p->seusers_len, file))
|
return -1;
|
|
}
|
if (p->user_extra) {
|
buf[0] = cpu_to_le32(SEPOL_PACKAGE_SECTION_USER_EXTRA);
|
if (put_entry(buf, sizeof(uint32_t), 1, file) != 1)
|
return -1;
|
if (write_helper(p->user_extra, p->user_extra_len, file))
|
return -1;
|
}
|
if (p->netfilter_contexts) {
|
buf[0] = cpu_to_le32(SEPOL_PACKAGE_SECTION_NETFILTER);
|
if (put_entry(buf, sizeof(uint32_t), 1, file) != 1)
|
return -1;
|
if (write_helper
|
(p->netfilter_contexts, p->netfilter_contexts_len, file))
|
return -1;
|
}
|
return 0;
|
}
|
|
int sepol_link_modules(sepol_handle_t * handle,
|
sepol_policydb_t * base,
|
sepol_policydb_t ** modules, size_t len, int verbose)
|
{
|
return link_modules(handle, &base->p, (policydb_t **) modules, len,
|
verbose);
|
}
|
|
int sepol_expand_module(sepol_handle_t * handle,
|
sepol_policydb_t * base,
|
sepol_policydb_t * out, int verbose, int check)
|
{
|
return expand_module(handle, &base->p, &out->p, verbose, check);
|
}
|