* [PATCH 2/7] libselinux: labeling support (try 4)
2007-06-15 23:29 [PATCH 1/7] libselinux: labeling support (try 4) Eamon Walsh
@ 2007-06-15 23:35 ` Eamon Walsh
2007-06-15 23:37 ` [PATCH 3/7] " Eamon Walsh
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Eamon Walsh @ 2007-06-15 23:35 UTC (permalink / raw)
To: SE Linux; +Cc: Stephen Smalley, Karl MacMillan
This patch includes the interface and generic handle code. Tested
with some sample input values, worked OK.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
include/selinux/label.h | 124 ++++++++++++++++++++++++++++++++++++++++++
src/label.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++
src/label_internal.h | 74 +++++++++++++++++++++++++
3 files changed, 338 insertions(+)
Index: libselinux/include/selinux/label.h
===================================================================
--- libselinux/include/selinux/label.h (revision 0)
+++ libselinux/include/selinux/label.h (revision 0)
@@ -0,0 +1,124 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELABEL_H_
+#define _SELABEL_H_
+
+#include <sys/types.h>
+#include <selinux/selinux.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Opaque type used for all label handles.
+ */
+
+struct selabel_handle;
+
+/*
+ * Available backends.
+ */
+
+/* file contexts */
+#define SELABEL_CTX_FILE 0
+/* media contexts */
+#define SELABEL_CTX_MEDIA 1
+/* x contexts */
+#define SELABEL_CTX_X 2
+
+/*
+ * Available options
+ */
+
+/* no-op option, useful for unused slots in an array of options */
+#define SELABEL_OPT_UNUSED 0
+/* validate contexts before returning them (boolean value) */
+#define SELABEL_OPT_VALIDATE 1
+/* don't use local customizations to backend data (boolean value) */
+#define SELABEL_OPT_BASEONLY 2
+/* specify an alternate path to use when loading backend data */
+#define SELABEL_OPT_PATH 3
+/* select a subset of the search space as an optimization (file backend) */
+#define SELABEL_OPT_SUBSET 4
+/* total number of options */
+#define SELABEL_NOPT 6
+
+struct selabel_opt {
+ int type;
+ const char *value;
+};
+
+/*
+ * Label operations
+ */
+
+/**
+ * selabel_open - Create a labeling handle.
+ * @backend: one of the constants specifying a supported labeling backend.
+ * @opts: array of selabel_opt structures specifying label options or NULL.
+ * @nopts: number of elements in opts array or zero for no options.
+ *
+ * Open a labeling backend for use. The available backend identifiers are
+ * listed above. Options may be provided via the opts parameter; available
+ * options are listed above. Not all options may be supported by every
+ * backend. Return value is the created handle on success or NULL with
+ * @errno set on failure.
+ */
+struct selabel_handle *selabel_open(unsigned int backend,
+ struct selabel_opt *opts, size_t nopts);
+
+/**
+ * selabel_close - Close a labeling handle.
+ * @handle: specifies handle to close
+ *
+ * Destroy the specified handle, closing files, freeing allocated memory,
+ * etc. The handle may not be further used after it has been closed.
+ */
+void selabel_close(struct selabel_handle *handle);
+
+/**
+ * selabel_lookup - Perform labeling lookup operation.
+ * @handle: specifies backend instance to query
+ * @con: returns the appropriate context with which to label the object
+ * @key: string input to lookup operation
+ * @type: numeric input to the lookup operation
+ *
+ * Perform a labeling lookup operation. Return %0 on success, -%1 with
+ * @errno set on failure. The key and type arguments are the inputs to the
+ * lookup operation; appropriate values are dictated by the backend in use.
+ * The result is returned in the memory pointed to by @con and must be freed
+ * by the user with freecon().
+ */
+int selabel_lookup(struct selabel_handle *handle, security_context_t *con,
+ const char *key, int type);
+int selabel_lookup_raw(struct selabel_handle *handle, security_context_t *con,
+ const char *key, int type);
+
+/**
+ * selabel_stats - log labeling operation statistics.
+ * @handle: specifies backend instance to query
+ *
+ * Log a message with information about the number of queries performed,
+ * number of unused matching entries, or other operational statistics.
+ * Message is backend-specific, some backends may not output a message.
+ */
+void selabel_stats(struct selabel_handle *handle);
+
+/*
+ * Type codes used by specific backends
+ */
+
+/* X backend */
+#define SELABEL_X_PROP 1
+#define SELABEL_X_EXT 2
+#define SELABEL_X_CLIENT 3
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SELABEL_H_ */
Index: libselinux/src/label_internal.h
===================================================================
--- libselinux/src/label_internal.h (revision 0)
+++ libselinux/src/label_internal.h (revision 0)
@@ -0,0 +1,74 @@
+/*
+ * This file describes the internal interface used by the labeler
+ * for calling the user-supplied memory allocation, validation,
+ * and locking routine.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+#ifndef _SELABEL_INTERNAL_H_
+#define _SELABEL_INTERNAL_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include "dso.h"
+
+/*
+ * Installed backends
+ */
+int selabel_file_init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t nopts) hidden;
+int selabel_media_init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t nopts) hidden;
+int selabel_x_init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t nopts) hidden;
+
+/*
+ * Labeling internal structures
+ */
+struct selabel_lookup_rec {
+ security_context_t ctx_raw;
+ security_context_t ctx_trans;
+ int validated;
+};
+
+struct selabel_handle {
+ /* arguments that were passed to selabel_open */
+ unsigned int backend;
+ int validating;
+
+ /* labeling operations */
+ struct selabel_lookup_rec *(*func_lookup) (struct selabel_handle *h,
+ const char *key, int type);
+ void (*func_close) (struct selabel_handle *h);
+ void (*func_stats) (struct selabel_handle *h);
+
+ /* supports backend-specific state information */
+ void *data;
+};
+
+/*
+ * Validation function
+ */
+extern int
+selabel_validate(struct selabel_handle *rec,
+ struct selabel_lookup_rec *contexts) hidden;
+
+/*
+ * Compatibility support
+ */
+extern void __attribute__ ((format(printf, 1, 2)))
+(*myprintf) (const char *fmt,...);
+
+#define COMPAT_LOG(type, fmt...) if (myprintf) \
+ myprintf(fmt); \
+ else \
+ selinux_log(type, fmt);
+
+extern int
+compat_validate(struct selabel_handle *rec,
+ struct selabel_lookup_rec *contexts,
+ const char *path, unsigned lineno) hidden;
+
+#endif /* _SELABEL_INTERNAL_H_ */
Index: libselinux/src/label.c
===================================================================
--- libselinux/src/label.c (revision 0)
+++ libselinux/src/label.c (revision 0)
@@ -0,0 +1,140 @@
+/*
+ * Generalized labeling frontend for userspace object managers.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+typedef int (*selabel_initfunc)(struct selabel_handle *rec,
+ struct selabel_opt *opts, size_t nopts);
+
+static selabel_initfunc initfuncs[] = {
+ &selabel_file_init,
+ &selabel_media_init,
+ &selabel_x_init
+};
+
+/*
+ * Validation functions
+ */
+
+static inline int selabel_is_validate_set(struct selabel_opt *opts, size_t n)
+{
+ while (n--)
+ if (opts[n].type == SELABEL_OPT_VALIDATE)
+ return !!opts[n].value;
+
+ return 0;
+}
+
+int selabel_validate(struct selabel_handle *rec,
+ struct selabel_lookup_rec *contexts)
+{
+ int rc = 0;
+
+ if (!rec->validating || contexts->validated)
+ goto out;
+
+ rc = selinux_validate(&contexts->ctx_raw);
+ if (rc < 0)
+ goto out;
+
+ contexts->validated = 1;
+out:
+ return rc;
+}
+
+/*
+ * Public API
+ */
+
+struct selabel_handle *selabel_open(unsigned int backend,
+ struct selabel_opt *opts, size_t nopts)
+{
+ struct selabel_handle *rec = NULL;
+
+ if (backend >= ARRAY_SIZE(initfuncs)) {
+ errno = EINVAL;
+ goto out;
+ }
+
+ rec = (struct selabel_handle *)malloc(sizeof(*rec));
+ if (!rec)
+ goto out;
+
+ memset(rec, 0, sizeof(*rec));
+ rec->backend = backend;
+ rec->validating = selabel_is_validate_set(opts, nopts);
+
+ if ((*initfuncs[backend])(rec, opts, nopts)) {
+ free(rec);
+ rec = NULL;
+ }
+
+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 = rec->func_lookup(rec, key, type);
+ if (!lr)
+ return NULL;
+
+ if (compat_validate(rec, lr, "file_contexts", 0))
+ return NULL;
+
+ if (translating &&
+ selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans))
+ return NULL;
+
+ return lr;
+}
+
+int selabel_lookup(struct selabel_handle *rec, security_context_t *con,
+ const char *key, int type)
+{
+ struct selabel_lookup_rec *lr;
+
+ lr = selabel_lookup_common(rec, 1, key, type);
+ if (!lr)
+ return -1;
+
+ *con = strdup(lr->ctx_trans);
+ return *con ? 0 : -1;
+}
+
+int selabel_lookup_raw(struct selabel_handle *rec, security_context_t *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;
+}
+
+void selabel_close(struct selabel_handle *rec)
+{
+ rec->func_close(rec);
+ free(rec);
+}
+
+void selabel_stats(struct selabel_handle *rec)
+{
+ rec->func_stats(rec);
+}
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 3/7] libselinux: labeling support (try 4)
2007-06-15 23:29 [PATCH 1/7] libselinux: labeling support (try 4) Eamon Walsh
2007-06-15 23:35 ` [PATCH 2/7] " Eamon Walsh
@ 2007-06-15 23:37 ` Eamon Walsh
2007-06-15 23:39 ` [PATCH 4/7] " Eamon Walsh
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Eamon Walsh @ 2007-06-15 23:37 UTC (permalink / raw)
To: SE Linux; +Cc: Stephen Smalley, Karl MacMillan
This patch includes the media backend code.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
label_media.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 219 insertions(+)
Index: libselinux/src/label_media.c
===================================================================
--- libselinux/src/label_media.c (revision 0)
+++ libselinux/src/label_media.c (revision 0)
@@ -0,0 +1,219 @@
+/*
+ * Media contexts backend for labeling system
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+/*
+ * Internals
+ */
+
+/* A context specification. */
+typedef struct spec {
+ struct selabel_lookup_rec lr; /* holds contexts for lookup result */
+ char *key; /* key string */
+ int matches; /* number of matches made during operation */
+} spec_t;
+
+struct saved_data {
+ unsigned int nspec;
+ spec_t *spec_arr;
+};
+
+static int process_line(const char *path, char *line_buf, int pass,
+ unsigned lineno, struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ int items;
+ char *buf_p;
+ char *key, *context;
+
+ buf_p = line_buf;
+ while (isspace(*buf_p))
+ buf_p++;
+ /* Skip comment lines and empty lines. */
+ if (*buf_p == '#' || *buf_p == 0)
+ return 0;
+ items = sscanf(line_buf, "%as %as ", &key, &context);
+ if (items < 2) {
+ selinux_log(SELINUX_WARNING,
+ "%s: line %d is missing fields, skipping\n", path,
+ lineno);
+ if (items == 1)
+ free(key);
+ return 0;
+ }
+
+ if (pass == 1) {
+ data->spec_arr[data->nspec].key = key;
+ data->spec_arr[data->nspec].lr.ctx_raw = context;
+ }
+
+ data->nspec++;
+ if (pass == 0) {
+ free(key);
+ free(context);
+ }
+ return 0;
+}
+
+static int init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t n)
+{
+ FILE *fp;
+ struct saved_data *data = (struct saved_data *)rec->data;
+ const char *path = NULL;
+ char *line_buf = NULL;
+ size_t line_len = 0;
+ int status = -1;
+ unsigned int lineno, pass, maxnspec;
+ struct stat sb;
+
+ /* Process arguments */
+ while (n--)
+ switch(opts[n].type) {
+ case SELABEL_OPT_PATH:
+ path = opts[n].value;
+ break;
+ }
+
+ /* Open the specification file. */
+ if (!path)
+ path = selinux_media_context_path();
+ if ((fp = fopen(path, "r")) == NULL)
+ return -1;
+ __fsetlocking(fp, FSETLOCKING_BYCALLER);
+
+ if (fstat(fileno(fp), &sb) < 0)
+ return -1;
+ if (!S_ISREG(sb.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Perform two passes over the specification file.
+ * The first pass counts the number of specifications and
+ * performs simple validation of the input. At the end
+ * of the first pass, the spec array is allocated.
+ * The second pass performs detailed validation of the input
+ * and fills in the spec array.
+ */
+ maxnspec = UINT_MAX / sizeof(spec_t);
+ for (pass = 0; pass < 2; pass++) {
+ lineno = 0;
+ data->nspec = 0;
+ while (getline(&line_buf, &line_len, fp) > 0 &&
+ data->nspec < maxnspec) {
+ if (process_line(path, line_buf, pass, ++lineno, rec))
+ goto finish;
+ }
+ lineno = 0;
+
+ if (pass == 0) {
+ if (data->nspec == 0) {
+ status = 0;
+ goto finish;
+ }
+ data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
+ if (data->spec_arr == NULL)
+ goto finish;
+ memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
+ maxnspec = data->nspec;
+ rewind(fp);
+ }
+ }
+ free(line_buf);
+
+ status = 0;
+finish:
+ fclose(fp);
+ return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void close(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct spec *spec, *spec_arr = data->spec_arr;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ spec = &spec_arr[i];
+ free(spec->key);
+ free(spec->lr.ctx_raw);
+ free(spec->lr.ctx_trans);
+ }
+
+ if (spec_arr)
+ free(spec_arr);
+
+ memset(data, 0, sizeof(*data));
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
+ const char *key,
+ int type __attribute__((unused)))
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ spec_t *spec_arr = data->spec_arr;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ if (!strncmp(spec_arr[i].key, key, strlen(key) + 1))
+ break;
+ if (!strncmp(spec_arr[i].key, "*", 2))
+ break;
+ }
+
+ if (i >= data->nspec) {
+ /* No matching specification. */
+ errno = ENOENT;
+ return NULL;
+ }
+
+ spec_arr[i].matches++;
+ return &spec_arr[i].lr;
+}
+
+static void stats(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ unsigned int i, total = 0;
+
+ for (i = 0; i < data->nspec; i++)
+ total += data->spec_arr[i].matches;
+
+ selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
+ data->nspec, total);
+}
+
+int selabel_media_init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t nopts)
+{
+ struct saved_data *data;
+
+ data = (struct saved_data *)malloc(sizeof(*data));
+ if (!data)
+ return -1;
+ memset(data, 0, sizeof(*data));
+
+ rec->data = data;
+ rec->func_close = &close;
+ rec->func_lookup = &lookup;
+ rec->func_stats = &stats;
+
+ return init(rec, opts, nopts);
+}
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 4/7] libselinux: labeling support (try 4)
2007-06-15 23:29 [PATCH 1/7] libselinux: labeling support (try 4) Eamon Walsh
2007-06-15 23:35 ` [PATCH 2/7] " Eamon Walsh
2007-06-15 23:37 ` [PATCH 3/7] " Eamon Walsh
@ 2007-06-15 23:39 ` Eamon Walsh
2007-06-15 23:40 ` [PATCH 5/7] " Eamon Walsh
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Eamon Walsh @ 2007-06-15 23:39 UTC (permalink / raw)
To: SE Linux; +Cc: Stephen Smalley, Karl MacMillan
This patch includes the file contexts backend code.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
label_file.c | 641 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 641 insertions(+)
Index: libselinux/src/label_file.c
===================================================================
--- libselinux/src/label_file.c (revision 0)
+++ libselinux/src/label_file.c (revision 0)
@@ -0,0 +1,641 @@
+/*
+ * File contexts backend for labeling system
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ * Author : Stephen Smalley <sds@tycho.nsa.gov>
+ *
+ * This library derived in part from setfiles and the setfiles.pl script
+ * developed by Secure Computing Corporation.
+ */
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+/*
+ * Internals, mostly moved over from matchpathcon.c
+ */
+
+/* A file security context specification. */
+typedef 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 */
+ regex_t regex; /* compiled regular expression */
+ mode_t mode; /* mode format value */
+ int matches; /* number of matching pathnames */
+ int hasMetaChars; /* regular expression has meta-chars */
+ int stem_id; /* indicates which stem-compression item */
+} spec_t;
+
+/* A regular expression stem */
+typedef struct stem {
+ char *buf;
+ int len;
+} stem_t;
+
+/* Our stored configuration */
+struct saved_data {
+ /*
+ * The array of specifications, initially in the same order as in
+ * the specification file. Sorting occurs based on hasMetaChars.
+ */
+ spec_t *spec_arr;
+ unsigned int nspec;
+
+ /*
+ * The array of regular expression stems.
+ */
+ stem_t *stem_arr;
+ int num_stems;
+ int alloc_stems;
+};
+
+/* Return the length of the text that can be considered the stem, returns 0
+ * if there is no identifiable stem */
+static int get_stem_from_spec(const char *const buf)
+{
+ const char *tmp = strchr(buf + 1, '/');
+ const char *ind;
+
+ if (!tmp)
+ return 0;
+
+ for (ind = buf; ind < tmp; ind++) {
+ if (strchr(".^$?*+|[({", (int)*ind))
+ return 0;
+ }
+ return tmp - buf;
+}
+
+/* return the length of the text that is the stem of a file name */
+static int get_stem_from_file_name(const char *const buf)
+{
+ const char *tmp = strchr(buf + 1, '/');
+
+ if (!tmp)
+ return 0;
+ return tmp - buf;
+}
+
+/* find the stem of a file spec, returns the index into stem_arr for a new
+ * or existing stem, (or -1 if there is no possible stem - IE for a file in
+ * the root directory or a regex that is too complex for us). Makes buf
+ * point to the text AFTER the stem. */
+static int find_stem_from_spec(struct saved_data *data, const char **buf)
+{
+ int i, num = data->num_stems;
+ int stem_len = get_stem_from_spec(*buf);
+
+ if (!stem_len)
+ return -1;
+ for (i = 0; i < num; i++) {
+ if (stem_len == data->stem_arr[i].len
+ && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
+ *buf += stem_len;
+ return i;
+ }
+ }
+ if (data->alloc_stems == num) {
+ stem_t *tmp_arr;
+ data->alloc_stems = data->alloc_stems * 2 + 16;
+ tmp_arr = realloc(data->stem_arr,
+ sizeof(stem_t) * data->alloc_stems);
+ if (!tmp_arr)
+ return -1;
+ data->stem_arr = tmp_arr;
+ }
+ data->stem_arr[num].len = stem_len;
+ data->stem_arr[num].buf = malloc(stem_len + 1);
+ if (!data->stem_arr[num].buf)
+ return -1;
+ memcpy(data->stem_arr[num].buf, *buf, stem_len);
+ data->stem_arr[num].buf[stem_len] = '\0';
+ data->num_stems++;
+ *buf += stem_len;
+ return num;
+}
+
+/* find the stem of a file name, returns the index into stem_arr (or -1 if
+ * there is no match - IE for a file in the root directory or a regex that is
+ * too complex for us). Makes buf point to the text AFTER the stem. */
+static int find_stem_from_file(struct saved_data *data, const char **buf)
+{
+ int i;
+ int stem_len = get_stem_from_file_name(*buf);
+
+ if (!stem_len)
+ return -1;
+ for (i = 0; i < data->num_stems; i++) {
+ if (stem_len == data->stem_arr[i].len
+ && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
+ *buf += stem_len;
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Warn about duplicate specifications.
+ */
+static void nodups_specs(struct saved_data *data, const char *path)
+{
+ unsigned int ii, jj;
+ struct spec *curr_spec, *spec_arr = data->spec_arr;
+
+ for (ii = 0; ii < data->nspec; ii++) {
+ curr_spec = &spec_arr[ii];
+ for (jj = ii + 1; jj < data->nspec; jj++) {
+ if ((!strcmp
+ (spec_arr[jj].regex_str, curr_spec->regex_str))
+ && (!spec_arr[jj].mode || !curr_spec->mode
+ || spec_arr[jj].mode == curr_spec->mode)) {
+ if (strcmp
+ (spec_arr[jj].lr.ctx_raw,
+ curr_spec->lr.ctx_raw)) {
+ COMPAT_LOG
+ (SELINUX_WARNING,
+ "%s: Multiple different specifications for %s (%s and %s).\n",
+ path, curr_spec->regex_str,
+ spec_arr[jj].lr.ctx_raw,
+ curr_spec->lr.ctx_raw);
+ } else {
+ COMPAT_LOG
+ (SELINUX_WARNING,
+ "%s: Multiple same specifications for %s.\n",
+ path, curr_spec->regex_str);
+ }
+ }
+ }
+ }
+}
+
+/* Determine if the regular expression specification has any meta characters. */
+static void spec_hasMetaChars(struct spec *spec)
+{
+ char *c;
+ int len;
+ char *end;
+
+ c = spec->regex_str;
+ len = strlen(spec->regex_str);
+ end = c + len;
+
+ spec->hasMetaChars = 0;
+
+ /* Look at each character in the RE specification string for a
+ * meta character. Return when any meta character reached. */
+ while (c != end) {
+ switch (*c) {
+ case '.':
+ case '^':
+ case '$':
+ case '?':
+ case '*':
+ case '+':
+ case '|':
+ case '[':
+ case '(':
+ case '{':
+ spec->hasMetaChars = 1;
+ return;
+ case '\\': /* skip the next character */
+ c++;
+ break;
+ default:
+ break;
+
+ }
+ c++;
+ }
+ return;
+}
+
+static int process_line(struct selabel_handle *rec,
+ const char *path, const char *prefix,
+ char *line_buf, int pass, unsigned lineno)
+{
+ int items, len, regerr;
+ char *buf_p, *regex, *anchored_regex, *type, *context;
+ const char *reg_buf;
+ struct saved_data *data = (struct saved_data *)rec->data;
+ spec_t *spec_arr = data->spec_arr;
+ unsigned int nspec = data->nspec;
+
+ len = strlen(line_buf);
+ if (line_buf[len - 1] == '\n')
+ line_buf[len - 1] = 0;
+ buf_p = line_buf;
+ while (isspace(*buf_p))
+ buf_p++;
+ /* Skip comment lines and empty lines. */
+ if (*buf_p == '#' || *buf_p == 0)
+ return 0;
+ items = sscanf(line_buf, "%as %as %as", ®ex, &type, &context);
+ if (items < 2) {
+ COMPAT_LOG(SELINUX_WARNING,
+ "%s: line %d is missing fields, skipping\n", path,
+ lineno);
+ return 0;
+ } else if (items == 2) {
+ /* The type field is optional. */
+ free(context);
+ context = type;
+ type = 0;
+ }
+
+ reg_buf = regex;
+ len = get_stem_from_spec(reg_buf);
+ if (len && prefix && strncmp(prefix, regex, len)) {
+ /* Stem of regex does not match requested prefix, discard. */
+ free(regex);
+ free(type);
+ free(context);
+ return 0;
+ }
+
+ if (pass == 1) {
+ /* On the second pass, compile and store the specification in spec. */
+ char *cp;
+ spec_arr[nspec].stem_id = find_stem_from_spec(data, ®_buf);
+ spec_arr[nspec].regex_str = regex;
+
+ /* Anchor the regular expression. */
+ len = strlen(reg_buf);
+ cp = anchored_regex = malloc(len + 3);
+ if (!anchored_regex)
+ return -1;
+ /* Create ^...$ regexp. */
+ *cp++ = '^';
+ cp = mempcpy(cp, reg_buf, len);
+ *cp++ = '$';
+ *cp = '\0';
+
+ /* Compile the regular expression. */
+ regerr =
+ regcomp(&spec_arr[nspec].regex,
+ anchored_regex, REG_EXTENDED | REG_NOSUB);
+ if (regerr != 0) {
+ size_t errsz = 0;
+ char *errbuf = NULL;
+ errsz = regerror(regerr, &spec_arr[nspec].regex,
+ errbuf, errsz);
+ if (errsz)
+ errbuf = malloc(errsz);
+ if (errbuf)
+ (void)regerror(regerr,
+ &spec_arr[nspec].regex,
+ errbuf, errsz);
+ COMPAT_LOG(SELINUX_WARNING,
+ "%s: line %d has invalid regex %s: %s\n",
+ path, lineno, anchored_regex,
+ (errbuf ? errbuf : "out of memory"));
+ free(anchored_regex);
+ return 0;
+ }
+ free(anchored_regex);
+
+ /* Convert the type string to a mode format */
+ spec_arr[nspec].type_str = type;
+ spec_arr[nspec].mode = 0;
+ if (!type)
+ goto skip_type;
+ len = strlen(type);
+ if (type[0] != '-' || len != 2) {
+ COMPAT_LOG(SELINUX_WARNING,
+ "%s: line %d has invalid file type %s\n",
+ path, lineno, type);
+ return 0;
+ }
+ switch (type[1]) {
+ case 'b':
+ spec_arr[nspec].mode = S_IFBLK;
+ break;
+ case 'c':
+ spec_arr[nspec].mode = S_IFCHR;
+ break;
+ case 'd':
+ spec_arr[nspec].mode = S_IFDIR;
+ break;
+ case 'p':
+ spec_arr[nspec].mode = S_IFIFO;
+ break;
+ case 'l':
+ spec_arr[nspec].mode = S_IFLNK;
+ break;
+ case 's':
+ spec_arr[nspec].mode = S_IFSOCK;
+ break;
+ case '-':
+ spec_arr[nspec].mode = S_IFREG;
+ break;
+ default:
+ COMPAT_LOG(SELINUX_WARNING,
+ "%s: line %d has invalid file type %s\n",
+ path, lineno, type);
+ return 0;
+ }
+
+ skip_type:
+ spec_arr[nspec].lr.ctx_raw = context;
+
+ /* Determine if specification has
+ * any meta characters in the RE */
+ spec_hasMetaChars(&spec_arr[nspec]);
+
+ if (strcmp(context, "<<none>>") && rec->validating)
+ compat_validate(rec, &spec_arr[nspec].lr, path, lineno);
+ }
+
+ data->nspec = ++nspec;
+ if (pass == 0) {
+ free(regex);
+ if (type)
+ free(type);
+ free(context);
+ }
+ return 0;
+}
+
+static int init(struct selabel_handle *rec, struct selabel_opt *opts, size_t n)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ const char *path = NULL;
+ const char *prefix = NULL;
+ FILE *fp;
+ FILE *localfp = NULL;
+ FILE *homedirfp = NULL;
+ char local_path[PATH_MAX + 1];
+ char homedir_path[PATH_MAX + 1];
+ char *line_buf = NULL;
+ size_t line_len = 0;
+ unsigned int lineno, pass, i, j, maxnspec;
+ spec_t *spec_copy = NULL;
+ int status = -1, baseonly = 0;
+ struct stat sb;
+
+ /* Process arguments */
+ while (n--)
+ switch(opts[n].type) {
+ case SELABEL_OPT_PATH:
+ path = opts[n].value;
+ break;
+ case SELABEL_OPT_SUBSET:
+ prefix = opts[n].value;
+ break;
+ case SELABEL_OPT_BASEONLY:
+ baseonly = !!opts[n].value;
+ break;
+ }
+
+ /* Open the specification file. */
+ if (!path)
+ path = selinux_file_context_path();
+ if ((fp = fopen(path, "r")) == NULL)
+ return -1;
+ __fsetlocking(fp, FSETLOCKING_BYCALLER);
+
+ if (fstat(fileno(fp), &sb) < 0)
+ return -1;
+ if (!S_ISREG(sb.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!baseonly) {
+ snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs",
+ path);
+ homedirfp = fopen(homedir_path, "r");
+ if (homedirfp != NULL)
+ __fsetlocking(homedirfp, FSETLOCKING_BYCALLER);
+
+ snprintf(local_path, sizeof(local_path), "%s.local", path);
+ localfp = fopen(local_path, "r");
+ if (localfp != NULL)
+ __fsetlocking(localfp, FSETLOCKING_BYCALLER);
+ }
+
+ /*
+ * Perform two passes over the specification file.
+ * The first pass counts the number of specifications and
+ * performs simple validation of the input. At the end
+ * of the first pass, the spec array is allocated.
+ * The second pass performs detailed validation of the input
+ * and fills in the spec array.
+ */
+ maxnspec = UINT_MAX / sizeof(spec_t);
+ for (pass = 0; pass < 2; pass++) {
+ lineno = 0;
+ data->nspec = 0;
+ while (getline(&line_buf, &line_len, fp) > 0
+ && data->nspec < maxnspec) {
+ if (process_line(rec, path, prefix, line_buf,
+ pass, ++lineno) != 0)
+ goto finish;
+ }
+ lineno = 0;
+ if (homedirfp)
+ while (getline(&line_buf, &line_len, homedirfp) > 0
+ && data->nspec < maxnspec) {
+ if (process_line
+ (rec, homedir_path, prefix,
+ line_buf, pass, ++lineno) != 0)
+ goto finish;
+ }
+
+ lineno = 0;
+ if (localfp)
+ while (getline(&line_buf, &line_len, localfp) > 0
+ && data->nspec < maxnspec) {
+ if (process_line
+ (rec, local_path, prefix, line_buf,
+ pass, ++lineno) != 0)
+ goto finish;
+ }
+
+ if (pass == 0) {
+ if (data->nspec == 0) {
+ status = 0;
+ goto finish;
+ }
+ if (NULL == (data->spec_arr =
+ malloc(sizeof(spec_t) * data->nspec)))
+ goto finish;
+ memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
+ maxnspec = data->nspec;
+ rewind(fp);
+ if (homedirfp)
+ rewind(homedirfp);
+ if (localfp)
+ rewind(localfp);
+ }
+ }
+ free(line_buf);
+
+ /* Move exact pathname specifications to the end. */
+ spec_copy = malloc(sizeof(spec_t) * data->nspec);
+ if (!spec_copy)
+ goto finish;
+ j = 0;
+ for (i = 0; i < data->nspec; i++)
+ if (data->spec_arr[i].hasMetaChars)
+ memcpy(&spec_copy[j++],
+ &data->spec_arr[i], sizeof(spec_t));
+ for (i = 0; i < data->nspec; i++)
+ if (!data->spec_arr[i].hasMetaChars)
+ memcpy(&spec_copy[j++],
+ &data->spec_arr[i], sizeof(spec_t));
+ free(data->spec_arr);
+ data->spec_arr = spec_copy;
+
+ nodups_specs(data, path);
+
+ status = 0;
+finish:
+ fclose(fp);
+ if (data->spec_arr != spec_copy)
+ free(data->spec_arr);
+ if (homedirfp)
+ fclose(homedirfp);
+ if (localfp)
+ fclose(localfp);
+ return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void close(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct spec *spec;
+ struct stem *stem;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ spec = &data->spec_arr[i];
+ free(spec->regex_str);
+ free(spec->type_str);
+ free(spec->lr.ctx_raw);
+ free(spec->lr.ctx_trans);
+ regfree(&spec->regex);
+ }
+
+ for (i = 0; i < (unsigned int)data->num_stems; i++) {
+ stem = &data->stem_arr[i];
+ free(stem->buf);
+ }
+
+ if (data->spec_arr)
+ free(data->spec_arr);
+ if (data->stem_arr)
+ free(data->stem_arr);
+
+ memset(data, 0, sizeof(*data));
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
+ const char *key, int type)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ spec_t *spec_arr = data->spec_arr;
+ int i, rc, file_stem;
+ mode_t mode = (mode_t)type;
+ const char *buf = key;
+
+ if (!data->nspec) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ file_stem = find_stem_from_file(data, &buf);
+ mode &= S_IFMT;
+
+ /*
+ * Check for matching specifications in reverse order, so that
+ * the last matching specification is used.
+ */
+ for (i = data->nspec - 1; i >= 0; i--) {
+ /* if the spec in question matches no stem or has the same
+ * stem as the file AND if the spec in question has no mode
+ * specified or if the mode matches the file mode then we do
+ * a regex check */
+ if ((spec_arr[i].stem_id == -1
+ || spec_arr[i].stem_id == file_stem)
+ && (!mode || !spec_arr[i].mode
+ || mode == spec_arr[i].mode)) {
+ if (spec_arr[i].stem_id == -1)
+ rc = regexec(&spec_arr[i].regex, key, 0, 0, 0);
+ else
+ rc = regexec(&spec_arr[i].regex, buf, 0, 0, 0);
+
+ if (rc == 0) {
+ spec_arr[i].matches++;
+ break;
+ }
+ if (rc == REG_NOMATCH)
+ continue;
+ /* else it's an error */
+ return NULL;
+ }
+ }
+
+ if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
+ /* No matching specification. */
+ errno = ENOENT;
+ return NULL;
+ }
+
+ return &spec_arr[i].lr;
+}
+
+static void stats(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ unsigned int i, nspec = data->nspec;
+ spec_t *spec_arr = data->spec_arr;
+
+ for (i = 0; i < nspec; i++) {
+ if (spec_arr[i].matches == 0) {
+ if (spec_arr[i].type_str) {
+ COMPAT_LOG(SELINUX_WARNING,
+ "Warning! No matches for (%s, %s, %s)\n",
+ spec_arr[i].regex_str,
+ spec_arr[i].type_str,
+ spec_arr[i].lr.ctx_raw);
+ } else {
+ COMPAT_LOG(SELINUX_WARNING,
+ "Warning! No matches for (%s, %s)\n",
+ spec_arr[i].regex_str,
+ spec_arr[i].lr.ctx_raw);
+ }
+ }
+ }
+}
+
+int selabel_file_init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t nopts)
+{
+ struct saved_data *data;
+
+ data = (struct saved_data *)malloc(sizeof(*data));
+ if (!data)
+ return -1;
+ memset(data, 0, sizeof(*data));
+
+ rec->data = data;
+ rec->func_close = &close;
+ rec->func_stats = &stats;
+ rec->func_lookup = &lookup;
+
+ return init(rec, opts, nopts);
+}
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 5/7] libselinux: labeling support (try 4)
2007-06-15 23:29 [PATCH 1/7] libselinux: labeling support (try 4) Eamon Walsh
` (2 preceding siblings ...)
2007-06-15 23:39 ` [PATCH 4/7] " Eamon Walsh
@ 2007-06-15 23:40 ` Eamon Walsh
2007-06-15 23:43 ` [PATCH 6/7] " Eamon Walsh
2007-06-15 23:46 ` [PATCH 7/7] " Eamon Walsh
5 siblings, 0 replies; 9+ messages in thread
From: Eamon Walsh @ 2007-06-15 23:40 UTC (permalink / raw)
To: SE Linux; +Cc: Stephen Smalley, Karl MacMillan
This patch includes the x contexts backend code.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
label_x.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 238 insertions(+)
Index: libselinux/src/label_x.c
===================================================================
--- libselinux/src/label_x.c (revision 0)
+++ libselinux/src/label_x.c (revision 0)
@@ -0,0 +1,238 @@
+/*
+ * Media contexts backend for X contexts
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+/*
+ * Internals
+ */
+
+/* A context specification. */
+typedef struct spec {
+ struct selabel_lookup_rec lr; /* holds contexts for lookup result */
+ char *key; /* key string */
+ int type; /* type of record (prop, ext, client) */
+ int matches; /* number of matches made during operation */
+} spec_t;
+
+struct saved_data {
+ unsigned int nspec;
+ spec_t *spec_arr;
+};
+
+static int process_line(const char *path, char *line_buf, int pass,
+ unsigned lineno, struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ int items;
+ char *buf_p;
+ char *type, *key, *context;
+
+ buf_p = line_buf;
+ while (isspace(*buf_p))
+ buf_p++;
+ /* Skip comment lines and empty lines. */
+ if (*buf_p == '#' || *buf_p == 0)
+ return 0;
+ items = sscanf(line_buf, "%as %as %as ", &type, &key, &context);
+ if (items < 3) {
+ selinux_log(SELINUX_WARNING,
+ "%s: line %d is missing fields, skipping\n", path,
+ lineno);
+ if (items > 0)
+ free(type);
+ if (items > 1)
+ free(key);
+ return 0;
+ }
+
+ if (pass == 1) {
+ /* Convert the type string to a mode format */
+ if (!strcmp(type, "property"))
+ data->spec_arr[data->nspec].type = SELABEL_X_PROP;
+ else if (!strcmp(type, "extension"))
+ data->spec_arr[data->nspec].type = SELABEL_X_EXT;
+ else if (!strcmp(type, "client"))
+ data->spec_arr[data->nspec].type = SELABEL_X_CLIENT;
+ else {
+ selinux_log(SELINUX_WARNING,
+ "%s: line %d has invalid file type %s\n",
+ path, lineno, type);
+ return 0;
+ }
+ data->spec_arr[data->nspec].key = key;
+ data->spec_arr[data->nspec].lr.ctx_raw = context;
+ free(type);
+ }
+
+ data->nspec++;
+ if (pass == 0) {
+ free(type);
+ free(key);
+ free(context);
+ }
+ return 0;
+}
+
+static int init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t n)
+{
+ FILE *fp;
+ struct saved_data *data = (struct saved_data *)rec->data;
+ const char *path = NULL;
+ char *line_buf = NULL;
+ size_t line_len = 0;
+ int status = -1;
+ unsigned int lineno, pass, maxnspec;
+ struct stat sb;
+
+ /* Process arguments */
+ while (n--)
+ switch(opts[n].type) {
+ case SELABEL_OPT_PATH:
+ path = opts[n].value;
+ break;
+ }
+
+ /* Open the specification file. */
+ if (!path)
+ path = "/etc/selinux/refpolicy/contexts/x_contexts";
+ if ((fp = fopen(path, "r")) == NULL)
+ return -1;
+ __fsetlocking(fp, FSETLOCKING_BYCALLER);
+
+ if (fstat(fileno(fp), &sb) < 0)
+ return -1;
+ if (!S_ISREG(sb.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Perform two passes over the specification file.
+ * The first pass counts the number of specifications and
+ * performs simple validation of the input. At the end
+ * of the first pass, the spec array is allocated.
+ * The second pass performs detailed validation of the input
+ * and fills in the spec array.
+ */
+ maxnspec = UINT_MAX / sizeof(spec_t);
+ for (pass = 0; pass < 2; pass++) {
+ lineno = 0;
+ data->nspec = 0;
+ while (getline(&line_buf, &line_len, fp) > 0 &&
+ data->nspec < maxnspec) {
+ if (process_line(path, line_buf, pass, ++lineno, rec))
+ goto finish;
+ }
+ lineno = 0;
+
+ if (pass == 0) {
+ if (data->nspec == 0) {
+ status = 0;
+ goto finish;
+ }
+ data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
+ if (data->spec_arr == NULL)
+ goto finish;
+ memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
+ maxnspec = data->nspec;
+ rewind(fp);
+ }
+ }
+ free(line_buf);
+
+ status = 0;
+finish:
+ fclose(fp);
+ return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void close(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct spec *spec, *spec_arr = data->spec_arr;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ spec = &spec_arr[i];
+ free(spec->key);
+ free(spec->lr.ctx_raw);
+ free(spec->lr.ctx_trans);
+ }
+
+ if (spec_arr)
+ free(spec_arr);
+
+ memset(data, 0, sizeof(*data));
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
+ const char *key, int type)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ spec_t *spec_arr = data->spec_arr;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ if (spec_arr[i].type != type)
+ continue;
+ if (!strncmp(spec_arr[i].key, key, strlen(key) + 1))
+ break;
+ if (!strncmp(spec_arr[i].key, "*", 2))
+ break;
+ }
+
+ if (i >= data->nspec) {
+ /* No matching specification. */
+ errno = ENOENT;
+ return NULL;
+ }
+
+ spec_arr[i].matches++;
+ return &spec_arr[i].lr;
+}
+
+static void stats(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ unsigned int i, total = 0;
+
+ for (i = 0; i < data->nspec; i++)
+ total += data->spec_arr[i].matches;
+
+ selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
+ data->nspec, total);
+}
+
+int selabel_x_init(struct selabel_handle *rec, struct selabel_opt *opts,
+ size_t nopts)
+{
+ struct saved_data *data;
+
+ data = (struct saved_data *)malloc(sizeof(*data));
+ if (!data)
+ return -1;
+ memset(data, 0, sizeof(*data));
+
+ rec->data = data;
+ rec->func_close = &close;
+ rec->func_lookup = &lookup;
+ rec->func_stats = &stats;
+
+ return init(rec, opts, nopts);
+}
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 6/7] libselinux: labeling support (try 4)
2007-06-15 23:29 [PATCH 1/7] libselinux: labeling support (try 4) Eamon Walsh
` (3 preceding siblings ...)
2007-06-15 23:40 ` [PATCH 5/7] " Eamon Walsh
@ 2007-06-15 23:43 ` Eamon Walsh
2007-06-20 14:43 ` Stephen Smalley
2007-06-15 23:46 ` [PATCH 7/7] " Eamon Walsh
5 siblings, 1 reply; 9+ messages in thread
From: Eamon Walsh @ 2007-06-15 23:43 UTC (permalink / raw)
To: SE Linux; +Cc: Stephen Smalley, Karl MacMillan
This patch rebases the matchpathcon code to use the new interface.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
matchpathcon.c | 803 +++++++--------------------------------------------------
1 file changed, 112 insertions(+), 691 deletions(-)
Index: libselinux/src/matchpathcon.c
===================================================================
--- libselinux/src/matchpathcon.c (revision 2474)
+++ libselinux/src/matchpathcon.c (working copy)
@@ -1,19 +1,54 @@
-#include <unistd.h>
-#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
-#include "selinux_internal.h"
-#include <stdio.h>
-#include <stdio_ext.h>
-#include <stdlib.h>
-#include <ctype.h>
#include <errno.h>
-#include <limits.h>
-#include <regex.h>
-#include <stdarg.h>
-#include "policy.h"
-#include "context_internal.h"
+#include <stdio.h>
+#include "selinux_internal.h"
+#include "label_internal.h"
+#include "callbacks.h"
+static __thread struct selabel_handle *hnd;
+
+/*
+ * An array for mapping integers to contexts
+ */
+static __thread char **con_array;
+static __thread int con_array_size;
+static __thread int con_array_used;
+
+static int add_array_elt(char *con)
+{
+ if (con_array_size) {
+ while (con_array_used >= con_array_size) {
+ con_array_size *= 2;
+ con_array = (char **)realloc(con_array, sizeof(char*) *
+ con_array_size);
+ if (!con_array) {
+ con_array_size = con_array_used = 0;
+ return -1;
+ }
+ }
+ } else {
+ con_array_size = 1000;
+ con_array = (char **)malloc(sizeof(char*) * con_array_size);
+ if (!con_array) {
+ con_array_size = con_array_used = 0;
+ return -1;
+ }
+ }
+
+ con_array[con_array_used] = strdup(con);
+ if (!con_array[con_array_used])
+ return -1;
+ return con_array_used++;
+}
+
+static void free_array_elts(void)
+{
+ con_array_size = con_array_used = 0;
+ free(con_array);
+ con_array = NULL;
+}
+
static void
#ifdef __GNUC__
__attribute__ ((format(printf, 1, 2)))
@@ -26,11 +61,11 @@
va_end(ap);
}
-static void
+void
#ifdef __GNUC__
__attribute__ ((format(printf, 1, 2)))
#endif
- (*myprintf) (const char *fmt,...) = &default_printf;
+ (*myprintf) (const char *fmt,...);
void set_matchpathcon_printf(void (*f) (const char *fmt, ...))
{
@@ -66,7 +101,7 @@
}
static int (*mycanoncon) (const char *p, unsigned l, char **c) =
- &default_canoncon;
+ NULL;
void set_matchpathcon_canoncon(int (*f) (const char *p, unsigned l, char **c))
{
@@ -76,137 +111,23 @@
mycanoncon = &default_canoncon;
}
-static __thread unsigned int myflags;
+static __thread struct selabel_opt options[SELABEL_NOPT];
+static __thread int notrans;
void set_matchpathcon_flags(unsigned int flags)
{
- myflags = flags;
-}
-
-/*
- * A file security context specification.
- */
-typedef struct spec {
- char *regex_str; /* regular expession string for diagnostic messages */
- char *type_str; /* type string for diagnostic messages */
- char *context; /* context string */
- int context_valid; /* context string has been validated/canonicalized */
- int translated; /* context string has been translated */
- regex_t regex; /* compiled regular expression */
- mode_t mode; /* mode format value */
- int matches; /* number of matching pathnames */
- int hasMetaChars; /* indicates whether the RE has
- any meta characters.
- 0 = no meta chars
- 1 = has one or more meta chars */
- int stem_id; /* indicates which of the stem-compression
- * items it matches */
-} spec_t;
-
-typedef struct stem {
- char *buf;
- int len;
-} stem_t;
-
-static stem_t *stem_arr = NULL;
-static int num_stems = 0;
-static int alloc_stems = 0;
-
-static const char *const regex_chars = ".^$?*+|[({";
-
-/* Return the length of the text that can be considered the stem, returns 0
- * if there is no identifiable stem */
-static int get_stem_from_spec(const char *const buf)
-{
- const char *tmp = strchr(buf + 1, '/');
- const char *ind;
-
- if (!tmp)
- return 0;
-
- for (ind = buf; ind < tmp; ind++) {
- if (strchr(regex_chars, (int)*ind))
- return 0;
- }
- return tmp - buf;
-}
-
-/* return the length of the text that is the stem of a file name */
-static int get_stem_from_file_name(const char *const buf)
-{
- const char *tmp = strchr(buf + 1, '/');
-
- if (!tmp)
- return 0;
- return tmp - buf;
-}
-
-/* find the stem of a file spec, returns the index into stem_arr for a new
- * or existing stem, (or -1 if there is no possible stem - IE for a file in
- * the root directory or a regex that is too complex for us). Makes buf
- * point to the text AFTER the stem. */
-static int find_stem_from_spec(const char **buf)
-{
int i;
- int stem_len = get_stem_from_spec(*buf);
-
- if (!stem_len)
- return -1;
- for (i = 0; i < num_stems; i++) {
- if (stem_len == stem_arr[i].len
- && !strncmp(*buf, stem_arr[i].buf, stem_len)) {
- *buf += stem_len;
- return i;
- }
- }
- if (num_stems == alloc_stems) {
- stem_t *tmp_arr;
- alloc_stems = alloc_stems * 2 + 16;
- tmp_arr = realloc(stem_arr, sizeof(stem_t) * alloc_stems);
- if (!tmp_arr)
- return -1;
- stem_arr = tmp_arr;
- }
- stem_arr[num_stems].len = stem_len;
- stem_arr[num_stems].buf = malloc(stem_len + 1);
- if (!stem_arr[num_stems].buf)
- return -1;
- memcpy(stem_arr[num_stems].buf, *buf, stem_len);
- stem_arr[num_stems].buf[stem_len] = '\0';
- num_stems++;
- *buf += stem_len;
- return num_stems - 1;
+ memset(options, 0, sizeof(options));
+ i = SELABEL_OPT_BASEONLY;
+ options[i].type = i;
+ options[i].value = (char *)(flags & MATCHPATHCON_BASEONLY);
+ i = SELABEL_OPT_VALIDATE;
+ options[i].type = i;
+ options[i].value = (char *)(flags & MATCHPATHCON_VALIDATE);
+ notrans = flags & MATCHPATHCON_NOTRANS;
}
-/* find the stem of a file name, returns the index into stem_arr (or -1 if
- * there is no match - IE for a file in the root directory or a regex that is
- * too complex for us). Makes buf point to the text AFTER the stem. */
-static int find_stem_from_file(const char **buf)
-{
- int i;
- int stem_len = get_stem_from_file_name(*buf);
-
- if (!stem_len)
- return -1;
- for (i = 0; i < num_stems; i++) {
- if (stem_len == stem_arr[i].len
- && !strncmp(*buf, stem_arr[i].buf, stem_len)) {
- *buf += stem_len;
- return i;
- }
- }
- return -1;
-}
-
/*
- * The array of specifications, initially in the
- * same order as in the specification file.
- * Sorting occurs based on hasMetaChars
- */
-static spec_t *spec_arr;
-static unsigned int nspec;
-
-/*
* An association between an inode and a
* specification.
*/
@@ -238,7 +159,7 @@
int matchpathcon_filespec_add(ino_t ino, int specind, const char *file)
{
file_spec_t *prevfl, *fl;
- int h, no_conflict, ret;
+ int h, ret;
struct stat sb;
if (!fl_head) {
@@ -264,21 +185,14 @@
}
- no_conflict =
- (strcmp
- (spec_arr[fl->specind].context,
- spec_arr[specind].context) == 0);
- if (no_conflict)
+ if (!strcmp(con_array[fl->specind],
+ con_array[specind]))
return fl->specind;
myprintf
("%s: conflicting specifications for %s and %s, using %s.\n",
__FUNCTION__, file, fl->file,
- ((specind >
- fl->specind) ? spec_arr[specind].
- context : spec_arr[fl->specind].context));
- fl->specind =
- (specind > fl->specind) ? specind : fl->specind;
+ con_array[fl->specind]);
free(fl->file);
fl->file = malloc(strlen(file) + 1);
if (!fl->file)
@@ -350,6 +264,8 @@
file_spec_t *fl, *tmp;
int h;
+ free_array_elts();
+
if (!fl_head)
return;
@@ -367,393 +283,20 @@
fl_head = NULL;
}
-/*
- * Warn about duplicate specifications.
- */
-static void nodups_specs(const char *path)
+int matchpathcon_init_prefix(const char *path, const char *subset)
{
- unsigned int ii, jj;
- struct spec *curr_spec;
+ if (!mycanoncon)
+ mycanoncon = default_canoncon;
- for (ii = 0; ii < nspec; ii++) {
- curr_spec = &spec_arr[ii];
- for (jj = ii + 1; jj < nspec; jj++) {
- if ((!strcmp
- (spec_arr[jj].regex_str, curr_spec->regex_str))
- && (!spec_arr[jj].mode || !curr_spec->mode
- || spec_arr[jj].mode == curr_spec->mode)) {
- if (strcmp
- (spec_arr[jj].context,
- curr_spec->context)) {
- myprintf
- ("%s: Multiple different specifications for %s (%s and %s).\n",
- path, curr_spec->regex_str,
- spec_arr[jj].context,
- curr_spec->context);
- } else {
- myprintf
- ("%s: Multiple same specifications for %s.\n",
- path, curr_spec->regex_str);
- }
- }
- }
- }
-}
+ options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET;
+ options[SELABEL_OPT_SUBSET].value = subset;
+ options[SELABEL_OPT_PATH].type = SELABEL_OPT_PATH;
+ options[SELABEL_OPT_PATH].value = path;
-/* Determine if the regular expression specification has any meta characters. */
-static void spec_hasMetaChars(struct spec *spec)
-{
- char *c;
- int len;
- char *end;
-
- c = spec->regex_str;
- len = strlen(spec->regex_str);
- end = c + len;
-
- spec->hasMetaChars = 0;
-
- /* Look at each character in the RE specification string for a
- * meta character. Return when any meta character reached. */
- while (c != end) {
- switch (*c) {
- case '.':
- case '^':
- case '$':
- case '?':
- case '*':
- case '+':
- case '|':
- case '[':
- case '(':
- case '{':
- spec->hasMetaChars = 1;
- return;
- case '\\': /* skip the next character */
- c++;
- break;
- default:
- break;
-
- }
- c++;
- }
- return;
+ hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT);
+ return hnd ? 0 : -1;
}
-static int process_line(const char *path, const char *prefix, char *line_buf,
- int pass, unsigned lineno)
-{
- int items, len, regerr, ret;
- char *buf_p, *ptr;
- char *regex=NULL, *type=NULL, *context=NULL;
- const char *reg_buf;
- char *anchored_regex = NULL;
- ret = 0;
- len = strlen(line_buf);
- if (line_buf[len - 1] == '\n')
- line_buf[len - 1] = 0;
- buf_p = line_buf;
- while (isspace(*buf_p))
- buf_p++;
- /* Skip comment lines and empty lines. */
- if (*buf_p == '#' || *buf_p == 0)
- return 0;
-
- items = 0;
- regex = strtok_r(buf_p, " \t", &ptr);
- if (regex)
- items += 1;
- type = strtok_r(NULL, " \t", &ptr);
- if (type)
- items += 1;
- context = strtok_r(NULL, " \t", &ptr);
- if (context)
- items += 1;
-
- if (items < 2) {
- myprintf("%s: line %d is missing fields, skipping\n", path,
- lineno);
- return 0;
- } else if (items == 2) {
- /* The type field is optional. */
- context = type;
- type = NULL;
- }
-
- regex = strdup(regex);
- if (!regex) {
- return -1;
- }
- if (type) {
- type = strdup(type);
- if (!type) {
- free(regex);
- return -1;
- }
- }
- context = strdup(context);
- if (!context) {
- ret = -1;
- goto finish;
- }
-
- reg_buf = regex;
- len = get_stem_from_spec(reg_buf);
- if (len && prefix && strncmp(prefix, regex, len)) {
- /* Stem of regex does not match requested prefix, discard. */
- goto finish;
- }
-
- if (pass == 1) {
- /* On the second pass, compile and store the specification in spec. */
- char *cp;
- spec_arr[nspec].stem_id = find_stem_from_spec(®_buf);
- spec_arr[nspec].regex_str = regex;
-
- /* Anchor the regular expression. */
- len = strlen(reg_buf);
- cp = anchored_regex = malloc(len + 3);
- if (!anchored_regex) {
- ret = -1;
- goto finish;
- }
- /* Create ^...$ regexp. */
- *cp++ = '^';
- cp = mempcpy(cp, reg_buf, len);
- *cp++ = '$';
- *cp = '\0';
-
- /* Compile the regular expression. */
- regerr =
- regcomp(&spec_arr[nspec].regex,
- anchored_regex, REG_EXTENDED | REG_NOSUB);
- if (regerr != 0) {
- size_t errsz = 0;
- char *errbuf = NULL;
- errsz = regerror(regerr, &spec_arr[nspec].regex,
- errbuf, errsz);
- if (errsz)
- errbuf = malloc(errsz);
- if (errbuf)
- (void)regerror(regerr,
- &spec_arr[nspec].regex,
- errbuf, errsz);
- myprintf("%s: line %d has invalid regex %s: %s\n",
- path, lineno, anchored_regex,
- (errbuf ? errbuf : "out of memory"));
- free(anchored_regex);
- anchored_regex = NULL;
- free(errbuf);
- goto finish;
- }
- free(anchored_regex);
- anchored_regex = NULL;
-
- /* Convert the type string to a mode format */
- spec_arr[nspec].type_str = type;
- spec_arr[nspec].mode = 0;
- if (!type)
- goto skip_type;
- len = strlen(type);
- if (type[0] != '-' || len != 2) {
- myprintf("%s: line %d has invalid file type %s\n",
- path, lineno, type);
- goto finish;
- }
- switch (type[1]) {
- case 'b':
- spec_arr[nspec].mode = S_IFBLK;
- break;
- case 'c':
- spec_arr[nspec].mode = S_IFCHR;
- break;
- case 'd':
- spec_arr[nspec].mode = S_IFDIR;
- break;
- case 'p':
- spec_arr[nspec].mode = S_IFIFO;
- break;
- case 'l':
- spec_arr[nspec].mode = S_IFLNK;
- break;
- case 's':
- spec_arr[nspec].mode = S_IFSOCK;
- break;
- case '-':
- spec_arr[nspec].mode = S_IFREG;
- break;
- default:
- myprintf("%s: line %d has invalid file type %s\n",
- path, lineno, type);
- goto finish;
- }
-
- skip_type:
- if (strcmp(context, "<<none>>")) {
- if (myflags & MATCHPATHCON_VALIDATE) {
- if (myinvalidcon) {
- /* Old-style validation of context. */
- if (myinvalidcon(path, lineno, context))
- goto finish;
- } else {
- /* New canonicalization of context. */
- if (mycanoncon(path, lineno, &context))
- goto finish;
- }
- spec_arr[nspec].context_valid = 1;
- }
- }
-
- spec_arr[nspec].context = context;
-
- /* Determine if specification has
- * any meta characters in the RE */
- spec_hasMetaChars(&spec_arr[nspec]);
-
- /* Prevent stored strings from being freed. */
- regex = NULL;
- type = NULL;
- context = NULL;
- }
-
- nspec++;
-finish:
- free(regex);
- free(type);
- free(context);
- return ret;
-}
-
-int matchpathcon_init_prefix(const char *path, const char *prefix)
-{
- FILE *fp;
- FILE *localfp = NULL;
- FILE *homedirfp = NULL;
- char local_path[PATH_MAX + 1];
- char homedir_path[PATH_MAX + 1];
- char *line_buf = NULL;
- size_t line_len = 0;
- unsigned int lineno, pass, i, j, maxnspec;
- spec_t *spec_copy = NULL;
- int status = -1;
- struct stat sb;
-
- /* Open the specification file. */
- if (!path)
- path = selinux_file_context_path();
- if ((fp = fopen(path, "r")) == NULL)
- return -1;
- __fsetlocking(fp, FSETLOCKING_BYCALLER);
-
- if (fstat(fileno(fp), &sb) < 0)
- return -1;
- if (!S_ISREG(sb.st_mode)) {
- errno = EINVAL;
- return -1;
- }
-
- if ((myflags & MATCHPATHCON_BASEONLY) == 0) {
- snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs",
- path);
- homedirfp = fopen(homedir_path, "r");
- if (homedirfp != NULL)
- __fsetlocking(homedirfp, FSETLOCKING_BYCALLER);
-
- snprintf(local_path, sizeof(local_path), "%s.local", path);
- localfp = fopen(local_path, "r");
- if (localfp != NULL)
- __fsetlocking(localfp, FSETLOCKING_BYCALLER);
- }
-
- /*
- * Perform two passes over the specification file.
- * The first pass counts the number of specifications and
- * performs simple validation of the input. At the end
- * of the first pass, the spec array is allocated.
- * The second pass performs detailed validation of the input
- * and fills in the spec array.
- */
- maxnspec = UINT_MAX / sizeof(spec_t);
- for (pass = 0; pass < 2; pass++) {
- lineno = 0;
- nspec = 0;
- while (getline(&line_buf, &line_len, fp) > 0
- && nspec < maxnspec) {
- if (process_line(path, prefix, line_buf, pass, ++lineno)
- != 0)
- goto finish;
- }
- lineno = 0;
- if (homedirfp)
- while (getline(&line_buf, &line_len, homedirfp) > 0
- && nspec < maxnspec) {
- if (process_line
- (homedir_path, prefix, line_buf, pass,
- ++lineno) != 0)
- goto finish;
- }
-
- lineno = 0;
- if (localfp)
- while (getline(&line_buf, &line_len, localfp) > 0
- && nspec < maxnspec) {
- if (process_line
- (local_path, prefix, line_buf, pass,
- ++lineno) != 0)
- goto finish;
- }
-
- if (pass == 0) {
- if (nspec == 0) {
- status = 0;
- goto finish;
- }
- if ((spec_arr = malloc(sizeof(spec_t) * nspec)) == NULL)
- goto finish;
- memset(spec_arr, '\0', sizeof(spec_t) * nspec);
- maxnspec = nspec;
- rewind(fp);
- if (homedirfp)
- rewind(homedirfp);
- if (localfp)
- rewind(localfp);
- }
- }
- free(line_buf);
- line_buf = NULL;
-
- /* Move exact pathname specifications to the end. */
- spec_copy = malloc(sizeof(spec_t) * nspec);
- if (!spec_copy)
- goto finish;
- j = 0;
- for (i = 0; i < nspec; i++) {
- if (spec_arr[i].hasMetaChars)
- memcpy(&spec_copy[j++], &spec_arr[i], sizeof(spec_t));
- }
- for (i = 0; i < nspec; i++) {
- if (!spec_arr[i].hasMetaChars)
- memcpy(&spec_copy[j++], &spec_arr[i], sizeof(spec_t));
- }
- free(spec_arr);
- spec_arr = spec_copy;
-
- nodups_specs(path);
-
- status = 0;
- finish:
- fclose(fp);
- free(line_buf);
- if (spec_arr != spec_copy)
- free(spec_arr);
- if (homedirfp)
- fclose(homedirfp);
- if (localfp)
- fclose(localfp);
- return status;
-}
-
hidden_def(matchpathcon_init_prefix)
int matchpathcon_init(const char *path)
@@ -763,172 +306,33 @@
void matchpathcon_fini(void)
{
- struct spec *spec;
- struct stem *stem;
- unsigned int i;
-
- for (i = 0; i < nspec; i++) {
- spec = &spec_arr[i];
- free(spec->regex_str);
- free(spec->type_str);
- free(spec->context);
- regfree(&spec->regex);
- }
- free(spec_arr);
- spec_arr = NULL;
- nspec = 0;
-
- for (i = 0; i < (unsigned int)num_stems; i++) {
- stem = &stem_arr[i];
- free(stem->buf);
- }
- free(stem_arr);
- stem_arr = NULL;
- num_stems = 0;
- alloc_stems = 0;
+ selabel_close(hnd);
+ hnd = NULL;
}
-static int matchpathcon_common(const char *name, mode_t mode)
-{
- int i, ret, file_stem;
- const char *buf = name;
-
- if (!nspec) {
- ret = matchpathcon_init_prefix(NULL, NULL);
- if (ret < 0)
- return ret;
- if (!nspec) {
- errno = ENOENT;
- return -1;
- }
- }
-
- file_stem = find_stem_from_file(&buf);
-
- mode &= S_IFMT;
-
- /*
- * Check for matching specifications in reverse order, so that
- * the last matching specification is used.
- */
- for (i = nspec - 1; i >= 0; i--) {
- /* if the spec in question matches no stem or has the same
- * stem as the file AND if the spec in question has no mode
- * specified or if the mode matches the file mode then we do
- * a regex check */
- if ((spec_arr[i].stem_id == -1
- || spec_arr[i].stem_id == file_stem)
- && (!mode || !spec_arr[i].mode
- || ((mode & S_IFMT) == spec_arr[i].mode))) {
- if (spec_arr[i].stem_id == -1)
- ret =
- regexec(&spec_arr[i].regex, name, 0, NULL,
- 0);
- else
- ret =
- regexec(&spec_arr[i].regex, buf, 0, NULL,
- 0);
- if (ret == 0)
- break;
-
- if (ret == REG_NOMATCH)
- continue;
- /* else it's an error */
- return -1;
- }
- }
-
- if (i < 0) {
- /* No matching specification. */
- errno = ENOENT;
- return -1;
- }
-
- spec_arr[i].matches++;
-
- return i;
-
-}
-
int matchpathcon(const char *name, mode_t mode, security_context_t * con)
{
- int i = matchpathcon_common(name, mode);
+ if (!mycanoncon)
+ mycanoncon = default_canoncon;
- if (i < 0)
- return -1;
-
- if (strcmp(spec_arr[i].context, "<<none>>") == 0) {
- errno = ENOENT;
- return -1;
- }
-
- if (!spec_arr[i].context_valid) {
- if (myinvalidcon) {
- /* Old-style validation of context. */
- if (myinvalidcon
- ("file_contexts", 0, spec_arr[i].context))
- goto bad;
- } else {
- /* New canonicalization of context. */
- if (mycanoncon
- ("file_contexts", 0, &spec_arr[i].context))
- goto bad;
- }
- spec_arr[i].context_valid = 1;
- }
-
- if (!spec_arr[i].translated && !(myflags & MATCHPATHCON_NOTRANS)) {
- char *tmpcon = NULL;
- if (selinux_raw_to_trans_context(spec_arr[i].context, &tmpcon))
- return -1;
- free(spec_arr[i].context);
- spec_arr[i].context = tmpcon;
- spec_arr[i].translated = 1;
- }
-
- *con = strdup(spec_arr[i].context);
- if (!(*con))
- return -1;
-
- return 0;
-
- bad:
- errno = EINVAL;
- return -1;
+ return notrans ?
+ selabel_lookup_raw(hnd, con, name, mode) :
+ selabel_lookup(hnd, con, name, mode);
}
int matchpathcon_index(const char *name, mode_t mode, security_context_t * con)
{
- int i = matchpathcon_common(name, mode);
+ int i = matchpathcon(name, mode, con);
if (i < 0)
return -1;
- *con = strdup(spec_arr[i].context);
- if (!(*con))
- return -1;
-
- return i;
+ return add_array_elt(*con);
}
-void matchpathcon_checkmatches(char *str)
+void matchpathcon_checkmatches(char *str __attribute__((unused)))
{
- unsigned int i;
- for (i = 0; i < nspec; i++) {
- if (spec_arr[i].matches == 0) {
- if (spec_arr[i].type_str) {
- myprintf
- ("%s: Warning! No matches for (%s, %s, %s)\n",
- str, spec_arr[i].regex_str,
- spec_arr[i].type_str, spec_arr[i].context);
- } else {
- myprintf
- ("%s: Warning! No matches for (%s, %s)\n",
- str, spec_arr[i].regex_str,
- spec_arr[i].context);
- }
- }
- }
+ selabel_stats(hnd);
}
/* Compare two contexts to see if their differences are "significant",
@@ -958,7 +362,6 @@
{
security_context_t con = NULL;
security_context_t fcontext = NULL;
- unsigned int localflags = myflags;
int rc = 0;
rc = lgetfilecon_raw(path, &con);
@@ -969,15 +372,14 @@
return 0;
}
- set_matchpathcon_flags(myflags | MATCHPATHCON_NOTRANS);
- if (matchpathcon(path, mode, &fcontext) != 0) {
+ if (selabel_lookup_raw(hnd, &fcontext, path, mode) != 0) {
if (errno != ENOENT)
rc = 1;
else
rc = 0;
} else
rc = (selinux_file_context_cmp(fcontext, con) == 0);
- set_matchpathcon_flags(localflags);
+
freecon(con);
freecon(fcontext);
return rc;
@@ -988,21 +390,40 @@
struct stat st;
int rc = -1;
security_context_t scontext = NULL;
- unsigned int localflags = myflags;
if (lstat(path, &st) != 0)
return rc;
- set_matchpathcon_flags(myflags | MATCHPATHCON_NOTRANS);
-
/* If there's an error determining the context, or it has none,
return to allow default context */
- if (matchpathcon(path, st.st_mode, &scontext)) {
+ if (selabel_lookup_raw(hnd, &scontext, path, st.st_mode)) {
if (errno == ENOENT)
rc = 0;
} else {
rc = lsetfilecon_raw(path, scontext);
freecon(scontext);
}
- set_matchpathcon_flags(localflags);
return rc;
}
+
+int compat_validate(struct selabel_handle *rec,
+ struct selabel_lookup_rec *contexts,
+ const char *path, unsigned lineno)
+{
+ int rc;
+ char **ctx = &contexts->ctx_raw;
+
+ if (myinvalidcon)
+ rc = myinvalidcon(path, lineno, *ctx);
+ else if (mycanoncon)
+ rc = mycanoncon(path, lineno, ctx);
+ else {
+ rc = selabel_validate(rec, contexts);
+ if (rc < 0) {
+ COMPAT_LOG(SELINUX_WARNING,
+ "%s: line %d has invalid context %s\n",
+ path, lineno, *ctx);
+ }
+ }
+
+ return rc ? -1 : 0;
+}
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 6/7] libselinux: labeling support (try 4)
2007-06-15 23:43 ` [PATCH 6/7] " Eamon Walsh
@ 2007-06-20 14:43 ` Stephen Smalley
2007-06-20 15:13 ` Stephen Smalley
0 siblings, 1 reply; 9+ messages in thread
From: Stephen Smalley @ 2007-06-20 14:43 UTC (permalink / raw)
To: Eamon Walsh; +Cc: SE Linux, Karl MacMillan
On Fri, 2007-06-15 at 19:43 -0400, Eamon Walsh wrote:
> This patch rebases the matchpathcon code to use the new interface.
matchpathcon() is supposed to internally call matchpathcon_init if it
hasn't already been called, and this behavior is relied upon by some
programs including restorecon. So I'll have to at least modify your
matchpathcon() to call matchpathcon_init if !hnd. You'll see similar
logic in the original matchpathcon_common() function before your patch.
>
> Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
> ---
>
> matchpathcon.c | 803 +++++++--------------------------------------------------
> 1 file changed, 112 insertions(+), 691 deletions(-)
>
>
> Index: libselinux/src/matchpathcon.c
> ===================================================================
> --- libselinux/src/matchpathcon.c (revision 2474)
> +++ libselinux/src/matchpathcon.c (working copy)
> @@ -1,19 +1,54 @@
> -#include <unistd.h>
> -#include <fcntl.h>
> #include <sys/stat.h>
> #include <string.h>
> -#include "selinux_internal.h"
> -#include <stdio.h>
> -#include <stdio_ext.h>
> -#include <stdlib.h>
> -#include <ctype.h>
> #include <errno.h>
> -#include <limits.h>
> -#include <regex.h>
> -#include <stdarg.h>
> -#include "policy.h"
> -#include "context_internal.h"
> +#include <stdio.h>
> +#include "selinux_internal.h"
> +#include "label_internal.h"
> +#include "callbacks.h"
>
> +static __thread struct selabel_handle *hnd;
> +
> +/*
> + * An array for mapping integers to contexts
> + */
> +static __thread char **con_array;
> +static __thread int con_array_size;
> +static __thread int con_array_used;
> +
> +static int add_array_elt(char *con)
> +{
> + if (con_array_size) {
> + while (con_array_used >= con_array_size) {
> + con_array_size *= 2;
> + con_array = (char **)realloc(con_array, sizeof(char*) *
> + con_array_size);
> + if (!con_array) {
> + con_array_size = con_array_used = 0;
> + return -1;
> + }
> + }
> + } else {
> + con_array_size = 1000;
> + con_array = (char **)malloc(sizeof(char*) * con_array_size);
> + if (!con_array) {
> + con_array_size = con_array_used = 0;
> + return -1;
> + }
> + }
> +
> + con_array[con_array_used] = strdup(con);
> + if (!con_array[con_array_used])
> + return -1;
> + return con_array_used++;
> +}
> +
> +static void free_array_elts(void)
> +{
> + con_array_size = con_array_used = 0;
> + free(con_array);
> + con_array = NULL;
> +}
> +
> static void
> #ifdef __GNUC__
> __attribute__ ((format(printf, 1, 2)))
> @@ -26,11 +61,11 @@
> va_end(ap);
> }
>
> -static void
> +void
> #ifdef __GNUC__
> __attribute__ ((format(printf, 1, 2)))
> #endif
> - (*myprintf) (const char *fmt,...) = &default_printf;
> + (*myprintf) (const char *fmt,...);
>
> void set_matchpathcon_printf(void (*f) (const char *fmt, ...))
> {
> @@ -66,7 +101,7 @@
> }
>
> static int (*mycanoncon) (const char *p, unsigned l, char **c) =
> - &default_canoncon;
> + NULL;
>
> void set_matchpathcon_canoncon(int (*f) (const char *p, unsigned l, char **c))
> {
> @@ -76,137 +111,23 @@
> mycanoncon = &default_canoncon;
> }
>
> -static __thread unsigned int myflags;
> +static __thread struct selabel_opt options[SELABEL_NOPT];
> +static __thread int notrans;
>
> void set_matchpathcon_flags(unsigned int flags)
> {
> - myflags = flags;
> -}
> -
> -/*
> - * A file security context specification.
> - */
> -typedef struct spec {
> - char *regex_str; /* regular expession string for diagnostic messages */
> - char *type_str; /* type string for diagnostic messages */
> - char *context; /* context string */
> - int context_valid; /* context string has been validated/canonicalized */
> - int translated; /* context string has been translated */
> - regex_t regex; /* compiled regular expression */
> - mode_t mode; /* mode format value */
> - int matches; /* number of matching pathnames */
> - int hasMetaChars; /* indicates whether the RE has
> - any meta characters.
> - 0 = no meta chars
> - 1 = has one or more meta chars */
> - int stem_id; /* indicates which of the stem-compression
> - * items it matches */
> -} spec_t;
> -
> -typedef struct stem {
> - char *buf;
> - int len;
> -} stem_t;
> -
> -static stem_t *stem_arr = NULL;
> -static int num_stems = 0;
> -static int alloc_stems = 0;
> -
> -static const char *const regex_chars = ".^$?*+|[({";
> -
> -/* Return the length of the text that can be considered the stem, returns 0
> - * if there is no identifiable stem */
> -static int get_stem_from_spec(const char *const buf)
> -{
> - const char *tmp = strchr(buf + 1, '/');
> - const char *ind;
> -
> - if (!tmp)
> - return 0;
> -
> - for (ind = buf; ind < tmp; ind++) {
> - if (strchr(regex_chars, (int)*ind))
> - return 0;
> - }
> - return tmp - buf;
> -}
> -
> -/* return the length of the text that is the stem of a file name */
> -static int get_stem_from_file_name(const char *const buf)
> -{
> - const char *tmp = strchr(buf + 1, '/');
> -
> - if (!tmp)
> - return 0;
> - return tmp - buf;
> -}
> -
> -/* find the stem of a file spec, returns the index into stem_arr for a new
> - * or existing stem, (or -1 if there is no possible stem - IE for a file in
> - * the root directory or a regex that is too complex for us). Makes buf
> - * point to the text AFTER the stem. */
> -static int find_stem_from_spec(const char **buf)
> -{
> int i;
> - int stem_len = get_stem_from_spec(*buf);
> -
> - if (!stem_len)
> - return -1;
> - for (i = 0; i < num_stems; i++) {
> - if (stem_len == stem_arr[i].len
> - && !strncmp(*buf, stem_arr[i].buf, stem_len)) {
> - *buf += stem_len;
> - return i;
> - }
> - }
> - if (num_stems == alloc_stems) {
> - stem_t *tmp_arr;
> - alloc_stems = alloc_stems * 2 + 16;
> - tmp_arr = realloc(stem_arr, sizeof(stem_t) * alloc_stems);
> - if (!tmp_arr)
> - return -1;
> - stem_arr = tmp_arr;
> - }
> - stem_arr[num_stems].len = stem_len;
> - stem_arr[num_stems].buf = malloc(stem_len + 1);
> - if (!stem_arr[num_stems].buf)
> - return -1;
> - memcpy(stem_arr[num_stems].buf, *buf, stem_len);
> - stem_arr[num_stems].buf[stem_len] = '\0';
> - num_stems++;
> - *buf += stem_len;
> - return num_stems - 1;
> + memset(options, 0, sizeof(options));
> + i = SELABEL_OPT_BASEONLY;
> + options[i].type = i;
> + options[i].value = (char *)(flags & MATCHPATHCON_BASEONLY);
> + i = SELABEL_OPT_VALIDATE;
> + options[i].type = i;
> + options[i].value = (char *)(flags & MATCHPATHCON_VALIDATE);
> + notrans = flags & MATCHPATHCON_NOTRANS;
> }
>
> -/* find the stem of a file name, returns the index into stem_arr (or -1 if
> - * there is no match - IE for a file in the root directory or a regex that is
> - * too complex for us). Makes buf point to the text AFTER the stem. */
> -static int find_stem_from_file(const char **buf)
> -{
> - int i;
> - int stem_len = get_stem_from_file_name(*buf);
> -
> - if (!stem_len)
> - return -1;
> - for (i = 0; i < num_stems; i++) {
> - if (stem_len == stem_arr[i].len
> - && !strncmp(*buf, stem_arr[i].buf, stem_len)) {
> - *buf += stem_len;
> - return i;
> - }
> - }
> - return -1;
> -}
> -
> /*
> - * The array of specifications, initially in the
> - * same order as in the specification file.
> - * Sorting occurs based on hasMetaChars
> - */
> -static spec_t *spec_arr;
> -static unsigned int nspec;
> -
> -/*
> * An association between an inode and a
> * specification.
> */
> @@ -238,7 +159,7 @@
> int matchpathcon_filespec_add(ino_t ino, int specind, const char *file)
> {
> file_spec_t *prevfl, *fl;
> - int h, no_conflict, ret;
> + int h, ret;
> struct stat sb;
>
> if (!fl_head) {
> @@ -264,21 +185,14 @@
>
> }
>
> - no_conflict =
> - (strcmp
> - (spec_arr[fl->specind].context,
> - spec_arr[specind].context) == 0);
> - if (no_conflict)
> + if (!strcmp(con_array[fl->specind],
> + con_array[specind]))
> return fl->specind;
>
> myprintf
> ("%s: conflicting specifications for %s and %s, using %s.\n",
> __FUNCTION__, file, fl->file,
> - ((specind >
> - fl->specind) ? spec_arr[specind].
> - context : spec_arr[fl->specind].context));
> - fl->specind =
> - (specind > fl->specind) ? specind : fl->specind;
> + con_array[fl->specind]);
> free(fl->file);
> fl->file = malloc(strlen(file) + 1);
> if (!fl->file)
> @@ -350,6 +264,8 @@
> file_spec_t *fl, *tmp;
> int h;
>
> + free_array_elts();
> +
> if (!fl_head)
> return;
>
> @@ -367,393 +283,20 @@
> fl_head = NULL;
> }
>
> -/*
> - * Warn about duplicate specifications.
> - */
> -static void nodups_specs(const char *path)
> +int matchpathcon_init_prefix(const char *path, const char *subset)
> {
> - unsigned int ii, jj;
> - struct spec *curr_spec;
> + if (!mycanoncon)
> + mycanoncon = default_canoncon;
>
> - for (ii = 0; ii < nspec; ii++) {
> - curr_spec = &spec_arr[ii];
> - for (jj = ii + 1; jj < nspec; jj++) {
> - if ((!strcmp
> - (spec_arr[jj].regex_str, curr_spec->regex_str))
> - && (!spec_arr[jj].mode || !curr_spec->mode
> - || spec_arr[jj].mode == curr_spec->mode)) {
> - if (strcmp
> - (spec_arr[jj].context,
> - curr_spec->context)) {
> - myprintf
> - ("%s: Multiple different specifications for %s (%s and %s).\n",
> - path, curr_spec->regex_str,
> - spec_arr[jj].context,
> - curr_spec->context);
> - } else {
> - myprintf
> - ("%s: Multiple same specifications for %s.\n",
> - path, curr_spec->regex_str);
> - }
> - }
> - }
> - }
> -}
> + options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET;
> + options[SELABEL_OPT_SUBSET].value = subset;
> + options[SELABEL_OPT_PATH].type = SELABEL_OPT_PATH;
> + options[SELABEL_OPT_PATH].value = path;
>
> -/* Determine if the regular expression specification has any meta characters. */
> -static void spec_hasMetaChars(struct spec *spec)
> -{
> - char *c;
> - int len;
> - char *end;
> -
> - c = spec->regex_str;
> - len = strlen(spec->regex_str);
> - end = c + len;
> -
> - spec->hasMetaChars = 0;
> -
> - /* Look at each character in the RE specification string for a
> - * meta character. Return when any meta character reached. */
> - while (c != end) {
> - switch (*c) {
> - case '.':
> - case '^':
> - case '$':
> - case '?':
> - case '*':
> - case '+':
> - case '|':
> - case '[':
> - case '(':
> - case '{':
> - spec->hasMetaChars = 1;
> - return;
> - case '\\': /* skip the next character */
> - c++;
> - break;
> - default:
> - break;
> -
> - }
> - c++;
> - }
> - return;
> + hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT);
> + return hnd ? 0 : -1;
> }
> -static int process_line(const char *path, const char *prefix, char *line_buf,
> - int pass, unsigned lineno)
> -{
> - int items, len, regerr, ret;
> - char *buf_p, *ptr;
> - char *regex=NULL, *type=NULL, *context=NULL;
> - const char *reg_buf;
> - char *anchored_regex = NULL;
>
> - ret = 0;
> - len = strlen(line_buf);
> - if (line_buf[len - 1] == '\n')
> - line_buf[len - 1] = 0;
> - buf_p = line_buf;
> - while (isspace(*buf_p))
> - buf_p++;
> - /* Skip comment lines and empty lines. */
> - if (*buf_p == '#' || *buf_p == 0)
> - return 0;
> -
> - items = 0;
> - regex = strtok_r(buf_p, " \t", &ptr);
> - if (regex)
> - items += 1;
> - type = strtok_r(NULL, " \t", &ptr);
> - if (type)
> - items += 1;
> - context = strtok_r(NULL, " \t", &ptr);
> - if (context)
> - items += 1;
> -
> - if (items < 2) {
> - myprintf("%s: line %d is missing fields, skipping\n", path,
> - lineno);
> - return 0;
> - } else if (items == 2) {
> - /* The type field is optional. */
> - context = type;
> - type = NULL;
> - }
> -
> - regex = strdup(regex);
> - if (!regex) {
> - return -1;
> - }
> - if (type) {
> - type = strdup(type);
> - if (!type) {
> - free(regex);
> - return -1;
> - }
> - }
> - context = strdup(context);
> - if (!context) {
> - ret = -1;
> - goto finish;
> - }
> -
> - reg_buf = regex;
> - len = get_stem_from_spec(reg_buf);
> - if (len && prefix && strncmp(prefix, regex, len)) {
> - /* Stem of regex does not match requested prefix, discard. */
> - goto finish;
> - }
> -
> - if (pass == 1) {
> - /* On the second pass, compile and store the specification in spec. */
> - char *cp;
> - spec_arr[nspec].stem_id = find_stem_from_spec(®_buf);
> - spec_arr[nspec].regex_str = regex;
> -
> - /* Anchor the regular expression. */
> - len = strlen(reg_buf);
> - cp = anchored_regex = malloc(len + 3);
> - if (!anchored_regex) {
> - ret = -1;
> - goto finish;
> - }
> - /* Create ^...$ regexp. */
> - *cp++ = '^';
> - cp = mempcpy(cp, reg_buf, len);
> - *cp++ = '$';
> - *cp = '\0';
> -
> - /* Compile the regular expression. */
> - regerr =
> - regcomp(&spec_arr[nspec].regex,
> - anchored_regex, REG_EXTENDED | REG_NOSUB);
> - if (regerr != 0) {
> - size_t errsz = 0;
> - char *errbuf = NULL;
> - errsz = regerror(regerr, &spec_arr[nspec].regex,
> - errbuf, errsz);
> - if (errsz)
> - errbuf = malloc(errsz);
> - if (errbuf)
> - (void)regerror(regerr,
> - &spec_arr[nspec].regex,
> - errbuf, errsz);
> - myprintf("%s: line %d has invalid regex %s: %s\n",
> - path, lineno, anchored_regex,
> - (errbuf ? errbuf : "out of memory"));
> - free(anchored_regex);
> - anchored_regex = NULL;
> - free(errbuf);
> - goto finish;
> - }
> - free(anchored_regex);
> - anchored_regex = NULL;
> -
> - /* Convert the type string to a mode format */
> - spec_arr[nspec].type_str = type;
> - spec_arr[nspec].mode = 0;
> - if (!type)
> - goto skip_type;
> - len = strlen(type);
> - if (type[0] != '-' || len != 2) {
> - myprintf("%s: line %d has invalid file type %s\n",
> - path, lineno, type);
> - goto finish;
> - }
> - switch (type[1]) {
> - case 'b':
> - spec_arr[nspec].mode = S_IFBLK;
> - break;
> - case 'c':
> - spec_arr[nspec].mode = S_IFCHR;
> - break;
> - case 'd':
> - spec_arr[nspec].mode = S_IFDIR;
> - break;
> - case 'p':
> - spec_arr[nspec].mode = S_IFIFO;
> - break;
> - case 'l':
> - spec_arr[nspec].mode = S_IFLNK;
> - break;
> - case 's':
> - spec_arr[nspec].mode = S_IFSOCK;
> - break;
> - case '-':
> - spec_arr[nspec].mode = S_IFREG;
> - break;
> - default:
> - myprintf("%s: line %d has invalid file type %s\n",
> - path, lineno, type);
> - goto finish;
> - }
> -
> - skip_type:
> - if (strcmp(context, "<<none>>")) {
> - if (myflags & MATCHPATHCON_VALIDATE) {
> - if (myinvalidcon) {
> - /* Old-style validation of context. */
> - if (myinvalidcon(path, lineno, context))
> - goto finish;
> - } else {
> - /* New canonicalization of context. */
> - if (mycanoncon(path, lineno, &context))
> - goto finish;
> - }
> - spec_arr[nspec].context_valid = 1;
> - }
> - }
> -
> - spec_arr[nspec].context = context;
> -
> - /* Determine if specification has
> - * any meta characters in the RE */
> - spec_hasMetaChars(&spec_arr[nspec]);
> -
> - /* Prevent stored strings from being freed. */
> - regex = NULL;
> - type = NULL;
> - context = NULL;
> - }
> -
> - nspec++;
> -finish:
> - free(regex);
> - free(type);
> - free(context);
> - return ret;
> -}
> -
> -int matchpathcon_init_prefix(const char *path, const char *prefix)
> -{
> - FILE *fp;
> - FILE *localfp = NULL;
> - FILE *homedirfp = NULL;
> - char local_path[PATH_MAX + 1];
> - char homedir_path[PATH_MAX + 1];
> - char *line_buf = NULL;
> - size_t line_len = 0;
> - unsigned int lineno, pass, i, j, maxnspec;
> - spec_t *spec_copy = NULL;
> - int status = -1;
> - struct stat sb;
> -
> - /* Open the specification file. */
> - if (!path)
> - path = selinux_file_context_path();
> - if ((fp = fopen(path, "r")) == NULL)
> - return -1;
> - __fsetlocking(fp, FSETLOCKING_BYCALLER);
> -
> - if (fstat(fileno(fp), &sb) < 0)
> - return -1;
> - if (!S_ISREG(sb.st_mode)) {
> - errno = EINVAL;
> - return -1;
> - }
> -
> - if ((myflags & MATCHPATHCON_BASEONLY) == 0) {
> - snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs",
> - path);
> - homedirfp = fopen(homedir_path, "r");
> - if (homedirfp != NULL)
> - __fsetlocking(homedirfp, FSETLOCKING_BYCALLER);
> -
> - snprintf(local_path, sizeof(local_path), "%s.local", path);
> - localfp = fopen(local_path, "r");
> - if (localfp != NULL)
> - __fsetlocking(localfp, FSETLOCKING_BYCALLER);
> - }
> -
> - /*
> - * Perform two passes over the specification file.
> - * The first pass counts the number of specifications and
> - * performs simple validation of the input. At the end
> - * of the first pass, the spec array is allocated.
> - * The second pass performs detailed validation of the input
> - * and fills in the spec array.
> - */
> - maxnspec = UINT_MAX / sizeof(spec_t);
> - for (pass = 0; pass < 2; pass++) {
> - lineno = 0;
> - nspec = 0;
> - while (getline(&line_buf, &line_len, fp) > 0
> - && nspec < maxnspec) {
> - if (process_line(path, prefix, line_buf, pass, ++lineno)
> - != 0)
> - goto finish;
> - }
> - lineno = 0;
> - if (homedirfp)
> - while (getline(&line_buf, &line_len, homedirfp) > 0
> - && nspec < maxnspec) {
> - if (process_line
> - (homedir_path, prefix, line_buf, pass,
> - ++lineno) != 0)
> - goto finish;
> - }
> -
> - lineno = 0;
> - if (localfp)
> - while (getline(&line_buf, &line_len, localfp) > 0
> - && nspec < maxnspec) {
> - if (process_line
> - (local_path, prefix, line_buf, pass,
> - ++lineno) != 0)
> - goto finish;
> - }
> -
> - if (pass == 0) {
> - if (nspec == 0) {
> - status = 0;
> - goto finish;
> - }
> - if ((spec_arr = malloc(sizeof(spec_t) * nspec)) == NULL)
> - goto finish;
> - memset(spec_arr, '\0', sizeof(spec_t) * nspec);
> - maxnspec = nspec;
> - rewind(fp);
> - if (homedirfp)
> - rewind(homedirfp);
> - if (localfp)
> - rewind(localfp);
> - }
> - }
> - free(line_buf);
> - line_buf = NULL;
> -
> - /* Move exact pathname specifications to the end. */
> - spec_copy = malloc(sizeof(spec_t) * nspec);
> - if (!spec_copy)
> - goto finish;
> - j = 0;
> - for (i = 0; i < nspec; i++) {
> - if (spec_arr[i].hasMetaChars)
> - memcpy(&spec_copy[j++], &spec_arr[i], sizeof(spec_t));
> - }
> - for (i = 0; i < nspec; i++) {
> - if (!spec_arr[i].hasMetaChars)
> - memcpy(&spec_copy[j++], &spec_arr[i], sizeof(spec_t));
> - }
> - free(spec_arr);
> - spec_arr = spec_copy;
> -
> - nodups_specs(path);
> -
> - status = 0;
> - finish:
> - fclose(fp);
> - free(line_buf);
> - if (spec_arr != spec_copy)
> - free(spec_arr);
> - if (homedirfp)
> - fclose(homedirfp);
> - if (localfp)
> - fclose(localfp);
> - return status;
> -}
> -
> hidden_def(matchpathcon_init_prefix)
>
> int matchpathcon_init(const char *path)
> @@ -763,172 +306,33 @@
>
> void matchpathcon_fini(void)
> {
> - struct spec *spec;
> - struct stem *stem;
> - unsigned int i;
> -
> - for (i = 0; i < nspec; i++) {
> - spec = &spec_arr[i];
> - free(spec->regex_str);
> - free(spec->type_str);
> - free(spec->context);
> - regfree(&spec->regex);
> - }
> - free(spec_arr);
> - spec_arr = NULL;
> - nspec = 0;
> -
> - for (i = 0; i < (unsigned int)num_stems; i++) {
> - stem = &stem_arr[i];
> - free(stem->buf);
> - }
> - free(stem_arr);
> - stem_arr = NULL;
> - num_stems = 0;
> - alloc_stems = 0;
> + selabel_close(hnd);
> + hnd = NULL;
> }
>
> -static int matchpathcon_common(const char *name, mode_t mode)
> -{
> - int i, ret, file_stem;
> - const char *buf = name;
> -
> - if (!nspec) {
> - ret = matchpathcon_init_prefix(NULL, NULL);
> - if (ret < 0)
> - return ret;
> - if (!nspec) {
> - errno = ENOENT;
> - return -1;
> - }
> - }
> -
> - file_stem = find_stem_from_file(&buf);
> -
> - mode &= S_IFMT;
> -
> - /*
> - * Check for matching specifications in reverse order, so that
> - * the last matching specification is used.
> - */
> - for (i = nspec - 1; i >= 0; i--) {
> - /* if the spec in question matches no stem or has the same
> - * stem as the file AND if the spec in question has no mode
> - * specified or if the mode matches the file mode then we do
> - * a regex check */
> - if ((spec_arr[i].stem_id == -1
> - || spec_arr[i].stem_id == file_stem)
> - && (!mode || !spec_arr[i].mode
> - || ((mode & S_IFMT) == spec_arr[i].mode))) {
> - if (spec_arr[i].stem_id == -1)
> - ret =
> - regexec(&spec_arr[i].regex, name, 0, NULL,
> - 0);
> - else
> - ret =
> - regexec(&spec_arr[i].regex, buf, 0, NULL,
> - 0);
> - if (ret == 0)
> - break;
> -
> - if (ret == REG_NOMATCH)
> - continue;
> - /* else it's an error */
> - return -1;
> - }
> - }
> -
> - if (i < 0) {
> - /* No matching specification. */
> - errno = ENOENT;
> - return -1;
> - }
> -
> - spec_arr[i].matches++;
> -
> - return i;
> -
> -}
> -
> int matchpathcon(const char *name, mode_t mode, security_context_t * con)
> {
> - int i = matchpathcon_common(name, mode);
> + if (!mycanoncon)
> + mycanoncon = default_canoncon;
>
> - if (i < 0)
> - return -1;
> -
> - if (strcmp(spec_arr[i].context, "<<none>>") == 0) {
> - errno = ENOENT;
> - return -1;
> - }
> -
> - if (!spec_arr[i].context_valid) {
> - if (myinvalidcon) {
> - /* Old-style validation of context. */
> - if (myinvalidcon
> - ("file_contexts", 0, spec_arr[i].context))
> - goto bad;
> - } else {
> - /* New canonicalization of context. */
> - if (mycanoncon
> - ("file_contexts", 0, &spec_arr[i].context))
> - goto bad;
> - }
> - spec_arr[i].context_valid = 1;
> - }
> -
> - if (!spec_arr[i].translated && !(myflags & MATCHPATHCON_NOTRANS)) {
> - char *tmpcon = NULL;
> - if (selinux_raw_to_trans_context(spec_arr[i].context, &tmpcon))
> - return -1;
> - free(spec_arr[i].context);
> - spec_arr[i].context = tmpcon;
> - spec_arr[i].translated = 1;
> - }
> -
> - *con = strdup(spec_arr[i].context);
> - if (!(*con))
> - return -1;
> -
> - return 0;
> -
> - bad:
> - errno = EINVAL;
> - return -1;
> + return notrans ?
> + selabel_lookup_raw(hnd, con, name, mode) :
> + selabel_lookup(hnd, con, name, mode);
> }
>
> int matchpathcon_index(const char *name, mode_t mode, security_context_t * con)
> {
> - int i = matchpathcon_common(name, mode);
> + int i = matchpathcon(name, mode, con);
>
> if (i < 0)
> return -1;
>
> - *con = strdup(spec_arr[i].context);
> - if (!(*con))
> - return -1;
> -
> - return i;
> + return add_array_elt(*con);
> }
>
> -void matchpathcon_checkmatches(char *str)
> +void matchpathcon_checkmatches(char *str __attribute__((unused)))
> {
> - unsigned int i;
> - for (i = 0; i < nspec; i++) {
> - if (spec_arr[i].matches == 0) {
> - if (spec_arr[i].type_str) {
> - myprintf
> - ("%s: Warning! No matches for (%s, %s, %s)\n",
> - str, spec_arr[i].regex_str,
> - spec_arr[i].type_str, spec_arr[i].context);
> - } else {
> - myprintf
> - ("%s: Warning! No matches for (%s, %s)\n",
> - str, spec_arr[i].regex_str,
> - spec_arr[i].context);
> - }
> - }
> - }
> + selabel_stats(hnd);
> }
>
> /* Compare two contexts to see if their differences are "significant",
> @@ -958,7 +362,6 @@
> {
> security_context_t con = NULL;
> security_context_t fcontext = NULL;
> - unsigned int localflags = myflags;
> int rc = 0;
>
> rc = lgetfilecon_raw(path, &con);
> @@ -969,15 +372,14 @@
> return 0;
> }
>
> - set_matchpathcon_flags(myflags | MATCHPATHCON_NOTRANS);
> - if (matchpathcon(path, mode, &fcontext) != 0) {
> + if (selabel_lookup_raw(hnd, &fcontext, path, mode) != 0) {
> if (errno != ENOENT)
> rc = 1;
> else
> rc = 0;
> } else
> rc = (selinux_file_context_cmp(fcontext, con) == 0);
> - set_matchpathcon_flags(localflags);
> +
> freecon(con);
> freecon(fcontext);
> return rc;
> @@ -988,21 +390,40 @@
> struct stat st;
> int rc = -1;
> security_context_t scontext = NULL;
> - unsigned int localflags = myflags;
> if (lstat(path, &st) != 0)
> return rc;
>
> - set_matchpathcon_flags(myflags | MATCHPATHCON_NOTRANS);
> -
> /* If there's an error determining the context, or it has none,
> return to allow default context */
> - if (matchpathcon(path, st.st_mode, &scontext)) {
> + if (selabel_lookup_raw(hnd, &scontext, path, st.st_mode)) {
> if (errno == ENOENT)
> rc = 0;
> } else {
> rc = lsetfilecon_raw(path, scontext);
> freecon(scontext);
> }
> - set_matchpathcon_flags(localflags);
> return rc;
> }
> +
> +int compat_validate(struct selabel_handle *rec,
> + struct selabel_lookup_rec *contexts,
> + const char *path, unsigned lineno)
> +{
> + int rc;
> + char **ctx = &contexts->ctx_raw;
> +
> + if (myinvalidcon)
> + rc = myinvalidcon(path, lineno, *ctx);
> + else if (mycanoncon)
> + rc = mycanoncon(path, lineno, ctx);
> + else {
> + rc = selabel_validate(rec, contexts);
> + if (rc < 0) {
> + COMPAT_LOG(SELINUX_WARNING,
> + "%s: line %d has invalid context %s\n",
> + path, lineno, *ctx);
> + }
> + }
> +
> + return rc ? -1 : 0;
> +}
>
>
--
Stephen Smalley
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 6/7] libselinux: labeling support (try 4)
2007-06-20 14:43 ` Stephen Smalley
@ 2007-06-20 15:13 ` Stephen Smalley
0 siblings, 0 replies; 9+ messages in thread
From: Stephen Smalley @ 2007-06-20 15:13 UTC (permalink / raw)
To: Eamon Walsh; +Cc: SE Linux, Karl MacMillan
On Wed, 2007-06-20 at 10:43 -0400, Stephen Smalley wrote:
> On Fri, 2007-06-15 at 19:43 -0400, Eamon Walsh wrote:
> > This patch rebases the matchpathcon code to use the new interface.
>
> matchpathcon() is supposed to internally call matchpathcon_init if it
> hasn't already been called, and this behavior is relied upon by some
> programs including restorecon. So I'll have to at least modify your
> matchpathcon() to call matchpathcon_init if !hnd. You'll see similar
> logic in the original matchpathcon_common() function before your patch.
Like so, on top of your patch:
diff -u libselinux/src/matchpathcon.c libselinux/src/matchpathcon.c
--- libselinux/src/matchpathcon.c (working copy)
+++ libselinux/src/matchpathcon.c (working copy)
@@ -312,8 +312,8 @@
int matchpathcon(const char *name, mode_t mode, security_context_t * con)
{
- if (!mycanoncon)
- mycanoncon = default_canoncon;
+ if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0))
+ return -1;
return notrans ?
selabel_lookup_raw(hnd, con, name, mode) :
--
Stephen Smalley
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 7/7] libselinux: labeling support (try 4)
2007-06-15 23:29 [PATCH 1/7] libselinux: labeling support (try 4) Eamon Walsh
` (4 preceding siblings ...)
2007-06-15 23:43 ` [PATCH 6/7] " Eamon Walsh
@ 2007-06-15 23:46 ` Eamon Walsh
5 siblings, 0 replies; 9+ messages in thread
From: Eamon Walsh @ 2007-06-15 23:46 UTC (permalink / raw)
To: SE Linux; +Cc: Stephen Smalley, Karl MacMillan
This patch changes setfiles to use the new interface.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
setfiles.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 210 insertions(+), 65 deletions(-)
Index: policycoreutils/setfiles/setfiles.c
===================================================================
--- policycoreutils/setfiles/setfiles.c (revision 2474)
+++ policycoreutils/setfiles/setfiles.c (working copy)
@@ -16,6 +16,7 @@
#include <limits.h>
#include <sepol/sepol.h>
#include <selinux/selinux.h>
+#include <selinux/label.h>
#include <syslog.h>
#include <libgen.h>
#ifdef USE_AUDIT
@@ -70,21 +71,165 @@
static int abort_on_error; /* Abort the file tree walk upon an error. */
static int add_assoc; /* Track inode associations for conflict detection. */
static int nftw_flags; /* Flags to nftw, e.g. follow links, follow mounts */
-static int matchpathcon_flags; /* Flags to matchpathcon */
+static int base_only; /* Don't use local file_contexts customizations */
+static int ctx_validate; /* Validate contexts */
+static const char *altpath; /* Alternate path to file_contexts */
-static void
-#ifdef __GNUC__
- __attribute__ ((format(printf, 1, 2)))
-#endif
- qprintf(const char *fmt, ...)
+/* Label interface handle */
+static struct selabel_handle *hnd;
+
+/*
+ * An association between an inode and a context.
+ */
+typedef struct file_spec {
+ ino_t ino; /* inode number */
+ char *con; /* matched context */
+ char *file; /* full pathname */
+ struct file_spec *next; /* next association in hash bucket chain */
+} file_spec_t;
+
+/*
+ * The hash table of associations, hashed by inode number.
+ * Chaining is used for collisions, with elements ordered
+ * by inode number in each bucket. Each hash bucket has a dummy
+ * header.
+ */
+#define HASH_BITS 16
+#define HASH_BUCKETS (1 << HASH_BITS)
+#define HASH_MASK (HASH_BUCKETS-1)
+static file_spec_t *fl_head;
+
+/*
+ * Try to add an association between an inode and a context.
+ * If there is a different context that matched the inode,
+ * then use the first context that matched.
+ */
+int filespec_add(ino_t ino, const security_context_t con, const char *file)
{
- va_list ap;
- va_start(ap, fmt);
- if (!quiet)
- vfprintf(stdout, fmt, ap);
- va_end(ap);
+ file_spec_t *prevfl, *fl;
+ int h, ret;
+ struct stat sb;
+
+ if (!fl_head) {
+ fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
+ if (!fl_head)
+ goto oom;
+ memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
+ }
+
+ h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
+ for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
+ prevfl = fl, fl = fl->next) {
+ if (ino == fl->ino) {
+ ret = lstat(fl->file, &sb);
+ if (ret < 0 || sb.st_ino != ino) {
+ freecon(fl->con);
+ free(fl->file);
+ fl->file = strdup(file);
+ if (!fl->file)
+ goto oom;
+ fl->con = strdup(con);
+ if (!fl->con)
+ goto oom;
+ return 1;
+ }
+
+ if (strcmp(fl->con, con) == 0)
+ return 1;
+
+ fprintf(stderr,
+ "%s: conflicting specifications for %s and %s, using %s.\n",
+ __FUNCTION__, file, fl->file, fl->con);
+ free(fl->file);
+ fl->file = strdup(file);
+ if (!fl->file)
+ goto oom;
+ return 1;
+ }
+
+ if (ino > fl->ino)
+ break;
+ }
+
+ fl = malloc(sizeof(file_spec_t));
+ if (!fl)
+ goto oom;
+ fl->ino = ino;
+ fl->con = strdup(con);
+ if (!fl->con)
+ goto oom_freefl;
+ fl->file = strdup(file);
+ if (!fl->file)
+ goto oom_freefl;
+ fl->next = prevfl->next;
+ prevfl->next = fl;
+ return 0;
+ oom_freefl:
+ free(fl);
+ oom:
+ fprintf(stderr,
+ "%s: insufficient memory for file label entry for %s\n",
+ __FUNCTION__, file);
+ return -1;
}
+/*
+ * Evaluate the association hash table distribution.
+ */
+void filespec_eval(void)
+{
+ file_spec_t *fl;
+ int h, used, nel, len, longest;
+
+ if (!fl_head)
+ return;
+
+ used = 0;
+ longest = 0;
+ nel = 0;
+ for (h = 0; h < HASH_BUCKETS; h++) {
+ len = 0;
+ for (fl = fl_head[h].next; fl; fl = fl->next) {
+ len++;
+ }
+ if (len)
+ used++;
+ if (len > longest)
+ longest = len;
+ nel += len;
+ }
+
+ printf
+ ("%s: hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
+ __FUNCTION__, nel, used, HASH_BUCKETS, longest);
+}
+
+/*
+ * Destroy the association hash table.
+ */
+void filespec_destroy(void)
+{
+ file_spec_t *fl, *tmp;
+ int h;
+
+ if (!fl_head)
+ return;
+
+ for (h = 0; h < HASH_BUCKETS; h++) {
+ fl = fl_head[h].next;
+ while (fl) {
+ tmp = fl;
+ fl = fl->next;
+ freecon(tmp->con);
+ free(tmp->file);
+ free(tmp);
+ }
+ fl_head[h].next = NULL;
+ }
+ free(fl_head);
+ fl_head = NULL;
+}
+
static int add_exclude(const char *directory)
{
struct stat sb;
@@ -230,9 +375,9 @@
if (rootpath != NULL && name[0] == '\0')
/* this is actually the root dir of the alt root */
- return matchpathcon_index("/", sb->st_mode, con);
+ return selabel_lookup_raw(hnd, con, "/", sb->st_mode);
else
- return matchpathcon_index(name, sb->st_mode, con);
+ return selabel_lookup_raw(hnd, con, name, sb->st_mode);
}
void usage(const char *const name)
@@ -282,7 +427,7 @@
{
char *my_file = strdupa(file);
struct stat my_sb;
- int i, j, ret;
+ int ret;
char *context, *newcon;
int user_only_changed = 0;
size_t len = strlen(my_file);
@@ -293,10 +438,9 @@
if (len > 1 && my_file[len - 1] == '/')
my_file[len - 1] = 0;
- i = match(my_file, &my_sb, &newcon);
- if (i < 0)
- /* No matching specification. */
- return 0;
+ if (match(my_file, &my_sb, &newcon) < 0)
+ /* Check for no matching specification. */
+ return (errno == ENOENT) ? 0 : -1;
if (progress) {
count++;
@@ -317,14 +461,13 @@
* then use the last matching specification.
*/
if (add_assoc) {
- j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file);
- if (j < 0)
+ ret = filespec_add(my_sb.st_ino, newcon, my_file);
+ if (ret < 0)
goto err;
- if (j != i) {
+ if (ret > 0)
/* There was already an association and it took precedence. */
goto out;
- }
}
if (debug) {
@@ -460,33 +603,25 @@
rootpathlen = len;
}
-int canoncon(const char *path, unsigned lineno, char **contextp)
+int canoncon(char **contextp)
{
char *context = *contextp, *tmpcon;
- int valid = 1;
+ int rc = 0;
if (policyfile) {
- valid = (sepol_check_context(context) >= 0);
- } else if (security_canonicalize_context_raw(context, &tmpcon) < 0) {
- if (errno != ENOENT) {
- valid = 0;
- inc_err();
+ if (sepol_check_context(context) < 0) {
+ fprintf(stderr, "invalid context %s\n", context);
+ exit(1);
}
- } else {
+ } else if (security_canonicalize_context_raw(context, &tmpcon) == 0) {
free(context);
*contextp = tmpcon;
+ } else if (errno != ENOENT) {
+ rc = -1;
+ inc_err();
}
- if (!valid) {
- fprintf(stderr, "%s: line %u has invalid context %s\n",
- path, lineno, context);
-
- /* Exit immediately if we're in checking mode. */
- if (policyfile)
- exit(1);
- }
-
- return !valid;
+ return rc;
}
static int pre_stat(const char *file_unused __attribute__ ((unused)),
@@ -557,10 +692,9 @@
out:
if (add_assoc) {
- set_matchpathcon_printf(&qprintf);
- matchpathcon_filespec_eval();
- set_matchpathcon_printf(NULL);
- matchpathcon_filespec_destroy();
+ if (!quiet)
+ filespec_eval();
+ filespec_destroy();
}
return rc;
@@ -605,14 +739,20 @@
int main(int argc, char **argv)
{
struct stat sb;
- int opt, rc, i = 0;
+ int opt, i = 0;
char *input_filename = NULL;
int use_input_file = 0;
char *buf = NULL;
size_t buf_len;
char *base;
+ struct selabel_opt opts[] = {
+ { SELABEL_OPT_VALIDATE, NULL },
+ { SELABEL_OPT_BASEONLY, NULL },
+ { SELABEL_OPT_PATH, NULL }
+ };
memset(excludeArray, 0, sizeof(excludeArray));
+ altpath = NULL;
progname = strdup(argv[0]);
if (!progname) {
@@ -637,7 +777,7 @@
abort_on_error = 1;
add_assoc = 1;
nftw_flags = FTW_PHYS | FTW_MOUNT;
- matchpathcon_flags = MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS;
+ ctx_validate = 1;
} else {
/*
* restorecon:
@@ -648,15 +788,15 @@
* Follows mounts,
* Does lazy validation of contexts upon use.
*/
- if (strcmp(base, RESTORECON))
- qprintf("Executed with an unrecognized name (%s), defaulting to %s behavior.\n", base, RESTORECON);
+ if (strcmp(base, RESTORECON) && !quiet)
+ printf("Executed with an unrecognized name (%s), defaulting to %s behavior.\n", base, RESTORECON);
iamrestorecon = 1;
recurse = 0;
expand_realpath = 1;
abort_on_error = 0;
add_assoc = 0;
nftw_flags = FTW_PHYS;
- matchpathcon_flags = MATCHPATHCON_NOTRANS;
+ ctx_validate = 0;
/* restorecon only: silent exit if no SELinux.
Allows unconditional execution by scripts. */
@@ -664,8 +804,6 @@
exit(0);
}
- set_matchpathcon_flags(matchpathcon_flags);
-
/* Process any options. */
while ((opt = getopt(argc, argv, "c:de:f:ilnpqrsvo:FRW")) > 0) {
switch (opt) {
@@ -700,9 +838,8 @@
/* Only process the specified file_contexts file, not
any .homedirs or .local files, and do not perform
context translations. */
- set_matchpathcon_flags(MATCHPATHCON_BASEONLY |
- MATCHPATHCON_NOTRANS |
- MATCHPATHCON_VALIDATE);
+ base_only = 1;
+ ctx_validate = 1;
break;
}
@@ -812,7 +949,8 @@
/* Use our own invalid context checking function so that
we can support either checking against the active policy or
checking against a binary policy file. */
- set_matchpathcon_canoncon(&canoncon);
+ selinux_set_callback(SELINUX_CB_VALIDATE,
+ (union selinux_callback)&canoncon);
if (stat(argv[optind], &sb) < 0) {
perror(argv[optind]);
@@ -824,19 +962,24 @@
exit(1);
}
- /* Load the file contexts configuration and check it. */
- rc = matchpathcon_init(argv[optind]);
- if (rc < 0) {
- perror(argv[optind]);
- exit(1);
- }
-
+ altpath = argv[optind];
optind++;
+ }
- if (nerr)
- exit(1);
+ /* Load the file contexts configuration and check it. */
+ opts[0].value = (char *)ctx_validate;
+ opts[1].value = (char *)base_only;
+ opts[2].value = altpath;
+
+ hnd = selabel_open(SELABEL_CTX_FILE, opts, 3);
+ if (!hnd) {
+ perror(altpath);
+ exit(1);
}
+ if (nerr)
+ exit(1);
+
if (use_input_file) {
FILE *f = stdin;
ssize_t len;
@@ -863,8 +1006,10 @@
maybe_audit_mass_relabel();
if (warn_no_match)
- matchpathcon_checkmatches(argv[0]);
+ selabel_stats(hnd);
+ selabel_close(hnd);
+
if (outfile)
fclose(outfile);
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 9+ messages in thread