diff options
author | Janis Danisevskis <jdanis@google.com> | 2016-03-29 19:17:10 +0100 |
---|---|---|
committer | Janis Danisevskis <jdanis@google.com> | 2016-05-05 18:42:09 +0100 |
commit | 31f5cd6e3ea8ed34a35bf43bf135565301ad7062 (patch) | |
tree | 1b850c15ccad2b9f1b9e94c6a890322218749024 | |
parent | 5f62b5073afe88699bfb2a7a036d1e0ef0d6a6b1 (diff) | |
download | libselinux-31f5cd6e3ea8ed34a35bf43bf135565301ad7062.tar.gz |
Port libselinux to pcre2
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 is still built against PCRE.
Bug: 24091652
Change-Id: Iafca54db166666f9e681022a529fd7409f1e4848
-rw-r--r-- | Android.mk | 43 | ||||
-rw-r--r-- | src/label_file.c | 90 | ||||
-rw-r--r-- | src/label_file.h | 51 | ||||
-rw-r--r-- | src/regex.c | 386 | ||||
-rw-r--r-- | src/regex.h | 166 | ||||
-rw-r--r-- | utils/sefcontext_compile.c | 47 |
6 files changed, 633 insertions, 150 deletions
@@ -1,5 +1,23 @@ LOCAL_PATH:= $(call my-dir) +# uncomment to build libselinux and related artifacts against PCRE2 +# common_USE_PCRE2 := 1 + +ifeq ($(common_USE_PCRE2), 1) +common_LIBRARIES := libpcre2 +common_C_INCLUDES := +common_CFLAGS := -DUSE_PCRE2 + +# Persistently stored patterns (pcre2) are architecture dependent. +# In particular paterns built on amd64 can not run on devices with armv7 +# (32bit). Therefore, this feature stays off for now. +common_CFLAGS += -DNO_PERSISTENTLY_STORED_PATTERNS +else +common_LIBRARIES := libpcre +common_C_INCLUDES := external/pcre +common_CFLAGS := +endif + common_SRC_FILES := \ src/booleans.c \ src/canonicalize_context.c \ @@ -38,6 +56,7 @@ common_HOST_FILES := \ src/label.c \ src/label_file.c \ src/label_android_property.c \ + src/regex.c \ src/label_support.c @@ -47,9 +66,10 @@ LOCAL_MODULE:= libselinux LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libcrypto_static LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_WHOLE_STATIC_LIBRARIES := libpcre libpackagelistparser +LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES) +LOCAL_WHOLE_STATIC_LIBRARIES += libpackagelistparser # 1003 corresponds to auditd, from system/core/logd/event.logtags -LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003 +LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003 $(common_CFLAGS) # mapping.c has redundant check of array p_in->perms. LOCAL_CLANG_CFLAGS += -Wno-pointer-bool-conversion LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include @@ -57,7 +77,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_CFLAGS := -DHOST +LOCAL_CFLAGS := -DHOST $(common_CFLAGS) ifeq ($(HOST_OS),darwin) LOCAL_CFLAGS += -DDARWIN @@ -66,7 +86,7 @@ endif LOCAL_SRC_FILES := $(common_HOST_FILES) LOCAL_MODULE:= libselinux LOCAL_MODULE_TAGS := eng -LOCAL_WHOLE_STATIC_LIBRARIES := libpcre +LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES) LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_HOST_STATIC_LIBRARY) @@ -76,9 +96,10 @@ LOCAL_SRC_FILES := $(common_SRC_FILES) $(common_HOST_FILES) src/android.c LOCAL_MODULE:= libselinux LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_SHARED_LIBRARIES := libcrypto liblog libpcre libpackagelistparser +LOCAL_SHARED_LIBRARIES := $(common_LIBRARIES) +LOCAL_SHARED_LIBRARIES += libcrypto liblog libpackagelistparser # 1003 corresponds to auditd, from system/core/logd/event.logtags -LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003 +LOCAL_CFLAGS := -DAUDITD_LOG_TAG=1003 $(common_CFLAGS) # mapping.c has redundant check of array p_in->perms. LOCAL_CLANG_CFLAGS += -Wno-pointer-bool-conversion LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include @@ -86,7 +107,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) -LOCAL_CFLAGS := -DHOST +LOCAL_CFLAGS := -DHOST $(common_CFLAGS) ifeq ($(HOST_OS),darwin) LOCAL_CFLAGS += -DDARWIN @@ -95,14 +116,14 @@ endif LOCAL_SRC_FILES := $(common_HOST_FILES) LOCAL_MODULE:= libselinux LOCAL_MODULE_TAGS := eng -LOCAL_WHOLE_STATIC_LIBRARIES := libpcre +LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES) LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_HOST_SHARED_LIBRARY) ################################# include $(CLEAR_VARS) -LOCAL_CFLAGS := -DHOST +LOCAL_CFLAGS := -DHOST $(common_CFLAGS) ifeq ($(HOST_OS),darwin) LOCAL_CFLAGS += -DDARWIN @@ -113,6 +134,6 @@ LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES := ../src/label_file.h LOCAL_SRC_FILES := utils/sefcontext_compile.c LOCAL_STATIC_LIBRARIES := libselinux -LOCAL_WHOLE_STATIC_LIBRARIES := libpcre -LOCAL_C_INCLUDES := external/pcre +LOCAL_WHOLE_STATIC_LIBRARIES := $(common_LIBRARIES) +LOCAL_C_INCLUDES := $(common_C_INCLUDES) include $(BUILD_HOST_EXECUTABLE) diff --git a/src/label_file.c b/src/label_file.c index 9d0a54c..d3e67c0 100644 --- a/src/label_file.c +++ b/src/label_file.c @@ -14,7 +14,6 @@ #include <errno.h> #include <limits.h> #include <stdint.h> -#include <pcre.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> @@ -174,7 +173,10 @@ static int load_mmap(struct selabel_handle *rec, const char *path, return -1; if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) { - len = strlen(pcre_version()); + if (!regex_version()) { + return -1; + } + len = strlen(regex_version()); rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); if (rc < 0) @@ -196,7 +198,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, regex_version()) != 0)) { free(str_buf); return -1; } @@ -362,46 +364,18 @@ 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); - 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; - } - spec->lsd.study_data = (void *)mmap_area->next_addr; - spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA; - rc = next_entry(NULL, mmap_area, entry_len); + rc = regex_load_mmap(mmap_area, &spec->regex); 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; - } +#ifndef NO_PERSISTENTLY_STORED_PATTERNS + spec->regcomp = 1; +#else + spec->regcomp = 0; +#endif data->nspec++; } + /* win */ rc = 0; err: @@ -569,10 +543,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++) { @@ -604,13 +575,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; @@ -637,9 +609,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. @@ -652,25 +621,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, ®ex_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; @@ -810,16 +773,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); diff --git a/src/label_file.h b/src/label_file.h index 901a3d9..d0ff254 100644 --- a/src/label_file.h +++ b/src/label_file.h @@ -6,6 +6,14 @@ #include <sys/stat.h> +/* + * Android: regex.h/c was 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" @@ -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 */ @@ -78,14 +77,6 @@ struct saved_data { struct mmap_area *mmap_areas; }; -static inline pcre_extra *get_pcre_extra(struct spec *spec) -{ - if (spec->from_mmap) - return &spec->lsd; - else - return spec->sd; -} - static inline mode_t string_to_mode(char *mode) { size_t len; @@ -328,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 */ @@ -358,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; } @@ -391,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, ®ex, &type, &context); if (items < 0) { @@ -451,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)) { selinux_log(SELINUX_ERROR, "%s: line %u has invalid regex %s: %s\n", path, lineno, regex, diff --git a/src/regex.c b/src/regex.c new file mode 100644 index 0000000..7a0d7e9 --- /dev/null +++ b/src/regex.c @@ -0,0 +1,386 @@ +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "regex.h" +#include "label_file.h" + +int regex_prepare_data(struct regex_data ** regex, char const * pattern_string, + struct regex_error_data * errordata) { + memset(errordata, 0, sizeof(struct regex_error_data)); + *regex = regex_data_create(); + if (!(*regex)) + return -1; +#ifdef USE_PCRE2 + (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string, + PCRE2_ZERO_TERMINATED, + PCRE2_DOTALL, + &errordata->error_code, + &errordata->error_offset, NULL); +#else + (*regex)->regex = pcre_compile(pattern_string, PCRE_DOTALL, + &errordata->error_buffer, + &errordata->error_offset, NULL); +#endif + if (!(*regex)->regex) { + goto err; + } + +#ifdef USE_PCRE2 + (*regex)->match_data = + pcre2_match_data_create_from_pattern((*regex)->regex, NULL); + if (!(*regex)->match_data) { + goto err; + } +#else + (*regex)->sd = pcre_study((*regex)->regex, 0, &errordata->error_buffer); + if (!(*regex)->sd && errordata->error_buffer) { + goto err; + } + (*regex)->extra_owned = !!(*regex)->sd; +#endif + return 0; + +err: regex_data_free(*regex); + *regex = NULL; + return -1; +} + +char const * regex_version() { +#ifdef USE_PCRE2 + static int initialized = 0; + static char * version_string = NULL; + size_t version_string_len; + if (!initialized) { + version_string_len = pcre2_config(PCRE2_CONFIG_VERSION, NULL); + version_string = (char*) malloc(version_string_len); + if (!version_string) { + return NULL; + } + pcre2_config(PCRE2_CONFIG_VERSION, version_string); + initialized = 1; + } + return version_string; +#else + return pcre_version(); +#endif +} + +int regex_load_mmap(struct mmap_area * mmap_area, struct regex_data ** regex) { + int rc; + size_t entry_len, info_len; + + rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); +#ifdef USE_PCRE2 + if (rc < 0) + return -1; + +#ifndef NO_PERSISTENTLY_STORED_PATTERNS + /* this should yield exactly one because we store one pattern at a time + */ + rc = pcre2_serialize_get_number_of_codes(mmap_area->next_addr); + if (rc != 1) + return -1; + + *regex = regex_data_create(); + if (!*regex) + return -1; + + rc = pcre2_serialize_decode(&(*regex)->regex, 1, + (PCRE2_SPTR)mmap_area->next_addr, NULL); + if (rc != 1) + goto err; + + (*regex)->match_data = + pcre2_match_data_create_from_pattern((*regex)->regex, NULL); + if (!(*regex)->match_data) + goto err; + +#endif /* NO_PERSISTENTLY_STORED_PATTERNS */ + /* and skip the decoded bit */ + rc = next_entry(NULL, mmap_area, entry_len); + if (rc < 0) + goto err; + + return 0; +#else + if (rc < 0 || !entry_len) { + rc = -1; + return -1; + } + *regex = regex_data_create(); + if (!(*regex)) + return -1; + + (*regex)->regex = (pcre *) mmap_area->next_addr; + rc = next_entry(NULL, mmap_area, entry_len); + if (rc < 0) + goto err; + + /* Check that regex lengths match. pcre_fullinfo() + * also validates its magic number. */ + rc = pcre_fullinfo((*regex)->regex, NULL, PCRE_INFO_SIZE, &info_len); + if (rc < 0 || info_len != entry_len) { + goto err; + } + + rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); + if (rc < 0 || !entry_len) { + goto err; + } + (*regex)->lsd.study_data = (void *) mmap_area->next_addr; + (*regex)->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((*regex)->regex, &(*regex)->lsd, + PCRE_INFO_STUDYSIZE, + &info_len); + if (rc < 0 || info_len != entry_len) { + goto err; + } + (*regex)->extra_owned = 0; + return 0; +#endif + err: regex_data_free(*regex); + *regex = NULL; + return -1; +} + +int regex_writef(struct regex_data * regex, FILE * fp) { + int rc; + size_t len; +#ifdef USE_PCRE2 + PCRE2_UCHAR * bytes; + PCRE2_SIZE to_write; + +#ifndef NO_PERSISTENTLY_STORED_PATTERNS + /* encode the patter for serialization */ + rc = pcre2_serialize_encode(®ex->regex, 1, &bytes, &to_write, NULL); + if (rc != 1) + return -1; + +#else + to_write = 0; +#endif + /* write serialized pattern's size */ + len = fwrite(&to_write, sizeof(uint32_t), 1, fp); + if (len != 1) { +#ifndef NO_PERSISTENTLY_STORED_PATTERNS + pcre2_serialize_free(bytes); +#endif + return -1; + } + +#ifndef NO_PERSISTENTLY_STORED_PATTERNS + /* write serialized pattern */ + len = fwrite(bytes, 1, to_write, fp); + if (len != to_write) { + pcre2_serialize_free(bytes); + return -1; + } + pcre2_serialize_free(bytes); +#endif +#else + uint32_t to_write; + size_t size; + pcre_extra * sd = regex->extra_owned ? regex->sd : ®ex->lsd; + + /* determine the size of the pcre data in bytes */ + rc = pcre_fullinfo(regex->regex, NULL, PCRE_INFO_SIZE, &size); + if (rc < 0) + return -1; + + /* write the number of bytes in the pcre data */ + to_write = size; + len = fwrite(&to_write, 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) + return -1; + + /* determine the size of the pcre study info */ + rc = pcre_fullinfo(regex->regex, sd, PCRE_INFO_STUDYSIZE, &size); + if (rc < 0) + return -1; + + /* write the number of bytes in the pcre study data */ + to_write = size; + len = fwrite(&to_write, sizeof(uint32_t), 1, fp); + if (len != 1) + return -1; + + /* write the actual pcre study data as a char array */ + len = fwrite(sd->study_data, 1, to_write, fp); + if (len != to_write) + return -1; +#endif + return 0; +} + +struct regex_data * regex_data_create() { + struct regex_data * dummy = (struct regex_data*) malloc( + sizeof(struct regex_data)); + if (dummy) { + memset(dummy, 0, sizeof(struct regex_data)); + } + return dummy; +} + +void regex_data_free(struct regex_data * regex) { + if (regex) { +#ifdef USE_PCRE2 + if (regex->regex) { + pcre2_code_free(regex->regex); + } + if (regex->match_data) { + pcre2_match_data_free(regex->match_data); + } +#else + if (regex->regex) + pcre_free(regex->regex); + if (regex->extra_owned && regex->sd) { + pcre_free_study(regex->sd); + } +#endif + free(regex); + } +} + +int regex_match(struct regex_data * regex, char const * subject, int partial) { + int rc; +#ifdef USE_PCRE2 + rc = pcre2_match(regex->regex, + (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0, + partial ? PCRE2_PARTIAL_SOFT : 0, regex->match_data, + NULL); + if (rc > 0) + return REGEX_MATCH; + switch (rc) { + case PCRE2_ERROR_PARTIAL: + return REGEX_MATCH_PARTIAL; + case PCRE2_ERROR_NOMATCH: + return REGEX_NO_MATCH; + default: + return REGEX_ERROR; + } +#else + rc = pcre_exec(regex->regex, + regex->extra_owned ? regex->sd : ®ex->lsd, subject, + strlen(subject), 0, partial ? PCRE_PARTIAL_SOFT : 0, + NULL, + 0); + switch (rc) { + case 0: + return REGEX_MATCH; + case PCRE_ERROR_PARTIAL: + return REGEX_MATCH_PARTIAL; + case PCRE_ERROR_NOMATCH: + return REGEX_NO_MATCH; + default: + return REGEX_ERROR; + } +#endif +} + +/* TODO Replace this compare function with something that actually compares the + * regular expressions. + * This compare function basically just compares the binary representations of + * the automatons, and because this representation contains pointers and + * metadata, it can only return a match if regex1 == regex2. + * Preferably, this function would be replaced with an algorithm that computes + * the equivalence of the automatons systematically. + */ +int regex_cmp(struct regex_data * regex1, struct regex_data * regex2) { + int rc; + size_t len1, len2; +#ifdef USE_PCRE2 + rc = pcre2_pattern_info(regex1->regex, PCRE2_INFO_SIZE, &len1); + assert(rc == 0); + rc = pcre2_pattern_info(regex2->regex, PCRE2_INFO_SIZE, &len2); + assert(rc == 0); + if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1)) + return SELABEL_INCOMPARABLE; +#else + rc = pcre_fullinfo(regex1->regex, NULL, PCRE_INFO_SIZE, &len1); + assert(rc == 0); + rc = pcre_fullinfo(regex2->regex, NULL, PCRE_INFO_SIZE, &len2); + assert(rc == 0); + if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1)) + return SELABEL_INCOMPARABLE; +#endif + return SELABEL_EQUAL; +} + +void regex_format_error(struct regex_error_data const * error_data, + char * buffer, size_t buf_size) { + unsigned the_end_length = buf_size > 4 ? 4 : buf_size; + char * ptr = &buffer[buf_size - the_end_length]; + int rc = 0; + size_t pos = 0; + if (!buffer || !buf_size) + return; + rc = snprintf(buffer, buf_size, "REGEX back-end error: "); + if (rc < 0) { + buffer[0] = '\0'; + return; + } + pos += rc; + if (pos >= buf_size) + goto truncated; + + if (error_data->error_offset > 0) { +#ifdef USE_PCRE2 + rc = snprintf(buffer + pos, buf_size - pos, "At offset %lu: ", + error_data->error_offset); +#else + rc = snprintf(buffer + pos, buf_size - pos, "At offset %d: ", + error_data->error_offset); +#endif + if (rc < 0) { + buffer[0] = '\0'; + return; + } + } + pos += rc; + if (pos >= buf_size) + goto truncated; + +#ifdef USE_PCRE2 + rc = pcre2_get_error_message(error_data->error_code, + (PCRE2_UCHAR*)(buffer + pos), + buf_size - pos); + if (rc == PCRE2_ERROR_NOMEMORY) + goto truncated; +#else + rc = snprintf(buffer + pos, buf_size - pos, "%s", + error_data->error_buffer); + if (rc < strlen(error_data->error_buffer)) + goto truncated; +#endif + + return; + + truncated: + /* replace end of string with "..." to indicate that it was truncated */ + switch (the_end_length) { + /* no break statements, fall-through is intended */ + case 4: + *ptr++ = '.'; + case 3: + *ptr++ = '.'; + case 2: + *ptr++ = '.'; + case 1: + *ptr++ = '\0'; + default: + break; + } + +} diff --git a/src/regex.h b/src/regex.h new file mode 100644 index 0000000..7b74f32 --- /dev/null +++ b/src/regex.h @@ -0,0 +1,166 @@ +#ifndef SRC_REGEX_H_ +#define SRC_REGEX_H_ + +#include <stdio.h> + +#ifdef USE_PCRE2 +#include <pcre2.h> +#else +#include <pcre.h> +#endif + +enum { + REGEX_MATCH, + REGEX_MATCH_PARTIAL, + REGEX_NO_MATCH, + REGEX_ERROR = -1, +}; + +#ifdef USE_PCRE2 +struct regex_data { + pcre2_code * regex; /* compiled regular expression */ + pcre2_match_data * match_data; /* match data block required for the compiled + pattern in regex2 */ +}; + +struct regex_error_data { + int error_code; + PCRE2_SIZE error_offset; +}; + +/* ^^^^^^ USE_PCRE2 ^^^^^^ */ +#else +/* vvvvvv USE_PCRE vvvvvv */ + +/* 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 + +struct regex_data { + pcre *regex; /* compiled regular expression */ + int extra_owned; + union { + pcre_extra *sd; /* pointer to extra compiled stuff */ + pcre_extra lsd; /* used to hold the mmap'd version */ + }; +}; + +struct regex_error_data { + char const * error_buffer; + int error_offset; +}; + +#endif /* USE_PCRE2 */ + +struct mmap_area; + +/** + * regex_verison returns the version string of the underlying regular + * regular expressions library. In the case of PCRE it just returns the + * result of pcre_version(). In the case of PCRE2, the very first time this + * function is called it allocates a buffer large enough to hold the version + * string and reads the PCRE2_CONFIG_VERSION option to fill the buffer. + * The allocated buffer will linger in memory until the calling process is being + * reaped. + * + * It may return NULL on error. + */ +char const * regex_version(); +/** + * This constructor function allocates a buffer for a regex_data structure. + * The buffer is being initialized with zeroes. + */ +struct regex_data * regex_data_create(); +/** + * This complementary destructor function frees the a given regex_data buffer. + * It also frees any non NULL member pointers with the appropriate pcreX_X_free + * function. For PCRE this function respects the extra_owned field and frees + * the pcre_extra data conditionally. Calling this function on a NULL pointer is + * save. + */ +void regex_data_free(struct regex_data * regex); +/** + * This function compiles the regular expression. Additionally, it prepares + * data structures required by the different underlying engines. For PCRE + * it calls pcre_study to generate optional data required for optimized + * execution of the compiled pattern. In the case of PCRE2, it allocates + * a pcre2_match_data structure of appropriate size to hold all possible + * matches created by the pattern. + * + * @arg regex If successful, the structure returned through *regex was allocated + * with regex_data_create and must be freed with regex_data_free. + * @arg pattern_string The pattern string that is to be compiled. + * @arg errordata A pointer to a regex_error_data structure must be passed + * to this function. This structure depends on the underlying + * implementation. It can be passed to regex_format_error + * to generate a human readable error message. + * @retval 0 on success + * @retval -1 on error + */ +int regex_prepare_data(struct regex_data ** regex, char const * pattern_string, + struct regex_error_data * errordata); +/** + * This function loads a serialized precompiled pattern from a contiguous + * data region given by map_area. + * + * @arg map_area Description of the memory region holding a serialized + * representation of the precompiled pattern. + * @arg regex If successful, the structure returned through *regex was allocated + * with regex_data_create and must be freed with regex_data_free. + * + * @retval 0 on success + * @retval -1 on error + */ +int regex_load_mmap(struct mmap_area * map_area, struct regex_data ** regex); +/** + * This function stores a precompiled regular expression to a file. + * In the case of PCRE, it just dumps the binary representation of the + * precomplied pattern into a file. In the case of PCRE2, it uses the + * serialization function provided by the library. + * + * @arg regex The precomplied regular expression data. + * @arg fp A file stream specifying the output file. + */ +int regex_writef(struct regex_data * regex, FILE * fp); +/** + * This function applies a precompiled pattern to a subject string and + * returns whether or not a match was found. + * + * @arg regex The precompiled pattern. + * @arg subject The subject string. + * @arg partial Boolean indicating if partial matches are wanted. A nonzero + * value is equivalent to specifying PCRE[2]_PARTIAL_SOFT as + * option to pcre_exec of pcre2_match. + * @retval REGEX_MATCH if a match was found + * @retval REGEX_MATCH_PARTIAL if a partial match was found + * @retval REGEX_NO_MATCH if no match was found + * @retval REGEX_ERROR if an error was encountered during the execution of the + * regular expression + */ +int regex_match(struct regex_data * regex, char const * subject, int partial); +/** + * This function compares two compiled regular expressions (regex1 and regex2). + * It compares the binary representations of the compiled patterns. It is a very + * crude approximation because the binary representation holds data like + * reference counters, that has nothing to do with the actual state machine. + * + * @retval SELABEL_EQUAL if the pattern's binary representations are exactly + * the same + * @retval SELABEL_INCOMPARABLE otherwise + */ +int regex_cmp(struct regex_data * regex1, struct regex_data * regex2); +/** + * This function takes the error data returned by regex_prepare_data and turns + * it in to a human readable error message. + * If the buffer given to hold the error message is to small it truncates the + * message and indicates the truncation with an ellipsis ("...") at the end of + * the buffer. + * + * @arg error_data Error data as returned by regex_prepare_data. + * @arg buffer String buffer to hold the formated error string. + * @arg buf_size Total size of the given bufer in bytes. + */ +void regex_format_error(struct regex_error_data const * error_data, + char * buffer, size_t buf_size); +#endif /* SRC_REGEX_H_ */ diff --git a/utils/sefcontext_compile.c b/utils/sefcontext_compile.c index 4160632..f88c756 100644 --- a/utils/sefcontext_compile.c +++ b/utils/sefcontext_compile.c @@ -1,6 +1,5 @@ #include <ctype.h> #include <errno.h> -#include <pcre.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -12,6 +11,7 @@ #include <selinux/selinux.h> #include "../src/label_file.h" +#include "../src/regex.h" static int validate_context(char __attribute__ ((unused)) **ctx) { @@ -101,12 +101,14 @@ static int write_binary_file(struct saved_data *data, int fd) if (len != 1) goto err; - /* write the pcre version */ - section_len = strlen(pcre_version()); + /* write version of the regex back-end */ + if (!regex_version()) + goto err; + section_len = strlen(regex_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); + len = fwrite(regex_version(), sizeof(char), section_len, bin_file); if (len != section_len) goto err; @@ -144,8 +146,7 @@ static int write_binary_file(struct saved_data *data, int fd) mode_t mode = specs[i].mode; size_t prefix_len = specs[i].prefix_len; int32_t stem_id = specs[i].stem_id; - pcre *re = specs[i].regex; - pcre_extra *sd = get_pcre_extra(&specs[i]); + struct regex_data *re = specs[i].regex; uint32_t to_write; size_t size; @@ -194,37 +195,10 @@ static int write_binary_file(struct saved_data *data, int fd) 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); + /* Write regex related data */ + rc = regex_writef(re, bin_file); 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; @@ -247,8 +221,7 @@ static void free_specs(struct saved_data *data) free(specs[i].lr.ctx_trans); free(specs[i].regex_str); free(specs[i].type_str); - pcre_free(specs[i].regex); - pcre_free_study(specs[i].sd); + regex_data_free(specs[i].regex); } free(specs); |