From c3345c58a4efc4a951b2dfbc5ddb9797938d52d9 Mon Sep 17 00:00:00 2001 From: Yuli Khodorkovskiy Date: Tue, 5 May 2015 15:19:59 -0400 Subject: [PATCH 1/4] libsepol/cil: Verify users prior to evaluating users If a userlevel or userrange statement is missing from a policy, evaluate_level_expression() and evaluate_levelrange_expression, respectively will have a NULL pointer dereference caused by a missing level in a user. Add cil_pre_verify() which verifies users have a valid level. Also, move loop checking in classpermissions into cil_pre_verify(). This fixes https://github.com/SELinuxProject/cil/issues/1. Signed-off-by: Yuli Khodorkovskiy --- libsepol/cil/src/cil_post.c | 20 ++++++++++++++++++- libsepol/cil/src/cil_verify.c | 37 +++++++++++++++++++---------------- libsepol/cil/src/cil_verify.h | 2 +- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c index 633a5a12..f91727f4 100644 --- a/libsepol/cil/src/cil_post.c +++ b/libsepol/cil/src/cil_post.c @@ -1691,12 +1691,30 @@ exit: return rc; } +static int cil_pre_verify(struct cil_db *db) +{ + int rc = SEPOL_ERR; + struct cil_args_verify extra_args; + + extra_args.db = db; + + rc = cil_tree_walk(db->ast->root, __cil_pre_verify_helper, NULL, NULL, &extra_args); + if (rc != SEPOL_OK) { + cil_log(CIL_ERR, "Failed to verify cil database\n"); + goto exit; + } + +exit: + return rc; +} + int cil_post_process(struct cil_db *db) { int rc = SEPOL_ERR; - rc = cil_verify_no_classperms_loop(db); + rc = cil_pre_verify(db); if (rc != SEPOL_OK) { + cil_log(CIL_ERR, "Failed to verify cil database\n"); goto exit; } diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c index 399c94a6..62b88d06 100644 --- a/libsepol/cil/src/cil_verify.c +++ b/libsepol/cil/src/cil_verify.c @@ -601,7 +601,7 @@ exit: return rc; } -int __cil_verify_user(struct cil_db *db, struct cil_tree_node *node) +static int __cil_verify_user_pre_eval(struct cil_tree_node *node) { int rc = SEPOL_ERR; struct cil_user *user = node->data; @@ -635,6 +635,17 @@ int __cil_verify_user(struct cil_db *db, struct cil_tree_node *node) } } + return SEPOL_OK; +exit: + cil_log(CIL_ERR, "Invalid user at line %d of %s\n", node->line, node->path); + return rc; +} + +static int __cil_verify_user_post_eval(struct cil_db *db, struct cil_tree_node *node) +{ + int rc = SEPOL_ERR; + struct cil_user *user = node->data; + /* Verify user range only if anonymous */ if (user->range->datum.name == NULL) { rc = __cil_verify_levelrange(db, user->range); @@ -1318,7 +1329,7 @@ int __cil_verify_helper(struct cil_tree_node *node, uint32_t *finished, void *ex case 0: { switch (node->flavor) { case CIL_USER: - rc = __cil_verify_user(db, node); + rc = __cil_verify_user_post_eval(db, node); break; case CIL_SELINUXUSERDEFAULT: (*nseuserdflt)++; @@ -1531,7 +1542,7 @@ static int __cil_verify_map_class(struct cil_tree_node *node) return SEPOL_OK; } -static int __cil_verify_no_classperms_loop_helper(struct cil_tree_node *node, uint32_t *finished, __attribute__((unused)) void *extra_args) +int __cil_pre_verify_helper(struct cil_tree_node *node, uint32_t *finished, __attribute__((unused)) void *extra_args) { int rc = SEPOL_ERR; @@ -1549,6 +1560,12 @@ static int __cil_verify_no_classperms_loop_helper(struct cil_tree_node *node, ui } switch (node->flavor) { + case CIL_USER: + rc = __cil_verify_user_pre_eval(node); + if (rc != SEPOL_OK) { + goto exit; + } + break; case CIL_MAP_CLASS: rc = __cil_verify_map_class(node); break; @@ -1563,17 +1580,3 @@ static int __cil_verify_no_classperms_loop_helper(struct cil_tree_node *node, ui exit: return rc; } - -int cil_verify_no_classperms_loop(struct cil_db *db) -{ - int rc = SEPOL_ERR; - - rc = cil_tree_walk(db->ast->root, __cil_verify_no_classperms_loop_helper, NULL, NULL, NULL); - if (rc != SEPOL_OK) { - cil_log(CIL_ERR, "Failed to verify no loops in class permissions\n"); - goto exit; - } - -exit: - return rc; -} diff --git a/libsepol/cil/src/cil_verify.h b/libsepol/cil/src/cil_verify.h index 1f47641f..bda1565f 100644 --- a/libsepol/cil/src/cil_verify.h +++ b/libsepol/cil/src/cil_verify.h @@ -68,6 +68,6 @@ int __cil_verify_ordered(struct cil_tree_node *current, enum cil_flavor flavor); int __cil_verify_initsids(struct cil_list *sids); int __cil_verify_senscat(struct cil_sens *sens, struct cil_cat *cat); int __cil_verify_helper(struct cil_tree_node *node, __attribute__((unused)) uint32_t *finished, void *extra_args); -int cil_verify_no_classperms_loop(struct cil_db *db); +int __cil_pre_verify_helper(struct cil_tree_node *node, __attribute__((unused)) uint32_t *finished, void *extra_args); #endif From 9ab426eea1870385792b1df418aead73f4d820ab Mon Sep 17 00:00:00 2001 From: James Carter Date: Tue, 5 May 2015 16:08:41 -0400 Subject: [PATCH 2/4] Update libsepol ChangeLog. Signed-off-by: James Carter --- libsepol/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/libsepol/ChangeLog b/libsepol/ChangeLog index 8fa634ff..ceacc16a 100644 --- a/libsepol/ChangeLog +++ b/libsepol/ChangeLog @@ -1,3 +1,4 @@ + * Verify users prior to evaluating users in cil, from Yuli Khodorkovskiy. * Binary modules do not support ioctl rules, from Stephen Smalley. * Add support for ioctl command whitelisting, from Jeff Vander Stoep. * Don't use symbol versioning for static object files, from Yuli Khodorkovskiy. From e7f970ffd1a8dbb26051405719a2288d34e856f6 Mon Sep 17 00:00:00 2001 From: Richard Haines Date: Wed, 6 May 2015 16:11:03 +0100 Subject: [PATCH 3/4] libselinux: Add selabel partial and best match APIs Add support for new API functions selabel_partial_match and selabel_lookup_best_match ported from the Android libselinux fork. Add supporting man(3) pages and test utilities: selabel_lookup, selabel_lookup_best_match and selabel_partial_match. Signed-off-by: Richard Haines Signed-off-by: Stephen Smalley --- libselinux/include/selinux/label.h | 8 + .../man/man3/selabel_lookup_best_match.3 | 100 +++++++++ .../man/man3/selabel_lookup_best_match_raw.3 | 1 + libselinux/man/man3/selabel_partial_match.3 | 34 +++ libselinux/src/label.c | 194 ++++++++++++++---- libselinux/src/label_file.c | 109 +++++++++- libselinux/src/label_file.h | 1 + libselinux/src/label_internal.h | 6 + libselinux/utils/selabel_lookup.c | 126 ++++++++++++ libselinux/utils/selabel_lookup_best_match.c | 164 +++++++++++++++ libselinux/utils/selabel_partial_match.c | 75 +++++++ 11 files changed, 766 insertions(+), 52 deletions(-) create mode 100644 libselinux/man/man3/selabel_lookup_best_match.3 create mode 100644 libselinux/man/man3/selabel_lookup_best_match_raw.3 create mode 100644 libselinux/man/man3/selabel_partial_match.3 create mode 100644 libselinux/utils/selabel_lookup.c create mode 100644 libselinux/utils/selabel_lookup_best_match.c create mode 100644 libselinux/utils/selabel_partial_match.c diff --git a/libselinux/include/selinux/label.h b/libselinux/include/selinux/label.h index 672a7c22..7a94a925 100644 --- a/libselinux/include/selinux/label.h +++ b/libselinux/include/selinux/label.h @@ -6,6 +6,7 @@ #ifndef _SELABEL_H_ #define _SELABEL_H_ +#include #include #include @@ -97,6 +98,13 @@ int selabel_lookup(struct selabel_handle *handle, char **con, int selabel_lookup_raw(struct selabel_handle *handle, char **con, const char *key, int type); +bool selabel_partial_match(struct selabel_handle *handle, const char *key); + +int selabel_lookup_best_match(struct selabel_handle *rec, char **con, + const char *key, const char **aliases, int type); +int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con, + const char *key, const char **aliases, int type); + /** * selabel_stats - log labeling operation statistics. * @handle: specifies backend instance to query diff --git a/libselinux/man/man3/selabel_lookup_best_match.3 b/libselinux/man/man3/selabel_lookup_best_match.3 new file mode 100644 index 00000000..ef2efb4a --- /dev/null +++ b/libselinux/man/man3/selabel_lookup_best_match.3 @@ -0,0 +1,100 @@ +.TH "selabel_lookup_best_match" "3" "05 May 2015" "Security Enhanced Linux" "SELinux API documentation" + +.SH "NAME" +selabel_lookup_best_match \- obtain a best match SELinux security +context \- Only supported on file backend. +. +.SH "SYNOPSIS" +.B #include +.br +.B #include +.sp +.BI "int selabel_lookup_best_match(struct selabel_handle *" hnd , +.in +\w'int selabel_lookup_best_match('u +.BI "char **" context , +.br +.BI "const char *" key , +.br +.BI "const char **" links , +.br +.BI "int " type ");" +.in +.sp +.BI "int selabel_lookup_best_match_raw(struct selabel_handle *" hnd , +.in +\w'int selabel_lookup_best_match_raw('u +.BI "char **" context , +.br +.BI "const char *" key , +.br +.BI "const char **" links , +.br +.BI "int " type ");" +.in +. +.SH "DESCRIPTION" +.BR selabel_lookup_best_match () +performs a best match lookup operation on the handle +.IR hnd , +returning the result in the memory pointed to by +.IR context , +which must be freed by the caller using +.BR freecon (3). +The \fIkey\fR parameter is a file path to check for best match using zero or +more \fIlink\fR (aliases) parameters. The order of precedence for best match is: +.RS +.IP "1." 4 +An exact match for the real path (\fIkey\fR) or +.IP "2." 4 +An exact match for any of the \fIlink\fRs (aliases), or +.IP "3." 4 +The longest fixed prefix match. +.RE +.sp +The \fItype\fR parameter is an optional file \fImode\fR argument that should +be set to the mode bits of the file, as determined by \fBlstat\fR(2). +\fImode\fR may be zero, however full matching may not occur. + +.BR selabel_lookup_best_match_raw () +behaves identically to +.BR selabel_lookup_best_match () +but does not perform context translation. +. +.SH "RETURN VALUE" +On success, zero is returned. On error, \-1 is returned and +.I errno +is set appropriately. +. +.SH "ERRORS" +.TP +.B ENOENT +No context corresponding to the input +.I key +and +.I type +was found. +.TP +.B EINVAL +The +.I key +and/or +.I type +inputs are invalid, or the context being returned failed validation. +.TP +.B ENOMEM +An attempt to allocate memory failed. +.sp +.SH "NOTES" +Example usage - When a service creates a device node, it may also create one +or more symlinks to the device node. These symlinks may be the only stable +name for the device, e.g. if the partition is dynamically assigned. +The file label backend supports this by looking up the "best match" +for a device node based on its real path (\fIkey\fR) and any \fIlink\fRs to it +(aliases). The order of precedence for best match is described above. +.sp +.SH "SEE ALSO" +.BR selabel_open (3), +.BR selabel_stats (3), +.BR selinux_set_callback (3), +.BR selinux (8), +.BR lstat (2), +.BR selabel_file (5) diff --git a/libselinux/man/man3/selabel_lookup_best_match_raw.3 b/libselinux/man/man3/selabel_lookup_best_match_raw.3 new file mode 100644 index 00000000..8982f17d --- /dev/null +++ b/libselinux/man/man3/selabel_lookup_best_match_raw.3 @@ -0,0 +1 @@ +.so man3/selabel_lookup_best_match.3 diff --git a/libselinux/man/man3/selabel_partial_match.3 b/libselinux/man/man3/selabel_partial_match.3 new file mode 100644 index 00000000..4cd46f74 --- /dev/null +++ b/libselinux/man/man3/selabel_partial_match.3 @@ -0,0 +1,34 @@ +.TH "selabel_partial_match" "3" "05 May 2015" "Security Enhanced Linux" "SELinux API documentation" + +.SH "NAME" +selabel_partial_match \- determine whether a direct or partial match is +possible on a file path \- Only supported on file backend. +. +.SH "SYNOPSIS" +.B #include +.br +.B #include +.br +.B #include +.sp +.BI "bool selabel_partial_match(struct selabel_handle *" hnd , +.in +\w'int selabel_partial_match('u +.BI "const char *" key ");" +.in +. +.SH "DESCRIPTION" +.BR selabel_partial_match () +performs a partial match operation on the handle +.IR hnd , +returning TRUE or FALSE. +The \fIkey\fR parameter is a file path to check for a direct or partial match. +.sp +.SH "RETURN VALUE" +TRUE is returned if a direct or partial match is found, FALSE if not. +.sp +.SH "SEE ALSO" +.BR selabel_open (3), +.BR selabel_stats (3), +.BR selinux_set_callback (3), +.BR selinux (8), +.BR selabel_file (5) diff --git a/libselinux/src/label.c b/libselinux/src/label.c index c3c099e4..759a3fa6 100644 --- a/libselinux/src/label.c +++ b/libselinux/src/label.c @@ -154,6 +154,98 @@ out: return rc; } +/* Public API helpers */ +static char *selabel_sub_key(struct selabel_handle *rec, const char *key) +{ + char *ptr = NULL; + char *dptr = NULL; + + ptr = selabel_sub(rec->subs, key); + if (ptr) { + dptr = selabel_sub(rec->dist_subs, ptr); + if (dptr) { + free(ptr); + ptr = dptr; + } + } else { + ptr = selabel_sub(rec->dist_subs, key); + } + if (ptr) + return ptr; + + return NULL; +} + +static int selabel_fini(struct selabel_handle *rec, + struct selabel_lookup_rec *lr, + int translating) +{ + if (compat_validate(rec, lr, rec->spec_file, 0)) + return -1; + + if (translating && !lr->ctx_trans && + selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans)) + return -1; + + return 0; +} + +static struct selabel_lookup_rec * +selabel_lookup_common(struct selabel_handle *rec, int translating, + const char *key, int type) +{ + struct selabel_lookup_rec *lr; + char *ptr = NULL; + + if (key == NULL) { + errno = EINVAL; + return NULL; + } + + ptr = selabel_sub_key(rec, key); + if (ptr) { + lr = rec->func_lookup(rec, ptr, type); + free(ptr); + } else { + lr = rec->func_lookup(rec, key, type); + } + if (!lr) + return NULL; + + if (selabel_fini(rec, lr, translating)) + return NULL; + + return lr; +} + +static struct selabel_lookup_rec * +selabel_lookup_bm_common(struct selabel_handle *rec, int translating, + const char *key, int type, const char **aliases) +{ + struct selabel_lookup_rec *lr; + char *ptr = NULL; + + if (key == NULL) { + errno = EINVAL; + return NULL; + } + + ptr = selabel_sub_key(rec, key); + if (ptr) { + lr = rec->func_lookup_best_match(rec, ptr, aliases, type); + free(ptr); + } else { + lr = rec->func_lookup_best_match(rec, key, aliases, type); + } + if (!lr) + return NULL; + + if (selabel_fini(rec, lr, translating)) + return NULL; + + return lr; +} + /* * Public API */ @@ -188,48 +280,6 @@ out: return rec; } -static struct selabel_lookup_rec * -selabel_lookup_common(struct selabel_handle *rec, int translating, - const char *key, int type) -{ - struct selabel_lookup_rec *lr; - char *ptr = NULL; - char *dptr = NULL; - - if (key == NULL) { - errno = EINVAL; - return NULL; - } - - ptr = selabel_sub(rec->subs, key); - if (ptr) { - dptr = selabel_sub(rec->dist_subs, ptr); - if (dptr) { - free(ptr); - ptr = dptr; - } - } else { - ptr = selabel_sub(rec->dist_subs, key); - } - if (ptr) { - lr = rec->func_lookup(rec, ptr, type); - free(ptr); - } else { - lr = rec->func_lookup(rec, key, type); - } - if (!lr) - return NULL; - - if (compat_validate(rec, lr, rec->spec_file, 0)) - return NULL; - - if (translating && !lr->ctx_trans && - selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans)) - return NULL; - - return lr; -} - int selabel_lookup(struct selabel_handle *rec, char **con, const char *key, int type) { @@ -256,6 +306,66 @@ int selabel_lookup_raw(struct selabel_handle *rec, char **con, return *con ? 0 : -1; } +bool selabel_partial_match(struct selabel_handle *rec, const char *key) +{ + char *ptr; + bool ret; + + if (!rec->func_partial_match) { + /* + * If the label backend does not support partial matching, + * then assume a match is possible. + */ + return true; + } + + ptr = selabel_sub_key(rec, key); + if (ptr) { + ret = rec->func_partial_match(rec, ptr); + free(ptr); + } else { + ret = rec->func_partial_match(rec, key); + } + + return ret; +} + +int selabel_lookup_best_match(struct selabel_handle *rec, char **con, + const char *key, const char **aliases, int type) +{ + struct selabel_lookup_rec *lr; + + if (!rec->func_lookup_best_match) { + errno = ENOTSUP; + return -1; + } + + lr = selabel_lookup_bm_common(rec, 1, key, type, aliases); + if (!lr) + return -1; + + *con = strdup(lr->ctx_trans); + return *con ? 0 : -1; +} + +int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con, + const char *key, const char **aliases, int type) +{ + struct selabel_lookup_rec *lr; + + if (!rec->func_lookup_best_match) { + errno = ENOTSUP; + return -1; + } + + lr = selabel_lookup_bm_common(rec, 0, key, type, aliases); + if (!lr) + return -1; + + *con = strdup(lr->ctx_raw); + return *con ? 0 : -1; +} + void selabel_close(struct selabel_handle *rec) { selabel_subs_fini(rec->subs); diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c index 45630159..b3e56713 100644 --- a/libselinux/src/label_file.c +++ b/libselinux/src/label_file.c @@ -601,15 +601,17 @@ static void closef(struct selabel_handle *rec) free(data); } -static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, - const char *key, int type) +static struct spec *lookup_common(struct selabel_handle *rec, + const char *key, + int type, + bool partial) { struct saved_data *data = (struct saved_data *)rec->data; struct spec *spec_arr = data->spec_arr; - int i, rc, file_stem; + int i, rc, file_stem, pcre_options = 0; mode_t mode = (mode_t)type; const char *buf; - struct selabel_lookup_rec *ret = NULL; + struct spec *ret = NULL; char *clean_key = NULL; const char *prev_slash, *next_slash; unsigned int sofar = 0; @@ -621,7 +623,7 @@ static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, /* Remove duplicate slashes */ if ((next_slash = strstr(key, "//"))) { - clean_key = malloc(strlen(key) + 1); + clean_key = (char *) malloc(strlen(key) + 1); if (!clean_key) goto finish; prev_slash = key; @@ -639,6 +641,9 @@ static struct selabel_lookup_rec *lookup(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. @@ -654,14 +659,22 @@ static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, if (compile_regex(data, spec, NULL) < 0) goto finish; if (spec->stem_id == -1) - rc = pcre_exec(spec->regex, get_pcre_extra(spec), key, strlen(key), 0, 0, NULL, 0); + rc = pcre_exec(spec->regex, + get_pcre_extra(spec), + key, strlen(key), 0, + pcre_options, NULL, 0); else - rc = pcre_exec(spec->regex, get_pcre_extra(spec), buf, strlen(buf), 0, 0, NULL, 0); - + rc = pcre_exec(spec->regex, + get_pcre_extra(spec), + buf, strlen(buf), 0, + pcre_options, NULL, 0); if (rc == 0) { spec->matches++; break; - } else if (rc == PCRE_ERROR_NOMATCH) + } else if (partial && rc == PCRE_ERROR_PARTIAL) + break; + + if (rc == PCRE_ERROR_NOMATCH) continue; errno = ENOENT; @@ -677,13 +690,87 @@ static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, } errno = 0; - ret = &spec_arr[i].lr; + ret = &spec_arr[i]; finish: free(clean_key); return ret; } +static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, + const char *key, int type) +{ + struct spec *spec; + + spec = lookup_common(rec, key, type, false); + if (spec) + return &spec->lr; + return NULL; +} + +static bool partial_match(struct selabel_handle *rec, const char *key) +{ + return lookup_common(rec, key, 0, true) ? true : false; +} + +static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec, + const char *key, + const char **aliases, + int type) +{ + size_t n, i; + int best = -1; + struct spec **specs; + size_t prefix_len = 0; + struct selabel_lookup_rec *lr = NULL; + + if (!aliases || !aliases[0]) + return lookup(rec, key, type); + + for (n = 0; aliases[n]; n++) + ; + + specs = calloc(n+1, sizeof(struct spec *)); + if (!specs) + return NULL; + specs[0] = lookup_common(rec, key, type, false); + if (specs[0]) { + if (!specs[0]->hasMetaChars) { + /* exact match on key */ + lr = &specs[0]->lr; + goto out; + } + best = 0; + prefix_len = specs[0]->prefix_len; + } + for (i = 1; i <= n; i++) { + specs[i] = lookup_common(rec, aliases[i-1], type, false); + if (specs[i]) { + if (!specs[i]->hasMetaChars) { + /* exact match on alias */ + lr = &specs[i]->lr; + goto out; + } + if (specs[i]->prefix_len > prefix_len) { + best = i; + prefix_len = specs[i]->prefix_len; + } + } + } + + if (best >= 0) { + /* longest fixed prefix match on key or alias */ + lr = &specs[best]->lr; + } else { + errno = ENOENT; + } + +out: + free(specs); + return lr; +} + + static void stats(struct selabel_handle *rec) { struct saved_data *data = (struct saved_data *)rec->data; @@ -722,6 +809,8 @@ int selabel_file_init(struct selabel_handle *rec, struct selinux_opt *opts, rec->func_close = &closef; rec->func_stats = &stats; rec->func_lookup = &lookup; + rec->func_partial_match = &partial_match; + rec->func_lookup_best_match = &lookup_best_match; return init(rec, opts, nopts); } diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h index e3a0445d..d37008f3 100644 --- a/libselinux/src/label_file.h +++ b/libselinux/src/label_file.h @@ -31,6 +31,7 @@ struct spec { char hasMetaChars; /* regular expression has meta-chars */ char regcomp; /* regex_str has been compiled to regex */ char from_mmap; /* this spec is from an mmap of the data */ + size_t prefix_len; /* length of fixed path prefix */ }; /* A regular expression stem */ diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h index a1fa4fdc..ef6ad260 100644 --- a/libselinux/src/label_internal.h +++ b/libselinux/src/label_internal.h @@ -57,6 +57,12 @@ struct selabel_handle { const char *key, int type); void (*func_close) (struct selabel_handle *h); void (*func_stats) (struct selabel_handle *h); + bool (*func_partial_match) (struct selabel_handle *h, const char *key); + struct selabel_lookup_rec *(*func_lookup_best_match) + (struct selabel_handle *h, + const char *key, + const char **aliases, + int type); /* supports backend-specific state information */ void *data; diff --git a/libselinux/utils/selabel_lookup.c b/libselinux/utils/selabel_lookup.c new file mode 100644 index 00000000..d0b14572 --- /dev/null +++ b/libselinux/utils/selabel_lookup.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s -b backend [-v] [-r] -k key [-t type] [-f file]\n\n" + "Where:\n\t" + "-b The backend - \"file\", \"media\", \"x\", \"db\" or " + "\"prop\"\n\t" + "-v Validate entries against loaded policy.\n\t" + "-r Use \"raw\" function.\n\t" + "-k Lookup key - Depends on backend.\n\t" + "-t Lookup type - Optional as depends on backend.\n\t" + "-f Optional file containing the specs (defaults to\n\t" + " those used by loaded policy).\n\n" + "Examples:\n\t" + "%s -v -b file -k /run -t 0\n\t" + " lookup with validation against the loaded policy, the\n\t" + " \"file\" backend for path \"/run\" with mode = 0\n\t" + "%s -r -b x -t 4 -k X11:ButtonPress\n\t" + " lookup_raw the \"X\" backend for type SELABEL_X_EVENT\n\t" + " using key \"X11:ButtonPress\"\n\n", + progname, progname, progname); + exit(1); +} + +int main(int argc, char **argv) +{ + int raw = 0, type = 0, backend = 0, rc, opt; + char *validate = NULL, *key = NULL, *context = NULL, *file = NULL; + + struct selabel_handle *hnd; + struct selinux_opt selabel_option[] = { + { SELABEL_OPT_PATH, file }, + { SELABEL_OPT_VALIDATE, validate } + }; + + if (argc < 3) + usage(argv[0]); + + while ((opt = getopt(argc, argv, "b:f:vrk:t:")) > 0) { + switch (opt) { + case 'b': + if (!strcasecmp(optarg, "file")) { + backend = SELABEL_CTX_FILE; + } else if (!strcmp(optarg, "media")) { + backend = SELABEL_CTX_MEDIA; + } else if (!strcmp(optarg, "x")) { + backend = SELABEL_CTX_X; + } else if (!strcmp(optarg, "db")) { + backend = SELABEL_CTX_DB; + } else if (!strcmp(optarg, "prop")) { + backend = SELABEL_CTX_ANDROID_PROP; + } else { + fprintf(stderr, "Unknown backend: %s\n", + optarg); + usage(argv[0]); + } + break; + case 'f': + file = optarg; + break; + case 'v': + validate = (char *)1; + break; + case 'r': + raw = 1; + break; + case 'k': + key = optarg; + break; + case 't': + type = atoi(optarg); + break; + default: + usage(argv[0]); + } + } + + selabel_option[0].value = file; + selabel_option[1].value = validate; + + hnd = selabel_open(backend, selabel_option, 2); + if (!hnd) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain " + "handle.\n"); + return -1; + } + + switch (raw) { + case 1: + rc = selabel_lookup_raw(hnd, &context, key, type); + break; + default: + rc = selabel_lookup(hnd, &context, key, type); + } + selabel_close(hnd); + + if (rc) { + switch (errno) { + case ENOENT: + fprintf(stderr, "ERROR: selabel_lookup failed to " + "find a valid context.\n"); + break; + case EINVAL: + fprintf(stderr, "ERROR: selabel_lookup failed to " + "validate context, or key / type are " + "invalid.\n"); + break; + default: + fprintf(stderr, "selabel_lookup ERROR: %s\n", + strerror(errno)); + } + } else { + printf("Default context: %s\n", context); + freecon(context); + } + + return rc; +} diff --git a/libselinux/utils/selabel_lookup_best_match.c b/libselinux/utils/selabel_lookup_best_match.c new file mode 100644 index 00000000..c4e8c10e --- /dev/null +++ b/libselinux/utils/selabel_lookup_best_match.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s [-v] [-r] -p path [-m mode] [-f file] [link...]\n\n" + "Where:\n\t" + "-v Validate file_contxts entries against loaded policy.\n\t" + "-r Use \"raw\" function.\n\t" + "-p Path to check for best match using the link(s) provided.\n\t" + "-m Optional mode (b, c, d, p, l, s or f) Defaults to 0.\n\t" + "-f Optional file containing the specs (defaults to\n\t" + " those used by loaded policy).\n\t" + "link Zero or more links to check against, the order of\n\t" + " precedence for best match is:\n\t\t" + " 1) An exact match for the real path (if no links), or\n\t\t" + " 2) An exact match for any of the links (aliases), or\n\t\t" + " 3) The longest fixed prefix match.\n\n" + "Example:\n\t" + "%s -p /dev/initctl /run/systemd/initctl/fifo\n\t" + " Find best matching context for the specified path using one link.\n\n", + progname, progname); + exit(1); +} + +static mode_t string_to_mode(char *s) +{ + switch (s[0]) { + case 'b': + return S_IFBLK; + case 'c': + return S_IFCHR; + case 'd': + return S_IFDIR; + case 'p': + return S_IFIFO; + case 'l': + return S_IFLNK; + case 's': + return S_IFSOCK; + case 'f': + return S_IFREG; + }; + return 0; +} + +int main(int argc, char **argv) +{ + int raw = 0, mode = 0, rc, opt, i, num_links, string_size; + char *validate = NULL, *path = NULL, *context = NULL, *file = NULL; + + char **links = NULL; + + struct selabel_handle *hnd; + struct selinux_opt options[] = { + { SELABEL_OPT_PATH, file }, + { SELABEL_OPT_VALIDATE, validate } + }; + + if (argc < 3) + usage(argv[0]); + + while ((opt = getopt(argc, argv, "f:vrp:m:")) > 0) { + switch (opt) { + case 'f': + file = optarg; + break; + case 'v': + validate = (char *)1; + break; + case 'r': + raw = 1; + break; + case 'p': + path = optarg; + break; + case 'm': + mode = string_to_mode(optarg); + break; + default: + usage(argv[0]); + } + } + + /* Count links */ + for (i = optind, num_links = 0; i < argc; i++, num_links++) + ; + + if (num_links != 0) { + links = malloc(sizeof(char *) * num_links); + + if (links == NULL) { + fprintf(stderr, "ERROR: malloc failed."); + exit(1); + } + + for (i = optind, num_links = 0; i < argc; i++, num_links++) { + string_size = strlen(argv[i]) + 1; + links[num_links] = malloc(string_size); + if (links[num_links] == NULL) { + fprintf(stderr, "ERROR: malloc failed."); + exit(1); + } + strcpy(links[num_links], argv[i]); + } + } + + options[0].value = file; + options[1].value = validate; + + hnd = selabel_open(SELABEL_CTX_FILE, options, 2); + if (!hnd) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain " + "handle.\n"); + rc = -1; + goto out; + } + + switch (raw) { + case 1: + rc = selabel_lookup_best_match_raw(hnd, &context, path, + (const char **)links, mode); + break; + default: + rc = selabel_lookup_best_match(hnd, &context, path, + (const char **)links, mode); + } + selabel_close(hnd); + + if (rc) { + switch (errno) { + case ENOENT: + fprintf(stderr, "ERROR: selabel_lookup_best_match " + "failed to find a valid context.\n"); + break; + case EINVAL: + fprintf(stderr, "ERROR: selabel_lookup_best_match " + "failed to validate context, or path / mode " + "are invalid.\n"); + break; + default: + fprintf(stderr, "selabel_lookup_best_match ERROR: " + "%s\n", strerror(errno)); + } + } else { + printf("Best match context: %s\n", context); + freecon(context); + } +out: + if (num_links != 0) { + for (i = 0; i < num_links; i++) + free(links[i]); + free(links); + } + + return rc; +} diff --git a/libselinux/utils/selabel_partial_match.c b/libselinux/utils/selabel_partial_match.c new file mode 100644 index 00000000..017702d9 --- /dev/null +++ b/libselinux/utils/selabel_partial_match.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s [-v] -p [-f file]\n\n" + "Where:\n\t" + "-v Validate file_contxts entries against loaded policy.\n\t" + "-p Path to check if a match or partial match is possible\n\t" + " against a regex entry in the file_contexts file.\n\t" + "-f Optional file_contexts file (defaults to current policy).\n\n" + "Example:\n\t" + "%s -p /sys/devices/system/cpu/online\n\t" + " Check if a match or partial match is possible against\n\t" + " the path \"/sys/devices/system/cpu/online\", returning\n\t" + " TRUE or FALSE.\n\n", progname, progname); + exit(1); +} + +int main(int argc, char **argv) +{ + int opt; + bool partial_match; + char *validate = NULL, *path = NULL, *file = NULL; + + struct selabel_handle *hnd; + struct selinux_opt selabel_option[] = { + { SELABEL_OPT_PATH, file }, + { SELABEL_OPT_VALIDATE, validate } + }; + + if (argc < 2) + usage(argv[0]); + + while ((opt = getopt(argc, argv, "f:vp:")) > 0) { + switch (opt) { + case 'f': + file = optarg; + break; + case 'v': + validate = (char *)1; + break; + case 'p': + path = optarg; + break; + default: + usage(argv[0]); + } + } + + selabel_option[0].value = file; + selabel_option[1].value = validate; + + hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 2); + if (!hnd) { + fprintf(stderr, "ERROR: selabel_open - Could not obtain " + "handle.\n"); + return -1; + } + + partial_match = selabel_partial_match(hnd, path); + + printf("Match or Partial match: %s\n", + partial_match == 1 ? "TRUE" : "FALSE"); + + selabel_close(hnd); + return partial_match; +} From 4d0d9c7004eece16f57bd794852cf721158e06e7 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 6 May 2015 11:59:18 -0400 Subject: [PATCH 4/4] Update libselinux ChangeLog. Signed-off-by: Stephen Smalley --- libselinux/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/libselinux/ChangeLog b/libselinux/ChangeLog index 14634614..026272f1 100644 --- a/libselinux/ChangeLog +++ b/libselinux/ChangeLog @@ -1,3 +1,4 @@ + * Add selabel partial and best match APIs, from Richard Haines. * Use os.walk() instead of the deprecated os.path.walk(), from Petr Lautrbach & Miro HronĨok * is_selinux_enabled(): drop no-policy-loaded test, from Stephen Smalley.