* [PATCH 1/5] libselinux: labeling API basic front-end interface
2006-11-16 2:25 [PATCH 0/5] libselinux: labeling API for userspace object managers Eamon Walsh
@ 2006-11-16 2:46 ` Eamon Walsh
2006-11-20 15:36 ` Karl MacMillan
0 siblings, 1 reply; 15+ messages in thread
From: Eamon Walsh @ 2006-11-16 2:46 UTC (permalink / raw)
To: selinux; +Cc: sds
[-- Attachment #1: Type: text/plain, Size: 365 bytes --]
This is the basic front-end interface. Note that backends may supply
additional values and flags, which will be seen with the file_contexts
backend.
---
include/selinux/label.h | 181 ++++++++++++++++++++++++
include/selinux/label_backend.h | 20 ++
2 files changed, 201 insertions(+)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: Type: text/x-patch, Size: 7664 bytes --]
Index: include/selinux/label.h
===================================================================
--- include/selinux/label.h (revision 0)
+++ include/selinux/label.h (revision 0)
@@ -0,0 +1,181 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELINUX_LABEL_H_
+#define _SELINUX_LABEL_H_
+
+#include <sys/types.h>
+#include <selinux/selinux.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * User-provided callbacks for memory, logging, locking, and extra actions
+ */
+
+/* These structures are passed by reference to label_init(). Passing
+ * a NULL reference will cause a default to be used. The default
+ * memory callbacks are malloc() and free(). The default logging method
+ * is to print on stderr. The default validation callback is based on
+ * selinux_check_context(). If no locking callbacks are passed, no locking
+ * will take place.
+ */
+ struct label_memory_callback {
+ /* malloc() equivalent. */
+ void *(*func_malloc) (size_t size);
+ /* free() equivalent. */
+ void (*func_free) (void *ptr);
+ /* Note that these functions should set errno on failure.
+ If not, some routines may return -1 without errno set. */
+ };
+
+ struct label_log_callback {
+ /* log the printf-style format and arguments,
+ with the type code indicating the type of message */
+ int (*func_log) (int type, const char *fmt, ...);
+ };
+
+ struct label_action_callback {
+ /* validate the supplied context, making changes if necessary,
+ returning 0 if valid or -1 with errno set otherwise. */
+ int (*func_validate) (security_class_t cls, char **context);
+ };
+
+ struct label_lock_callback {
+ /* create a lock and return an opaque pointer to it. */
+ void *(*func_alloc_lock) (void);
+ /* obtain a given lock, blocking if necessary. */
+ void (*func_get_lock) (void *lock);
+ /* release a given lock. */
+ void (*func_release_lock) (void *lock);
+ /* destroy a given lock (free memory, etc.) */
+ void (*func_free_lock) (void *lock);
+ };
+
+/* Logging type codes, passed to the logging callback */
+#define LABEL_ERROR 0
+#define LABEL_WARNING 1
+#define LABEL_INFO 2
+#define LABEL_INVALIDCON 3
+
+/*
+ * Label operations
+ */
+
+/**
+ * label_init - Initialize the labeling system.
+ * @prefix: used by the backend for selecting a configuration or package
+ * @mem_callbacks: user-supplied memory callbacks
+ * @log_callbacks: user-supplied logging callbacks
+ * @action_callbacks: user-supplied validation callbacks
+ * @lock_callbacks: user-supplied locking callbacks
+ *
+ * Initialize the labeling subsystem. Return %0 on success or -%1 with @errno
+ * set on failure. Argument @name must not be NULL or empty. If any callback
+ * structure references are NULL, use default methods for those callbacks (see
+ * the definition of the callback structures above).
+ */
+ int label_init(const char *prefix,
+ const struct label_memory_callback *mem_callbacks,
+ const struct label_log_callback *log_callbacks,
+ const struct label_action_callback *action_callbacks,
+ const struct label_lock_callback *lock_callbacks);
+
+/**
+ * label_change_name - Change system prefix
+ * @prefix: used by the backend for selecting a configuration or package
+ *
+ * Change the prefix string used by the labeling system on future calls. This
+ * function may be called multiple times. Backend configurations are kept
+ * separately by prefix, meaning that after a call to this function, calls to
+ * label_configure() may be necessary to re-initialize backends. However,
+ * switching back to a prefix will preserve any configuration made previously
+ * while that prefix was in effect.
+ */
+ void label_change_name(const char *prefix);
+
+/**
+ * label_configure - Configure specific labeling backend.
+ * @cls: specifies the backend to configure
+ * @flags: global and/or backend-specific configuration flags
+ * @arg: additional backend-specific configuration data
+ *
+ * Configure a specific labeling backend. This function should be called
+ * after a successful call to label_init and is optional; it will be called
+ * automatically with default arguments by label_lookup if necessary. The
+ * result of the call depends on the specific flags and the backend, although
+ * typically the backend will reset its internal state. Return %0 on success
+ * or -%1 with @errno set on failure.
+ */
+ int label_configure(security_class_t cls,
+ int flags,
+ void *arg);
+
+/* Global flags available for use on all backends. Note that the first 16
+ * bits are reserved for this purpose, while the next 16 are for backend flags.
+ */
+#define LABEL_BASEONLY 1 /* Don't use local configuration overrides */
+#define LABEL_TRANS 2 /* Translate contexts */
+#define LABEL_VALIDATE 4 /* Validate/canonicalize contexts */
+
+/**
+ * label_reset - Configure specific labeling backend using the defaults.
+ * @cls: specifies the backend to configure
+ *
+ * Configure a specific labeling backend. Equivalent to calling
+ * label_configure with a flags argument of %0 and an arg of NULL.
+ * Return %0 on success or -%1 with @errno set on failure.
+ */
+ int label_reset(security_class_t cls);
+
+/**
+ * label_destroy - Free all labeling structures.
+ *
+ * Destroy all labeling structures, close all backends, and free all allocated
+ * memory. User-supplied callbacks will be retained, but any backend
+ * configuration will not. User must call label_init() if further use
+ * of labeling subsystem is desired.
+ */
+ void label_destroy(void);
+
+/**
+ * label_lookup - Perform labeling lookup operation.
+ * @key: backend-specific string representation of object being labeled
+ * @cls: class of object being labeled; along with prefix, selects backend
+ * @con: returns the appropriate context with which to label the object
+ *
+ * Perform a labeling lookup operation. Return %0 on success, -%1 with
+ * @errno set on failure. Some backends may return additional, positive
+ * values indicating qualified success.
+ */
+ int label_lookup(const char *key, security_class_t cls,
+ security_context_t * con);
+
+/**
+ * label_cache_stats - log caching or other internal memory usage statistics.
+ * @cls: selects backend to query
+ *
+ * Log a message with information about internal caching, memory usage,
+ * table efficiency, or other such statistics. Message is backend-specific,
+ * some backends may not output a message. Intended for debugging purposes.
+ */
+ void label_cache_stats(security_class_t cls);
+
+/**
+ * label_op_stats - log labeling operation statistics.
+ * @cls: selects backend 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 label_op_stats(security_class_t cls);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SELINUX_LABEL_H_ */
Index: include/selinux/label_backend.h
===================================================================
--- include/selinux/label_backend.h (revision 0)
+++ include/selinux/label_backend.h (revision 0)
@@ -0,0 +1,20 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ * Backend-specific things are kept here.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELINUX_LABEL_BACKEND_H_
+#define _SELINUX_LABEL_BACKEND_H_
+
+/*
+ * Generic labeling backend
+ */
+/* coming soon */
+
+/*
+ * File contexts backend
+ */
+/* coming soon */
+
+#endif /* _SELINUX_LABEL_BACKEND_H_ */
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/5] libselinux: labeling API basic front-end interface
2006-11-16 2:46 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
@ 2006-11-20 15:36 ` Karl MacMillan
2006-11-27 22:23 ` Eamon Walsh
0 siblings, 1 reply; 15+ messages in thread
From: Karl MacMillan @ 2006-11-20 15:36 UTC (permalink / raw)
To: ewalsh; +Cc: selinux, sds
Eamon Walsh wrote:
> This is the basic front-end interface. Note that backends may supply
> additional values and flags, which will be seen with the file_contexts
> backend.
>
General comment - I think that providing this infrastructure is a good
idea and the basic approach is sane.
> ---
> include/selinux/label.h | 181 ++++++++++++++++++++++++
> include/selinux/label_backend.h | 20 ++
> 2 files changed, 201 insertions(+)
>
>
>
>
> ------------------------------------------------------------------------
>
> Index: include/selinux/label.h
> ===================================================================
> --- include/selinux/label.h (revision 0)
> +++ include/selinux/label.h (revision 0)
> @@ -0,0 +1,181 @@
> +/*
> + * Labeling interface for userspace object managers and others.
> + *
> + * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
> + */
> +#ifndef _SELINUX_LABEL_H_
> +#define _SELINUX_LABEL_H_
> +
> +#include <sys/types.h>
> +#include <selinux/selinux.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/*
> + * User-provided callbacks for memory, logging, locking, and extra actions
> + */
> +
> +/* These structures are passed by reference to label_init(). Passing
> + * a NULL reference will cause a default to be used. The default
> + * memory callbacks are malloc() and free(). The default logging method
> + * is to print on stderr. The default validation callback is based on
> + * selinux_check_context(). If no locking callbacks are passed, no locking
> + * will take place.
> + */
> + struct label_memory_callback {
> + /* malloc() equivalent. */
> + void *(*func_malloc) (size_t size);
> + /* free() equivalent. */
> + void (*func_free) (void *ptr);
> + /* Note that these functions should set errno on failure.
> + If not, some routines may return -1 without errno set. */
> + };
> +
Do you have an example of when this flexibility would be needed?
> + struct label_log_callback {
> + /* log the printf-style format and arguments,
> + with the type code indicating the type of message */
> + int (*func_log) (int type, const char *fmt, ...);
> + };
> +
> + struct label_action_callback {
> + /* validate the supplied context, making changes if necessary,
> + returning 0 if valid or -1 with errno set otherwise. */
> + int (*func_validate) (security_class_t cls, char **context);
> + };
> +
> + struct label_lock_callback {
> + /* create a lock and return an opaque pointer to it. */
> + void *(*func_alloc_lock) (void);
> + /* obtain a given lock, blocking if necessary. */
> + void (*func_get_lock) (void *lock);
> + /* release a given lock. */
> + void (*func_release_lock) (void *lock);
> + /* destroy a given lock (free memory, etc.) */
> + void (*func_free_lock) (void *lock);
> + };
> +
I would prefer to let clients to do locking by providing clear
guarantees on the threading properties of the functions. Any reasons why
this would be required.
Additionally, if we take this callback approach can we combine this with
the related userspace AVC callbacks? Would a single client need to
provide different callbacks for locking and memory between the labeling
and AVC interfaces?
> +/* Logging type codes, passed to the logging callback */
> +#define LABEL_ERROR 0
> +#define LABEL_WARNING 1
> +#define LABEL_INFO 2
> +#define LABEL_INVALIDCON 3
> +
> +/*
> + * Label operations
> + */
> +
> +/**
> + * label_init - Initialize the labeling system.
> + * @prefix: used by the backend for selecting a configuration or package
> + * @mem_callbacks: user-supplied memory callbacks
> + * @log_callbacks: user-supplied logging callbacks
> + * @action_callbacks: user-supplied validation callbacks
> + * @lock_callbacks: user-supplied locking callbacks
> + *
> + * Initialize the labeling subsystem. Return %0 on success or -%1 with @errno
> + * set on failure. Argument @name must not be NULL or empty. If any callback
> + * structure references are NULL, use default methods for those callbacks (see
> + * the definition of the callback structures above).
> + */
> + int label_init(const char *prefix,
> + const struct label_memory_callback *mem_callbacks,
> + const struct label_log_callback *log_callbacks,
> + const struct label_action_callback *action_callbacks,
> + const struct label_lock_callback *lock_callbacks);
> +
First - my biggest confusion here is how the backend, prefix, and label
action callbacks all relate. To me - this is not immediately clear so at
least some documentation is needed. I think, however, that it might be a
sign that the API needs some work. Can you give some more explanation.
Also, a little code showing how a client would use this API would also
be helpful.
Second - I question the storage of "subsystems" (not certain if that is
the correct term) in a global array in the library. Why not return a
handle that is managed by the client? That could also be used to make
the locking semantics clearer and allow client locking.
> +/**
> + * label_change_name - Change system prefix
> + * @prefix: used by the backend for selecting a configuration or package
> + *
> + * Change the prefix string used by the labeling system on future calls. This
> + * function may be called multiple times. Backend configurations are kept
> + * separately by prefix, meaning that after a call to this function, calls to
> + * label_configure() may be necessary to re-initialize backends. However,
> + * switching back to a prefix will preserve any configuration made previously
> + * while that prefix was in effect.
> + */
> + void label_change_name(const char *prefix);
> +
This just becomes more confusing to me - the comment says that prefixes
are "used by the backend" and then says "Backend configurations are kept
separately by prefix". OK - do prefixes refer to backends or are they
used by backends?
> +/**
> + * label_configure - Configure specific labeling backend.
> + * @cls: specifies the backend to configure
> + * @flags: global and/or backend-specific configuration flags
> + * @arg: additional backend-specific configuration data
> + *
> + * Configure a specific labeling backend. This function should be called
> + * after a successful call to label_init and is optional; it will be called
> + * automatically with default arguments by label_lookup if necessary. The
> + * result of the call depends on the specific flags and the backend, although
> + * typically the backend will reset its internal state. Return %0 on success
> + * or -%1 with @errno set on failure.
> + */
> + int label_configure(security_class_t cls,
> + int flags,
> + void *arg);
> +
I'm not a big fan of these kinds of multiplexing configuration calls.
Again, if you have a labeling handle it would be simple for backends to
provide other configuration functions that simply take that handle,
eliminating this cumbersome interface.
Karl
--
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] 15+ messages in thread
* Re: [PATCH 1/5] libselinux: labeling API basic front-end interface
2006-11-20 15:36 ` Karl MacMillan
@ 2006-11-27 22:23 ` Eamon Walsh
0 siblings, 0 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-27 22:23 UTC (permalink / raw)
To: Karl MacMillan; +Cc: selinux
On Mon, 2006-11-20 at 10:36 -0500, Karl MacMillan wrote:
> Eamon Walsh wrote:
> > This is the basic front-end interface. Note that backends may supply
> > additional values and flags, which will be seen with the file_contexts
> > backend.
> >
>
> General comment - I think that providing this infrastructure is a good
> idea and the basic approach is sane.
Thanks for the comments.
>
> > ---
> > include/selinux/label.h | 181 ++++++++++++++++++++++++
> > include/selinux/label_backend.h | 20 ++
> > 2 files changed, 201 insertions(+)
> >
> >
> >
> >
> > ------------------------------------------------------------------------
> >
> > Index: include/selinux/label.h
> > ===================================================================
> > --- include/selinux/label.h (revision 0)
> > +++ include/selinux/label.h (revision 0)
> > @@ -0,0 +1,181 @@
> > +/*
> > + * Labeling interface for userspace object managers and others.
> > + *
> > + * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
> > + */
> > +#ifndef _SELINUX_LABEL_H_
> > +#define _SELINUX_LABEL_H_
> > +
> > +#include <sys/types.h>
> > +#include <selinux/selinux.h>
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +/*
> > + * User-provided callbacks for memory, logging, locking, and extra actions
> > + */
> > +
> > +/* These structures are passed by reference to label_init(). Passing
> > + * a NULL reference will cause a default to be used. The default
> > + * memory callbacks are malloc() and free(). The default logging method
> > + * is to print on stderr. The default validation callback is based on
> > + * selinux_check_context(). If no locking callbacks are passed, no locking
> > + * will take place.
> > + */
> > + struct label_memory_callback {
> > + /* malloc() equivalent. */
> > + void *(*func_malloc) (size_t size);
> > + /* free() equivalent. */
> > + void (*func_free) (void *ptr);
> > + /* Note that these functions should set errno on failure.
> > + If not, some routines may return -1 without errno set. */
> > + };
> > +
>
> Do you have an example of when this flexibility would be needed?
Yes, the X server has an "xalloc" function that is used internally.
There have been experiments with userspace memory managers to reduce
fragmentation, although right now xalloc just wraps malloc. I'd think
that databases may do the same type of thing - I don't know, however.
>
> > + struct label_log_callback {
> > + /* log the printf-style format and arguments,
> > + with the type code indicating the type of message */
> > + int (*func_log) (int type, const char *fmt, ...);
> > + };
> > +
> > + struct label_action_callback {
> > + /* validate the supplied context, making changes if necessary,
> > + returning 0 if valid or -1 with errno set otherwise. */
> > + int (*func_validate) (security_class_t cls, char **context);
> > + };
> > +
> > + struct label_lock_callback {
> > + /* create a lock and return an opaque pointer to it. */
> > + void *(*func_alloc_lock) (void);
> > + /* obtain a given lock, blocking if necessary. */
> > + void (*func_get_lock) (void *lock);
> > + /* release a given lock. */
> > + void (*func_release_lock) (void *lock);
> > + /* destroy a given lock (free memory, etc.) */
> > + void (*func_free_lock) (void *lock);
> > + };
> > +
>
> I would prefer to let clients to do locking by providing clear
> guarantees on the threading properties of the functions. Any reasons why
> this would be required.
This is a hold-over from the userspace AVC interface. The userspace AVC
can start a background thread that listens on netlink for selinux policy
reload notifications so it can flush its cache. The locking can't be
done by the client in this case.
Compatibility with both the userspace AVC and the matchpathcon interface
played a large part in my thinking.
>
> Additionally, if we take this callback approach can we combine this with
> the related userspace AVC callbacks? Would a single client need to
> provide different callbacks for locking and memory between the labeling
> and AVC interfaces?
The callbacks and callback structures are identical, allowing reuse.
>
> > +/* Logging type codes, passed to the logging callback */
> > +#define LABEL_ERROR 0
> > +#define LABEL_WARNING 1
> > +#define LABEL_INFO 2
> > +#define LABEL_INVALIDCON 3
> > +
> > +/*
> > + * Label operations
> > + */
> > +
> > +/**
> > + * label_init - Initialize the labeling system.
> > + * @prefix: used by the backend for selecting a configuration or package
> > + * @mem_callbacks: user-supplied memory callbacks
> > + * @log_callbacks: user-supplied logging callbacks
> > + * @action_callbacks: user-supplied validation callbacks
> > + * @lock_callbacks: user-supplied locking callbacks
> > + *
> > + * Initialize the labeling subsystem. Return %0 on success or -%1 with @errno
> > + * set on failure. Argument @name must not be NULL or empty. If any callback
> > + * structure references are NULL, use default methods for those callbacks (see
> > + * the definition of the callback structures above).
> > + */
> > + int label_init(const char *prefix,
> > + const struct label_memory_callback *mem_callbacks,
> > + const struct label_log_callback *log_callbacks,
> > + const struct label_action_callback *action_callbacks,
> > + const struct label_lock_callback *lock_callbacks);
> > +
>
> First - my biggest confusion here is how the backend, prefix, and label
> action callbacks all relate. To me - this is not immediately clear so at
> least some documentation is needed. I think, however, that it might be a
> sign that the API needs some work. Can you give some more explanation.
> Also, a little code showing how a client would use this API would also
> be helpful.
I'll send out a patch to setfiles so you can see how it would be used.
The name "prefix" may not be very descriptive, I couldn't think of a
better name for it so I just kept the name from the avc_init() prefix
argument, which serves as the prefix to the AVC messages.
Anyway, this string serves as an argument to the backend's
initialization routine, which returns a handle that is stored in a
linked list. Each time the prefix string is changed with
label_change_name(), an additional handle is obtained from the backend.
So you have a mapping of:
(object class, prefix string) -> handle
where the prefix string is set with label_init()/label_change_name(),
and the object class is provided to label_configure()/label_lookup().
The reason for the prefix string is package separation; think of the
prefix string as a package name. It would allow, say, the file contexts
for each package to be shipped and installed separately, then the
installer could use the package name as the prefix at install time to
open the file contexts for just the new package and relabel them.
Another, more immediate reason for it is that there is both a
file_contexts configuration and a media_contexts configuration (from the
udev folks) both associated with object class file and I needed a way to
select between them (as can be seen by the string constants in
label_backend_file.h).
>
> Second - I question the storage of "subsystems" (not certain if that is
> the correct term) in a global array in the library. Why not return a
> handle that is managed by the client? That could also be used to make
> the locking semantics clearer and allow client locking.
The internal handle could be returned all the way out to the client. My
thinking in not doing this is that by tracking the handles internally,
there is less work that has to be done by the client code itself, which
makes it easier to justify upstream in many of the large, multi-OS
projects we are targeting such as X, Postgres, etc.
Another reason is that I was thinking of a package manager or relabeler,
that may be going through all the packages on a system or otherwise
using many prefixes, which would require keeping track of many handles.
I thought it would be easier to just track them internally.
As discussed above, the background netlink thread thing in the userspace
AVC makes it difficult to do client locking without callbacks.
>
> > +/**
> > + * label_change_name - Change system prefix
> > + * @prefix: used by the backend for selecting a configuration or package
> > + *
> > + * Change the prefix string used by the labeling system on future calls. This
> > + * function may be called multiple times. Backend configurations are kept
> > + * separately by prefix, meaning that after a call to this function, calls to
> > + * label_configure() may be necessary to re-initialize backends. However,
> > + * switching back to a prefix will preserve any configuration made previously
> > + * while that prefix was in effect.
> > + */
> > + void label_change_name(const char *prefix);
> > +
>
> This just becomes more confusing to me - the comment says that prefixes
> are "used by the backend" and then says "Backend configurations are kept
> separately by prefix". OK - do prefixes refer to backends or are they
> used by backends?
Both - see above. The prefix is passed to the backend's init function
to get a handle that is then stored in the linked list, with the prefix
as the key.
>
> > +/**
> > + * label_configure - Configure specific labeling backend.
> > + * @cls: specifies the backend to configure
> > + * @flags: global and/or backend-specific configuration flags
> > + * @arg: additional backend-specific configuration data
> > + *
> > + * Configure a specific labeling backend. This function should be called
> > + * after a successful call to label_init and is optional; it will be called
> > + * automatically with default arguments by label_lookup if necessary. The
> > + * result of the call depends on the specific flags and the backend, although
> > + * typically the backend will reset its internal state. Return %0 on success
> > + * or -%1 with @errno set on failure.
> > + */
> > + int label_configure(security_class_t cls,
> > + int flags,
> > + void *arg);
> > +
>
> I'm not a big fan of these kinds of multiplexing configuration calls.
> Again, if you have a labeling handle it would be simple for backends to
> provide other configuration functions that simply take that handle,
> eliminating this cumbersome interface.
OK, perhaps the void pointer can be removed from this call and instead,
backends can provide additional configuration functions. It's a
tradeoff between the void pointer and more exported functions in the
library.
>
> Karl
>
>
> --
> 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.
--
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] 15+ messages in thread
* [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2)
@ 2006-11-30 3:47 Eamon Walsh
2006-11-30 4:05 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
` (6 more replies)
0 siblings, 7 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 3:47 UTC (permalink / raw)
To: selinux
This is a companion interface to the userspace AVC, for use by userspace
object managers to look up contexts for use in labeling their objects.
It also provides an alternate interface to the file contexts
configuration.
In this iteration, the interface has been changed to return handles to
the user. This should clear up confusion regarding the "prefix" string
(now "name") which is now passed together with the security class to the
label_open() function that creates the handle.
Also added is the av_perm to string call requested by KaiGai Kohei.
There are two functions, security_av_perm_to_string() which does a
single bit, and security_av_string() which duplicates
print_access_vector() except returns the result in a malloc'ed buffer.
Patch 1: basic interface
Patch 2: basic implementation
Patch 3: string functions
Patch 4: simple, generic backend
Patch 5: file contexts backend
Comments welcome!
--
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] 15+ messages in thread
* [PATCH 1/5] libselinux: labeling API basic front-end interface
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
@ 2006-11-30 4:05 ` Eamon Walsh
2006-12-06 17:15 ` Karl MacMillan
2006-11-30 4:08 ` [PATCH 2/5] libselinux: labeling API basic front-end implementation Eamon Walsh
` (5 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 4:05 UTC (permalink / raw)
To: selinux
[-- Attachment #1: Type: text/plain, Size: 407 bytes --]
This is the basic front-end interface. I will shortly post some
examples of how it would be used, including a patch for setfiles.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
libselinux/include/selinux/label.h | 165 +++++++++++++
libselinux/include/selinux/label_backend.h | 20 +
2 files changed, 185 insertions(+)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: Type: text/x-patch, Size: 7145 bytes --]
Index: libselinux/include/selinux/label.h
===================================================================
--- libselinux/include/selinux/label.h (revision 0)
+++ libselinux/include/selinux/label.h (revision 0)
@@ -0,0 +1,165 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELINUX_LABEL_H_
+#define _SELINUX_LABEL_H_
+
+#include <sys/types.h>
+#include <selinux/selinux.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * User-provided callbacks for memory, logging, locking, and extra actions
+ */
+
+/* These structures are passed by reference to label_init(). Passing
+ * a NULL reference will cause a default to be used. The default
+ * memory callbacks are malloc() and free(). The default logging method
+ * is to print on stderr. The default validation callback is based on
+ * selinux_check_context(). If no locking callbacks are passed, no locking
+ * will take place.
+ */
+ struct label_memory_callback {
+ /* malloc() equivalent. */
+ void *(*func_malloc) (size_t size);
+ /* free() equivalent. */
+ void (*func_free) (void *ptr);
+ /* Note that these functions should set errno on failure.
+ If not, some routines may return -1 without errno set. */
+ };
+
+ struct label_log_callback {
+ /* log the printf-style format and arguments,
+ with the type code indicating the type of message */
+ int (*func_log) (int type, const char *fmt, ...);
+ };
+
+ struct label_action_callback {
+ /* validate the supplied context, making changes if necessary,
+ returning 0 if valid or -1 with errno set otherwise. */
+ int (*func_validate) (security_class_t cls, char **context);
+ };
+
+ struct label_lock_callback {
+ /* create a lock and return an opaque pointer to it. */
+ void *(*func_alloc_lock) (void);
+ /* obtain a given lock, blocking if necessary. */
+ void (*func_get_lock) (void *lock);
+ /* release a given lock. */
+ void (*func_release_lock) (void *lock);
+ /* destroy a given lock (free memory, etc.) */
+ void (*func_free_lock) (void *lock);
+ };
+
+/* Logging type codes, passed to the logging callback */
+#define LABEL_ERROR 0
+#define LABEL_WARNING 1
+#define LABEL_INFO 2
+#define LABEL_INVALIDCON 3
+
+/*
+ * Label operations
+ */
+
+/**
+ * label_init - Initialize the labeling system.
+ * @mem_callbacks: user-supplied memory callbacks
+ * @log_callbacks: user-supplied logging callbacks
+ * @action_callbacks: user-supplied validation callbacks
+ * @lock_callbacks: user-supplied locking callbacks
+ *
+ * Initialize the labeling subsystem. Return %0 on success or -%1 with @errno
+ * set on failure. If any callback structure references are NULL, use default
+ * methods for those callbacks (see the definition of the callback structures
+ * above).
+ */
+ int label_init(const struct label_memory_callback *mem_callbacks,
+ const struct label_log_callback *log_callbacks,
+ const struct label_action_callback *action_callbacks,
+ const struct label_lock_callback *lock_callbacks);
+
+/**
+ * label_open - Create a labeling handle.
+ * @name: used by the backend for selecting a configuration or package
+ * @cls: specifies the backend to open
+ * @flags: global and/or backend-specific configuration flags
+ * @arg: additional backend-specific configuration data if not NULL
+ *
+ * Open a labeling backend for use. The backend is identified by the specified
+ * object class in conjunction with the name argument, which is typically used
+ * to select a certain data source or subset, such as a specific package.
+ * Return value is the created handle on success or NULL with @errno set on
+ * failure. See the backend documentation for additional flags and arg value.
+ */
+ typedef struct label_rec *label_handle_t;
+ label_handle_t label_open(const char *name, security_class_t cls,
+ int flags, void *arg);
+
+/* Global flags available for use on all backends. Note that the first 16
+ * bits are reserved for this purpose, while the next 16 are for backend flags.
+ */
+#define LABEL_BASEONLY 1 /* Don't use local configuration overrides */
+#define LABEL_TRANS 2 /* Translate contexts */
+#define LABEL_VALIDATE 4 /* Validate/canonicalize contexts */
+
+/**
+ * label_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 label_close(label_handle_t handle);
+
+/**
+ * label_destroy - Close all handles and shut down labeling subsystem.
+ *
+ * Destroy all labeling structures, close all backends, and free all allocated
+ * memory. Any open handles will be destroyed and may not be further used.
+ * User must call label_init() if further use of labeling subsystem is desired.
+ */
+ void label_destroy(void);
+
+/**
+ * label_lookup - Perform labeling lookup operation.
+ * @handle: specifies backend instance to query
+ * @key: backend-specific string representation of object being labeled
+ * @cls: class of object being labeled; along with prefix, selects backend
+ * @con: returns the appropriate context with which to label the object
+ *
+ * Perform a labeling lookup operation. Return %0 on success, -%1 with
+ * @errno set on failure. Some backends may return additional, positive
+ * values indicating qualified success.
+ */
+ int label_lookup(label_handle_t handle, const char *key,
+ security_class_t cls, security_context_t *con);
+
+/**
+ * label_cache_stats - log caching or other internal memory usage statistics.
+ * @handle: specifies backend instance to query
+ *
+ * Log a message with information about internal caching, memory usage,
+ * table efficiency, or other such statistics. Message is backend-specific,
+ * some backends may not output a message. Intended for debugging purposes.
+ */
+ void label_cache_stats(label_handle_t handle);
+
+/**
+ * label_op_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 label_op_stats(label_handle_t handle);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SELINUX_LABEL_H_ */
Index: libselinux/include/selinux/label_backend.h
===================================================================
--- libselinux/include/selinux/label_backend.h (revision 0)
+++ libselinux/include/selinux/label_backend.h (revision 0)
@@ -0,0 +1,20 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ * Backend-specific things are kept here.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELINUX_LABEL_BACKEND_H_
+#define _SELINUX_LABEL_BACKEND_H_
+
+/*
+ * Generic labeling backend
+ */
+/* no include file for this backend */
+
+/*
+ * File contexts backend (includes media contexts)
+ */
+#include <selinux/label_backend_file.h>
+
+#endif /* _SELINUX_LABEL_BACKEND_H_ */
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/5] libselinux: labeling API basic front-end implementation
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
2006-11-30 4:05 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
@ 2006-11-30 4:08 ` Eamon Walsh
2006-11-30 4:15 ` [PATCH 3/5] libselinux: class and av_perm to string functions Eamon Walsh
` (4 subsequent siblings)
6 siblings, 0 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 4:08 UTC (permalink / raw)
To: selinux
[-- Attachment #1: Type: text/plain, Size: 363 bytes --]
This is the front-end implementation.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
libselinux/src/label.c | 180 ++++++++++++++++++++++++
libselinux/src/label_internal.c | 58 +++++++
libselinux/src/label_internal.h | 98 +++++++++++++
3 files changed, 336 insertions(+)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: Type: text/x-patch, Size: 8635 bytes --]
Index: libselinux/src/label_internal.h
===================================================================
--- libselinux/src/label_internal.h (revision 0)
+++ libselinux/src/label_internal.h (revision 0)
@@ -0,0 +1,98 @@
+/*
+ * 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 _SELINUX_LABEL_INTERNAL_H_
+#define _SELINUX_LABEL_INTERNAL_H_
+
+#include <stdlib.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include "dso.h"
+
+/* backend interface */
+struct label_rec {
+ /* linked list of all open handles */
+ struct label_rec *next;
+
+ /* object class, name, and flags for this instance */
+ security_class_t cls;
+ char *name;
+ int flags;
+
+ /* labeling operations */
+ int (*func_lookup) (struct label_rec *h, const char *key,
+ security_class_t cls, security_context_t *ctx);
+ void (*func_destroy) (struct label_rec *h);
+ void (*func_cache_stats) (struct label_rec *h);
+ void (*func_op_stats) (struct label_rec *h);
+
+ /* supports backend-specific state information */
+ void *data;
+};
+
+struct label_backend {
+ security_class_t cls;
+ int (*func_init) (struct label_rec *rec, void *arg);
+};
+
+/* installed backends */
+int backend_file_init(struct label_rec *rec, void *arg) hidden;
+int backend_media_init(struct label_rec *rec, void *arg) hidden;
+int backend_generic_init(struct label_rec *rec, void *arg) hidden;
+
+/* callback pointers */
+extern void *(*label_func_malloc) (size_t) hidden;
+extern void (*label_func_free) (void *) hidden;
+
+extern int (*label_func_log) (int type, const char *, ...) hidden;
+extern int (*label_func_validate) (security_class_t cls, char **ctx) hidden;
+
+extern void *(*label_func_alloc_lock) (void) hidden;
+extern void (*label_func_get_lock) (void *) hidden;
+extern void (*label_func_release_lock) (void *) hidden;
+extern void (*label_func_free_lock) (void *) hidden;
+
+/* user-supplied callback interface for avc */
+static inline void *label_malloc(size_t size)
+{
+ return label_func_malloc(size);
+}
+
+static inline void label_free(void *ptr)
+{
+ label_func_free(ptr);
+}
+
+/* this is a macro in order to use the variadic capability. */
+#define label_log(type, format...) label_func_log(type, format)
+
+static inline int label_validate(security_class_t cls, char **ctx)
+{
+ return label_func_validate(cls, ctx);
+}
+
+static inline void *label_alloc_lock(void)
+{
+ return label_func_alloc_lock();
+}
+
+static inline void label_get_lock(void *lock)
+{
+ label_func_get_lock(lock);
+}
+
+static inline void label_release_lock(void *lock)
+{
+ label_func_release_lock(lock);
+}
+
+static inline void label_free_lock(void *lock)
+{
+ label_func_free_lock(lock);
+}
+
+#endif /* _SELINUX_LABEL_INTERNAL_H_ */
Index: libselinux/src/label.c
===================================================================
--- libselinux/src/label.c (revision 0)
+++ libselinux/src/label.c (revision 0)
@@ -0,0 +1,180 @@
+/*
+ * 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 <selinux/flask.h>
+#include <selinux/label.h>
+#include "label_internal.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static struct label_rec *rec_list = NULL;
+
+static void *label_lock = NULL;
+static void *label_log_lock = NULL;
+
+struct label_backend backends[] = {
+ {SECCLASS_FILE, &backend_file_init},
+ {SECCLASS_XEXTENSION, &backend_generic_init}
+};
+
+static struct label_rec *open_backend(struct label_backend *backend,
+ const char *name, int flags, void *arg)
+{
+ int rc;
+ struct label_rec *rec = (struct label_rec *)label_malloc(sizeof(*rec));
+ if (!rec)
+ goto err1;
+
+ memset(rec, 0, sizeof(*rec));
+ rec->cls = backend->cls;
+ rec->flags = flags;
+ rec->name = strdup(name);
+ if (!rec->name)
+ goto err2;
+
+ rc = backend->func_init(rec, arg);
+ if (rc) {
+ label_log(LABEL_ERROR,
+ "failed to open labeling backend for class %d\n",
+ backend->cls);
+ goto err3;
+ }
+
+ rec->next = rec_list;
+ rec_list = rec;
+ return rec;
+
+err3:
+ label_free(rec->name);
+err2:
+ label_free(rec);
+err1:
+ return NULL;
+}
+
+int label_init(const struct label_memory_callback *mem_cb,
+ const struct label_log_callback *log_cb,
+ const struct label_action_callback *action_cb,
+ const struct label_lock_callback *lock_cb)
+{
+ if (mem_cb) {
+ label_func_malloc = mem_cb->func_malloc;
+ label_func_free = mem_cb->func_free;
+ }
+ if (log_cb) {
+ label_func_log = log_cb->func_log;
+ }
+ if (action_cb) {
+ label_func_validate = action_cb->func_validate;
+ }
+ if (lock_cb) {
+ label_func_alloc_lock = lock_cb->func_alloc_lock;
+ label_func_get_lock = lock_cb->func_get_lock;
+ label_func_release_lock = lock_cb->func_release_lock;
+ label_func_free_lock = lock_cb->func_free_lock;
+ }
+
+ label_lock = label_alloc_lock();
+ label_log_lock = label_alloc_lock();
+
+ return 0;
+}
+
+struct label_rec *label_open(const char *name, security_class_t cls,
+ int flags, void *arg)
+{
+ struct label_rec *rec = NULL;
+ unsigned int i;
+
+ if (name == NULL || name[0] == '\0') {
+ errno = EINVAL;
+ goto out;
+ }
+
+ label_get_lock(label_lock);
+
+ /* need to open backend at this point */
+ for (i = 0; i < ARRAY_SIZE(backends); i++)
+ if (backends[i].cls == cls) {
+ rec = open_backend(backends+i, name, flags, arg);
+ goto out;
+ }
+
+ /* backend not supported! this is highly irregular */
+ errno = ENOENT;
+ label_log(LABEL_ERROR,
+ "no labeling backend is available for class %d\n",
+ cls);
+
+out:
+ label_release_lock(label_lock);
+ return rec;
+}
+
+int label_lookup(struct label_rec *rec, const char *key,
+ security_class_t cls, security_context_t *con)
+{
+ return rec->func_lookup(rec, key, cls, con);
+}
+
+void label_close(struct label_rec *rec)
+{
+ label_get_lock(label_lock);
+
+ if (!rec_list)
+ return; /* list of open handles is empty! */
+
+ if (rec_list == rec)
+ rec_list = rec->next;
+ else {
+ struct label_rec *ptr = rec_list;
+ while (ptr->next && ptr->next != rec)
+ ptr = ptr->next;
+ if (!ptr->next)
+ return; /* handle not in list of open handles! */
+ ptr->next = ptr->next->next;
+ }
+
+ rec->func_destroy(rec);
+ label_free(rec->name);
+ label_free(rec);
+ label_release_lock(label_lock);
+}
+
+void label_destroy(void)
+{
+ struct label_rec *rec = rec_list;
+
+ label_get_lock(label_lock);
+
+ while (rec) {
+ rec->func_destroy(rec);
+ rec = rec->next;
+ label_free(rec->name);
+ label_free(rec);
+ }
+
+ rec_list = NULL;
+ label_release_lock(label_lock);
+
+ label_free_lock(label_lock);
+ label_free_lock(label_log_lock);
+}
+
+void label_cache_stats(struct label_rec *rec)
+{
+ rec->func_cache_stats(rec);
+}
+
+void label_op_stats(struct label_rec *rec)
+{
+ rec->func_op_stats(rec);
+}
Index: libselinux/src/label_internal.c
===================================================================
--- libselinux/src/label_internal.c (revision 0)
+++ libselinux/src/label_internal.c (revision 0)
@@ -0,0 +1,58 @@
+/*
+ * Callbacks for user-supplied memory allocation, supplemental
+ * auditing, and locking routines.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <selinux/selinux.h>
+#include "label_internal.h"
+
+/* default callbacks */
+static void def_stub(void *arg __attribute__ ((unused)))
+{
+}
+
+static void *def_alloc_lock(void)
+{
+ return NULL;
+}
+
+static int __attribute__ ((format(printf, 2, 3)))
+def_log(int type __attribute__ ((unused)),
+ const char *format, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start(ap, format);
+ rc = vfprintf(stderr, format, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+static int def_validate(security_class_t cls __attribute__ ((unused)),
+ char **ctx)
+{
+ return security_check_context(*ctx);
+}
+
+/* callback pointers */
+void *(*label_func_malloc) (size_t) = malloc;
+void (*label_func_free) (void *) = free;
+
+int (*label_func_log) (int, const char *, ...)
+ __attribute__ ((format(printf, 2, 3))) = &def_log;
+
+int (*label_func_validate) (security_class_t cls, char **ctx) = &def_validate;
+
+void *(*label_func_alloc_lock) (void) = &def_alloc_lock;
+void (*label_func_get_lock) (void *) = &def_stub;
+void (*label_func_release_lock) (void *) = &def_stub;
+void (*label_func_free_lock) (void *) = &def_stub;
+
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/5] libselinux: class and av_perm to string functions
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
2006-11-30 4:05 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
2006-11-30 4:08 ` [PATCH 2/5] libselinux: labeling API basic front-end implementation Eamon Walsh
@ 2006-11-30 4:15 ` Eamon Walsh
2006-11-30 4:19 ` [PATCH 4/5] libselinux: labeling API simple backend Eamon Walsh
` (3 subsequent siblings)
6 siblings, 0 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 4:15 UTC (permalink / raw)
To: selinux
[-- Attachment #1: Type: text/plain, Size: 382 bytes --]
New helper functions to convert from class and av_permission values to
string representations.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
libselinux/include/selinux/selinux.h | 12 +-
libselinux/src/avc.c | 99 +++++++++++++++++++
2 files changed, 109 insertions(+), 2 deletions(-)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: label3.patch --]
[-- Type: text/x-patch, Size: 3537 bytes --]
Index: libselinux/include/selinux/selinux.h
===================================================================
--- libselinux/include/selinux/selinux.h (revision 2116)
+++ libselinux/include/selinux/selinux.h (working copy)
@@ -277,13 +277,21 @@
/* Common helpers */
-/* Return the security class value for a given class name. */
+/* Convert between security class values and string names */
extern security_class_t string_to_security_class(const char *name);
+ extern const char *security_class_to_string(security_class_t cls);
-/* Return an access vector for a given class and permission name. */
+/* Convert between individual access vector permissions and string names */
+ extern const char *security_av_perm_to_string(security_class_t tclass,
+ access_vector_t perm);
extern access_vector_t string_to_av_perm(security_class_t tclass,
const char *name);
+/* Returns an access vector in a string representation. User must free the
+ * returned string via free(). */
+ extern int security_av_string(security_class_t tclass,
+ access_vector_t av, char **result);
+
/* Display an access vector in a string representation. */
extern void print_access_vector(security_class_t tclass,
access_vector_t av);
Index: libselinux/src/avc.c
===================================================================
--- libselinux/src/avc.c (revision 2116)
+++ libselinux/src/avc.c (working copy)
@@ -1338,6 +1338,105 @@
return 0;
}
+const char *security_class_to_string(security_class_t tclass)
+{
+ tclass = (tclass > 0 && tclass < NCLASSES) ? tclass : 0;
+ return class_to_string_data.str + class_to_string[tclass];
+}
+
+const char *security_av_perm_to_string(security_class_t tclass,
+ access_vector_t av)
+{
+ const u16 *common_pts_idx = 0;
+ access_vector_t common_base = 0;
+ unsigned int i;
+
+ if (!av)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(av_inherit); i++) {
+ if (av_inherit[i].tclass == tclass) {
+ common_pts_idx =
+ &common_perm_to_string.data[av_inherit[i].
+ common_pts_idx];
+ common_base = av_inherit[i].common_base;
+ break;
+ }
+ }
+
+ if (av < common_base) {
+ i = 0;
+ while (!(av & 1)) {
+ av >>= 1;
+ i++;
+ }
+ return common_perm_to_string_data.str + common_pts_idx[i];
+ }
+
+ for (i = 0; i < NVECTORS; i++) {
+ if (av_perm_to_string[i].tclass == tclass &&
+ av_perm_to_string[i].value == av)
+ return av_perm_to_string_data.str
+ + av_perm_to_string[i].nameidx;
+ }
+
+ return NULL;
+}
+
+int security_av_string(security_class_t tclass, access_vector_t av, char **res)
+{
+ unsigned int i = 0;
+ size_t len = 5;
+ access_vector_t tmp = av;
+ int rc = 0;
+ const char *str;
+ char *ptr;
+
+ /* first pass computes the required length */
+ while (tmp) {
+ if (tmp & 1) {
+ str = security_av_perm_to_string(tclass, av & (1<<i));
+ if (str)
+ len += strlen(str) + 1;
+ else {
+ rc = -1;
+ errno = EINVAL;
+ goto out;
+ }
+ }
+ tmp >>= 1;
+ i++;
+ }
+
+ *res = malloc(len);
+ if (!*res) {
+ rc = -1;
+ goto out;
+ }
+
+ /* second pass constructs the string */
+ i = 0;
+ tmp = av;
+ ptr = *res;
+
+ if (!av) {
+ sprintf(ptr, "null");
+ goto out;
+ }
+
+ ptr += sprintf(ptr, "{ ");
+ while (tmp) {
+ if (tmp & 1)
+ ptr += sprintf(ptr, "%s ", security_av_perm_to_string(
+ tclass, av & (1<<i)));
+ tmp >>= 1;
+ i++;
+ }
+ sprintf(ptr, "}");
+out:
+ return rc;
+}
+
void print_access_vector(security_class_t tclass, access_vector_t av)
{
const u16 *common_pts_idx = 0;
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/5] libselinux: labeling API simple backend
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
` (2 preceding siblings ...)
2006-11-30 4:15 ` [PATCH 3/5] libselinux: class and av_perm to string functions Eamon Walsh
@ 2006-11-30 4:19 ` Eamon Walsh
2006-11-30 4:22 ` [PATCH 5/5] libselinux: labeling API file_contexts backend Eamon Walsh
` (2 subsequent siblings)
6 siblings, 0 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 4:19 UTC (permalink / raw)
To: selinux
[-- Attachment #1: Type: text/plain, Size: 569 bytes --]
This is a simple, generic example labeling backend. It expects files
stored under "<selinux_contexts_path>/<class_name>/<prefix>.contexts"
and right now just does a simple string compare from top to bottom until
a match is found or the "*" key is encountered.
More featureful wildcarding or regexp matching can be added in the
future if needed.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
libselinux/src/label_backend_generic.c | 273 +++++++++++++++++
1 file changed, 273 insertions(+)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: Type: text/x-patch, Size: 6935 bytes --]
Index: libselinux/src/label_backend_generic.c
===================================================================
--- libselinux/src/label_backend_generic.c (revision 0)
+++ libselinux/src/label_backend_generic.c (revision 0)
@@ -0,0 +1,273 @@
+/*
+ * Generic 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 <selinux/flask.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/label_backend.h>
+#include "label_internal.h"
+
+/*
+ * Internals
+ */
+/*
+ * A context specification.
+ */
+typedef struct spec {
+ char *key_str; /* key string */
+ char *context; /* context string */
+ int context_valid; /* context string has been validated */
+ int translated; /* context string has been translated */
+ int matches; /* number of matches made during operation */
+} spec_t;
+
+struct saved_conf {
+ unsigned int nspec;
+ spec_t *spec_arr;
+};
+
+static int process_line(const char *path, char *line_buf, int pass,
+ unsigned lineno, struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ int items, len;
+ char *buf_p;
+ char *key, *context;
+
+ 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", &key, &context);
+ if (items < 2) {
+ label_log(LABEL_WARNING,
+ "%s: line %d is missing fields, skipping\n", path,
+ lineno);
+ return 0;
+ }
+
+ if (pass == 1) {
+ /* On the second pass, store the specification in spec. */
+ if (rec->flags & LABEL_VALIDATE) {
+ if (label_validate(rec->cls, &context)) {
+ label_log(LABEL_INVALIDCON,
+ "%s: line %u has invalid context %s\n",
+ path, lineno, context);
+ return 0;
+ }
+ conf->spec_arr[conf->nspec].context_valid = 1;
+ }
+ conf->spec_arr[conf->nspec].key_str = key;
+ conf->spec_arr[conf->nspec].context = context;
+ }
+
+ conf->nspec++;
+ if (pass == 0) {
+ free(key);
+ free(context);
+ }
+ return 0;
+}
+
+static int init(struct label_rec *rec, const char *path_arg)
+{
+ FILE *fp;
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ char path[PATH_MAX + 1];
+ char *line_buf = NULL;
+ size_t line_len = 0;
+ int status = -1;
+ unsigned int lineno, pass, maxnspec;
+ struct stat sb;
+
+ /* Open the specification file. */
+ if (path_arg)
+ strncpy(path, path_arg, PATH_MAX);
+ else
+ snprintf(path, PATH_MAX + 1, "%s/%s/%s.contexts",
+ selinux_contexts_path(),
+ security_class_to_string(rec->cls),
+ rec->name);
+ 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;
+ conf->nspec = 0;
+ while (getline(&line_buf, &line_len, fp) > 0
+ && conf->nspec < maxnspec) {
+ if (process_line(path, line_buf, pass, ++lineno, rec))
+ goto finish;
+ }
+ lineno = 0;
+
+ if (pass == 0) {
+ if (conf->nspec == 0) {
+ status = 0;
+ goto finish;
+ }
+ conf->spec_arr = malloc(sizeof(spec_t)*conf->nspec);
+ if (conf->spec_arr == NULL)
+ goto finish;
+ memset(conf->spec_arr, 0, sizeof(spec_t)*conf->nspec);
+ maxnspec = conf->nspec;
+ rewind(fp);
+ }
+ }
+ free(line_buf);
+
+ status = 0;
+finish:
+ fclose(fp);
+ return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void destroy(struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ struct spec *spec, *spec_arr = conf->spec_arr;
+ unsigned int i;
+
+ for (i = 0; i < conf->nspec; i++) {
+ spec = &spec_arr[i];
+ free(spec->key_str);
+ free(spec->context);
+ }
+
+ if (spec_arr)
+ free(spec_arr);
+
+ memset(conf, 0, sizeof(*conf));
+}
+
+static int lookup(struct label_rec *rec, const char *key,
+ security_class_t cls, security_context_t *con)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ unsigned int i, nspec = conf->nspec;
+ spec_t *spec_arr = conf->spec_arr;
+
+ for (i = 0; i < nspec; i++) {
+ if (!strncmp(spec_arr[i].key_str, key, strlen(key) + 1))
+ break;
+ if (!strncmp(spec_arr[i].key_str, "*", 2))
+ break;
+ }
+
+ if (i >= nspec) {
+ /* No matching specification. */
+ errno = ENOENT;
+ return -1;
+ }
+
+ spec_arr[i].matches++;
+ if (!spec_arr[i].context_valid && rec->flags & LABEL_VALIDATE) {
+ if (label_validate(cls, &spec_arr[i].context)) {
+ label_log(LABEL_INVALIDCON,
+ "%s: line %u has invalid context %s\n",
+ key, 0, spec_arr[i].context);
+ goto bad;
+ }
+ spec_arr[i].context_valid = 1;
+ }
+
+ if (!spec_arr[i].translated && rec->flags & LABEL_TRANS) {
+ 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;
+}
+
+static void cache_stats(struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ unsigned int i, total = 0;
+
+ for (i = 0; i < conf->nspec; i++) {
+ total += strlen(conf->spec_arr[i].key_str) + 1;
+ total += strlen(conf->spec_arr[i].context) + 1;
+ }
+
+ label_log(LABEL_INFO, "%s: class %s, %u entries, %u bytes used.\n",
+ rec->name, security_class_to_string(rec->cls),
+ conf->nspec, conf->nspec*sizeof(spec_t) + total);
+}
+
+static void op_stats(struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ unsigned int i, total = 0;
+
+ for (i = 0; i < conf->nspec; i++)
+ total += conf->spec_arr[i].matches;
+
+ label_log(LABEL_INFO, "%s: class %s, %u entries, %u matches made\n",
+ rec->name, security_class_to_string(rec->cls),
+ conf->nspec, total);
+}
+
+int backend_generic_init(struct label_rec *rec, void *arg)
+{
+ struct saved_conf *conf;
+
+ conf = (struct saved_conf *)label_malloc(sizeof(*conf));
+ if (!conf)
+ return -1;
+ memset(conf, 0, sizeof(*conf));
+
+ rec->data = conf;
+ rec->func_destroy = &destroy;
+ rec->func_lookup = &lookup;
+ rec->func_cache_stats = &cache_stats;
+ rec->func_op_stats = &op_stats;
+
+ return init(rec, (char *)arg);
+}
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/5] libselinux: labeling API file_contexts backend
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
` (3 preceding siblings ...)
2006-11-30 4:19 ` [PATCH 4/5] libselinux: labeling API simple backend Eamon Walsh
@ 2006-11-30 4:22 ` Eamon Walsh
2006-11-30 21:18 ` [PATCH] labeling API examples: setfiles patch and simple program Eamon Walsh
2006-12-01 2:46 ` [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Joshua Brindle
6 siblings, 0 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 4:22 UTC (permalink / raw)
To: selinux
[-- Attachment #1: Type: text/plain, Size: 390 bytes --]
This backend fully replicates the matchpathcon and matchmediacon
interfaces through the labeling front-end.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
libselinux/include/selinux/label_backend_file.h | 67
libselinux/src/label_backend_file.c | 932 ++++++++++
2 files changed, 999 insertions(+)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: Type: text/x-patch, Size: 25912 bytes --]
Index: libselinux/include/selinux/label_backend_file.h
===================================================================
--- libselinux/include/selinux/label_backend_file.h (revision 0)
+++ libselinux/include/selinux/label_backend_file.h (revision 0)
@@ -0,0 +1,67 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ * File labeling backend specific things are kept here.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELINUX_LABEL_BACKEND_FILE_H_
+#define _SELINUX_LABEL_BACKEND_FILE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Pre-defined prefix arguments for use with this backend.
+ */
+
+/* regular file contexts mode */
+#define FILE_CONTEXTS "_file_contexts"
+/* media contexts mode */
+#define MEDIA_CONTEXTS "_media_contexts"
+
+
+/*
+ * Extra configuration flags available with this backend.
+ */
+
+/* Enables inode tracking for use in relabeling operations, e.g. by setfiles */
+#define LABEL_FILE_INODE (1<<16)
+
+
+/*
+ * Additional configuration argument for this backend.
+ */
+ struct label_file_opts {
+ char *path; /* base path of configuration to use */
+ char *prefix; /* optimize lookups by using path prefix */
+ char *rootpath; /* strip this prefix from lookup keys */
+ };
+
+
+/*
+ * Extra routines for this backend
+ */
+
+/**
+ * label_file_reset_inodes - Reset inode associations when tracking inodes.
+ * @handle: labeling handle, which must be associated with the file backend
+ *
+ * This function should only be called on labeling handles associated with the
+ * file backend and with the INODE_CONTEXTS configuration. Clears the inode
+ * table in preparation for the next filesystem.
+ */
+ void label_file_reset_inodes(label_handle_t handle);
+
+
+/*
+ * Extra values returned from label_lookup().
+ */
+
+/* A previous, conflicting match was found that dominates the current one */
+#define LABEL_FILE_CONFLICT 1
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SELINUX_LABEL_BACKEND_FILE_H_ */
Index: libselinux/src/label_backend_file.c
===================================================================
--- libselinux/src/label_backend_file.c (revision 0)
+++ libselinux/src/label_backend_file.c (revision 0)
@@ -0,0 +1,932 @@
+/*
+ * 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 <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.h>
+#include <selinux/flask.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/label_backend_file.h>
+#include "label_internal.h"
+
+/*
+ * Internals, mostly moved over from matchpathcon.c
+ */
+
+/* A file security context specification. */
+typedef struct spec {
+ char *regex_str; /* regular expession string for diagnostics */
+ char *type_str; /* type string for diagnostic messages */
+ char *context; /* context string */
+ int context_valid; /* context string has been validated */
+ 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; /* regular expression has meta-chars */
+ int stem_id; /* indicates which of the stem-compression
+ * items it matches */
+} spec_t;
+
+/* An association between an inode and a specification. */
+#define HASH_BITS 16
+#define HASH_BUCKETS (1 << HASH_BITS)
+#define HASH_MASK (HASH_BUCKETS-1)
+
+typedef struct ino_spec {
+ ino_t ino; /* inode number */
+ int specind; /* index of specification in spec_arr */
+ char *file; /* full pathname for conflict messages */
+ struct ino_spec *next; /* next association in hash bucket chain */
+} ino_spec_t;
+
+/* A regular expression stem */
+typedef struct stem {
+ char *buf;
+ int len;
+} stem_t;
+
+/* Our stored configuration */
+struct saved_conf {
+ /*
+ * The chroot'ed path option
+ * path options.
+ */
+ char *rootpath;
+ unsigned int rootpathlen;
+
+ /*
+ * 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;
+
+ /*
+ * 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.
+ */
+ ino_spec_t *fl_head;
+};
+
+/* 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_conf *conf, const char **buf)
+{
+ int i, num = conf->num_stems;
+ int stem_len = get_stem_from_spec(*buf);
+
+ if (!stem_len)
+ return -1;
+ for (i = 0; i < num; i++) {
+ if (stem_len == conf->stem_arr[i].len
+ && !strncmp(*buf, conf->stem_arr[i].buf, stem_len)) {
+ *buf += stem_len;
+ return i;
+ }
+ }
+ if (conf->alloc_stems == num) {
+ stem_t *tmp_arr;
+ conf->alloc_stems = conf->alloc_stems * 2 + 16;
+ tmp_arr = realloc(conf->stem_arr,
+ sizeof(stem_t) * conf->alloc_stems);
+ if (!tmp_arr)
+ return -1;
+ conf->stem_arr = tmp_arr;
+ }
+ conf->stem_arr[num].len = stem_len;
+ conf->stem_arr[num].buf = malloc(stem_len + 1);
+ if (!conf->stem_arr[num].buf)
+ return -1;
+ memcpy(conf->stem_arr[num].buf, *buf, stem_len);
+ conf->stem_arr[num].buf[stem_len] = '\0';
+ conf->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_conf *conf, const char **buf)
+{
+ int i;
+ int stem_len = get_stem_from_file_name(*buf);
+
+ if (!stem_len)
+ return -1;
+ for (i = 0; i < conf->num_stems; i++) {
+ if (stem_len == conf->stem_arr[i].len
+ && !strncmp(*buf, conf->stem_arr[i].buf, stem_len)) {
+ *buf += stem_len;
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Warn about duplicate specifications.
+ */
+static void nodups_specs(struct saved_conf *conf, const char *path)
+{
+ unsigned int ii, jj;
+ struct spec *curr_spec, *spec_arr = conf->spec_arr;
+
+ for (ii = 0; ii < conf->nspec; ii++) {
+ curr_spec = &spec_arr[ii];
+ for (jj = ii + 1; jj < conf->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)) {
+ label_log
+ (LABEL_WARNING,
+ "%s: Multiple different specifications for %s (%s and %s).\n",
+ path, curr_spec->regex_str,
+ spec_arr[jj].context,
+ curr_spec->context);
+ } else {
+ label_log
+ (LABEL_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 mode_t mode_from_class(security_class_t cls)
+{
+ switch (cls) {
+ case SECCLASS_LNK_FILE: return S_IFLNK;
+ case SECCLASS_CHR_FILE: return S_IFCHR;
+ case SECCLASS_BLK_FILE: return S_IFBLK;
+ case SECCLASS_SOCK_FILE: return S_IFSOCK;
+ case SECCLASS_FIFO_FILE: return S_IFIFO;
+ case SECCLASS_DIR: return S_IFDIR;
+ default: return S_IFREG;
+ }
+}
+
+static int process_line(struct saved_conf *conf, int flags,
+ 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;
+ spec_t *spec_arr = conf->spec_arr;
+ unsigned int nspec = conf->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) {
+ label_log(LABEL_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(conf, ®_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);
+ label_log(LABEL_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) {
+ label_log(LABEL_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:
+ label_log(LABEL_WARNING,
+ "%s: line %d has invalid file type %s\n",
+ path, lineno, type);
+ return 0;
+ }
+
+ skip_type:
+ if (flags & LABEL_VALIDATE && strcmp(context, "<<none>>")) {
+ if (label_validate(SECCLASS_FILE, &context)) {
+ label_log(LABEL_INVALIDCON,
+ "%s: line %u has invalid context %s\n",
+ path, lineno, context);
+ return 0;
+ }
+ 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]);
+ }
+
+ conf->nspec = ++nspec;
+ if (pass == 0) {
+ free(regex);
+ if (type)
+ free(type);
+ free(context);
+ }
+ return 0;
+}
+
+static int init(struct label_rec *rec, struct label_file_opts *opts)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ const char *path, *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, flags = rec->flags;
+ struct stat sb;
+
+ /* Process arguments */
+ path = opts->path ? opts->path : NULL;
+ prefix = opts->prefix ? opts->prefix : NULL;
+ if (opts->rootpath) {
+ conf->rootpathlen = strlen(opts->rootpath);
+ conf->rootpath = strdup(opts->rootpath);
+ if (!conf->rootpath)
+ return -1;
+ }
+
+ /* 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 ((flags & LABEL_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;
+ conf->nspec = 0;
+ while (getline(&line_buf, &line_len, fp) > 0
+ && conf->nspec < maxnspec) {
+ if (process_line(conf, flags, path, prefix, line_buf,
+ pass, ++lineno) != 0)
+ goto finish;
+ }
+ lineno = 0;
+ if (homedirfp)
+ while (getline(&line_buf, &line_len, homedirfp) > 0
+ && conf->nspec < maxnspec) {
+ if (process_line
+ (conf, flags, homedir_path, prefix,
+ line_buf, pass, ++lineno) != 0)
+ goto finish;
+ }
+
+ lineno = 0;
+ if (localfp)
+ while (getline(&line_buf, &line_len, localfp) > 0
+ && conf->nspec < maxnspec) {
+ if (process_line
+ (conf, flags, local_path, prefix, line_buf,
+ pass, ++lineno) != 0)
+ goto finish;
+ }
+
+ if (pass == 0) {
+ if (conf->nspec == 0) {
+ status = 0;
+ goto finish;
+ }
+ if (NULL == (conf->spec_arr =
+ malloc(sizeof(spec_t) * conf->nspec)))
+ goto finish;
+ memset(conf->spec_arr, 0, sizeof(spec_t)*conf->nspec);
+ maxnspec = conf->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) * conf->nspec);
+ if (!spec_copy)
+ goto finish;
+ j = 0;
+ for (i = 0; i < conf->nspec; i++)
+ if (conf->spec_arr[i].hasMetaChars)
+ memcpy(&spec_copy[j++],
+ &conf->spec_arr[i], sizeof(spec_t));
+ for (i = 0; i < conf->nspec; i++)
+ if (!conf->spec_arr[i].hasMetaChars)
+ memcpy(&spec_copy[j++],
+ &conf->spec_arr[i], sizeof(spec_t));
+ free(conf->spec_arr);
+ conf->spec_arr = spec_copy;
+
+ nodups_specs(conf, path);
+
+ status = 0;
+finish:
+ fclose(fp);
+ if (conf->spec_arr != spec_copy)
+ free(conf->spec_arr);
+ if (homedirfp)
+ fclose(homedirfp);
+ if (localfp)
+ fclose(localfp);
+ return status;
+}
+
+static int lookup_common(struct label_rec *rec, const char *key, mode_t mode)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ int i, rc, file_stem;
+ const char *buf = key;
+ spec_t *spec_arr = conf->spec_arr;
+
+ if (!conf->nspec) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ file_stem = find_stem_from_file(conf, &buf);
+
+ mode &= S_IFMT;
+
+ /*
+ * Check for matching specifications in reverse order, so that
+ * the last matching specification is used.
+ */
+ for (i = conf->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)
+ rc = regexec(&spec_arr[i].regex, key, 0, 0, 0);
+ else
+ rc = regexec(&spec_arr[i].regex, buf, 0, 0, 0);
+ if (rc == 0)
+ break;
+
+ if (rc == 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;
+}
+
+/*
+ * Try to add an association between an inode and
+ * a specification. If there is already an association
+ * for the inode and it conflicts with this specification,
+ * then use the specification that occurs later in the
+ * specification array.
+ */
+static int filespec_add(struct saved_conf *conf, ino_t ino,
+ int specind, const char *file)
+{
+ ino_spec_t *prevfl, *fl;
+ int h, no_conflict, ret;
+ struct stat sb;
+ spec_t *spec_arr = conf->spec_arr;
+
+ if (!conf->fl_head) {
+ conf->fl_head = malloc(sizeof(ino_spec_t) * HASH_BUCKETS);
+ if (!conf->fl_head)
+ goto oom;
+ memset(conf->fl_head, 0, sizeof(ino_spec_t) * HASH_BUCKETS);
+ }
+
+ h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
+ for (prevfl = &conf->fl_head[h], fl = conf->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) {
+ fl->specind = specind;
+ free(fl->file);
+ fl->file = malloc(strlen(file) + 1);
+ if (!fl->file)
+ goto oom;
+ strcpy(fl->file, file);
+ return fl->specind;
+
+ }
+
+ no_conflict =
+ (strcmp
+ (spec_arr[fl->specind].context,
+ spec_arr[specind].context) == 0);
+ if (no_conflict)
+ return fl->specind;
+
+ label_log(LABEL_WARNING,
+ "%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;
+ free(fl->file);
+ fl->file = malloc(strlen(file) + 1);
+ if (!fl->file)
+ goto oom;
+ strcpy(fl->file, file);
+ return fl->specind;
+ }
+
+ if (ino > fl->ino)
+ break;
+ }
+
+ fl = malloc(sizeof(ino_spec_t));
+ if (!fl)
+ goto oom;
+ fl->ino = ino;
+ fl->specind = specind;
+ fl->file = malloc(strlen(file) + 1);
+ if (!fl->file)
+ goto oom_freefl;
+ strcpy(fl->file, file);
+ fl->next = prevfl->next;
+ prevfl->next = fl;
+ return fl->specind;
+oom_freefl:
+ free(fl);
+oom:
+ label_log(LABEL_ERROR,
+ "%s: insufficient memory for file label entry for %s\n",
+ __FUNCTION__, file);
+ return -1;
+}
+
+/* Destroy the association hash table. */
+static void filespec_destroy(struct saved_conf *conf)
+{
+ ino_spec_t *fl, *tmp;
+ int h;
+
+ if (!conf->fl_head)
+ return;
+
+ for (h = 0; h < HASH_BUCKETS; h++) {
+ fl = conf->fl_head[h].next;
+ while (fl) {
+ tmp = fl;
+ fl = fl->next;
+ free(tmp->file);
+ free(tmp);
+ }
+ conf->fl_head[h].next = NULL;
+ }
+ free(conf->fl_head);
+ conf->fl_head = NULL;
+}
+
+/*
+ * Backend interface routines
+ */
+static void destroy(struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ struct spec *spec;
+ struct stem *stem;
+ unsigned int i;
+
+ filespec_destroy(conf);
+
+ for (i = 0; i < conf->nspec; i++) {
+ spec = &conf->spec_arr[i];
+ free(spec->regex_str);
+ free(spec->type_str);
+ free(spec->context);
+ regfree(&spec->regex);
+ }
+
+ for (i = 0; i < (unsigned int)conf->num_stems; i++) {
+ stem = &conf->stem_arr[i];
+ free(stem->buf);
+ }
+
+ if (conf->spec_arr)
+ free(conf->spec_arr);
+ if (conf->stem_arr)
+ free(conf->stem_arr);
+ if (conf->rootpath)
+ free(conf->rootpath);
+
+ memset(conf, 0, sizeof(*conf));
+}
+
+static int lookup_index(struct label_rec *rec, const char *name,
+ security_class_t cls __attribute__ ((unused)),
+ security_context_t *con)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ int i, rc;
+ struct stat sb;
+ const char *fullname = name;
+
+ /* fullname will be the real file that gets labeled
+ * name will be what is matched in the policy */
+ if (NULL != conf->rootpath) {
+ if (0 != strncmp(conf->rootpath, name, conf->rootpathlen)) {
+ fprintf(stderr, "%s is not located in %s\n",
+ name, conf->rootpath);
+ return -1;
+ }
+ name += conf->rootpathlen;
+ }
+ if(lstat(fullname, &sb)) {
+ label_log(LABEL_ERROR, "unable to stat file %s\n", fullname);
+ return -1;
+ }
+ if (conf->rootpath != NULL && name[0] == '\0')
+ /* this is actually the root dir of the alt root */
+ name = "/";
+
+ i = lookup_common(rec, name, sb.st_mode);
+ if (i < 0)
+ return -1;
+
+ rc = filespec_add(conf, sb.st_ino, i, fullname);
+ if (rc < 0)
+ return rc;
+ if (rc > 0 && rc != i)
+ return LABEL_FILE_CONFLICT;
+
+ *con = strdup(conf->spec_arr[i].context);
+ if (!(*con))
+ return -1;
+
+ return 0;
+}
+
+static int lookup(struct label_rec *rec, const char *key,
+ security_class_t cls, security_context_t *con)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ spec_t *spec_arr = conf->spec_arr;
+ int i = lookup_common(rec, key, mode_from_class(cls));
+ if (i < 0)
+ return -1;
+
+ if (strcmp(spec_arr[i].context, "<<none>>") == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (!spec_arr[i].context_valid) {
+ if (label_validate(SECCLASS_FILE, &spec_arr[i].context)) {
+ label_log(LABEL_INVALIDCON,
+ "%s: line %u has invalid context %s\n",
+ key, 0, spec_arr[i].context);
+ goto bad;
+ }
+ spec_arr[i].context_valid = 1;
+ }
+
+ if (!spec_arr[i].translated && rec->flags & LABEL_TRANS) {
+ 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;
+}
+
+static void cache_stats(struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ ino_spec_t *fl;
+ int h, used, nel, len, longest;
+
+ if (!conf->fl_head)
+ return;
+
+ used = 0;
+ longest = 0;
+ nel = 0;
+ for (h = 0; h < HASH_BUCKETS; h++) {
+ len = 0;
+ for (fl = conf->fl_head[h].next; fl; fl = fl->next) {
+ len++;
+ }
+ if (len)
+ used++;
+ if (len > longest)
+ longest = len;
+ nel += len;
+ }
+
+ label_log(LABEL_INFO,
+ "%s: hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
+ __FUNCTION__, nel, used, HASH_BUCKETS, longest);
+}
+
+static void op_stats(struct label_rec *rec)
+{
+ struct saved_conf *conf = (struct saved_conf *)rec->data;
+ unsigned int i, nspec = conf->nspec;
+ spec_t *spec_arr = conf->spec_arr;
+
+ for (i = 0; i < nspec; i++) {
+ if (spec_arr[i].matches == 0) {
+ if (spec_arr[i].type_str) {
+ label_log(LABEL_WARNING,
+ "Warning! No matches for (%s, %s, %s)\n",
+ spec_arr[i].regex_str,
+ spec_arr[i].type_str, spec_arr[i].context);
+ } else {
+ label_log(LABEL_WARNING,
+ "Warning! No matches for (%s, %s)\n",
+ spec_arr[i].regex_str,
+ spec_arr[i].context);
+ }
+ }
+ }
+}
+
+int backend_file_init(struct label_rec *rec, void *arg)
+{
+ struct saved_conf *conf;
+ struct label_file_opts defaults = {NULL, NULL, NULL};
+
+ /* redirect media contexts configuration to the media backend */
+ if (!strncmp(rec->name, MEDIA_CONTEXTS, sizeof(MEDIA_CONTEXTS)))
+ return backend_media_init(rec, arg);
+
+ /*
+ * at this point it is assumed that rec->name is FILE_CONTEXTS;
+ * something special could be done here by package name, etc.
+ */
+ conf = (struct saved_conf *)label_malloc(sizeof(*conf));
+ if (!conf)
+ return -1;
+ memset(conf, 0, sizeof(*conf));
+
+ rec->data = conf;
+ rec->func_destroy = &destroy;
+ rec->func_cache_stats = &cache_stats;
+ rec->func_op_stats = &op_stats;
+ rec->func_lookup = (rec->flags & LABEL_FILE_INODE) ?
+ &lookup_index : &lookup;
+
+ return init(rec, arg ? (struct label_file_opts *)arg : &defaults);
+}
+
+/*
+ * Backend public routines
+ */
+void label_file_reset_inodes(struct label_rec *rec)
+{
+ filespec_destroy((struct saved_conf *)rec->data);
+}
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] labeling API examples: setfiles patch and simple program
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
` (4 preceding siblings ...)
2006-11-30 4:22 ` [PATCH 5/5] libselinux: labeling API file_contexts backend Eamon Walsh
@ 2006-11-30 21:18 ` Eamon Walsh
2006-12-01 2:46 ` [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Joshua Brindle
6 siblings, 0 replies; 15+ messages in thread
From: Eamon Walsh @ 2006-11-30 21:18 UTC (permalink / raw)
To: selinux
[-- Attachment #1: Type: text/plain, Size: 451 bytes --]
A patch to setfiles allowing it to use the new labeling API. Should be
compatible with existing behavior.
Also attached is a simple usage example program, displaying how a
userspace object manager might use it.
Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
---
policycoreutils/setfiles/setfiles.c | 127 ++++++--------------
1 file changed, 40 insertions(+), 87 deletions(-)
--
Eamon Walsh <ewalsh@tycho.nsa.gov>
National Security Agency
[-- Attachment #2: Type: text/x-patch, Size: 6441 bytes --]
Index: policycoreutils/setfiles/setfiles.c
===================================================================
--- policycoreutils/setfiles/setfiles.c (revision 2116)
+++ policycoreutils/setfiles/setfiles.c (working copy)
@@ -66,14 +66,13 @@
#include <stdio_ext.h>
#include <string.h>
#include <errno.h>
-#include <ctype.h>
-#include <regex.h>
-#include <sys/vfs.h>
#define __USE_XOPEN_EXTENDED 1 /* nftw */
#include <ftw.h>
-#include <limits.h>
#include <sepol/sepol.h>
+#include <selinux/flask.h>
#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/label_backend.h>
#include <syslog.h>
#include <libgen.h>
#ifdef USE_AUDIT
@@ -84,7 +83,6 @@
#endif
#endif
-static int add_assoc = 1;
static FILE *outfile = NULL;
static int force = 0;
#define STAT_BLOCK_SIZE 1
@@ -112,7 +110,7 @@
static int log = 0;
static int warn_no_match = 0;
static char *rootpath = NULL;
-static int rootpathlen = 0;
+static label_handle_t labelh;
static char *progname;
@@ -181,41 +179,6 @@
return 0;
}
-int match(const char *name, struct stat *sb, char **con)
-{
- int ret;
- const char *fullname = name;
-
- /* fullname will be the real file that gets labeled
- * name will be what is matched in the policy */
- if (NULL != rootpath) {
- if (0 != strncmp(rootpath, name, rootpathlen)) {
- fprintf(stderr, "%s: %s is not located in %s\n",
- progname, name, rootpath);
- return -1;
- }
- name += rootpathlen;
- }
-
- if (excludeCtr > 0) {
- if (exclude(fullname)) {
- return -1;
- }
- }
- ret = lstat(fullname, sb);
- if (ret) {
- fprintf(stderr, "%s: unable to stat file %s\n", progname,
- fullname);
- return -1;
- }
-
- if (rootpath != NULL && name[0] == '\0')
- /* this is actually the root dir of the alt root */
- return matchpathcon_index("/", sb->st_mode, con);
- else
- return matchpathcon_index(name, sb->st_mode, con);
-}
-
void usage(const char *const name)
{
fprintf(stderr,
@@ -263,10 +226,8 @@
int flag, struct FTW *s_unused __attribute__ ((unused)))
{
const char *my_file;
- struct stat my_sb;
- int i, j, ret;
char *context, *newcon;
- int user_only_changed = 0;
+ int ret, user_only_changed = 0;
char buf[STAT_BLOCK_SIZE];
if (pipe_fds[0] != -1
&& read(pipe_fds[0], buf, STAT_BLOCK_SIZE) != STAT_BLOCK_SIZE) {
@@ -286,10 +247,15 @@
return 0;
}
- i = match(my_file, &my_sb, &newcon);
- if (i < 0)
- /* No matching specification. */
+ if (excludeCtr > 0 && exclude(my_file))
return 0;
+ ret = label_lookup(labelh, my_file, SECCLASS_FILE, &newcon);
+ if (ret < 0)
+ /* No matching specification on ENOENT, otherwise error */
+ return (errno == ENOENT) ? 0 : -1;
+ if (ret == LABEL_FILE_CONFLICT)
+ /* There was already an association and it took precedence. */
+ return 0;
if (progress) {
count++;
@@ -303,23 +269,6 @@
}
}
- /*
- * Try to add an association between this inode and
- * this specification. If there is already an association
- * for this inode and it conflicts with this specification,
- * then use the last matching specification.
- */
- if (add_assoc) {
- j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file);
- if (j < 0)
- goto err;
-
- if (j != i) {
- /* There was already an association and it took precedence. */
- goto out;
- }
- }
-
if (debug) {
printf("%s: %s matched by %s\n", progname, my_file, newcon);
}
@@ -428,10 +377,9 @@
len = strlen(rootpath);
while (len && ('/' == rootpath[len - 1]))
rootpath[--len] = 0;
- rootpathlen = len;
}
-int canoncon(const char *path, unsigned lineno, char **contextp)
+int canoncon(security_class_t cls __attribute__ ((unused)), char **contextp)
{
char *context = *contextp, *tmpcon;
int valid = 1;
@@ -448,14 +396,9 @@
*contextp = tmpcon;
}
- if (!valid) {
- fprintf(stderr, "%s: line %u has invalid context %s\n",
- path, lineno, context);
-
+ if (!valid && policyfile)
/* Exit immediately if we're in checking mode. */
- if (policyfile)
- exit(1);
- }
+ exit(1);
return !valid;
}
@@ -507,13 +450,15 @@
int main(int argc, char **argv)
{
struct stat sb;
- int opt, rc, i;
+ int opt, rc, i, flags, add_assoc = 1;
int done_root = 0; /* have we processed the / directory as an arg */
+ struct label_file_opts opts;
+ struct label_action_callback actions;
memset(excludeArray, 0, sizeof(excludeArray));
- /* Validate all file contexts during matchpathcon_init. */
- set_matchpathcon_flags(MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS);
+ /* Validate all file contexts. */
+ flags = LABEL_VALIDATE;
/* Process any options. */
while ((opt = getopt(argc, argv, "Fc:dlnpqrsvWe:o:")) > 0) {
@@ -546,9 +491,7 @@
/* 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);
+ flags |= LABEL_BASEONLY;
break;
}
@@ -640,7 +583,12 @@
/* 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);
+ actions.func_validate = &canoncon;
+ rc = label_init(NULL, NULL, &actions, NULL);
+ if (rc < 0) {
+ perror("label_init");
+ exit(1);
+ }
if (stat(argv[optind], &sb) < 0) {
perror(argv[optind]);
@@ -653,8 +601,14 @@
}
/* Load the file contexts configuration and check it. */
- rc = matchpathcon_init(argv[optind]);
- if (rc < 0) {
+ if (add_assoc)
+ flags |= LABEL_FILE_INODE;
+
+ opts.path = argv[optind];
+ opts.prefix = NULL;
+ opts.rootpath = rootpath;
+ labelh = label_open(FILE_CONTEXTS, SECCLASS_FILE, flags, &opts);
+ if (!labelh) {
perror(argv[optind]);
exit(1);
}
@@ -737,18 +691,17 @@
* Evaluate the association hash table distribution for the
* directory tree just traversed.
*/
- set_matchpathcon_printf(&qprintf);
- matchpathcon_filespec_eval();
- set_matchpathcon_printf(NULL);
+ if (!quiet)
+ label_cache_stats(labelh);
/* Reset the association hash table for the next directory tree. */
- matchpathcon_filespec_destroy();
+ label_file_reset_inodes(labelh);
}
maybe_audit_mass_relabel(done_root, 0);
if (warn_no_match)
- matchpathcon_checkmatches(argv[0]);
+ label_op_stats(labelh);
if (outfile)
fclose(outfile);
[-- Attachment #3: Type: text/x-csrc, Size: 611 bytes --]
#include <stdlib.h>
#include <stdio.h>
#include <selinux/flask.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
static label_handle_t h;
int main(void) {
int rc;
security_context_t con;
/* at startup time */
label_init(NULL, NULL, NULL, NULL);
h = label_open("xorg-x11-server", SECCLASS_XEXTENSION, 0, NULL);
/* at labeling time */
rc = label_lookup(h, "my_name_I_know_about", SECCLASS_XEXTENSION, &con);
if (rc < 0)
exit(1);
else
myobj.label = security_context_to_sid(con); /* whatever */
/* at shutdown time */
label_destroy();
return 0;
}
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2)
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
` (5 preceding siblings ...)
2006-11-30 21:18 ` [PATCH] labeling API examples: setfiles patch and simple program Eamon Walsh
@ 2006-12-01 2:46 ` Joshua Brindle
2006-12-01 17:04 ` Karl MacMillan
6 siblings, 1 reply; 15+ messages in thread
From: Joshua Brindle @ 2006-12-01 2:46 UTC (permalink / raw)
To: ewalsh; +Cc: selinux
Eamon Walsh wrote:
> This is a companion interface to the userspace AVC, for use by userspace
> object managers to look up contexts for use in labeling their objects.
> It also provides an alternate interface to the file contexts
> configuration.
>
>
If we go forward with this do we really expect every object manager that
has context matching more complicated than exact matches to upstream
changes to libselinux? This doesn't seem to scale well.. Policy server
would need a backend, do you know if dbus and X would need new backends?
I still don't think this is the right approach, LDAP and rdbms's, for
example, would likely have their initial contexts in the schema.
I can think of few object managers that this scheme works with. Things
like groupware apps, chat servers, mail servers, etc are going to have
labeling done at runtime (based on who creates an object or where it
comes from), databases will certainly store contexts in their schema,
etc. This is a pretty large change to make the file context interface a
little prettier..
--
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] 15+ messages in thread
* Re: [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2)
2006-12-01 2:46 ` [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Joshua Brindle
@ 2006-12-01 17:04 ` Karl MacMillan
2006-12-01 21:24 ` Eamon Walsh
0 siblings, 1 reply; 15+ messages in thread
From: Karl MacMillan @ 2006-12-01 17:04 UTC (permalink / raw)
To: Joshua Brindle; +Cc: ewalsh, selinux
Joshua Brindle wrote:
> Eamon Walsh wrote:
>> This is a companion interface to the userspace AVC, for use by userspace
>> object managers to look up contexts for use in labeling their objects.
>> It also provides an alternate interface to the file contexts
>> configuration.
>>
>>
> If we go forward with this do we really expect every object manager that
> has context matching more complicated than exact matches to upstream
> changes to libselinux? This doesn't seem to scale well.. Policy server
> would need a backend, do you know if dbus and X would need new backends?
> I still don't think this is the right approach, LDAP and rdbms's, for
> example, would likely have their initial contexts in the schema.
>
I agree that this would be problematic - very specific libselinux
dependencies are going to be a nightmare for distributions. Seems like
it should be possible to have a callback style api that would allow
sufficient customization for almost all object managers.
> I can think of few object managers that this scheme works with. Things
> like groupware apps, chat servers, mail servers, etc are going to have
> labeling done at runtime (based on who creates an object or where it
> comes from), databases will certainly store contexts in their schema,
> etc. This is a pretty large change to make the file context interface a
> little prettier..
>
It this only works for file contexts then it doesn't seem worth doing,
but I think that there is hope that it can be made to work more generally.
Karl
--
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] 15+ messages in thread
* Re: [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2)
2006-12-01 17:04 ` Karl MacMillan
@ 2006-12-01 21:24 ` Eamon Walsh
2006-12-02 3:36 ` Joshua Brindle
0 siblings, 1 reply; 15+ messages in thread
From: Eamon Walsh @ 2006-12-01 21:24 UTC (permalink / raw)
To: Karl MacMillan; +Cc: Joshua Brindle, selinux, kaigai
On Fri, 2006-12-01 at 12:04 -0500, Karl MacMillan wrote:
> Joshua Brindle wrote:
> > If we go forward with this do we really expect every object manager that
> > has context matching more complicated than exact matches to upstream
> > changes to libselinux? This doesn't seem to scale well.. Policy server
> > would need a backend, do you know if dbus and X would need new backends?
> > I still don't think this is the right approach, LDAP and rdbms's, for
> > example, would likely have their initial contexts in the schema.
Exact string matching with a default fallback match meets the needs of
both dbus and X in their current forms. However I've said that I
support adding regex matching to the generic backend as well. I really
think that this will satisfy the requirements in most cases. The only
major holdup I can think of is where there might be an arbitrary number
of keys involved (see database discussion below).
> >
>
> I agree that this would be problematic - very specific libselinux
> dependencies are going to be a nightmare for distributions. Seems like
> it should be possible to have a callback style api that would allow
> sufficient customization for almost all object managers.
I will try to work in such callbacks on the next go-round. Thanks for
the suggestion.
>
> > I can think of few object managers that this scheme works with. Things
> > like groupware apps, chat servers, mail servers, etc are going to have
> > labeling done at runtime (based on who creates an object or where it
> > comes from),
security_compute_create() can be used to do the labeling in the case
where you have a related object. The proposed interface is _only_ for
the case where security_compute_create() is not sufficient because
additional meaning is vested in some string name, path, key, etc.
> databases will certainly store contexts in their schema, etc.
What if you change policies and your database needs to be relabeled?
What if you are importing bulk data? It's just like the filesystem,
there will have to be a "setfiles" equivalent built into the DBMS, and
for each row, some mapping from the column contents to the security
context for that row.
This brings up the point that perhaps lookups should support more than
one key, and perhaps numeric keys as well as string keys. KaiGai, do
you have any input based on the Postgres work?
--
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] 15+ messages in thread
* Re: [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2)
2006-12-01 21:24 ` Eamon Walsh
@ 2006-12-02 3:36 ` Joshua Brindle
0 siblings, 0 replies; 15+ messages in thread
From: Joshua Brindle @ 2006-12-02 3:36 UTC (permalink / raw)
To: ewalsh; +Cc: Karl MacMillan, selinux, kaigai
Eamon Walsh wrote:
> On Fri, 2006-12-01 at 12:04 -0500, Karl MacMillan wrote:
>> Joshua Brindle wrote:
>>> If we go forward with this do we really expect every object manager that
>>> has context matching more complicated than exact matches to upstream
>>> changes to libselinux? This doesn't seem to scale well.. Policy server
>>> would need a backend, do you know if dbus and X would need new backends?
>>> I still don't think this is the right approach, LDAP and rdbms's, for
>>> example, would likely have their initial contexts in the schema.
>
> Exact string matching with a default fallback match meets the needs of
> both dbus and X in their current forms. However I've said that I
> support adding regex matching to the generic backend as well. I really
> think that this will satisfy the requirements in most cases. The only
> major holdup I can think of is where there might be an arbitrary number
> of keys involved (see database discussion below).
>
unfortunately policy server couldn't even use regex, it uses a special
labeling scheme that determines specificity based on the type hierarchy
(dots). I'm not saying that a single object manager justifies ditching
this but I keep coming back to the fact that object managers own their
labels, period. It is up to them how they want to store, manage, etc them.
>> I agree that this would be problematic - very specific libselinux
>> dependencies are going to be a nightmare for distributions. Seems like
>> it should be possible to have a callback style api that would allow
>> sufficient customization for almost all object managers.
>
> I will try to work in such callbacks on the next go-round. Thanks for
> the suggestion.
>
I agree that callbacks are better, I still disagree with the notion that
this is the correct route.
>>> I can think of few object managers that this scheme works with. Things
>>> like groupware apps, chat servers, mail servers, etc are going to have
>>> labeling done at runtime (based on who creates an object or where it
>>> comes from),
>
> security_compute_create() can be used to do the labeling in the case
> where you have a related object. The proposed interface is _only_ for
> the case where security_compute_create() is not sufficient because
> additional meaning is vested in some string name, path, key, etc.
>
Yes, security_compute_create() would be used, but the labels would be
stored in the object manager somewhere (on disk db, memory, etc).
>> databases will certainly store contexts in their schema, etc.
>
> What if you change policies and your database needs to be relabeled?
> What if you are importing bulk data? It's just like the filesystem,
> there will have to be a "setfiles" equivalent built into the DBMS, and
> for each row, some mapping from the column contents to the security
> context for that row.
>
This is tricky, setfiles is generally used to relabel system files,
running it on user files is not suggested because of customizations
made, etc (and in fact we have labeling exceptions for custom labels
anyway). Running an equivalent on a database would result in a major
loss of state (who created what, what levels rows are, etc). I think
databases are going to need something more sophisticated than blind
relabeling via an initial contexts file.
> This brings up the point that perhaps lookups should support more than
> one key, and perhaps numeric keys as well as string keys. KaiGai, do
> you have any input based on the Postgres work?
>
>
what would supporting more than one key help?
--
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] 15+ messages in thread
* Re: [PATCH 1/5] libselinux: labeling API basic front-end interface
2006-11-30 4:05 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
@ 2006-12-06 17:15 ` Karl MacMillan
0 siblings, 0 replies; 15+ messages in thread
From: Karl MacMillan @ 2006-12-06 17:15 UTC (permalink / raw)
To: ewalsh; +Cc: selinux, James Athey
Eamon Walsh wrote:
> This is the basic front-end interface. I will shortly post some
> examples of how it would be used, including a patch for setfiles.
>
> Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
>
> ---
> libselinux/include/selinux/label.h | 165 +++++++++++++
> libselinux/include/selinux/label_backend.h | 20 +
> 2 files changed, 185 insertions(+)
>
>
General comments:
Don't these functions and types need a better prefix (selinux_*)? Well -
I don't quite understand the current prefixing, but we need to decide on
something moving forward.
What is your plan for maintaining the old interfaces?
Have you seen the FCGlob suggestions from James Athey? How would that
fit in with that work (if it is actually needed) and is this the right
time to introduce the FCGlob syntax?
>
>
> ------------------------------------------------------------------------
>
> Index: libselinux/include/selinux/label.h
> ===================================================================
> --- libselinux/include/selinux/label.h (revision 0)
> +++ libselinux/include/selinux/label.h (revision 0)
> @@ -0,0 +1,165 @@
> +/*
> + * Labeling interface for userspace object managers and others.
> + *
> + * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
> + */
> +#ifndef _SELINUX_LABEL_H_
> +#define _SELINUX_LABEL_H_
> +
> +#include <sys/types.h>
> +#include <selinux/selinux.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/*
> + * User-provided callbacks for memory, logging, locking, and extra actions
> + */
> +
> +/* These structures are passed by reference to label_init(). Passing
> + * a NULL reference will cause a default to be used. The default
> + * memory callbacks are malloc() and free(). The default logging method
> + * is to print on stderr. The default validation callback is based on
> + * selinux_check_context(). If no locking callbacks are passed, no locking
> + * will take place.
> + */
> + struct label_memory_callback {
> + /* malloc() equivalent. */
> + void *(*func_malloc) (size_t size);
> + /* free() equivalent. */
> + void (*func_free) (void *ptr);
> + /* Note that these functions should set errno on failure.
> + If not, some routines may return -1 without errno set. */
> + };
> +
> + struct label_log_callback {
> + /* log the printf-style format and arguments,
> + with the type code indicating the type of message */
> + int (*func_log) (int type, const char *fmt, ...);
> + };
> +
> + struct label_action_callback {
> + /* validate the supplied context, making changes if necessary,
> + returning 0 if valid or -1 with errno set otherwise. */
> + int (*func_validate) (security_class_t cls, char **context);
> + };
> +
> + struct label_lock_callback {
> + /* create a lock and return an opaque pointer to it. */
> + void *(*func_alloc_lock) (void);
> + /* obtain a given lock, blocking if necessary. */
> + void (*func_get_lock) (void *lock);
> + /* release a given lock. */
> + void (*func_release_lock) (void *lock);
> + /* destroy a given lock (free memory, etc.) */
> + void (*func_free_lock) (void *lock);
> + };
> +
> +/* Logging type codes, passed to the logging callback */
> +#define LABEL_ERROR 0
> +#define LABEL_WARNING 1
> +#define LABEL_INFO 2
> +#define LABEL_INVALIDCON 3
> +
> +/*
> + * Label operations
> + */
> +
> +/**
> + * label_init - Initialize the labeling system.
> + * @mem_callbacks: user-supplied memory callbacks
> + * @log_callbacks: user-supplied logging callbacks
> + * @action_callbacks: user-supplied validation callbacks
> + * @lock_callbacks: user-supplied locking callbacks
> + *
> + * Initialize the labeling subsystem. Return %0 on success or -%1 with @errno
> + * set on failure. If any callback structure references are NULL, use default
> + * methods for those callbacks (see the definition of the callback structures
> + * above).
> + */
> + int label_init(const struct label_memory_callback *mem_callbacks,
> + const struct label_log_callback *log_callbacks,
> + const struct label_action_callback *action_callbacks,
> + const struct label_lock_callback *lock_callbacks);
> +
I'm still not convinced about these.
1) libselinux does lots of allocations with plain malloc - why are these
special and need client control over how they are done?
2) For locking - why can't we just mark which functions for each handle
are re-entrant and which are not? That way the caller can protect them
with whatever locks are needed (including reader / writer locks if we
mark them correctly). There are other parts of this library that are
already sort-of this way (fscontext setting for example) and it seems
simpler.
3) The logging and action callbacks seem - potentially - handle
specific. Is global for the library really the right answer?
2) If the memory and locking callbacks are truly needed, then should
they be library wide (for both the avc and the labeling). It seems
unlikely that those are going to be need to be different. The existing
avc symbols can be preserved, but new global ones introduced as well.
> +/**
> + * label_open - Create a labeling handle.
> + * @name: used by the backend for selecting a configuration or package
> + * @cls: specifies the backend to open
> + * @flags: global and/or backend-specific configuration flags
> + * @arg: additional backend-specific configuration data if not NULL
> + *
> + * Open a labeling backend for use. The backend is identified by the specified
> + * object class in conjunction with the name argument, which is typically used
> + * to select a certain data source or subset, such as a specific package.
> + * Return value is the created handle on success or NULL with @errno set on
> + * failure. See the backend documentation for additional flags and arg value.
> + */
> + typedef struct label_rec *label_handle_t;
> + label_handle_t label_open(const char *name, security_class_t cls,
> + int flags, void *arg);
> +
I'm still confused about name and cls and how they relate. Is it likely
that most object managers are going to get labeling decisions for a
single object class? Your setfiles example passes in SECCLASS_FILE for
the class, but setfiles receives answers for dirs, link files, etc. in
addition to plain files. Is the example wrong or is just one class
chosen arbitrarily to represent the backend? What if I want to label
file objects differently than file contexts for a specific object manager?
What happens if I pass in the same name for a different object class? Is
a different file opened? How does name get mapped to different files?
Will that require library changes?
This all - to me - goes right to the heart of Josh's criticism. How can
this mechanism be extended without making changes to the library? To me,
mixing up choosing which database to open (or file contexts file) with
the mechanism for matching is just going to cause problems in the
future. It seems simpler to pass in a fd or buffer with the data and
tell the library what format that data is in. Then all of this name /
cls choosing stuff can just go away, right?
If we are going to allow backends to have additional configuration
functions taking the handles, is the void *arg needed?
I'm going to leave off comments about the implementation until we reach
some resolution about the above.
Karl
--
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] 15+ messages in thread
end of thread, other threads:[~2006-12-06 17:15 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-11-30 3:47 [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Eamon Walsh
2006-11-30 4:05 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
2006-12-06 17:15 ` Karl MacMillan
2006-11-30 4:08 ` [PATCH 2/5] libselinux: labeling API basic front-end implementation Eamon Walsh
2006-11-30 4:15 ` [PATCH 3/5] libselinux: class and av_perm to string functions Eamon Walsh
2006-11-30 4:19 ` [PATCH 4/5] libselinux: labeling API simple backend Eamon Walsh
2006-11-30 4:22 ` [PATCH 5/5] libselinux: labeling API file_contexts backend Eamon Walsh
2006-11-30 21:18 ` [PATCH] labeling API examples: setfiles patch and simple program Eamon Walsh
2006-12-01 2:46 ` [PATCH 0/5] libselinux: labeling API for userspace object managers (try 2) Joshua Brindle
2006-12-01 17:04 ` Karl MacMillan
2006-12-01 21:24 ` Eamon Walsh
2006-12-02 3:36 ` Joshua Brindle
-- strict thread matches above, loose matches on Subject: below --
2006-11-16 2:25 [PATCH 0/5] libselinux: labeling API for userspace object managers Eamon Walsh
2006-11-16 2:46 ` [PATCH 1/5] libselinux: labeling API basic front-end interface Eamon Walsh
2006-11-20 15:36 ` Karl MacMillan
2006-11-27 22:23 ` Eamon Walsh
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.