From f8c74eaf19df123deabe9e2c71bd5b3c2beba06a Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Thu, 3 Feb 2022 17:53:24 +0100 Subject: [PATCH] libsemanage: move compressed file handling into a separate object In order to reduce exisiting and future code duplication and to avoid some unnecessary allocations and copying, factor the compressed file utility functions out into a separate C/header file and refactor their interface. Note that this change effectively removes the __fsetlocking(3) call from semanage_load_files() - I haven't been able to figure out what purpose it serves, but it seems pointless... Signed-off-by: Ondrej Mosnacek --- libsemanage/src/compressed_file.c | 224 +++++++++++++++++++++++++ libsemanage/src/compressed_file.h | 78 +++++++++ libsemanage/src/direct_api.c | 263 +++++------------------------- libsemanage/src/direct_api.h | 4 - libsemanage/src/semanage_store.c | 52 ++---- 5 files changed, 354 insertions(+), 267 deletions(-) create mode 100644 libsemanage/src/compressed_file.c create mode 100644 libsemanage/src/compressed_file.h diff --git a/libsemanage/src/compressed_file.c b/libsemanage/src/compressed_file.c new file mode 100644 index 00000000..5546b830 --- /dev/null +++ b/libsemanage/src/compressed_file.c @@ -0,0 +1,224 @@ +/* Author: Jason Tang + * Christopher Ashworth + * Ondrej Mosnacek + * + * Copyright (C) 2004-2006 Tresys Technology, LLC + * Copyright (C) 2005-2021 Red Hat, Inc. + * + * 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 +#include +#include + +#include +#include + +#include + +#include "compressed_file.h" + +#include "debug.h" + +#define BZ2_MAGICSTR "BZh" +#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1) + +/* bzip() a data to a file, returning the total number of compressed bytes + * in the file. Returns -1 if file could not be compressed. */ +static int bzip(semanage_handle_t *sh, const char *filename, void *data, + size_t num_bytes) +{ + BZFILE* b; + size_t size = 1<<16; + int bzerror; + size_t total = 0; + size_t len = 0; + FILE *f; + + if ((f = fopen(filename, "wb")) == NULL) { + return -1; + } + + if (!sh->conf->bzip_blocksize) { + if (fwrite(data, 1, num_bytes, f) < num_bytes) { + fclose(f); + return -1; + } + fclose(f); + return 0; + } + + b = BZ2_bzWriteOpen( &bzerror, f, sh->conf->bzip_blocksize, 0, 0); + if (bzerror != BZ_OK) { + BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); + fclose(f); + return -1; + } + + while ( num_bytes > total ) { + if (num_bytes - total > size) { + len = size; + } else { + len = num_bytes - total; + } + BZ2_bzWrite ( &bzerror, b, (uint8_t *)data + total, len ); + if (bzerror == BZ_IO_ERROR) { + BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); + fclose(f); + return -1; + } + total += len; + } + + BZ2_bzWriteClose ( &bzerror, b, 0, 0, 0 ); + fclose(f); + if (bzerror == BZ_IO_ERROR) { + return -1; + } + return 0; +} + +/* bunzip() a file to '*data', returning the total number of uncompressed bytes + * in the file. Returns -1 if file could not be decompressed. */ +static ssize_t bunzip(semanage_handle_t *sh, FILE *f, void **data) +{ + BZFILE* b = NULL; + size_t nBuf; + uint8_t* buf = NULL; + size_t size = 1<<18; + size_t bufsize = size; + int bzerror; + size_t total = 0; + uint8_t* uncompress = NULL; + uint8_t* tmpalloc = NULL; + int ret = -1; + + buf = malloc(bufsize); + if (buf == NULL) { + ERR(sh, "Failure allocating memory."); + goto exit; + } + + /* Check if the file is bzipped */ + bzerror = fread(buf, 1, BZ2_MAGICLEN, f); + rewind(f); + if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN)) { + goto exit; + } + + b = BZ2_bzReadOpen ( &bzerror, f, 0, sh->conf->bzip_small, NULL, 0 ); + if ( bzerror != BZ_OK ) { + ERR(sh, "Failure opening bz2 archive."); + goto exit; + } + + uncompress = malloc(size); + if (uncompress == NULL) { + ERR(sh, "Failure allocating memory."); + goto exit; + } + + while ( bzerror == BZ_OK) { + nBuf = BZ2_bzRead ( &bzerror, b, buf, bufsize); + if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) { + if (total + nBuf > size) { + size *= 2; + tmpalloc = realloc(uncompress, size); + if (tmpalloc == NULL) { + ERR(sh, "Failure allocating memory."); + goto exit; + } + uncompress = tmpalloc; + } + memcpy(&uncompress[total], buf, nBuf); + total += nBuf; + } + } + if ( bzerror != BZ_STREAM_END ) { + ERR(sh, "Failure reading bz2 archive."); + goto exit; + } + + ret = total; + *data = uncompress; + +exit: + BZ2_bzReadClose ( &bzerror, b ); + free(buf); + if ( ret < 0 ) { + free(uncompress); + } + return ret; +} + +int map_compressed_file(semanage_handle_t *sh, const char *path, + struct file_contents *contents) +{ + ssize_t size = -1; + void *uncompress; + int ret = 0, fd = -1; + FILE *file = NULL; + + fd = open(path, O_RDONLY); + if (fd == -1) { + ERR(sh, "Unable to open %s\n", path); + return -1; + } + + file = fdopen(fd, "r"); + if (file == NULL) { + ERR(sh, "Unable to open %s\n", path); + close(fd); + return -1; + } + + if ((size = bunzip(sh, file, &uncompress)) >= 0) { + contents->data = uncompress; + contents->len = size; + contents->compressed = 1; + } else { + struct stat sb; + if (fstat(fd, &sb) == -1 || + (uncompress = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == + MAP_FAILED) { + ret = -1; + } else { + contents->data = uncompress; + contents->len = sb.st_size; + contents->compressed = 0; + } + } + fclose(file); + return ret; +} + +void unmap_compressed_file(struct file_contents *contents) +{ + if (!contents->data) + return; + + if (contents->compressed) { + free(contents->data); + } else { + munmap(contents->data, contents->len); + } +} + +int write_compressed_file(semanage_handle_t *sh, const char *path, + void *data, size_t len) +{ + return bzip(sh, path, data, len); +} diff --git a/libsemanage/src/compressed_file.h b/libsemanage/src/compressed_file.h new file mode 100644 index 00000000..96cfb4b6 --- /dev/null +++ b/libsemanage/src/compressed_file.h @@ -0,0 +1,78 @@ +/* Author: Jason Tang + * Christopher Ashworth + * Ondrej Mosnacek + * + * Copyright (C) 2004-2006 Tresys Technology, LLC + * Copyright (C) 2005-2021 Red Hat, Inc. + * + * 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 + */ + +#ifndef _SEMANAGE_CIL_FILE_H_ +#define _SEMANAGE_CIL_FILE_H_ + +#include +#include + +#include "handle.h" + +struct file_contents { + void *data; /** file contents (uncompressed) */ + size_t len; /** length of contents */ + int compressed; /** whether file was compressed */ +}; + +/** + * Map/read a possibly-compressed file into memory. + * + * If the file is bzip compressed map_file will uncompress the file into + * @p contents. The caller is responsible for calling + * @ref unmap_compressed_file on @p contents on success. + * + * @param sh semanage handle + * @param path path to the file + * @param contents pointer to struct file_contents, which will be + * populated with data pointer, size, and an indication whether + * the file was compressed or not + * + * @return 0 on success, -1 otherwise. + */ +int map_compressed_file(semanage_handle_t *sh, const char *path, + struct file_contents *contents); + +/** + * Destroy a previously mapped possibly-compressed file. + * + * If all fields of @p contents are zero/NULL, the function is + * guaranteed to do nothing. + * + * @param contents pointer to struct file_contents to destroy + */ +void unmap_compressed_file(struct file_contents *contents); + +/** + * Write bytes into a file, using compression if configured. + * + * @param sh semanage handle + * @param path path to the file + * @param data pointer to the data + * @param len length of the data + * + * @return 0 on success, -1 otherwise. + */ +int write_compressed_file(semanage_handle_t *sh, const char *path, + void *data, size_t len); + +#endif diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c index 7638653a..63a18808 100644 --- a/libsemanage/src/direct_api.c +++ b/libsemanage/src/direct_api.c @@ -50,6 +50,7 @@ #include "debug.h" #include "handle.h" +#include "compressed_file.h" #include "modules.h" #include "direct_api.h" #include "semanage_store.h" @@ -446,194 +447,6 @@ static int parse_module_headers(semanage_handle_t * sh, char *module_data, return 0; } -#include -#include -#include -#include - -/* bzip() a data to a file, returning the total number of compressed bytes - * in the file. Returns -1 if file could not be compressed. */ -static ssize_t bzip(semanage_handle_t *sh, const char *filename, char *data, - size_t num_bytes) -{ - BZFILE* b; - size_t size = 1<<16; - int bzerror; - size_t total = 0; - size_t len = 0; - FILE *f; - - if ((f = fopen(filename, "wb")) == NULL) { - return -1; - } - - if (!sh->conf->bzip_blocksize) { - if (fwrite(data, 1, num_bytes, f) < num_bytes) { - fclose(f); - return -1; - } - fclose(f); - return num_bytes; - } - - b = BZ2_bzWriteOpen( &bzerror, f, sh->conf->bzip_blocksize, 0, 0); - if (bzerror != BZ_OK) { - BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); - return -1; - } - - while ( num_bytes > total ) { - if (num_bytes - total > size) { - len = size; - } else { - len = num_bytes - total; - } - BZ2_bzWrite ( &bzerror, b, &data[total], len ); - if (bzerror == BZ_IO_ERROR) { - BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 ); - return -1; - } - total += len; - } - - BZ2_bzWriteClose ( &bzerror, b, 0, 0, 0 ); - fclose(f); - if (bzerror == BZ_IO_ERROR) { - return -1; - } - return total; -} - -#define BZ2_MAGICSTR "BZh" -#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1) - -/* bunzip() a file to '*data', returning the total number of uncompressed bytes - * in the file. Returns -1 if file could not be decompressed. */ -ssize_t bunzip(semanage_handle_t *sh, FILE *f, char **data) -{ - BZFILE* b = NULL; - size_t nBuf; - char* buf = NULL; - size_t size = 1<<18; - size_t bufsize = size; - int bzerror; - size_t total = 0; - char* uncompress = NULL; - char* tmpalloc = NULL; - int ret = -1; - - buf = malloc(bufsize); - if (buf == NULL) { - ERR(sh, "Failure allocating memory."); - goto exit; - } - - /* Check if the file is bzipped */ - bzerror = fread(buf, 1, BZ2_MAGICLEN, f); - rewind(f); - if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN)) { - goto exit; - } - - b = BZ2_bzReadOpen ( &bzerror, f, 0, sh->conf->bzip_small, NULL, 0 ); - if ( bzerror != BZ_OK ) { - ERR(sh, "Failure opening bz2 archive."); - goto exit; - } - - uncompress = malloc(size); - if (uncompress == NULL) { - ERR(sh, "Failure allocating memory."); - goto exit; - } - - while ( bzerror == BZ_OK) { - nBuf = BZ2_bzRead ( &bzerror, b, buf, bufsize); - if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) { - if (total + nBuf > size) { - size *= 2; - tmpalloc = realloc(uncompress, size); - if (tmpalloc == NULL) { - ERR(sh, "Failure allocating memory."); - goto exit; - } - uncompress = tmpalloc; - } - memcpy(&uncompress[total], buf, nBuf); - total += nBuf; - } - } - if ( bzerror != BZ_STREAM_END ) { - ERR(sh, "Failure reading bz2 archive."); - goto exit; - } - - ret = total; - *data = uncompress; - -exit: - BZ2_bzReadClose ( &bzerror, b ); - free(buf); - if ( ret < 0 ) { - free(uncompress); - } - return ret; -} - -/* mmap() a file to '*data', - * If the file is bzip compressed map_file will uncompress - * the file into '*data'. - * Returns the total number of bytes in memory . - * Returns -1 if file could not be opened or mapped. */ -static ssize_t map_file(semanage_handle_t *sh, const char *path, char **data, - int *compressed) -{ - ssize_t size = -1; - char *uncompress; - int fd = -1; - FILE *file = NULL; - - fd = open(path, O_RDONLY); - if (fd == -1) { - ERR(sh, "Unable to open %s\n", path); - return -1; - } - - file = fdopen(fd, "r"); - if (file == NULL) { - ERR(sh, "Unable to open %s\n", path); - close(fd); - return -1; - } - - if ((size = bunzip(sh, file, &uncompress)) > 0) { - *data = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); - if (*data == MAP_FAILED) { - free(uncompress); - fclose(file); - return -1; - } else { - memcpy(*data, uncompress, size); - } - free(uncompress); - *compressed = 1; - } else { - struct stat sb; - if (fstat(fd, &sb) == -1 || - (*data = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == - MAP_FAILED) { - size = -1; - } else { - size = sb.st_size; - } - *compressed = 0; - } - - fclose(file); - - return size; -} - /* Writes a block of data to a file. Returns 0 on success, -1 on * error. */ static int write_file(semanage_handle_t * sh, @@ -1045,15 +858,12 @@ static int semanage_compile_module(semanage_handle_t *sh, char *compiler_path = NULL; char *cil_data = NULL; char *err_data = NULL; - char *hll_data = NULL; char *start = NULL; char *end = NULL; - ssize_t hll_data_len = 0; - ssize_t bzip_status; int status = 0; - int compressed; size_t cil_data_len = 0; size_t err_data_len = 0; + struct file_contents hll_contents = {}; if (!strcasecmp(modinfo->lang_ext, "cil")) { goto cleanup; @@ -1084,13 +894,15 @@ static int semanage_compile_module(semanage_handle_t *sh, goto cleanup; } - if ((hll_data_len = map_file(sh, hll_path, &hll_data, &compressed)) <= 0) { + status = map_compressed_file(sh, hll_path, &hll_contents); + if (status < 0) { ERR(sh, "Unable to read file %s\n", hll_path); - status = -1; goto cleanup; } - status = semanage_pipe_data(sh, compiler_path, hll_data, (size_t)hll_data_len, &cil_data, &cil_data_len, &err_data, &err_data_len); + status = semanage_pipe_data(sh, compiler_path, hll_contents.data, + hll_contents.len, &cil_data, &cil_data_len, + &err_data, &err_data_len); if (err_data_len > 0) { for (start = end = err_data; end < err_data + err_data_len; end++) { if (*end == '\n') { @@ -1110,10 +922,9 @@ static int semanage_compile_module(semanage_handle_t *sh, goto cleanup; } - bzip_status = bzip(sh, cil_path, cil_data, cil_data_len); - if (bzip_status == -1) { - ERR(sh, "Failed to bzip %s\n", cil_path); - status = -1; + status = write_compressed_file(sh, cil_path, cil_data, cil_data_len); + if (status == -1) { + ERR(sh, "Failed to write %s\n", cil_path); goto cleanup; } @@ -1131,9 +942,7 @@ static int semanage_compile_module(semanage_handle_t *sh, } cleanup: - if (hll_data_len > 0) { - munmap(hll_data, hll_data_len); - } + unmap_compressed_file(&hll_contents); free(cil_data); free(err_data); free(compiler_path); @@ -1749,19 +1558,17 @@ static int semanage_direct_install_file(semanage_handle_t * sh, { int retval = -1; - char *data = NULL; - ssize_t data_len = 0; - int compressed = 0; char *path = NULL; char *filename; char *lang_ext = NULL; char *module_name = NULL; char *separator; char *version = NULL; + struct file_contents contents = {}; - if ((data_len = map_file(sh, install_filename, &data, &compressed)) <= 0) { + retval = map_compressed_file(sh, install_filename, &contents); + if (retval < 0) { ERR(sh, "Unable to read file %s\n", install_filename); - retval = -1; goto cleanup; } @@ -1774,7 +1581,7 @@ static int semanage_direct_install_file(semanage_handle_t * sh, filename = basename(path); - if (compressed) { + if (contents.compressed) { separator = strrchr(filename, '.'); if (separator == NULL) { ERR(sh, "Compressed module does not have a valid extension."); @@ -1798,7 +1605,8 @@ static int semanage_direct_install_file(semanage_handle_t * sh, } if (strcmp(lang_ext, "pp") == 0) { - retval = parse_module_headers(sh, data, data_len, &module_name, &version); + retval = parse_module_headers(sh, contents.data, contents.len, + &module_name, &version); free(version); if (retval != 0) goto cleanup; @@ -1815,10 +1623,11 @@ static int semanage_direct_install_file(semanage_handle_t * sh, fprintf(stderr, "Warning: SELinux userspace will refer to the module from %s as %s rather than %s\n", install_filename, module_name, filename); } - retval = semanage_direct_install(sh, data, data_len, module_name, lang_ext); + retval = semanage_direct_install(sh, contents.data, contents.len, + module_name, lang_ext); cleanup: - if (data_len > 0) munmap(data, data_len); + unmap_compressed_file(&contents); free(module_name); free(path); @@ -1837,10 +1646,8 @@ static int semanage_direct_extract(semanage_handle_t * sh, enum semanage_module_path_type file_type; int rc = -1; semanage_module_info_t *_modinfo = NULL; - ssize_t _data_len; - char *_data; - int compressed; struct stat sb; + struct file_contents contents = {}; /* get path of module */ rc = semanage_module_get_path( @@ -1896,19 +1703,33 @@ static int semanage_direct_extract(semanage_handle_t * sh, } } - _data_len = map_file(sh, input_file, &_data, &compressed); - if (_data_len <= 0) { + rc = map_compressed_file(sh, input_file, &contents); + if (rc < 0) { ERR(sh, "Error mapping file: %s", input_file); - rc = -1; goto cleanup; } + /* The API promises an mmap'ed pointer */ + if (contents.compressed) { + *mapped_data = mmap(NULL, contents.len, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + if (*mapped_data == MAP_FAILED) { + ERR(sh, "Unable to map memory"); + rc = -1; + goto cleanup; + } + memcpy(*mapped_data, contents.data, contents.len); + free(contents.data); + } else { + *mapped_data = contents.data; + } + *modinfo = _modinfo; - *data_len = (size_t)_data_len; - *mapped_data = _data; + *data_len = contents.len; cleanup: if (rc != 0) { + unmap_compressed_file(&contents); semanage_module_info_destroy(sh, _modinfo); free(_modinfo); } @@ -2857,8 +2678,8 @@ static int semanage_direct_install_info(semanage_handle_t *sh, goto cleanup; } - ret = bzip(sh, path, data, data_len); - if (ret <= 0) { + ret = write_compressed_file(sh, path, data, data_len); + if (ret < 0) { ERR(sh, "Error while writing to %s.", path); status = -3; goto cleanup; diff --git a/libsemanage/src/direct_api.h b/libsemanage/src/direct_api.h index e56107b2..ffd428eb 100644 --- a/libsemanage/src/direct_api.h +++ b/libsemanage/src/direct_api.h @@ -39,8 +39,4 @@ int semanage_direct_access_check(struct semanage_handle *sh); int semanage_direct_mls_enabled(struct semanage_handle *sh); -#include -#include -ssize_t bunzip(struct semanage_handle *sh, FILE *f, char **data); - #endif diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c index ae023582..c5ce071c 100644 --- a/libsemanage/src/semanage_store.c +++ b/libsemanage/src/semanage_store.c @@ -59,6 +59,7 @@ typedef struct dbase_policydb dbase_t; #include "debug.h" #include "utilities.h" +#include "compressed_file.h" #define SEMANAGE_CONF_FILE "semanage.conf" /* relative path names to enum semanage_paths to special files and @@ -2055,60 +2056,27 @@ int semanage_direct_get_serial(semanage_handle_t * sh) int semanage_load_files(semanage_handle_t * sh, cil_db_t *cildb, char **filenames, int numfiles) { - int retval = 0; - FILE *fp; - ssize_t size; - char *data = NULL; + int i, retval = 0; char *filename; - int i; + struct file_contents contents = {}; for (i = 0; i < numfiles; i++) { filename = filenames[i]; - if ((fp = fopen(filename, "rb")) == NULL) { - ERR(sh, "Could not open module file %s for reading.", filename); - goto cleanup; - } - - if ((size = bunzip(sh, fp, &data)) <= 0) { - rewind(fp); - __fsetlocking(fp, FSETLOCKING_BYCALLER); - - if (fseek(fp, 0, SEEK_END) != 0) { - ERR(sh, "Failed to determine size of file %s.", filename); - goto cleanup; - } - size = ftell(fp); - rewind(fp); - - data = malloc(size); - if (fread(data, size, 1, fp) != 1) { - ERR(sh, "Failed to read file %s.", filename); - goto cleanup; - } - } + retval = map_compressed_file(sh, filename, &contents); + if (retval < 0) + return -1; - fclose(fp); - fp = NULL; + retval = cil_add_file(cildb, filename, contents.data, contents.len); + unmap_compressed_file(&contents); - retval = cil_add_file(cildb, filename, data, size); if (retval != SEPOL_OK) { ERR(sh, "Error while reading from file %s.", filename); - goto cleanup; + return -1; } - - free(data); - data = NULL; } - return retval; - - cleanup: - if (fp != NULL) { - fclose(fp); - } - free(data); - return -1; + return 0; } /* -- 2.30.2