399 lines
8.8 KiB
C
399 lines
8.8 KiB
C
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <pcre.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <linux/limits.h>
|
|
|
|
#include "../src/label_file.h"
|
|
|
|
static int process_file(struct saved_data *data, const char *filename)
|
|
{
|
|
struct spec *spec;
|
|
unsigned int line_num;
|
|
char *line_buf = NULL;
|
|
size_t line_len;
|
|
ssize_t len;
|
|
FILE *context_file;
|
|
|
|
context_file = fopen(filename, "r");
|
|
if (!context_file) {
|
|
fprintf(stderr, "Error opening %s: %s\n", filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
line_num = 0;
|
|
while ((len = getline(&line_buf, &line_len, context_file)) != -1) {
|
|
char *context;
|
|
char *mode;
|
|
char *regex;
|
|
char *cp, *anchored_regex;
|
|
char *buf_p;
|
|
pcre *re;
|
|
pcre_extra *sd;
|
|
const char *err;
|
|
int items, erroff, rc;
|
|
size_t regex_len;
|
|
int32_t stem_id;
|
|
|
|
len = strlen(line_buf);
|
|
if (line_buf[len - 1] == '\n')
|
|
line_buf[len - 1] = 0;
|
|
buf_p = line_buf;
|
|
while (isspace(*buf_p))
|
|
buf_p++;
|
|
/* Skip comment lines and empty lines. */
|
|
if (*buf_p == '#' || *buf_p == 0)
|
|
continue;
|
|
|
|
items = sscanf(line_buf, "%ms %ms %ms", ®ex, &mode, &context);
|
|
if (items < 2 || items > 3) {
|
|
fprintf(stderr, "invalid entry, skipping:%s", line_buf);
|
|
continue;
|
|
}
|
|
|
|
if (items == 2) {
|
|
context = mode;
|
|
mode = NULL;
|
|
}
|
|
|
|
rc = grow_specs(data);
|
|
if (rc) {
|
|
fprintf(stderr, "grow_specs failed: %s\n", strerror(errno));
|
|
return rc;
|
|
}
|
|
|
|
spec = &data->spec_arr[data->nspec];
|
|
|
|
spec->lr.ctx_raw = context;
|
|
spec->mode = string_to_mode(mode);
|
|
if (spec->mode == (mode_t)-1) {
|
|
fprintf(stderr, "%s: line %d has invalid file type %s\n",
|
|
regex, line_num + 1, mode);
|
|
spec->mode = 0;
|
|
}
|
|
free(mode);
|
|
spec->regex_str = regex;
|
|
|
|
stem_id = find_stem_from_spec(data, regex);
|
|
spec->stem_id = stem_id;
|
|
/* skip past the fixed stem part */
|
|
if (stem_id != -1)
|
|
regex += data->stem_arr[stem_id].len;
|
|
|
|
regex_len = strlen(regex);
|
|
cp = anchored_regex = malloc(regex_len + 3);
|
|
if (!cp) {
|
|
fprintf(stderr, "Malloc Failed: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
*cp++ = '^';
|
|
memcpy(cp, regex, regex_len);
|
|
cp += regex_len;
|
|
*cp++ = '$';
|
|
*cp = '\0';
|
|
|
|
spec_hasMetaChars(spec);
|
|
|
|
re = pcre_compile(anchored_regex, PCRE_DOTALL, &err, &erroff, NULL);
|
|
if (!re) {
|
|
fprintf(stderr, "PCRE compilation failed for %s at offset %d: %s\n", anchored_regex, erroff, err);
|
|
return -1;
|
|
}
|
|
spec->regex = re;
|
|
|
|
sd = pcre_study(re, 0, &err);
|
|
if (!sd) {
|
|
fprintf(stderr, "PCRE study failed for %s: %s\n", anchored_regex, err);
|
|
return -1;
|
|
}
|
|
free(anchored_regex);
|
|
spec->sd = sd;
|
|
|
|
line_num++;
|
|
data->nspec++;
|
|
}
|
|
|
|
free(line_buf);
|
|
fclose(context_file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
* mode_t - mode bits
|
|
* s32 - stemid associated with the regex
|
|
* u32 - spec has meta characters
|
|
* u32 - data length of the pcre regex
|
|
* char - a bufer 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
|
|
*/
|
|
static int write_binary_file(struct saved_data *data, int fd)
|
|
{
|
|
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;
|
|
|
|
bin_file = fdopen(fd, "w");
|
|
if (!bin_file) {
|
|
perror("fopen output_file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* write some magic number */
|
|
len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
|
|
if (len != 1)
|
|
goto err;
|
|
|
|
/* write the version */
|
|
section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
|
|
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
|
if (len != 1)
|
|
goto err;
|
|
|
|
/* write the pcre version */
|
|
section_len = strlen(pcre_version());
|
|
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
|
if (len != 1)
|
|
goto err;
|
|
len = fwrite(pcre_version(), sizeof(char), section_len, bin_file);
|
|
if (len != section_len)
|
|
goto err;
|
|
|
|
/* write the number of stems coming */
|
|
section_len = data->num_stems;
|
|
len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
|
|
if (len != 1)
|
|
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;
|
|
int32_t stem_id = specs[i].stem_id;
|
|
pcre *re = specs[i].regex;
|
|
pcre_extra *sd = get_pcre_extra(&specs[i]);
|
|
uint32_t to_write;
|
|
size_t size;
|
|
|
|
/* 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 strin (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 */
|
|
len = fwrite(&mode, sizeof(mode), 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;
|
|
|
|
/* determine the size of the pcre data in bytes */
|
|
rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
|
|
if (rc < 0)
|
|
goto err;
|
|
|
|
/* write the number of bytes in the pcre data */
|
|
to_write = size;
|
|
len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
|
|
if (len != 1)
|
|
goto err;
|
|
|
|
/* write the actual pcre data as a char array */
|
|
len = fwrite(re, 1, to_write, bin_file);
|
|
if (len != to_write)
|
|
goto err;
|
|
|
|
/* determine the size of the pcre study info */
|
|
rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
|
|
if (rc < 0)
|
|
goto err;
|
|
|
|
/* write the number of bytes in the pcre study data */
|
|
to_write = size;
|
|
len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
|
|
if (len != 1)
|
|
goto err;
|
|
|
|
/* write the actual pcre study data as a char array */
|
|
len = fwrite(sd->study_data, 1, to_write, bin_file);
|
|
if (len != to_write)
|
|
goto err;
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
fclose(bin_file);
|
|
return rc;
|
|
err:
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
static int 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);
|
|
pcre_free(specs[i].regex);
|
|
pcre_free_study(specs[i].sd);
|
|
}
|
|
free(specs);
|
|
|
|
num_entries = data->num_stems;
|
|
for (i = 0; i < num_entries; i++) {
|
|
free(data->stem_arr[i].buf);
|
|
}
|
|
free(data->stem_arr);
|
|
|
|
memset(data, 0, sizeof(*data));
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct saved_data data;
|
|
const char *path;
|
|
char stack_path[PATH_MAX + 1];
|
|
int rc;
|
|
char *tmp= NULL;
|
|
int fd;
|
|
struct stat buf;
|
|
|
|
if (argc != 2) {
|
|
fprintf(stderr, "usage: %s input_file\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
path = argv[1];
|
|
|
|
if (stat(path, &buf) < 0) {
|
|
fprintf(stderr, "Can not stat: %s: %m\n", path);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
rc = process_file(&data, path);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = sort_specs(&data);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
|
|
if (rc < 0 || rc >= (int)sizeof(stack_path))
|
|
return rc;
|
|
|
|
if (asprintf(&tmp, "%sXXXXXX", stack_path) < 0)
|
|
return -1;
|
|
|
|
fd = mkstemp(tmp);
|
|
if (fd < 0)
|
|
goto err;
|
|
|
|
rc = fchmod(fd, buf.st_mode);
|
|
if (rc < 0) {
|
|
perror("fchmod failed to set permission on compiled regexs");
|
|
goto err;
|
|
}
|
|
|
|
rc = write_binary_file(&data, fd);
|
|
|
|
if (rc < 0)
|
|
goto err;
|
|
|
|
rename(tmp, stack_path);
|
|
rc = free_specs(&data);
|
|
if (rc < 0)
|
|
goto err;
|
|
|
|
rc = 0;
|
|
out:
|
|
free(tmp);
|
|
return rc;
|
|
err:
|
|
rc = -1;
|
|
goto out;
|
|
}
|