| /* 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> |
| |
| #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 size_t policy_file_length(struct policy_file *fp) |
| { |
| long prev_offset, end_offset; |
| switch (fp->type) { |
| case PF_USE_STDIO: |
| prev_offset = ftell(fp->fp); |
| fseek(fp->fp, 0L, SEEK_END); |
| end_offset = ftell(fp->fp); |
| fseek(fp->fp, prev_offset, SEEK_SET); |
| return end_offset; |
| case PF_USE_MEMORY: |
| return fp->size; |
| default: |
| 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) |
| { |
| *p = calloc(1, sizeof(sepol_module_package_t)); |
| if (!(*p)) |
| return -1; |
| return module_package_init(*p); |
| } |
| |
| 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; |
| } |
| } |
| |
| off[nsec] = policy_file_length(file); |
| 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: %ux ", |
| 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]); |
| *name = malloc(len + 1); |
| if (!*name) { |
| ERR(file->handle, "out of memory"); |
| goto cleanup; |
| } |
| rc = next_entry(*name, file, len); |
| if (rc < 0) { |
| ERR(file->handle, |
| "cannot get module name string (at section %u)", |
| i); |
| goto cleanup; |
| } |
| (*name)[len] = '\0'; |
| 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]); |
| *version = malloc(len + 1); |
| if (!*version) { |
| ERR(file->handle, "out of memory"); |
| goto cleanup; |
| } |
| rc = next_entry(*version, file, len); |
| if (rc < 0) { |
| ERR(file->handle, |
| "cannot get module version string (at section %u)", |
| i); |
| goto cleanup; |
| } |
| (*version)[len] = '\0'; |
| 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); |
| } |