aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Haines <richard_c_haines@btinternet.com>2015-05-06 16:11:03 +0100
committerStephen Smalley <sds@tycho.nsa.gov>2015-05-06 11:58:44 -0400
commite7f970ffd1a8dbb26051405719a2288d34e856f6 (patch)
tree72afdbdaa30ca966a44767bb36888b7cc146548c
parent9ab426eea1870385792b1df418aead73f4d820ab (diff)
downloadandroid_external_selinux-e7f970ffd1a8dbb26051405719a2288d34e856f6.tar.gz
android_external_selinux-e7f970ffd1a8dbb26051405719a2288d34e856f6.tar.bz2
android_external_selinux-e7f970ffd1a8dbb26051405719a2288d34e856f6.zip
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 <richard_c_haines@btinternet.com> Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
-rw-r--r--libselinux/include/selinux/label.h8
-rw-r--r--libselinux/man/man3/selabel_lookup_best_match.3100
-rw-r--r--libselinux/man/man3/selabel_lookup_best_match_raw.31
-rw-r--r--libselinux/man/man3/selabel_partial_match.334
-rw-r--r--libselinux/src/label.c182
-rw-r--r--libselinux/src/label_file.c109
-rw-r--r--libselinux/src/label_file.h1
-rw-r--r--libselinux/src/label_internal.h6
-rw-r--r--libselinux/utils/selabel_lookup.c126
-rw-r--r--libselinux/utils/selabel_lookup_best_match.c164
-rw-r--r--libselinux/utils/selabel_partial_match.c75
11 files changed, 760 insertions, 46 deletions
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 <stdbool.h>
#include <sys/types.h>
#include <selinux/selinux.h>
@@ -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 <selinux/selinux.h>
+.br
+.B #include <selinux/label.h>
+.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 <stdbool.h>
+.br
+.B #include <selinux/selinux.h>
+.br
+.B #include <selinux/label.h>
+.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,54 +280,67 @@ out:
return rec;
}
-static struct selabel_lookup_rec *
-selabel_lookup_common(struct selabel_handle *rec, int translating,
- const char *key, int type)
+int selabel_lookup(struct selabel_handle *rec, char **con,
+ const char *key, int type)
{
struct selabel_lookup_rec *lr;
- char *ptr = NULL;
- char *dptr = NULL;
- if (key == NULL) {
- errno = EINVAL;
- return NULL;
- }
+ lr = selabel_lookup_common(rec, 1, key, type);
+ if (!lr)
+ return -1;
- 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);
+ *con = strdup(lr->ctx_trans);
+ return *con ? 0 : -1;
+}
+
+int selabel_lookup_raw(struct selabel_handle *rec, char **con,
+ const char *key, int type)
+{
+ struct selabel_lookup_rec *lr;
+
+ lr = selabel_lookup_common(rec, 0, key, type);
+ if (!lr)
+ return -1;
+
+ *con = strdup(lr->ctx_raw);
+ 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) {
- lr = rec->func_lookup(rec, ptr, type);
+ ret = rec->func_partial_match(rec, ptr);
free(ptr);
} else {
- lr = rec->func_lookup(rec, key, type);
+ ret = rec->func_partial_match(rec, key);
}
- 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;
+ return ret;
}
-int selabel_lookup(struct selabel_handle *rec, char **con,
- const char *key, int type)
+int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
+ const char *key, const char **aliases, int type)
{
struct selabel_lookup_rec *lr;
- lr = selabel_lookup_common(rec, 1, key, type);
+ if (!rec->func_lookup_best_match) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ lr = selabel_lookup_bm_common(rec, 1, key, type, aliases);
if (!lr)
return -1;
@@ -243,12 +348,17 @@ int selabel_lookup(struct selabel_handle *rec, char **con,
return *con ? 0 : -1;
}
-int selabel_lookup_raw(struct selabel_handle *rec, char **con,
- const char *key, int type)
+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;
- lr = selabel_lookup_common(rec, 0, key, type);
+ if (!rec->func_lookup_best_match) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ lr = selabel_lookup_bm_common(rec, 0, key, type, aliases);
if (!lr)
return -1;
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+ "usage: %s [-v] -p <path> [-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;
+}