mirror of
https://github.com/SELinuxProject/selinux
synced 2025-05-13 21:18:01 +00:00
libselinux: rework selabel_file(5) database
Currently the database for file backend of selabel stores the file context specifications in a single long array. This array is sorted by special precedence rules, e.g. regular expressions without meta character first, ordered by length, and the remaining regular expressions ordered by stem (the prefix part of the regular expressions without meta characters) length. This results in suboptimal lookup performance for two reasons; File context specifications without any meta characters (e.g. '/etc/passwd') are still matched via an expensive regular expression match operation. All such trivial regular expressions are matched against before any non- trivial regular expression, resulting in thousands of regex match operations for lookups for paths not matching any of the trivial ones. Rework the internal representation of the database in two ways: Convert regular expressions without any meta characters and containing only supported escaped characters (e.g. '/etc/rc\.d/init\.d') into literal strings, which get compared via strcmp(3) later on. Store the specifications in a tree structure to reduce the to number of specifications that need to be checked. Since the internal representation is completely rewritten introduce a new compiled file context file format mirroring the tree structure. The new format also stores all multi-byte data in network byte-order, so that such compiled files can be cross-compiled, e.g. for embedded devices with read-only filesystems (except for the regular expressions, which are still architecture-dependent, but ignored on architecture mis- match). The improved lookup performance will also benefit SELinux aware daemons, which create files with their default context, e.g. systemd. Fedora 41 (pre-compiled regular expressions are omitted on Fedora): file_contexts.bin: 567248 -> 413191 (bytes) file_contexts.homedirs.bin: 20677 -> 13107 (bytes) Debian Sid (pre-compiled regular expressions are included): file_contexts.bin: 7790690 -> 3646256 (bytes) file_contexts.homedirs.bin: 835950 -> 708793 (bytes) (selabel_lookup -b file -k /bin/bash) Fedora 41 in VM: text: time: 7.2 ms -> 3.5 ms peak heap: 2.33M -> 1.81M peak rss: 6.64M -> 6.37M compiled: time: 5.9 ms -> 1.6 ms peak heap: 2.14M -> 1.23M peak rss: 6.76M -> 5.91M Debian Sid on Raspberry Pi 3: text: time: 33.4 ms -> 21.2 ms peak heap: 10.59M -> 607.32K peak rss: 6.55M -> 4.46M compiled: time: 38.3 ms -> 23.5 ms peak heap: 13.28M -> 2.00M peak rss: 12.21M -> 7.60M (restorecon -vRn /) Fedora 41 in VM: 9.6 s -> 1.3 s Debian Sid on Raspberry Pi 3: 94.6 s -> 12.1 s (restorecon -vRn -T0 /) Fedora 39 in VM (8 cores): 10.9 s -> 1.0 s Debian Sid on Raspberry Pi 3 (4 cores): 58.9 s -> 12.6 s (note: I am unsure why the parallel runs on Fedora are slower) There might be subtle differences in lookup results which evaded my testing, because some precedence rules are oblique. For example `/usr/(.*/)?lib(/.*)?` has to have a higher precedence than `/usr/(.*/)?bin(/.*)?` to match the current Fedora behavior. Please report any behavior changes. The maximum node depth in the database is set to 3, which seems to give the best performance to memory usage ratio. Might be tweaked for systems with different filesystem hierarchies (Android?). I am not that familiar with the selabel_partial_match(3), selabel_get_digests_all_partial_matches(3) and selabel_hash_all_partial_matches(3) related interfaces, so I only did some rudimentary tests for them. CC: Petr Lautrbach <plautrba@redhat.com> CC: James Carter <jwcart2@gmail.com> CC: Stephen Smalley <stephen.smalley.work@gmail.com> Signed-off-by: Christian Göttsche <cgzones@googlemail.com> Acked-by: James Carter <jwcart2@gmail.com>
This commit is contained in:
parent
90b1c237a5
commit
92306daf52
@ -91,7 +91,7 @@ static int process_line(struct selabel_handle *rec,
|
||||
unsigned int nspec = data->nspec;
|
||||
const char *errbuf = NULL;
|
||||
|
||||
items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
|
||||
items = read_spec_entries(line_buf, strlen(line_buf), &errbuf, 2, &prop, &context);
|
||||
if (items < 0) {
|
||||
if (errbuf) {
|
||||
selinux_log(SELINUX_ERROR,
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -71,8 +71,8 @@ extern void digest_gen_hash(struct selabel_digest *digest);
|
||||
struct selabel_lookup_rec {
|
||||
char * ctx_raw;
|
||||
char * ctx_trans;
|
||||
int validated;
|
||||
unsigned lineno;
|
||||
unsigned int lineno;
|
||||
bool validated;
|
||||
};
|
||||
|
||||
struct selabel_handle {
|
||||
@ -143,6 +143,6 @@ compat_validate(const struct selabel_handle *rec,
|
||||
* The read_spec_entries function may be used to
|
||||
* replace sscanf to read entries from spec files.
|
||||
*/
|
||||
extern int read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...);
|
||||
extern int read_spec_entries(char *line_buf, size_t nread, const char **errbuf, int num_args, ...);
|
||||
|
||||
#endif /* _SELABEL_INTERNAL_H_ */
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Author : Richard Haines <richard_c_haines@btinternet.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
@ -21,10 +22,11 @@
|
||||
* errno will be set.
|
||||
*
|
||||
*/
|
||||
static inline int read_spec_entry(char **entry, char **ptr, int *len, const char **errbuf)
|
||||
static inline int read_spec_entry(char **entry, const char **ptr, size_t *len, const char **errbuf)
|
||||
{
|
||||
const char *tmp_buf;
|
||||
|
||||
*entry = NULL;
|
||||
char *tmp_buf = NULL;
|
||||
|
||||
while (isspace((unsigned char)**ptr) && **ptr != '\0')
|
||||
(*ptr)++;
|
||||
@ -43,6 +45,9 @@ static inline int read_spec_entry(char **entry, char **ptr, int *len, const char
|
||||
}
|
||||
|
||||
if (*len) {
|
||||
if (*len >= UINT16_MAX)
|
||||
return -1;
|
||||
|
||||
*entry = strndup(tmp_buf, *len);
|
||||
if (!*entry)
|
||||
return -1;
|
||||
@ -62,22 +67,23 @@ static inline int read_spec_entry(char **entry, char **ptr, int *len, const char
|
||||
* This function calls read_spec_entry() to do the actual string processing.
|
||||
* As such, can return anything from that function as well.
|
||||
*/
|
||||
int read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...)
|
||||
int read_spec_entries(char *line_buf, size_t nread, const char **errbuf, int num_args, ...)
|
||||
{
|
||||
char **spec_entry, *buf_p;
|
||||
int len, rc, items, entry_len = 0;
|
||||
char **spec_entry;
|
||||
const char *buf_p;
|
||||
size_t entry_len = 0;
|
||||
int rc, items;
|
||||
va_list ap;
|
||||
|
||||
*errbuf = NULL;
|
||||
|
||||
len = strlen(line_buf);
|
||||
if (line_buf[len - 1] == '\n')
|
||||
line_buf[len - 1] = '\0';
|
||||
if (line_buf[nread - 1] == '\n')
|
||||
line_buf[nread - 1] = '\0';
|
||||
else
|
||||
/* Handle case if line not \n terminated by bumping
|
||||
* the len for the check below (as the line is NUL
|
||||
* terminated by getline(3)) */
|
||||
len++;
|
||||
nread++;
|
||||
|
||||
buf_p = line_buf;
|
||||
while (isspace((unsigned char)*buf_p))
|
||||
@ -94,7 +100,7 @@ int read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...)
|
||||
while (items < num_args) {
|
||||
spec_entry = va_arg(ap, char **);
|
||||
|
||||
if (len - 1 == buf_p - line_buf) {
|
||||
if (buf_p[0] == '\0' || nread - 1 == (size_t)(buf_p - line_buf)) {
|
||||
va_end(ap);
|
||||
return items;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <assert.h>
|
||||
#include <endian.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@ -18,7 +19,6 @@
|
||||
|
||||
/* If the compiler doesn't define __BYTE_ORDER__, try to use the C
|
||||
* library <endian.h> header definitions. */
|
||||
#include <endian.h>
|
||||
#ifndef __BYTE_ORDER
|
||||
#error Neither __BYTE_ORDER__ nor __BYTE_ORDER defined. Unable to determine endianness.
|
||||
#endif
|
||||
@ -116,13 +116,15 @@ int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
|
||||
int do_load_precompregex, bool *regex_compiled)
|
||||
{
|
||||
int rc;
|
||||
uint32_t entry_len;
|
||||
uint32_t data_u32, entry_len;
|
||||
|
||||
*regex_compiled = false;
|
||||
rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
|
||||
rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
entry_len = be32toh(data_u32);
|
||||
|
||||
if (entry_len && do_load_precompregex) {
|
||||
/*
|
||||
* this should yield exactly one because we store one pattern at
|
||||
@ -169,7 +171,7 @@ int regex_writef(struct regex_data *regex, FILE *fp, int do_write_precompregex)
|
||||
int rc = 0;
|
||||
size_t len;
|
||||
PCRE2_SIZE serialized_size;
|
||||
uint32_t to_write = 0;
|
||||
uint32_t to_write = 0, data_u32;
|
||||
PCRE2_UCHAR *bytes = NULL;
|
||||
|
||||
if (do_write_precompregex) {
|
||||
@ -177,14 +179,15 @@ int regex_writef(struct regex_data *regex, FILE *fp, int do_write_precompregex)
|
||||
rc = pcre2_serialize_encode((const pcre2_code **)®ex->regex,
|
||||
1, &bytes, &serialized_size, NULL);
|
||||
if (rc != 1 || serialized_size >= UINT32_MAX) {
|
||||
rc = -1;
|
||||
rc = -3;
|
||||
goto out;
|
||||
}
|
||||
to_write = serialized_size;
|
||||
}
|
||||
|
||||
/* write serialized pattern's size */
|
||||
len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
|
||||
data_u32 = htobe32(to_write);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, fp);
|
||||
if (len != 1) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
@ -355,11 +358,15 @@ int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
|
||||
int do_load_precompregex __attribute__((unused)), bool *regex_compiled)
|
||||
{
|
||||
int rc;
|
||||
uint32_t entry_len;
|
||||
uint32_t data_u32, entry_len;
|
||||
size_t info_len;
|
||||
|
||||
rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
|
||||
if (rc < 0 || !entry_len)
|
||||
rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
entry_len = be32toh(data_u32);
|
||||
if (!entry_len)
|
||||
return -1;
|
||||
|
||||
*regex = regex_data_create();
|
||||
@ -380,10 +387,12 @@ int regex_load_mmap(struct mmap_area *mmap_area, struct regex_data **regex,
|
||||
if (rc < 0 || info_len != entry_len)
|
||||
goto err;
|
||||
|
||||
rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
|
||||
rc = next_entry(&data_u32, mmap_area, sizeof(uint32_t));
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
entry_len = be32toh(data_u32);
|
||||
|
||||
if (entry_len) {
|
||||
(*regex)->lsd.study_data = (void *)mmap_area->next_addr;
|
||||
(*regex)->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
|
||||
@ -424,45 +433,45 @@ int regex_writef(struct regex_data *regex, FILE *fp,
|
||||
{
|
||||
int rc;
|
||||
size_t len;
|
||||
uint32_t to_write;
|
||||
uint32_t data_u32;
|
||||
size_t size;
|
||||
pcre_extra *sd = get_pcre_extra(regex);
|
||||
|
||||
/* determine the size of the pcre data in bytes */
|
||||
rc = pcre_fullinfo(regex->regex, NULL, PCRE_INFO_SIZE, &size);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
if (rc < 0 || size >= UINT32_MAX)
|
||||
return -3;
|
||||
|
||||
/* write the number of bytes in the pcre data */
|
||||
to_write = size;
|
||||
len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
|
||||
data_u32 = htobe32(size);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, fp);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write the actual pcre data as a char array */
|
||||
len = fwrite(regex->regex, 1, to_write, fp);
|
||||
if (len != to_write)
|
||||
len = fwrite(regex->regex, 1, size, fp);
|
||||
if (len != size)
|
||||
return -1;
|
||||
|
||||
if (sd) {
|
||||
/* determine the size of the pcre study info */
|
||||
rc =
|
||||
pcre_fullinfo(regex->regex, sd, PCRE_INFO_STUDYSIZE, &size);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
if (rc < 0 || size >= UINT32_MAX)
|
||||
return -3;
|
||||
} else
|
||||
size = 0;
|
||||
|
||||
/* write the number of bytes in the pcre study data */
|
||||
to_write = size;
|
||||
len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
|
||||
data_u32 = htobe32(size);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, fp);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
if (sd) {
|
||||
/* write the actual pcre study data as a char array */
|
||||
len = fwrite(sd->study_data, 1, to_write, fp);
|
||||
if (len != to_write)
|
||||
len = fwrite(sd->study_data, 1, size, fp);
|
||||
if (len != size)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,25 @@
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <selinux/selinux.h>
|
||||
#include <sepol/sepol.h>
|
||||
|
||||
#include "../src/avc_sidtab.h"
|
||||
#include "../src/label_file.h"
|
||||
#include "../src/regex.h"
|
||||
|
||||
|
||||
static const char *policy_file;
|
||||
static int ctx_err;
|
||||
|
||||
static int validate_context(char **ctxp)
|
||||
{
|
||||
char *ctx = *ctxp;
|
||||
const char *ctx = *ctxp;
|
||||
|
||||
if (policy_file && sepol_check_context(ctx) < 0) {
|
||||
ctx_err = -1;
|
||||
@ -35,20 +35,20 @@ static int process_file(struct selabel_handle *rec, const char *filename)
|
||||
int rc;
|
||||
char *line_buf = NULL;
|
||||
size_t line_len = 0;
|
||||
ssize_t nread;
|
||||
FILE *context_file;
|
||||
const char *prefix = NULL;
|
||||
|
||||
context_file = fopen(filename, "r");
|
||||
context_file = fopen(filename, "re");
|
||||
if (!context_file) {
|
||||
fprintf(stderr, "Error opening %s: %s\n",
|
||||
filename, strerror(errno));
|
||||
fprintf(stderr, "Error opening %s: %m\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
line_num = 0;
|
||||
rc = 0;
|
||||
while (getline(&line_buf, &line_len, context_file) > 0) {
|
||||
rc = process_line(rec, filename, prefix, line_buf, ++line_num);
|
||||
while ((nread = getline(&line_buf, &line_len, context_file)) > 0) {
|
||||
rc = process_line(rec, filename, prefix, line_buf, nread, ++line_num);
|
||||
if (rc || ctx_err) {
|
||||
/* With -p option need to check and fail if ctx err as
|
||||
* process_line() context validation on Linux does not
|
||||
@ -65,211 +65,445 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int literal_spec_to_sidtab(const struct literal_spec *lspec, struct sidtab *stab)
|
||||
{
|
||||
security_id_t dummy;
|
||||
|
||||
return sidtab_context_to_sid(stab, lspec->lr.ctx_raw, &dummy);
|
||||
}
|
||||
|
||||
static int regex_spec_to_sidtab(const struct regex_spec *rspec, struct sidtab *stab)
|
||||
{
|
||||
security_id_t dummy;
|
||||
|
||||
return sidtab_context_to_sid(stab, rspec->lr.ctx_raw, &dummy);
|
||||
}
|
||||
|
||||
static int spec_node_to_sidtab(const struct spec_node *node, struct sidtab *stab)
|
||||
{
|
||||
int rc;
|
||||
|
||||
for (uint32_t i = 0; i < node->literal_specs_num; i++) {
|
||||
rc = literal_spec_to_sidtab(&node->literal_specs[i], stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->regex_specs_num; i++) {
|
||||
rc = regex_spec_to_sidtab(&node->regex_specs[i], stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->children_num; i++) {
|
||||
rc = spec_node_to_sidtab(&node->children[i], stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_sidtab(const struct saved_data *data, struct sidtab *stab)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = sidtab_init(stab);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return spec_node_to_sidtab(data->root, stab);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* File Format
|
||||
*
|
||||
* u32 - magic number
|
||||
* u32 - version
|
||||
* u32 - length of pcre version EXCLUDING nul
|
||||
* char - pcre version string EXCLUDING nul
|
||||
* u32 - number of stems
|
||||
* ** Stems
|
||||
* u32 - length of stem EXCLUDING nul
|
||||
* char - stem char array INCLUDING nul
|
||||
* u32 - number of regexs
|
||||
* ** Regexes
|
||||
* u32 - length of upcoming context INCLUDING nul
|
||||
* char - char array of the raw context
|
||||
* u32 - length of the upcoming regex_str
|
||||
* char - char array of the original regex string including the stem.
|
||||
* u32 - mode bits for >= SELINUX_COMPILED_FCONTEXT_MODE
|
||||
* mode_t for <= SELINUX_COMPILED_FCONTEXT_PCRE_VERS
|
||||
* s32 - stemid associated with the regex
|
||||
* u32 - spec has meta characters
|
||||
* u32 - The specs prefix_len if >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
|
||||
* u32 - data length of the pcre regex
|
||||
* char - a buffer holding the raw pcre regex info
|
||||
* u32 - data length of the pcre regex study daya
|
||||
* char - a buffer holding the raw pcre regex study data
|
||||
* The format uses network byte-order.
|
||||
*
|
||||
* u32 - magic number
|
||||
* u32 - version
|
||||
* u32 - length of upcoming pcre version EXCLUDING nul
|
||||
* [char] - pcre version string EXCLUDING nul
|
||||
* u32 - length of upcoming pcre architecture EXCLUDING nul
|
||||
* [char] - pcre architecture string EXCLUDING nul
|
||||
* u64 - number of total specifications
|
||||
* u32 - number of upcoming context definitions
|
||||
* [Ctx] - array of context definitions
|
||||
* Node - root node
|
||||
*
|
||||
* Context Definition Format (Ctx)
|
||||
*
|
||||
* u16 - length of upcoming raw context EXCLUDING nul
|
||||
* [char] - char array of the raw context EXCLUDING nul
|
||||
*
|
||||
* Node Format
|
||||
*
|
||||
* u16 - length of upcoming stem INCLUDING nul
|
||||
* [char] - stem char array INCLUDING nul
|
||||
* u32 - number of upcoming literal specifications
|
||||
* [LSpec] - array of literal specifications
|
||||
* u32 - number of upcoming regular expression specifications
|
||||
* [RSpec] - array of regular expression specifications
|
||||
* u32 - number of upcoming child nodes
|
||||
* [Node] - array of child nodes
|
||||
*
|
||||
* Literal Specification Format (LSpec)
|
||||
*
|
||||
* u32 - context table index for raw context (1-based)
|
||||
* u16 - length of upcoming regex_str INCLUDING nul
|
||||
* [char] - char array of the original regex string including the stem INCLUDING nul
|
||||
* u16 - length of upcoming literal match INCLUDING nul
|
||||
* [char] - char array of the simplified literal match INCLUDING nul
|
||||
* u8 - file kind (LABEL_FILE_KIND_*)
|
||||
*
|
||||
* Regular Expression Specification Format (RSpec)
|
||||
*
|
||||
* u32 - context table index for raw context (1-based)
|
||||
* u16 - length of upcoming regex_str INCLUDING nul
|
||||
* [char] - char array of the original regex string including the stem INCLUDING nul
|
||||
* u16 - length of the fixed path prefix
|
||||
* u8 - file kind (LABEL_FILE_KIND_*)
|
||||
* [Regex] - serialized pattern of regex, subject to underlying regex library
|
||||
*/
|
||||
static int write_binary_file(struct saved_data *data, int fd,
|
||||
int do_write_precompregex)
|
||||
{
|
||||
struct spec *specs = data->spec_arr;
|
||||
FILE *bin_file;
|
||||
size_t len;
|
||||
uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
|
||||
uint32_t section_len;
|
||||
uint32_t i;
|
||||
int rc;
|
||||
const char *reg_version;
|
||||
const char *reg_arch;
|
||||
|
||||
bin_file = fdopen(fd, "w");
|
||||
|
||||
static int security_id_compare(const void *a, const void *b)
|
||||
{
|
||||
const struct security_id *sid_a = a, *sid_b = b;
|
||||
|
||||
return (sid_a->id > sid_b->id) - (sid_a->id < sid_b->id);
|
||||
}
|
||||
|
||||
static int write_sidtab(FILE *bin_file, const struct sidtab *stab)
|
||||
{
|
||||
struct security_id *sids;
|
||||
uint32_t data_u32, index;
|
||||
uint16_t data_u16;
|
||||
size_t len;
|
||||
|
||||
/* write number of entries */
|
||||
data_u32 = htobe32(stab->nel);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* sort entries by id */
|
||||
sids = calloc(stab->nel, sizeof(*sids));
|
||||
if (!sids)
|
||||
return -1;
|
||||
index = 0;
|
||||
for (unsigned i = 0; i < SIDTAB_SIZE; i++) {
|
||||
const struct sidtab_node *cur = stab->htable[i];
|
||||
|
||||
while (cur) {
|
||||
sids[index++] = cur->sid_s;
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
assert(index == stab->nel);
|
||||
qsort(sids, stab->nel, sizeof(struct security_id), security_id_compare);
|
||||
assert(sids[0].id == 1);
|
||||
assert(sids[stab->nel - 1].id == stab->nel);
|
||||
|
||||
/* write raw contexts sorted by id */
|
||||
for (uint32_t i = 0; i < stab->nel; i++) {
|
||||
const char *ctx = sids[i].ctx;
|
||||
size_t ctx_len = strlen(ctx);
|
||||
|
||||
if (ctx_len == 0 || ctx_len >= UINT16_MAX) {
|
||||
free(sids);
|
||||
return -2;
|
||||
}
|
||||
data_u16 = htobe16(ctx_len);
|
||||
len = fwrite(&data_u16, sizeof(uint16_t), 1, bin_file);
|
||||
if (len != 1) {
|
||||
free(sids);
|
||||
return -1;
|
||||
}
|
||||
len = fwrite(ctx, sizeof(char), ctx_len, bin_file);
|
||||
if (len != ctx_len) {
|
||||
free(sids);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
free(sids);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_literal_spec(FILE *bin_file, const struct literal_spec *lspec, const struct sidtab *stab)
|
||||
{
|
||||
const struct security_id *sid;
|
||||
const char *orig_regex, *literal_match;
|
||||
size_t orig_regex_len, literal_match_len;
|
||||
uint32_t data_u32;
|
||||
uint16_t data_u16;
|
||||
uint8_t data_u8;
|
||||
size_t len;
|
||||
|
||||
/* write raw context sid */
|
||||
sid = sidtab_context_lookup(stab, lspec->lr.ctx_raw);
|
||||
assert(sid); /* should be set via create_sidtab() */
|
||||
data_u32 = htobe32(sid->id);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write original regex string */
|
||||
orig_regex = lspec->regex_str;
|
||||
orig_regex_len = strlen(orig_regex);
|
||||
if (orig_regex_len == 0 || orig_regex_len >= UINT16_MAX)
|
||||
return -2;
|
||||
orig_regex_len += 1;
|
||||
data_u16 = htobe16(orig_regex_len);
|
||||
len = fwrite(&data_u16, sizeof(uint16_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
len = fwrite(orig_regex, sizeof(char), orig_regex_len, bin_file);
|
||||
if (len != orig_regex_len)
|
||||
return -1;
|
||||
|
||||
/* write literal match string */
|
||||
literal_match = lspec->literal_match;
|
||||
literal_match_len = strlen(literal_match);
|
||||
if (literal_match_len == 0 || literal_match_len >= UINT16_MAX)
|
||||
return -2;
|
||||
literal_match_len += 1;
|
||||
data_u16 = htobe16(literal_match_len);
|
||||
len = fwrite(&data_u16, sizeof(uint16_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
len = fwrite(literal_match, sizeof(char), literal_match_len, bin_file);
|
||||
if (len != literal_match_len)
|
||||
return -1;
|
||||
|
||||
/* write file kind */
|
||||
data_u8 = lspec->file_kind;
|
||||
len = fwrite(&data_u8, sizeof(uint8_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_regex_spec(FILE *bin_file, bool do_write_precompregex, const struct regex_spec *rspec, const struct sidtab *stab)
|
||||
{
|
||||
const struct security_id *sid;
|
||||
const char *regex;
|
||||
size_t regex_len;
|
||||
uint32_t data_u32;
|
||||
uint16_t data_u16;
|
||||
uint8_t data_u8;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* write raw context sid */
|
||||
sid = sidtab_context_lookup(stab, rspec->lr.ctx_raw);
|
||||
assert(sid); /* should be set via create_sidtab() */
|
||||
data_u32 = htobe32(sid->id);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write regex string */
|
||||
regex = rspec->regex_str;
|
||||
regex_len = strlen(regex);
|
||||
if (regex_len == 0 || regex_len >= UINT16_MAX)
|
||||
return -2;
|
||||
regex_len += 1;
|
||||
data_u16 = htobe16(regex_len);
|
||||
len = fwrite(&data_u16, sizeof(uint16_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
len = fwrite(regex, sizeof(char), regex_len, bin_file);
|
||||
if (len != regex_len)
|
||||
return -1;
|
||||
|
||||
/* write prefix length */
|
||||
data_u16 = htobe16(rspec->prefix_len);
|
||||
len = fwrite(&data_u16, sizeof(uint16_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write file kind */
|
||||
data_u8 = rspec->file_kind;
|
||||
len = fwrite(&data_u8, sizeof(uint8_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* Write serialized regex */
|
||||
rc = regex_writef(rspec->regex, bin_file, do_write_precompregex);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_spec_node(FILE *bin_file, bool do_write_precompregex, const struct spec_node *node, const struct sidtab *stab)
|
||||
{
|
||||
size_t stem_len;
|
||||
uint32_t data_u32;
|
||||
uint16_t data_u16;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
stem_len = node->stem_len;
|
||||
if ((stem_len == 0 && node->parent) || stem_len >= UINT16_MAX)
|
||||
return -2;
|
||||
stem_len += 1;
|
||||
data_u16 = htobe16(stem_len);
|
||||
len = fwrite(&data_u16, sizeof(uint16_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
len = fwrite(node->stem ?: "", sizeof(char), stem_len, bin_file);
|
||||
if (len != stem_len)
|
||||
return -1;
|
||||
|
||||
/* write number of literal specs */
|
||||
data_u32 = htobe32(node->literal_specs_num);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write literal specs */
|
||||
for (uint32_t i = 0; i < node->literal_specs_num; i++) {
|
||||
rc = write_literal_spec(bin_file, &node->literal_specs[i], stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* write number of regex specs */
|
||||
data_u32 = htobe32(node->regex_specs_num);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write regex specs */
|
||||
for (uint32_t i = 0; i < node->regex_specs_num; i++) {
|
||||
rc = write_regex_spec(bin_file, do_write_precompregex, &node->regex_specs[i], stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* write number of child nodes */
|
||||
data_u32 = htobe32(node->children_num);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
|
||||
/* write child nodes */
|
||||
for (uint32_t i = 0; i < node->children_num; i++) {
|
||||
rc = write_spec_node(bin_file, do_write_precompregex, &node->children[i], stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_binary_file(const struct saved_data *data, const struct sidtab *stab,
|
||||
int fd, const char *path, bool do_write_precompregex,
|
||||
const char *progname)
|
||||
{
|
||||
FILE *bin_file;
|
||||
const char *reg_arch, *reg_version;
|
||||
size_t len, reg_arch_len, reg_version_len;
|
||||
uint64_t data_u64;
|
||||
uint32_t data_u32;
|
||||
int rc;
|
||||
|
||||
bin_file = fdopen(fd, "we");
|
||||
if (!bin_file) {
|
||||
perror("fopen output_file");
|
||||
exit(EXIT_FAILURE);
|
||||
fprintf(stderr, "%s: failed to open %s: %m\n", progname, path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* write some magic number */
|
||||
len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
|
||||
data_u32 = htobe32(SELINUX_MAGIC_COMPILED_FCONTEXT);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
goto err_write;
|
||||
|
||||
/* write the version */
|
||||
section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
|
||||
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
||||
data_u32 = htobe32(SELINUX_COMPILED_FCONTEXT_MAX_VERS);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
goto err_write;
|
||||
|
||||
/* write version of the regex back-end */
|
||||
reg_version = regex_version();
|
||||
if (!reg_version)
|
||||
goto err;
|
||||
section_len = strlen(reg_version);
|
||||
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
||||
goto err_check;
|
||||
reg_version_len = strlen(reg_version);
|
||||
if (reg_version_len == 0 || reg_version_len >= UINT32_MAX)
|
||||
goto err_check;
|
||||
data_u32 = htobe32(reg_version_len);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
len = fwrite(reg_version, sizeof(char), section_len, bin_file);
|
||||
if (len != section_len)
|
||||
goto err;
|
||||
goto err_write;
|
||||
len = fwrite(reg_version, sizeof(char), reg_version_len, bin_file);
|
||||
if (len != reg_version_len)
|
||||
goto err_write;
|
||||
|
||||
/* write regex arch string */
|
||||
reg_arch = regex_arch_string();
|
||||
if (!reg_arch)
|
||||
goto err;
|
||||
section_len = strlen(reg_arch);
|
||||
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
||||
goto err_check;
|
||||
reg_arch_len = strlen(reg_arch);
|
||||
if (reg_arch_len == 0 || reg_arch_len >= UINT32_MAX)
|
||||
goto err_check;
|
||||
data_u32 = htobe32(reg_arch_len);
|
||||
len = fwrite(&data_u32, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
len = fwrite(reg_arch, sizeof(char), section_len, bin_file);
|
||||
if (len != section_len)
|
||||
goto err;
|
||||
goto err_write;
|
||||
len = fwrite(reg_arch, sizeof(char), reg_arch_len, bin_file);
|
||||
if (len != reg_arch_len)
|
||||
goto err_write;
|
||||
|
||||
/* write the number of stems coming */
|
||||
section_len = data->num_stems;
|
||||
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
||||
/* write number of total specifications */
|
||||
data_u64 = htobe64(data->num_specs);
|
||||
len = fwrite(&data_u64, sizeof(uint64_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err_write;
|
||||
|
||||
/* write context table */
|
||||
rc = write_sidtab(bin_file, stab);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = write_spec_node(bin_file, do_write_precompregex, data->root, stab);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < section_len; i++) {
|
||||
char *stem = data->stem_arr[i].buf;
|
||||
uint32_t stem_len = data->stem_arr[i].len;
|
||||
|
||||
/* write the strlen (aka no nul) */
|
||||
len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* include the nul in the file */
|
||||
stem_len += 1;
|
||||
len = fwrite(stem, sizeof(char), stem_len, bin_file);
|
||||
if (len != stem_len)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* write the number of regexes coming */
|
||||
section_len = data->nspec;
|
||||
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < section_len; i++) {
|
||||
char *context = specs[i].lr.ctx_raw;
|
||||
char *regex_str = specs[i].regex_str;
|
||||
mode_t mode = specs[i].mode;
|
||||
size_t prefix_len = specs[i].prefix_len;
|
||||
int32_t stem_id = specs[i].stem_id;
|
||||
struct regex_data *re = specs[i].regex;
|
||||
uint32_t to_write;
|
||||
|
||||
/* length of the context string (including nul) */
|
||||
to_write = strlen(context) + 1;
|
||||
len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* original context string (including nul) */
|
||||
len = fwrite(context, sizeof(char), to_write, bin_file);
|
||||
if (len != to_write)
|
||||
goto err;
|
||||
|
||||
/* length of the original regex string (including nul) */
|
||||
to_write = strlen(regex_str) + 1;
|
||||
len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* original regex string */
|
||||
len = fwrite(regex_str, sizeof(char), to_write, bin_file);
|
||||
if (len != to_write)
|
||||
goto err;
|
||||
|
||||
/* binary F_MODE bits */
|
||||
to_write = mode;
|
||||
len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* stem for this regex (could be -1) */
|
||||
len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* does this spec have a metaChar? */
|
||||
to_write = specs[i].hasMetaChars;
|
||||
len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
|
||||
to_write = prefix_len;
|
||||
len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
|
||||
if (len != 1)
|
||||
goto err;
|
||||
|
||||
/* Write regex related data */
|
||||
rc = regex_writef(re, bin_file, do_write_precompregex);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
fclose(bin_file);
|
||||
return rc;
|
||||
err:
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void free_specs(struct saved_data *data)
|
||||
{
|
||||
struct spec *specs = data->spec_arr;
|
||||
unsigned int num_entries = data->nspec;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
free(specs[i].lr.ctx_raw);
|
||||
free(specs[i].lr.ctx_trans);
|
||||
free(specs[i].regex_str);
|
||||
free(specs[i].type_str);
|
||||
regex_data_free(specs[i].regex);
|
||||
if (fclose(bin_file) && rc == 0) {
|
||||
fprintf(stderr, "%s: failed to close %s: %m\n", progname, path);
|
||||
rc = -1;
|
||||
}
|
||||
free(specs);
|
||||
return rc;
|
||||
|
||||
num_entries = data->num_stems;
|
||||
for (i = 0; i < num_entries; i++)
|
||||
free(data->stem_arr[i].buf);
|
||||
free(data->stem_arr);
|
||||
err_check:
|
||||
rc = -2;
|
||||
goto err;
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
err_write:
|
||||
rc = -1;
|
||||
goto err;
|
||||
|
||||
err:
|
||||
fprintf(stderr, "%s: failed to compile file context specifications: %s\n",
|
||||
progname,
|
||||
(rc == -3) ? "regex serialization failure" :
|
||||
((rc == -2) ? "invalid fcontext specification" : "write failure"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
static __attribute__ ((__noreturn__)) void usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [-o out_file] [-p policy_file] fc_file\n"
|
||||
"usage: %s [-iV] [-o out_file] [-p policy_file] fc_file\n"
|
||||
"Where:\n\t"
|
||||
"-o Optional file name of the PCRE formatted binary\n\t"
|
||||
" file to be output. If not specified the default\n\t"
|
||||
@ -287,28 +521,32 @@ static __attribute__ ((__noreturn__)) void usage(const char *progname)
|
||||
" Arch identifier format (PCRE2):\n\t"
|
||||
" <pointer width>-<size type width>-<endianness>, e.g.,\n\t"
|
||||
" \"8-8-el\" for x86_64.\n\t"
|
||||
"-V Print binary output format version and exit.\n\t"
|
||||
"fc_file The text based file contexts file to be processed.\n",
|
||||
progname);
|
||||
exit(EXIT_FAILURE);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *path = NULL;
|
||||
const char *path;
|
||||
const char *out_file = NULL;
|
||||
int do_write_precompregex = 1;
|
||||
bool do_write_precompregex = true;
|
||||
char stack_path[PATH_MAX + 1];
|
||||
char *tmp = NULL;
|
||||
size_t len;
|
||||
int fd, rc, opt;
|
||||
FILE *policy_fp = NULL;
|
||||
struct stat buf;
|
||||
struct selabel_handle *rec = NULL;
|
||||
struct saved_data *data = NULL;
|
||||
struct spec_node *root = NULL;
|
||||
struct sidtab stab = {};
|
||||
|
||||
if (argc < 2)
|
||||
usage(argv[0]);
|
||||
|
||||
while ((opt = getopt(argc, argv, "io:p:r")) > 0) {
|
||||
while ((opt = getopt(argc, argv, "io:p:rV")) > 0) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
out_file = optarg;
|
||||
@ -317,18 +555,20 @@ int main(int argc, char *argv[])
|
||||
policy_file = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
do_write_precompregex = 0;
|
||||
do_write_precompregex = false;
|
||||
break;
|
||||
case 'i':
|
||||
printf("%s (%s)\n", regex_version(),
|
||||
regex_arch_string());
|
||||
printf("%s (%s)\n", regex_version(), regex_arch_string());
|
||||
return 0;
|
||||
case 'V':
|
||||
printf("Compiled fcontext format version %d\n", SELINUX_COMPILED_FCONTEXT_MAX_VERS);
|
||||
return 0;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
if (optind + 1 != argc)
|
||||
usage(argv[0]);
|
||||
|
||||
path = argv[optind];
|
||||
@ -339,7 +579,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* Open binary policy if supplied. */
|
||||
if (policy_file) {
|
||||
policy_fp = fopen(policy_file, "r");
|
||||
policy_fp = fopen(policy_file, "re");
|
||||
|
||||
if (!policy_fp) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n",
|
||||
@ -373,7 +613,7 @@ int main(int argc, char *argv[])
|
||||
* error is detected, the process will be aborted. */
|
||||
rec->validating = 1;
|
||||
selinux_set_callback(SELINUX_CB_VALIDATE,
|
||||
(union selinux_callback)&validate_context);
|
||||
(union selinux_callback) { .func_validate = &validate_context });
|
||||
|
||||
data = (struct saved_data *)calloc(1, sizeof(*data));
|
||||
if (!data) {
|
||||
@ -384,6 +624,17 @@ int main(int argc, char *argv[])
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
root = calloc(1, sizeof(*root));
|
||||
if (!root) {
|
||||
fprintf(stderr, "%s: calloc failed: %s\n", argv[0], strerror(errno));
|
||||
free(data);
|
||||
free(rec);
|
||||
if (policy_fp)
|
||||
fclose(policy_fp);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
data->root = root;
|
||||
rec->data = data;
|
||||
|
||||
rc = process_file(rec, path);
|
||||
@ -392,9 +643,11 @@ int main(int argc, char *argv[])
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = sort_specs(data);
|
||||
if (rc) {
|
||||
fprintf(stderr, "%s: sort_specs failed\n", argv[0]);
|
||||
sort_specs(data);
|
||||
|
||||
rc = create_sidtab(data, &stab);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "%s: failed to generate sidtab: %s\n", argv[0], strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -403,40 +656,41 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
|
||||
|
||||
if (rc < 0 || rc >= (int)sizeof(stack_path)) {
|
||||
if (rc < 0 || (size_t)rc >= sizeof(stack_path)) {
|
||||
fprintf(stderr, "%s: snprintf failed\n", argv[0]);
|
||||
goto err;
|
||||
}
|
||||
len = rc;
|
||||
|
||||
tmp = malloc(strlen(stack_path) + 7);
|
||||
tmp = malloc(len + 7);
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "%s: malloc failed: %s\n", argv[0], strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = sprintf(tmp, "%sXXXXXX", stack_path);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "%s: sprintf failed\n", argv[0]);
|
||||
rc = snprintf(tmp, len + 7, "%sXXXXXX", stack_path);
|
||||
if (rc < 0 || (size_t)rc >= len + 7) {
|
||||
fprintf(stderr, "%s: snprintf failed\n", argv[0]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
fd = mkstemp(tmp);
|
||||
fd = mkstemp(tmp);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "%s: mkstemp %s failed: %s\n", argv[0], tmp, strerror(errno));
|
||||
close(fd);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = fchmod(fd, buf.st_mode);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "%s: fchmod %s failed: %s\n", argv[0], tmp, strerror(errno));
|
||||
close(fd);
|
||||
goto err_unlink;
|
||||
}
|
||||
|
||||
rc = write_binary_file(data, fd, do_write_precompregex);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "%s: write_binary_file %s failed\n", argv[0], tmp);
|
||||
rc = write_binary_file(data, &stab, fd, tmp, do_write_precompregex, argv[0]);
|
||||
if (rc < 0)
|
||||
goto err_unlink;
|
||||
}
|
||||
|
||||
rc = rename(tmp, stack_path);
|
||||
if (rc < 0) {
|
||||
@ -449,9 +703,11 @@ out:
|
||||
if (policy_fp)
|
||||
fclose(policy_fp);
|
||||
|
||||
free_specs(data);
|
||||
free(rec);
|
||||
sidtab_destroy(&stab);
|
||||
free_spec_node(data->root);
|
||||
free(data->root);
|
||||
free(data);
|
||||
free(rec);
|
||||
free(tmp);
|
||||
return rc;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user