Skip to content

Commit

Permalink
libselinux: add support for pcre2
Browse files Browse the repository at this point in the history
This patch moves all pcre1/2 dependencies into the new files regex.h
and regex.c implementing the common denominator of features needed
by libselinux. The compiler flag -DUSE_PCRE2 toggles between the
used implementations.

As of this patch libselinux supports either pcre or pcre2 but not
both at the same time. The persistently stored file contexts
information differs. This means libselinux can only load file
context files generated by sefcontext_compile build with the
same pcre variant.

Also, for pcre2 the persistent format is architecture dependant.
Stored precompiled regular expressions can only be used on the
same architecture they were generated on. If pcre2 is used and
sefcontext_compile shall generate portable output, it and libselinux
must be compiled with -DNO_PERSISTENTLY_STORED_PATTERNS, at the
cost of having to recompile the regular expressions at load time.

Signed-off-by: Janis Danisevskis <jdanis@google.com>

This patch includes includes:

libselinux: fix memory leak on pcre2

Introduced a malloc on pcre_version(). Libselinux
expected this to be static, just use a static
internal buffer.

Signed-off-by: William Roberts <william.c.roberts@intel.com>
  • Loading branch information
Janis Danisevskis authored and Janis Danisevskis committed Sep 7, 2016
1 parent 36a21c3 commit 23838ba
Show file tree
Hide file tree
Showing 8 changed files with 699 additions and 155 deletions.
13 changes: 13 additions & 0 deletions libselinux/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ ifeq ($(DISABLE_SETRANS),y)
endif
export DISABLE_AVC DISABLE_SETRANS DISABLE_RPM DISABLE_BOOL EMFLAGS

USE_PCRE2 ?= n
DISABLE_PERSISTENTLY_STORED_REGEX_PATTERNS ?= n
ifeq ($(USE_PCRE2),y)
PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8
ifeq ($(DISABLE_PERSISTENTLY_STORED_REGEX_PATTERNS), y)
PCRE_CFLAGS += -DNO_PERSISTENTLY_STORED_PATTERNS
endif
PCRE_LDFLAGS := -lpcre2-8
else
PCRE_LDFLAGS := -lpcre
endif
export PCRE_CFLAGS PCRE_LDFLAGS

all install relabel clean distclean indent:
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
Expand Down
4 changes: 2 additions & 2 deletions libselinux/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ CFLAGS ?= -O -Wall -W -Wundef -Wformat-y2k -Wformat-security -Winit-self -Wmissi
-fipa-pure-const -Wno-suggest-attribute=pure -Wno-suggest-attribute=const \
-Werror -Wno-aggregate-return -Wno-redundant-decls

override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS)
override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS) $(PCRE_CFLAGS)

SWIG_CFLAGS += -Wno-error -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-parameter \
-Wno-shadow -Wno-uninitialized -Wno-missing-prototypes -Wno-missing-declarations
Expand Down Expand Up @@ -113,7 +113,7 @@ $(LIBA): $(OBJS)
$(RANLIB) $@

$(LIBSO): $(LOBJS)
$(CC) $(CFLAGS) -shared -o $@ $^ -lpcre -ldl $(LDFLAGS) -L$(LIBDIR) -Wl,-soname,$(LIBSO),-z,defs,-z,relro
$(CC) $(CFLAGS) -shared -o $@ $^ $(PCRE_LDFLAGS) -ldl $(LDFLAGS) -L$(LIBDIR) -Wl,-soname,$(LIBSO),-z,defs,-z,relro
ln -sf $@ $(TARGET)

$(LIBPC): $(LIBPC).in ../VERSION
Expand Down
94 changes: 24 additions & 70 deletions libselinux/src/label_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <pcre.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
Expand Down Expand Up @@ -112,6 +111,7 @@ static int load_mmap(struct selabel_handle *rec, const char *path,
struct mmap_area *mmap_area;
uint32_t i, magic, version;
uint32_t entry_len, stem_map_len, regex_array_len;
const char *reg_version;

if (isbinary) {
len = strlen(path);
Expand Down Expand Up @@ -175,8 +175,13 @@ static int load_mmap(struct selabel_handle *rec, const char *path,
if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
return -1;

reg_version = regex_version();
if (!reg_version)
return -1;

if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) {
len = strlen(pcre_version());

len = strlen(reg_version);

rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
if (rc < 0)
Expand All @@ -198,7 +203,7 @@ static int load_mmap(struct selabel_handle *rec, const char *path,
}

str_buf[entry_len] = '\0';
if ((strcmp(str_buf, pcre_version()) != 0)) {
if ((strcmp(str_buf, reg_version) != 0)) {
free(str_buf);
return -1;
}
Expand Down Expand Up @@ -278,7 +283,11 @@ static int load_mmap(struct selabel_handle *rec, const char *path,

spec = &data->spec_arr[data->nspec];
spec->from_mmap = 1;
#if defined USE_PCRE2 && defined NO_PERSISTENTLY_STORED_PATTERNS
spec->regcomp = 0;
#else
spec->regcomp = 1;
#endif

/* Process context */
rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
Expand Down Expand Up @@ -364,47 +373,10 @@ static int load_mmap(struct selabel_handle *rec, const char *path,
spec->prefix_len = prefix_len;
}

/* Process regex and study_data entries */
rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
if (rc < 0 || !entry_len) {
rc = -1;
goto err;
}
spec->regex = (pcre *)mmap_area->next_addr;
rc = next_entry(NULL, mmap_area, entry_len);
rc = regex_load_mmap(mmap_area, &spec->regex);
if (rc < 0)
goto err;

/* Check that regex lengths match. pcre_fullinfo()
* also validates its magic number. */
rc = pcre_fullinfo(spec->regex, NULL, PCRE_INFO_SIZE, &len);
if (rc < 0 || len != entry_len) {
rc = -1;
goto err;
}

rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
if (rc < 0 || !entry_len) {
rc = -1;
goto err;
}

if (entry_len) {
spec->lsd.study_data = (void *)mmap_area->next_addr;
spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
rc = next_entry(NULL, mmap_area, entry_len);
if (rc < 0)
goto err;

/* Check that study data lengths match. */
rc = pcre_fullinfo(spec->regex, &spec->lsd,
PCRE_INFO_STUDYSIZE, &len);
if (rc < 0 || len != entry_len) {
rc = -1;
goto err;
}
}

data->nspec++;
}

Expand Down Expand Up @@ -609,10 +581,7 @@ static void closef(struct selabel_handle *rec)
continue;
free(spec->regex_str);
free(spec->type_str);
if (spec->regcomp) {
pcre_free(spec->regex);
pcre_free_study(spec->sd);
}
regex_data_free(spec->regex);
}

for (i = 0; i < (unsigned int)data->num_stems; i++) {
Expand Down Expand Up @@ -644,13 +613,14 @@ static struct spec *lookup_common(struct selabel_handle *rec,
{
struct saved_data *data = (struct saved_data *)rec->data;
struct spec *spec_arr = data->spec_arr;
int i, rc, file_stem, pcre_options = 0;
int i, rc, file_stem;
mode_t mode = (mode_t)type;
const char *buf;
struct spec *ret = NULL;
char *clean_key = NULL;
const char *prev_slash, *next_slash;
unsigned int sofar = 0;
struct regex_error_data regex_error_data;

if (!data->nspec) {
errno = ENOENT;
Expand All @@ -677,9 +647,6 @@ static struct spec *lookup_common(struct selabel_handle *rec,
file_stem = find_stem_from_file(data, &buf);
mode &= S_IFMT;

if (partial)
pcre_options |= PCRE_PARTIAL_SOFT;

/*
* Check for matching specifications in reverse order, so that
* the last matching specification is used.
Expand All @@ -692,25 +659,19 @@ static struct spec *lookup_common(struct selabel_handle *rec,
* a regex check */
if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&
(!mode || !spec->mode || mode == spec->mode)) {
if (compile_regex(data, spec, NULL) < 0)
if (compile_regex(data, spec, &regex_error_data) < 0)
goto finish;
if (spec->stem_id == -1)
rc = pcre_exec(spec->regex,
get_pcre_extra(spec),
key, strlen(key), 0,
pcre_options, NULL, 0);
rc = regex_match(spec->regex, key, partial);
else
rc = pcre_exec(spec->regex,
get_pcre_extra(spec),
buf, strlen(buf), 0,
pcre_options, NULL, 0);
if (rc == 0) {
rc = regex_match(spec->regex, buf, partial);
if (rc == REGEX_MATCH) {
spec->matches++;
break;
} else if (partial && rc == PCRE_ERROR_PARTIAL)
} else if (partial && rc == REGEX_MATCH_PARTIAL)
break;

if (rc == PCRE_ERROR_NOMATCH)
if (rc == REGEX_NO_MATCH)
continue;

errno = ENOENT;
Expand Down Expand Up @@ -850,16 +811,9 @@ static enum selabel_cmp_result cmp(struct selabel_handle *h1,
}

if (spec1->regcomp && spec2->regcomp) {
size_t len1, len2;
int rc;

rc = pcre_fullinfo(spec1->regex, NULL, PCRE_INFO_SIZE, &len1);
assert(rc == 0);
rc = pcre_fullinfo(spec2->regex, NULL, PCRE_INFO_SIZE, &len2);
assert(rc == 0);
if (len1 != len2 ||
memcmp(spec1->regex, spec2->regex, len1))
if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){
return incomp(spec1, spec2, "regex", i, j);
}
} else {
if (strcmp(spec1->regex_str, spec2->regex_str))
return incomp(spec1, spec2, "regex_str", i, j);
Expand Down
54 changes: 16 additions & 38 deletions libselinux/src/label_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

#include <sys/stat.h>

/*
* regex.h/c were introduced to hold all dependencies on the regular
* expression back-end when we started supporting PCRE2. regex.h defines a
* minimal interface required by libselinux, so that the remaining code
* can be agnostic about the underlying implementation.
*/
#include "regex.h"

#include "callbacks.h"
#include "label_internal.h"

Expand All @@ -19,21 +27,12 @@

#define SELINUX_COMPILED_FCONTEXT_MAX_VERS SELINUX_COMPILED_FCONTEXT_PREFIX_LEN

/* Prior to version 8.20, libpcre did not have pcre_free_study() */
#if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20))
#define pcre_free_study pcre_free
#endif

/* A file security context specification. */
struct spec {
struct selabel_lookup_rec lr; /* holds contexts for lookup result */
char *regex_str; /* regular expession string for diagnostics */
char *type_str; /* type string for diagnostic messages */
pcre *regex; /* compiled regular expression */
union {
pcre_extra *sd; /* pointer to extra compiled stuff */
pcre_extra lsd; /* used to hold the mmap'd version */
};
struct regex_data * regex; /* backend dependent regular expression data */
mode_t mode; /* mode format value */
int matches; /* number of matching pathnames */
int stem_id; /* indicates which stem-compression item */
Expand Down Expand Up @@ -78,17 +77,6 @@ struct saved_data {
struct mmap_area *mmap_areas;
};

static inline pcre_extra *get_pcre_extra(struct spec *spec)
{
if (spec->from_mmap) {
if (spec->lsd.study_data)
return &spec->lsd;
else
return NULL;
} else
return spec->sd;
}

static inline mode_t string_to_mode(char *mode)
{
size_t len;
Expand Down Expand Up @@ -331,13 +319,12 @@ static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
}

static inline int compile_regex(struct saved_data *data, struct spec *spec,
const char **errbuf)
struct regex_error_data * error_data)
{
const char *tmperrbuf;
char *reg_buf, *anchored_regex, *cp;
struct stem *stem_arr = data->stem_arr;
size_t len;
int erroff;
int rc;

if (spec->regcomp)
return 0; /* already done */
Expand All @@ -361,19 +348,9 @@ static inline int compile_regex(struct saved_data *data, struct spec *spec,
*cp = '\0';

/* Compile the regular expression. */
spec->regex = pcre_compile(anchored_regex, PCRE_DOTALL, &tmperrbuf,
&erroff, NULL);
rc = regex_prepare_data(&spec->regex, anchored_regex, error_data);
free(anchored_regex);
if (!spec->regex) {
if (errbuf)
*errbuf = tmperrbuf;
return -1;
}

spec->sd = pcre_study(spec->regex, 0, &tmperrbuf);
if (!spec->sd && tmperrbuf) {
if (errbuf)
*errbuf = tmperrbuf;
if (rc < 0) {
return -1;
}

Expand All @@ -394,7 +371,8 @@ static inline int process_line(struct selabel_handle *rec,
struct saved_data *data = (struct saved_data *)rec->data;
struct spec *spec_arr;
unsigned int nspec = data->nspec;
const char *errbuf = NULL;
char const *errbuf;
struct regex_error_data error_data;

items = read_spec_entries(line_buf, &errbuf, 3, &regex, &type, &context);
if (items < 0) {
Expand Down Expand Up @@ -454,7 +432,7 @@ static inline int process_line(struct selabel_handle *rec,
data->nspec++;

if (rec->validating &&
compile_regex(data, &spec_arr[nspec], &errbuf)) {
compile_regex(data, &spec_arr[nspec], &error_data)) {
COMPAT_LOG(SELINUX_ERROR,
"%s: line %u has invalid regex %s: %s\n",
path, lineno, regex,
Expand Down

0 comments on commit 23838ba

Please sign in to comment.