Project import
diff --git a/libselinux/Android.bp b/libselinux/Android.bp
new file mode 100644
index 0000000..9c4b1d4
--- /dev/null
+++ b/libselinux/Android.bp
@@ -0,0 +1,119 @@
+common_LIBRARIES = ["libpcre2"]
+common_CFLAGS = [
+ "-DUSE_PCRE2",
+
+ // Persistently stored patterns (pcre2) are architecture dependent.
+ // In particular paterns built on amd64 can not run on devices with armv7
+ // (32bit). Therefore, this feature stays off for now.
+ "-DNO_PERSISTENTLY_STORED_PATTERNS",
+]
+
+// uncomment to build libselinux and related artifacts against PCRE
+// common_LIBRARIES = ["libpcre"]
+// common_CFLAGS = []
+
+cc_defaults {
+ name: "libselinux_flags",
+
+ cflags: common_CFLAGS,
+
+ target: {
+ host: {
+ cflags: ["-DHOST"],
+ },
+ darwin: {
+ cflags: ["-DDARWIN"],
+ },
+ },
+}
+
+cc_library {
+ name: "libselinux",
+ defaults: ["libselinux_flags"],
+ host_supported: true,
+
+ srcs: [
+ "src/callbacks.c",
+ "src/check_context.c",
+ "src/freecon.c",
+ "src/init.c",
+ "src/label.c",
+ "src/label_file.c",
+ "src/label_android_property.c",
+ "src/regex.c",
+ "src/label_support.c",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "src/booleans.c",
+ "src/canonicalize_context.c",
+ "src/disable.c",
+ "src/enabled.c",
+ "src/fgetfilecon.c",
+ "src/fsetfilecon.c",
+ "src/getenforce.c",
+ "src/getfilecon.c",
+ "src/getpeercon.c",
+ "src/lgetfilecon.c",
+ "src/load_policy.c",
+ "src/lsetfilecon.c",
+ "src/policyvers.c",
+ "src/procattr.c",
+ "src/setenforce.c",
+ "src/setfilecon.c",
+ "src/context.c",
+ "src/mapping.c",
+ "src/stringrep.c",
+ "src/compute_create.c",
+ "src/compute_av.c",
+ "src/avc.c",
+ "src/avc_internal.c",
+ "src/avc_sidtab.c",
+ "src/get_initial_context.c",
+ "src/checkAccess.c",
+ "src/sestatus.c",
+ "src/deny_unknown.c",
+
+ "src/android.c",
+ ],
+
+ shared_libs: [
+ "libcrypto",
+ "liblog",
+ ],
+ static: {
+ whole_static_libs: ["libpackagelistparser"],
+ },
+ shared: {
+ shared_libs: ["libpackagelistparser"],
+ },
+
+ // 1003 corresponds to auditd, from system/core/logd/event.logtags
+ cflags: ["-DAUDITD_LOG_TAG=1003"],
+ // mapping.c has redundant check of array p_in->perms.
+ clang_cflags: ["-Wno-pointer-bool-conversion"],
+ },
+ },
+
+ static: {
+ whole_static_libs: common_LIBRARIES,
+ },
+ shared: {
+ shared_libs: common_LIBRARIES,
+ },
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+//################################
+cc_binary_host {
+ name: "sefcontext_compile",
+ defaults: ["libselinux_flags"],
+ srcs: ["utils/sefcontext_compile.c"],
+
+ static_libs: ["libselinux"],
+ whole_static_libs: common_LIBRARIES,
+}
diff --git a/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN b/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN
diff --git a/libselinux/NOTICE b/libselinux/NOTICE
new file mode 100644
index 0000000..d386268
--- /dev/null
+++ b/libselinux/NOTICE
@@ -0,0 +1,21 @@
+This library (libselinux) is public domain software, i.e. not copyrighted.
+
+Warranty Exclusion
+------------------
+You agree that this software is a
+non-commercially developed program that may contain "bugs" (as that
+term is used in the industry) and that it may not function as intended.
+The software is licensed "as is". NSA makes no, and hereby expressly
+disclaims all, warranties, express, implied, statutory, or otherwise
+with respect to the software, including noninfringement and the implied
+warranties of merchantability and fitness for a particular purpose.
+
+Limitation of Liability
+-----------------------
+In no event will NSA be liable for any damages, including loss of data,
+lost profits, cost of cover, or other special, incidental,
+consequential, direct or indirect damages arising from the software or
+the use thereof, however caused and on any theory of liability. This
+limitation will apply even if NSA has been advised of the possibility
+of such damage. You acknowledge that this is a reasonable allocation of
+risk.
diff --git a/libselinux/README.android b/libselinux/README.android
new file mode 100644
index 0000000..e01f889
--- /dev/null
+++ b/libselinux/README.android
@@ -0,0 +1,62 @@
+This directory contains a small port of libselinux for Android.
+It was originally forked in mid-2011, circa libselinux 2.1.0.
+Some changes have been cherry-picked from the upstream libselinux.
+Upstream git repository is https://github.com/SELinuxProject/selinux
+(libselinux subdirectory) and official releases are available from
+https://github.com/SELinuxProject/selinux/wiki/Releases.
+
+This fork differs from upstream libselinux in at least the following ways:
+
+* Dependencies on glibc-specific features have been removed/replaced
+in order to work with bionic,
+
+* Legacy code and compatibility interfaces have been removed,
+
+* Many interfaces, functions, and files are omitted since they are
+unused in Android,
+
+* The python bindings are omitted since they are unused in Android,
+
+* The setrans (context translation) support has been removed since
+there is no need for MLS label translation in Android and the support
+imposes extra overhead on calls passing security contexts,
+
+* The SELinux policy files are all located in / rather than under
+/etc/selinux since /etc is not available in Android until /system
+is mounted and use fixed paths, not dependent on /etc/selinux/config,
+
+* The kernel policy file (sepolicy in Android, policy.N in Linux) does
+not include a version suffix since Android does not need to support
+booting multiple kernels,
+
+* The policy loading logic does not support automatic downgrading of
+the kernel policy file to a version known to the kernel, since this
+requires libsepol on the device and is only needed to support mixing
+and matching kernels and userspace easily,
+
+* The selabel interface and label_file backend have been extended to
+support label-by-symlink and partial matching support for use by ueventd
+in labeling device nodes based on stable symlink names and by init for
+optimizing its restorecon_recursive of /sys,
+
+* Since the fork, upstream libselinux has switched the label_file
+backend to use a binary version of the file_contexts file
+(file_contexts.bin) that contains precompiled versions of the pcre
+regexes. This reduces the time to load the file_contexts
+configuration, which in Linux can be significant due to the large
+number of entries (> 5000). As Android has far fewer entries (~400),
+this has not yet seemed necessary.
+
+* restorecon functionality, including recursive restorecon, has been
+fully implemented within new libselinux functions, along with optimizations
+to prune the tree walk if no change has occurred in file_contexts since
+the last restorecon,
+
+* Support for new Android-specific SELinux configuration files, such
+as seapp_contexts, property_contexts, and service_contexts, has been
+added.
+
+New files added for Android:
+* libselinux/include/selinux/android.h
+* libselinux/src/android.c
+* libselinux/src/label_android_property.c (later added upstream)
diff --git a/libselinux/include/selinux/android.h b/libselinux/include/selinux/android.h
new file mode 100644
index 0000000..21a41a0
--- /dev/null
+++ b/libselinux/include/selinux/android.h
@@ -0,0 +1,56 @@
+#ifndef _SELINUX_ANDROID_H_
+#define _SELINUX_ANDROID_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <selinux/label.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct selabel_handle* selinux_android_file_context_handle(void);
+
+extern struct selabel_handle* selinux_android_prop_context_handle(void);
+
+extern struct selabel_handle* selinux_android_service_context_handle(void);
+
+extern void selinux_android_set_sehandle(const struct selabel_handle *hndl);
+
+extern int selinux_android_load_policy(void);
+
+extern int selinux_android_setcon(const char *con);
+
+extern int selinux_android_setcontext(uid_t uid,
+ bool isSystemServer,
+ const char *seinfo,
+ const char *name);
+
+extern int selinux_android_setfilecon(const char *pkgdir,
+ const char *pkgname,
+ const char *seinfo,
+ uid_t uid);
+
+extern int selinux_log_callback(int type, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+#define SELINUX_ANDROID_RESTORECON_NOCHANGE 1
+#define SELINUX_ANDROID_RESTORECON_VERBOSE 2
+#define SELINUX_ANDROID_RESTORECON_RECURSE 4
+#define SELINUX_ANDROID_RESTORECON_FORCE 8
+#define SELINUX_ANDROID_RESTORECON_DATADATA 16
+extern int selinux_android_restorecon(const char *file, unsigned int flags);
+
+extern int selinux_android_restorecon_pkgdir(const char *pkgdir,
+ const char *seinfo,
+ uid_t uid,
+ unsigned int flags);
+
+extern int selinux_android_seapp_context_reload(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libselinux/include/selinux/avc.h b/libselinux/include/selinux/avc.h
new file mode 100644
index 0000000..8a1a6df
--- /dev/null
+++ b/libselinux/include/selinux/avc.h
@@ -0,0 +1,507 @@
+/*
+ * Access vector cache interface for object managers.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_H_
+#define _SELINUX_AVC_H_
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <selinux/selinux.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SID format and operations
+ */
+struct security_id {
+ char * ctx;
+ unsigned int refcnt;
+};
+typedef struct security_id *security_id_t;
+
+#define SECSID_WILD (security_id_t)NULL /* unspecified SID */
+
+/**
+ * avc_sid_to_context - get copy of context corresponding to SID.
+ * @sid: input SID
+ * @ctx: pointer to context reference
+ *
+ * Return a copy of the security context corresponding to the input
+ * @sid in the memory referenced by @ctx. The caller is expected to
+ * free the context with freecon(). Return %0 on success, -%1 on
+ * failure, with @errno set to %ENOMEM if insufficient memory was
+ * available to make the copy, or %EINVAL if the input SID is invalid.
+ */
+int avc_sid_to_context(security_id_t sid, char ** ctx);
+int avc_sid_to_context_raw(security_id_t sid, char ** ctx);
+
+/**
+ * avc_context_to_sid - get SID for context.
+ * @ctx: input security context
+ * @sid: pointer to SID reference
+ *
+ * Look up security context @ctx in SID table, making
+ * a new entry if @ctx is not found. Increment the
+ * reference counter for the SID. Store a pointer
+ * to the SID structure into the memory referenced by @sid,
+ * returning %0 on success or -%1 on error with @errno set.
+ */
+int avc_context_to_sid(const char * ctx, security_id_t * sid);
+int avc_context_to_sid_raw(const char * ctx, security_id_t * sid);
+
+/**
+ * sidget - increment SID reference counter.
+ * @sid: SID reference
+ *
+ * Increment the reference counter for @sid, indicating that
+ * @sid is in use by an (additional) object. Return the
+ * new reference count, or zero if @sid is invalid (has zero
+ * reference count). Note that avc_context_to_sid() also
+ * increments reference counts.
+ */
+int sidget(security_id_t sid);
+
+/**
+ * sidput - decrement SID reference counter.
+ * @sid: SID reference
+ *
+ * Decrement the reference counter for @sid, indicating that
+ * a reference to @sid is no longer in use. Return the
+ * new reference count. When the reference count reaches
+ * zero, the SID is invalid, and avc_context_to_sid() must
+ * be called to obtain a new SID for the security context.
+ */
+int sidput(security_id_t sid);
+
+/**
+ * avc_get_initial_sid - get SID for an initial kernel security identifier
+ * @name: input name of initial kernel security identifier
+ * @sid: pointer to a SID reference
+ *
+ * Get the context for an initial kernel security identifier specified by
+ * @name using security_get_initial_context() and then call
+ * avc_context_to_sid() to get the corresponding SID.
+ */
+int avc_get_initial_sid(const char *name, security_id_t * sid);
+
+/*
+ * AVC entry
+ */
+struct avc_entry;
+struct avc_entry_ref {
+ struct avc_entry *ae;
+};
+
+/**
+ * avc_entry_ref_init - initialize an AVC entry reference.
+ * @aeref: pointer to avc entry reference structure
+ *
+ * Use this macro to initialize an avc entry reference structure
+ * before first use. These structures are passed to avc_has_perm(),
+ * which stores cache entry references in them. They can increase
+ * performance on repeated queries.
+ */
+#define avc_entry_ref_init(aeref) ((aeref)->ae = NULL)
+
+/*
+ * User-provided callbacks for memory, auditing, and locking
+ */
+
+/* These structures are passed by reference to avc_init(). Passing
+ * a NULL reference will cause the AVC to use a default. The default
+ * memory callbacks are malloc() and free(). The default logging method
+ * is to print on stderr. If no thread callbacks are passed, a separate
+ * listening thread won't be started for kernel policy change messages.
+ * If no locking callbacks are passed, no locking will take place.
+ */
+struct avc_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 avc routines may return -1 without errno set. */
+};
+
+struct avc_log_callback {
+ /* log the printf-style format and arguments. */
+ void (*func_log) (const char *fmt, ...);
+ /* store a string representation of auditdata (corresponding
+ to the given security class) into msgbuf. */
+ void (*func_audit) (void *auditdata, security_class_t cls,
+ char *msgbuf, size_t msgbufsize);
+};
+
+struct avc_thread_callback {
+ /* create and start a thread, returning an opaque pointer to it;
+ the thread should run the given function. */
+ void *(*func_create_thread) (void (*run) (void));
+ /* cancel a given thread and free its resources. */
+ void (*func_stop_thread) (void *thread);
+};
+
+struct avc_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);
+};
+
+/*
+ * Available options
+ */
+
+/* no-op option, useful for unused slots in an array of options */
+#define AVC_OPT_UNUSED 0
+/* override kernel enforcing mode (boolean value) */
+#define AVC_OPT_SETENFORCE 1
+
+/*
+ * AVC operations
+ */
+
+/**
+ * avc_init - Initialize the AVC.
+ * @msgprefix: prefix for log messages
+ * @mem_callbacks: user-supplied memory callbacks
+ * @log_callbacks: user-supplied logging callbacks
+ * @thread_callbacks: user-supplied threading callbacks
+ * @lock_callbacks: user-supplied locking callbacks
+ *
+ * Initialize the access vector cache. Return %0 on
+ * success or -%1 with @errno set on failure.
+ * If @msgprefix is NULL, use "uavc". If any callback
+ * structure references are NULL, use default methods
+ * for those callbacks (see the definition of the callback
+ * structures above).
+ */
+int avc_init(const char *msgprefix,
+ const struct avc_memory_callback *mem_callbacks,
+ const struct avc_log_callback *log_callbacks,
+ const struct avc_thread_callback *thread_callbacks,
+ const struct avc_lock_callback *lock_callbacks);
+
+/**
+ * avc_open - Initialize the AVC.
+ * @opts: array of selabel_opt structures specifying AVC options or NULL.
+ * @nopts: number of elements in opts array or zero for no options.
+ *
+ * This function is identical to avc_init(), except the message prefix
+ * is set to "avc" and any callbacks desired should be specified via
+ * selinux_set_callback(). Available options are listed above.
+ */
+int avc_open(struct selinux_opt *opts, unsigned nopts);
+
+/**
+ * avc_cleanup - Remove unused SIDs and AVC entries.
+ *
+ * Search the SID table for SID structures with zero
+ * reference counts, and remove them along with all
+ * AVC entries that reference them. This can be used
+ * to return memory to the system.
+ */
+void avc_cleanup(void);
+
+/**
+ * avc_reset - Flush the cache and reset statistics.
+ *
+ * Remove all entries from the cache and reset all access
+ * statistics (as returned by avc_cache_stats()) to zero.
+ * The SID mapping is not affected. Return %0 on success,
+ * -%1 with @errno set on error.
+ */
+int avc_reset(void);
+
+/**
+ * avc_destroy - Free all AVC structures.
+ *
+ * Destroy all AVC structures and free all allocated
+ * memory. User-supplied locking, memory, and audit
+ * callbacks will be retained, but security-event
+ * callbacks will not. All SID's will be invalidated.
+ * User must call avc_init() if further use of AVC is desired.
+ */
+void avc_destroy(void);
+
+/**
+ * avc_has_perm_noaudit - Check permissions but perform no auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @aeref: AVC entry reference
+ * @avd: access vector decisions
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache. Update @aeref to refer to an AVC
+ * entry with the resulting decisions, and return a copy of the decisions
+ * in @avd. Return %0 if all @requested permissions are granted, -%1 with
+ * @errno set to %EACCES if any permissions are denied, or to another value
+ * upon other errors. This function is typically called by avc_has_perm(),
+ * but may also be called directly to separate permission checking from
+ * auditing, e.g. in cases where a lock must be held for the check but
+ * should be released for the auditing.
+ */
+int avc_has_perm_noaudit(security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass,
+ access_vector_t requested,
+ struct avc_entry_ref *aeref, struct av_decision *avd);
+
+/**
+ * avc_has_perm - Check permissions and perform any appropriate auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @aeref: AVC entry reference
+ * @auditdata: auxiliary audit data
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache. Update @aeref to refer to an AVC
+ * entry with the resulting decisions. Audit the granting or denial of
+ * permissions in accordance with the policy. Return %0 if all @requested
+ * permissions are granted, -%1 with @errno set to %EACCES if any permissions
+ * are denied or to another value upon other errors.
+ */
+int avc_has_perm(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t requested,
+ struct avc_entry_ref *aeref, void *auditdata);
+
+/**
+ * avc_audit - Audit the granting or denial of permissions.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions
+ * @avd: access vector decisions
+ * @result: result from avc_has_perm_noaudit
+ * @auditdata: auxiliary audit data
+ *
+ * Audit the granting or denial of permissions in accordance
+ * with the policy. This function is typically called by
+ * avc_has_perm() after a permission check, but can also be
+ * called directly by callers who use avc_has_perm_noaudit()
+ * in order to separate the permission check from the auditing.
+ * For example, this separation is useful when the permission check must
+ * be performed under a lock, to allow the lock to be released
+ * before calling the auditing code.
+ */
+void avc_audit(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t requested,
+ struct av_decision *avd, int result, void *auditdata);
+
+/**
+ * avc_compute_create - Compute SID for labeling a new object.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @newsid: pointer to SID reference
+ *
+ * Call the security server to obtain a context for labeling a
+ * new object. Look up the context in the SID table, making
+ * a new entry if not found. Increment the reference counter
+ * for the SID. Store a pointer to the SID structure into the
+ * memory referenced by @newsid, returning %0 on success or -%1 on
+ * error with @errno set.
+ */
+int avc_compute_create(security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass, security_id_t * newsid);
+
+/**
+ * avc_compute_member - Compute SID for polyinstantation.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @newsid: pointer to SID reference
+ *
+ * Call the security server to obtain a context for labeling an
+ * object instance. Look up the context in the SID table, making
+ * a new entry if not found. Increment the reference counter
+ * for the SID. Store a pointer to the SID structure into the
+ * memory referenced by @newsid, returning %0 on success or -%1 on
+ * error with @errno set.
+ */
+int avc_compute_member(security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass, security_id_t * newsid);
+
+/*
+ * security event callback facility
+ */
+
+/* security events */
+#define AVC_CALLBACK_GRANT 1
+#define AVC_CALLBACK_TRY_REVOKE 2
+#define AVC_CALLBACK_REVOKE 4
+#define AVC_CALLBACK_RESET 8
+#define AVC_CALLBACK_AUDITALLOW_ENABLE 16
+#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
+#define AVC_CALLBACK_AUDITDENY_ENABLE 64
+#define AVC_CALLBACK_AUDITDENY_DISABLE 128
+
+/**
+ * avc_add_callback - Register a callback for security events.
+ * @callback: callback function
+ * @events: bitwise OR of desired security events
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions
+ *
+ * Register a callback function for events in the set @events
+ * related to the SID pair (@ssid, @tsid) and
+ * and the permissions @perms, interpreting
+ * @perms based on @tclass. Returns %0 on success or
+ * -%1 if insufficient memory exists to add the callback.
+ */
+int avc_add_callback(int (*callback)
+ (uint32_t event, security_id_t ssid,
+ security_id_t tsid, security_class_t tclass,
+ access_vector_t perms,
+ access_vector_t * out_retained),
+ uint32_t events, security_id_t ssid,
+ security_id_t tsid, security_class_t tclass,
+ access_vector_t perms);
+
+/*
+ * AVC statistics
+ */
+
+/* If set, cache statistics are tracked. This may
+ * become a compile-time option in the future.
+ */
+#define AVC_CACHE_STATS 1
+
+struct avc_cache_stats {
+ unsigned entry_lookups;
+ unsigned entry_hits;
+ unsigned entry_misses;
+ unsigned entry_discards;
+ unsigned cav_lookups;
+ unsigned cav_hits;
+ unsigned cav_probes;
+ unsigned cav_misses;
+};
+
+/**
+ * avc_cache_stats - get cache access statistics.
+ * @stats: reference to statistics structure
+ *
+ * Fill the supplied structure with information about AVC
+ * activity since the last call to avc_init() or
+ * avc_reset(). See the structure definition for
+ * details.
+ */
+void avc_cache_stats(struct avc_cache_stats *stats);
+
+/**
+ * avc_av_stats - log av table statistics.
+ *
+ * Log a message with information about the size and
+ * distribution of the access vector table. The audit
+ * callback is used to print the message.
+ */
+void avc_av_stats(void);
+
+/**
+ * avc_sid_stats - log SID table statistics.
+ *
+ * Log a message with information about the size and
+ * distribution of the SID table. The audit callback
+ * is used to print the message.
+ */
+void avc_sid_stats(void);
+
+/**
+ * avc_netlink_open - Create a netlink socket and connect to the kernel.
+ */
+int avc_netlink_open(int blocking);
+
+/**
+ * avc_netlink_loop - Wait for netlink messages from the kernel
+ */
+void avc_netlink_loop(void);
+
+/**
+ * avc_netlink_close - Close the netlink socket
+ */
+void avc_netlink_close(void);
+
+/**
+ * avc_netlink_acquire_fd - Acquire netlink socket fd.
+ *
+ * Allows the application to manage messages from the netlink socket in
+ * its own main loop.
+ */
+int avc_netlink_acquire_fd(void);
+
+/**
+ * avc_netlink_release_fd - Release netlink socket fd.
+ *
+ * Returns ownership of the netlink socket to the library.
+ */
+void avc_netlink_release_fd(void);
+
+/**
+ * avc_netlink_check_nb - Check netlink socket for new messages.
+ *
+ * Called by the application when using avc_netlink_acquire_fd() to
+ * process kernel netlink events.
+ */
+int avc_netlink_check_nb(void);
+
+/**
+ * selinux_status_open - Open and map SELinux kernel status page
+ *
+ */
+int selinux_status_open(int fallback);
+
+/**
+ * selinux_status_close - Unmap and close SELinux kernel status page
+ *
+ */
+void selinux_status_close(void);
+
+/**
+ * selinux_status_updated - Inform us whether the kernel status has been updated
+ *
+ */
+int selinux_status_updated(void);
+
+/**
+ * selinux_status_getenforce - Get the enforce flag value
+ *
+ */
+int selinux_status_getenforce(void);
+
+/**
+ * selinux_status_policyload - Get the number of policy reloaded
+ *
+ */
+int selinux_status_policyload(void);
+
+/**
+ * selinux_status_deny_unknown - Get the behavior for undefined classes/permissions
+ *
+ */
+int selinux_status_deny_unknown(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SELINUX_AVC_H_ */
diff --git a/libselinux/include/selinux/context.h b/libselinux/include/selinux/context.h
new file mode 100644
index 0000000..949fb1e
--- /dev/null
+++ b/libselinux/include/selinux/context.h
@@ -0,0 +1,50 @@
+#ifndef _SELINUX_CONTEXT_H_
+#define _SELINUX_CONTEXT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Functions to deal with security contexts in user space.
+ */
+
+ typedef struct {
+ void *ptr;
+ } context_s_t;
+
+ typedef context_s_t *context_t;
+
+/* Return a new context initialized to a context string */
+
+ extern context_t context_new(const char *);
+
+/*
+ * Return a pointer to the string value of the context_t
+ * Valid until the next call to context_str or context_free
+ * for the same context_t*
+ */
+
+ extern char *context_str(context_t);
+
+/* Free the storage used by a context */
+ extern void context_free(context_t);
+
+/* Get a pointer to the string value of a context component */
+
+ extern const char *context_type_get(context_t);
+ extern const char *context_range_get(context_t);
+ extern const char *context_role_get(context_t);
+ extern const char *context_user_get(context_t);
+
+/* Set a context component. Returns nonzero if unsuccessful */
+
+ extern int context_type_set(context_t, const char *);
+ extern int context_range_set(context_t, const char *);
+ extern int context_role_set(context_t, const char *);
+ extern int context_user_set(context_t, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libselinux/include/selinux/label.h b/libselinux/include/selinux/label.h
new file mode 100644
index 0000000..512c71f
--- /dev/null
+++ b/libselinux/include/selinux/label.h
@@ -0,0 +1,165 @@
+/*
+ * Labeling interface for userspace object managers and others.
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+#ifndef _SELABEL_H_
+#define _SELABEL_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <selinux/selinux.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Opaque type used for all label handles.
+ */
+
+struct selabel_handle;
+
+/*
+ * Available backends.
+ */
+
+/* file contexts */
+#define SELABEL_CTX_FILE 0
+/* media contexts */
+#define SELABEL_CTX_MEDIA 1
+/* x contexts */
+#define SELABEL_CTX_X 2
+/* db objects */
+#define SELABEL_CTX_DB 3
+/* Android property service contexts */
+#define SELABEL_CTX_ANDROID_PROP 4
+
+/*
+ * Available options
+ */
+
+/* no-op option, useful for unused slots in an array of options */
+#define SELABEL_OPT_UNUSED 0
+/* validate contexts before returning them (boolean value) */
+#define SELABEL_OPT_VALIDATE 1
+/* don't use local customizations to backend data (boolean value) */
+#define SELABEL_OPT_BASEONLY 2
+/* specify an alternate path to use when loading backend data */
+#define SELABEL_OPT_PATH 3
+/* select a subset of the search space as an optimization (file backend) */
+#define SELABEL_OPT_SUBSET 4
+/* total number of options */
+#define SELABEL_NOPT 5
+
+/*
+ * Label operations
+ */
+
+/**
+ * selabel_open - Create a labeling handle.
+ * @backend: one of the constants specifying a supported labeling backend.
+ * @opts: array of selabel_opt structures specifying label options or NULL.
+ * @nopts: number of elements in opts array or zero for no options.
+ *
+ * Open a labeling backend for use. The available backend identifiers are
+ * listed above. Options may be provided via the opts parameter; available
+ * options are listed above. Not all options may be supported by every
+ * backend. Return value is the created handle on success or NULL with
+ * @errno set on failure.
+ */
+struct selabel_handle *selabel_open(unsigned int backend,
+ const struct selinux_opt *opts,
+ unsigned nopts);
+
+/**
+ * selabel_close - Close a labeling handle.
+ * @handle: specifies handle to close
+ *
+ * Destroy the specified handle, closing files, freeing allocated memory,
+ * etc. The handle may not be further used after it has been closed.
+ */
+void selabel_close(struct selabel_handle *handle);
+
+/**
+ * selabel_lookup - Perform labeling lookup operation.
+ * @handle: specifies backend instance to query
+ * @con: returns the appropriate context with which to label the object
+ * @key: string input to lookup operation
+ * @type: numeric input to the lookup operation
+ *
+ * Perform a labeling lookup operation. Return %0 on success, -%1 with
+ * @errno set on failure. The key and type arguments are the inputs to the
+ * lookup operation; appropriate values are dictated by the backend in use.
+ * The result is returned in the memory pointed to by @con and must be freed
+ * by the user with freecon().
+ */
+int selabel_lookup(struct selabel_handle *handle, char **con,
+ const char *key, int type);
+int selabel_lookup_raw(struct selabel_handle *handle, char **con,
+ const char *key, int type);
+
+bool selabel_partial_match(struct selabel_handle *handle, const char *key);
+
+int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
+ const char *key, const char **aliases, int type);
+
+enum selabel_cmp_result {
+ SELABEL_SUBSET,
+ SELABEL_EQUAL,
+ SELABEL_SUPERSET,
+ SELABEL_INCOMPARABLE
+};
+
+/**
+ * selabel_cmp - Compare two label configurations.
+ * @h1: handle for the first label configuration
+ * @h2: handle for the first label configuration
+ *
+ * Compare two label configurations.
+ * Return %SELABEL_SUBSET if @h1 is a subset of @h2, %SELABEL_EQUAL
+ * if @h1 is identical to @h2, %SELABEL_SUPERSET if @h1 is a superset
+ * of @h2, and %SELABEL_INCOMPARABLE if @h1 and @h2 are incomparable.
+ */
+enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
+ struct selabel_handle *h2);
+
+/**
+ * selabel_stats - log labeling operation statistics.
+ * @handle: specifies backend instance to query
+ *
+ * Log a message with information about the number of queries performed,
+ * number of unused matching entries, or other operational statistics.
+ * Message is backend-specific, some backends may not output a message.
+ */
+void selabel_stats(struct selabel_handle *handle);
+
+/*
+ * Type codes used by specific backends
+ */
+
+/* X backend */
+#define SELABEL_X_PROP 1
+#define SELABEL_X_EXT 2
+#define SELABEL_X_CLIENT 3
+#define SELABEL_X_EVENT 4
+#define SELABEL_X_SELN 5
+#define SELABEL_X_POLYPROP 6
+#define SELABEL_X_POLYSELN 7
+
+/* DB backend */
+#define SELABEL_DB_DATABASE 1
+#define SELABEL_DB_SCHEMA 2
+#define SELABEL_DB_TABLE 3
+#define SELABEL_DB_COLUMN 4
+#define SELABEL_DB_SEQUENCE 5
+#define SELABEL_DB_VIEW 6
+#define SELABEL_DB_PROCEDURE 7
+#define SELABEL_DB_BLOB 8
+#define SELABEL_DB_TUPLE 9
+#define SELABEL_DB_LANGUAGE 10
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SELABEL_H_ */
diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
new file mode 100644
index 0000000..8827da8
--- /dev/null
+++ b/libselinux/include/selinux/selinux.h
@@ -0,0 +1,291 @@
+#ifndef _SELINUX_H_
+#define _SELINUX_H_
+
+#include <sys/types.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return 1 if we are running on a SELinux kernel, or 0 if not or -1 if we get an error. */
+extern int is_selinux_enabled(void);
+/* Return 1 if we are running on a SELinux MLS kernel, or 0 otherwise. */
+extern int is_selinux_mls_enabled(void);
+
+/* No longer used; here for compatibility with legacy callers. */
+typedef char *security_context_t;
+
+/* Free the memory allocated for a context by any of the below get* calls. */
+extern void freecon(char * con);
+
+/* Free the memory allocated for a context array by security_compute_user. */
+extern void freeconary(char ** con);
+
+/* Wrappers for the /proc/pid/attr API. */
+
+/* Get current context, and set *con to refer to it.
+ Caller must free via freecon. */
+extern int getcon(char ** con);
+
+/* Set the current security context to con.
+ Note that use of this function requires that the entire application
+ be trusted to maintain any desired separation between the old and new
+ security contexts, unlike exec-based transitions performed via setexeccon.
+ When possible, decompose your application and use setexeccon()+execve()
+ instead. Note that the application may lose access to its open descriptors
+ as a result of a setcon() unless policy allows it to use descriptors opened
+ by the old context. */
+extern int setcon(const char * con);
+
+/* Get context of process identified by pid, and
+ set *con to refer to it. Caller must free via freecon. */
+extern int getpidcon(pid_t pid, char ** con);
+
+/* Get previous context (prior to last exec), and set *con to refer to it.
+ Caller must free via freecon. */
+extern int getprevcon(char ** con);
+
+/* Get exec context, and set *con to refer to it.
+ Sets *con to NULL if no exec context has been set, i.e. using default.
+ If non-NULL, caller must free via freecon. */
+extern int getexeccon(char ** con);
+
+/* Set exec security context for the next execve.
+ Call with NULL if you want to reset to the default. */
+extern int setexeccon(const char * con);
+
+/* Get fscreate context, and set *con to refer to it.
+ Sets *con to NULL if no fs create context has been set, i.e. using default.
+ If non-NULL, caller must free via freecon. */
+extern int getfscreatecon(char ** con);
+
+/* Set the fscreate security context for subsequent file creations.
+ Call with NULL if you want to reset to the default. */
+extern int setfscreatecon(const char * context);
+
+/* Get keycreate context, and set *con to refer to it.
+ Sets *con to NULL if no key create context has been set, i.e. using default.
+ If non-NULL, caller must free via freecon. */
+extern int getkeycreatecon(char ** con);
+
+/* Set the keycreate security context for subsequent key creations.
+ Call with NULL if you want to reset to the default. */
+extern int setkeycreatecon(const char * context);
+
+/* Get sockcreate context, and set *con to refer to it.
+ Sets *con to NULL if no socket create context has been set, i.e. using default.
+ If non-NULL, caller must free via freecon. */
+extern int getsockcreatecon(char ** con);
+
+/* Set the sockcreate security context for subsequent socket creations.
+ Call with NULL if you want to reset to the default. */
+extern int setsockcreatecon(const char * context);
+
+/* Wrappers for the xattr API. */
+
+/* Get file context, and set *con to refer to it.
+ Caller must free via freecon. */
+extern int getfilecon(const char *path, char ** con);
+extern int lgetfilecon(const char *path, char ** con);
+extern int fgetfilecon(int fd, char ** con);
+
+/* Set file context */
+extern int setfilecon(const char *path, const char *con);
+extern int lsetfilecon(const char *path, const char *con);
+extern int fsetfilecon(int fd, const char *con);
+
+/* Wrappers for the socket API */
+
+/* Get context of peer socket, and set *con to refer to it.
+ Caller must free via freecon. */
+extern int getpeercon(int fd, char ** con);
+
+/* Wrappers for the selinuxfs (policy) API. */
+
+typedef unsigned int access_vector_t;
+typedef unsigned short security_class_t;
+
+struct av_decision {
+ access_vector_t allowed;
+ access_vector_t decided;
+ access_vector_t auditallow;
+ access_vector_t auditdeny;
+ unsigned int seqno;
+ unsigned int flags;
+};
+
+/* Definitions of av_decision.flags */
+#define SELINUX_AVD_FLAGS_PERMISSIVE 0x0001
+
+/* Structure for passing options, used by AVC and label subsystems */
+struct selinux_opt {
+ int type;
+ const char *value;
+};
+
+/* Callback facilities */
+union selinux_callback {
+ /* log the printf-style format and arguments,
+ with the type code indicating the type of message */
+ int
+#ifdef __GNUC__
+__attribute__ ((format(printf, 2, 3)))
+#endif
+ (*func_log) (int type, const char *fmt, ...);
+ /* store a string representation of auditdata (corresponding
+ to the given security class) into msgbuf. */
+ int (*func_audit) (void *auditdata, security_class_t cls,
+ char *msgbuf, size_t msgbufsize);
+ /* validate the supplied context, modifying if necessary */
+ int (*func_validate) (char **ctx);
+ /* netlink callback for setenforce message */
+ int (*func_setenforce) (int enforcing);
+ /* netlink callback for policyload message */
+ int (*func_policyload) (int seqno);
+};
+
+#define SELINUX_CB_LOG 0
+#define SELINUX_CB_AUDIT 1
+#define SELINUX_CB_VALIDATE 2
+#define SELINUX_CB_SETENFORCE 3
+#define SELINUX_CB_POLICYLOAD 4
+
+extern union selinux_callback selinux_get_callback(int type);
+extern void selinux_set_callback(int type, union selinux_callback cb);
+
+ /* Logging type codes, passed to the logging callback */
+#define SELINUX_ERROR 0
+#define SELINUX_WARNING 1
+#define SELINUX_INFO 2
+#define SELINUX_AVC 3
+
+/* Compute an access decision. */
+extern int security_compute_av(const char * scon,
+ const char * tcon,
+ security_class_t tclass,
+ access_vector_t requested,
+ struct av_decision *avd);
+
+/* Compute a labeling decision and set *newcon to refer to it.
+ Caller must free via freecon. */
+extern int security_compute_create(const char * scon,
+ const char * tcon,
+ security_class_t tclass,
+ char ** newcon);
+
+/* Compute a relabeling decision and set *newcon to refer to it.
+ Caller must free via freecon. */
+extern int security_compute_relabel(const char * scon,
+ const char * tcon,
+ security_class_t tclass,
+ char ** newcon);
+
+/* Compute a polyinstantiation member decision and set *newcon to refer to it.
+ Caller must free via freecon. */
+extern int security_compute_member(const char * scon,
+ const char * tcon,
+ security_class_t tclass,
+ char ** newcon);
+
+/* Compute the set of reachable user contexts and set *con to refer to
+ the NULL-terminated array of contexts. Caller must free via freeconary. */
+extern int security_compute_user(const char * scon,
+ const char *username,
+ char *** con);
+
+/* Load a policy configuration. */
+extern int security_load_policy(void *data, size_t len);
+
+/* Get the context of an initial kernel security identifier by name.
+ Caller must free via freecon */
+extern int security_get_initial_context(const char *name,
+ char ** con);
+
+/* Translate boolean strict to name value pair. */
+typedef struct {
+ const char *name;
+ int value;
+} SELboolean;
+/* save a list of booleans in a single transaction. */
+extern int security_set_boolean_list(size_t boolcnt,
+ SELboolean * const boollist, int permanent);
+
+/* Check the validity of a security context. */
+extern int security_check_context(const char * con);
+
+/* Canonicalize a security context. */
+extern int security_canonicalize_context(const char * con,
+ char ** canoncon);
+
+/* Get the enforce flag value. */
+extern int security_getenforce(void);
+
+/* Set the enforce flag value. */
+extern int security_setenforce(int value);
+
+/* Get the behavior for undefined classes/permissions */
+extern int security_deny_unknown(void);
+
+/* Disable SELinux at runtime (must be done prior to initial policy load). */
+extern int security_disable(void);
+
+/* Get the policy version number. */
+extern int security_policyvers(void);
+
+/* Get the boolean names */
+extern int security_get_boolean_names(char ***names, int *len);
+
+/* Get the pending value for the boolean */
+extern int security_get_boolean_pending(const char *name);
+
+/* Get the active value for the boolean */
+extern int security_get_boolean_active(const char *name);
+
+/* Set the pending value for the boolean */
+extern int security_set_boolean(const char *name, int value);
+
+/* Commit the pending values for the booleans */
+extern int security_commit_booleans(void);
+
+/* Userspace class mapping support */
+struct security_class_mapping {
+ const char *name;
+ const char *perms[sizeof(access_vector_t) * 8 + 1];
+};
+
+extern int selinux_set_mapping(struct security_class_mapping *map);
+
+/* Common helpers */
+
+/* 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);
+
+/* 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);
+
+/* Check permissions and perform appropriate auditing. */
+extern int selinux_check_access(const char * scon,
+ const char * tcon,
+ const char *tclass,
+ const char *perm, void *aux);
+
+/* Set the path to the selinuxfs mount point explicitly.
+ Normally, this is determined automatically during libselinux
+ initialization, but this is not always possible, e.g. for /sbin/init
+ which performs the initial mount of selinuxfs. */
+void set_selinuxmnt(const char *mnt);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libselinux/src/android.c b/libselinux/src/android.c
new file mode 100644
index 0000000..b748ca5
--- /dev/null
+++ b/libselinux/src/android.c
@@ -0,0 +1,1516 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/avc.h>
+#include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
+#include <log/log.h>
+#include "policy.h"
+#include "callbacks.h"
+#include "selinux_internal.h"
+#include "label_internal.h"
+#include <fnmatch.h>
+#include <limits.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <libgen.h>
+#include <packagelistparser/packagelistparser.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+/*
+ * XXX Where should this configuration file be located?
+ * Needs to be accessible by zygote and installd when
+ * setting credentials for app processes and setting permissions
+ * on app data directories.
+ */
+static char const * const seapp_contexts_file = "/seapp_contexts";
+
+static const struct selinux_opt seopts =
+ { SELABEL_OPT_PATH, "/file_contexts.bin" };
+
+static const char *const sepolicy_file = "/sepolicy";
+
+static const struct selinux_opt seopts_prop =
+ { SELABEL_OPT_PATH, "/property_contexts" };
+
+static const struct selinux_opt seopts_service =
+ { SELABEL_OPT_PATH, "/service_contexts" };
+
+enum levelFrom {
+ LEVELFROM_NONE,
+ LEVELFROM_APP,
+ LEVELFROM_USER,
+ LEVELFROM_ALL
+};
+
+#if DEBUG
+static char const * const levelFromName[] = {
+ "none",
+ "app",
+ "user",
+ "all"
+};
+#endif
+
+struct prefix_str {
+ size_t len;
+ char *str;
+ char is_prefix;
+};
+
+static void free_prefix_str(struct prefix_str *p)
+{
+ if (!p)
+ return;
+ free(p->str);
+}
+
+struct seapp_context {
+ /* input selectors */
+ bool isSystemServer;
+ bool isOwnerSet;
+ bool isOwner;
+ struct prefix_str user;
+ char *seinfo;
+ struct prefix_str name;
+ struct prefix_str path;
+ bool isPrivAppSet;
+ bool isPrivApp;
+ /* outputs */
+ char *domain;
+ char *type;
+ char *level;
+ enum levelFrom levelFrom;
+};
+
+static void free_seapp_context(struct seapp_context *s)
+{
+ if (!s)
+ return;
+
+ free_prefix_str(&s->user);
+ free(s->seinfo);
+ free_prefix_str(&s->name);
+ free_prefix_str(&s->path);
+ free(s->domain);
+ free(s->type);
+ free(s->level);
+}
+
+static bool seapp_contexts_dup = false;
+
+static int seapp_context_cmp(const void *A, const void *B)
+{
+ const struct seapp_context *const *sp1 = (const struct seapp_context *const *) A;
+ const struct seapp_context *const *sp2 = (const struct seapp_context *const *) B;
+ const struct seapp_context *s1 = *sp1, *s2 = *sp2;
+ bool dup;
+
+ /* Give precedence to isSystemServer=true. */
+ if (s1->isSystemServer != s2->isSystemServer)
+ return (s1->isSystemServer ? -1 : 1);
+
+ /* Give precedence to a specified isOwner= over an unspecified isOwner=. */
+ if (s1->isOwnerSet != s2->isOwnerSet)
+ return (s1->isOwnerSet ? -1 : 1);
+
+ /* Give precedence to a specified user= over an unspecified user=. */
+ if (s1->user.str && !s2->user.str)
+ return -1;
+ if (!s1->user.str && s2->user.str)
+ return 1;
+
+ if (s1->user.str) {
+ /* Give precedence to a fixed user= string over a prefix. */
+ if (s1->user.is_prefix != s2->user.is_prefix)
+ return (s2->user.is_prefix ? -1 : 1);
+
+ /* Give precedence to a longer prefix over a shorter prefix. */
+ if (s1->user.is_prefix && s1->user.len != s2->user.len)
+ return (s1->user.len > s2->user.len) ? -1 : 1;
+ }
+
+ /* Give precedence to a specified seinfo= over an unspecified seinfo=. */
+ if (s1->seinfo && !s2->seinfo)
+ return -1;
+ if (!s1->seinfo && s2->seinfo)
+ return 1;
+
+ /* Give precedence to a specified name= over an unspecified name=. */
+ if (s1->name.str && !s2->name.str)
+ return -1;
+ if (!s1->name.str && s2->name.str)
+ return 1;
+
+ if (s1->name.str) {
+ /* Give precedence to a fixed name= string over a prefix. */
+ if (s1->name.is_prefix != s2->name.is_prefix)
+ return (s2->name.is_prefix ? -1 : 1);
+
+ /* Give precedence to a longer prefix over a shorter prefix. */
+ if (s1->name.is_prefix && s1->name.len != s2->name.len)
+ return (s1->name.len > s2->name.len) ? -1 : 1;
+ }
+
+ /* Give precedence to a specified path= over an unspecified path=. */
+ if (s1->path.str && !s2->path.str)
+ return -1;
+ if (!s1->path.str && s2->path.str)
+ return 1;
+
+ if (s1->path.str) {
+ /* Give precedence to a fixed path= string over a prefix. */
+ if (s1->path.is_prefix != s2->path.is_prefix)
+ return (s2->path.is_prefix ? -1 : 1);
+
+ /* Give precedence to a longer prefix over a shorter prefix. */
+ if (s1->path.is_prefix && s1->path.len != s2->path.len)
+ return (s1->path.len > s2->path.len) ? -1 : 1;
+ }
+
+ /* Give precedence to a specified isPrivApp= over an unspecified isPrivApp=. */
+ if (s1->isPrivAppSet != s2->isPrivAppSet)
+ return (s1->isPrivAppSet ? -1 : 1);
+
+ /*
+ * Check for a duplicated entry on the input selectors.
+ * We already compared isSystemServer, isOwnerSet, and isOwner above.
+ * We also have already checked that both entries specify the same
+ * string fields, so if s1 has a non-NULL string, then so does s2.
+ */
+ dup = (!s1->user.str || !strcmp(s1->user.str, s2->user.str)) &&
+ (!s1->seinfo || !strcmp(s1->seinfo, s2->seinfo)) &&
+ (!s1->name.str || !strcmp(s1->name.str, s2->name.str)) &&
+ (!s1->path.str || !strcmp(s1->path.str, s2->path.str));
+ if (dup) {
+ seapp_contexts_dup = true;
+ selinux_log(SELINUX_ERROR, "seapp_contexts: Duplicated entry\n");
+ if (s1->user.str)
+ selinux_log(SELINUX_ERROR, " user=%s\n", s1->user.str);
+ if (s1->seinfo)
+ selinux_log(SELINUX_ERROR, " seinfo=%s\n", s1->seinfo);
+ if (s1->name.str)
+ selinux_log(SELINUX_ERROR, " name=%s\n", s1->name.str);
+ if (s1->path.str)
+ selinux_log(SELINUX_ERROR, " path=%s\n", s1->path.str);
+ }
+
+ /* Anything else has equal precedence. */
+ return 0;
+}
+
+static struct seapp_context **seapp_contexts = NULL;
+static int nspec = 0;
+
+static void free_seapp_contexts(void)
+{
+ int n;
+
+ if (!seapp_contexts)
+ return;
+
+ for (n = 0; n < nspec; n++)
+ free_seapp_context(seapp_contexts[n]);
+
+ free(seapp_contexts);
+ seapp_contexts = NULL;
+ nspec = 0;
+}
+
+int selinux_android_seapp_context_reload(void)
+{
+ FILE *fp = NULL;
+ char line_buf[BUFSIZ];
+ char *token;
+ unsigned lineno;
+ struct seapp_context *cur;
+ char *p, *name = NULL, *value = NULL, *saveptr;
+ size_t len;
+ int n, ret;
+
+ fp = fopen(seapp_contexts_file, "re");
+ if (!fp) {
+ selinux_log(SELINUX_ERROR, "%s: could not open any seapp_contexts file", __FUNCTION__);
+ return -1;
+ }
+
+ free_seapp_contexts();
+
+ nspec = 0;
+ while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+ p = line_buf;
+ while (isspace(*p))
+ p++;
+ if (*p == '#' || *p == 0)
+ continue;
+ nspec++;
+ }
+
+ seapp_contexts = (struct seapp_context **) calloc(nspec, sizeof(struct seapp_context *));
+ if (!seapp_contexts)
+ goto oom;
+
+ rewind(fp);
+ nspec = 0;
+ lineno = 1;
+ while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+ len = strlen(line_buf);
+ if (line_buf[len - 1] == '\n')
+ line_buf[len - 1] = 0;
+ p = line_buf;
+ while (isspace(*p))
+ p++;
+ if (*p == '#' || *p == 0)
+ continue;
+
+ cur = (struct seapp_context *) calloc(1, sizeof(struct seapp_context));
+ if (!cur)
+ goto oom;
+
+ token = strtok_r(p, " \t", &saveptr);
+ if (!token) {
+ free_seapp_context(cur);
+ goto err;
+ }
+
+ while (1) {
+ name = token;
+ value = strchr(name, '=');
+ if (!value) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ *value++ = 0;
+
+ if (!strcasecmp(name, "isSystemServer")) {
+ if (!strcasecmp(value, "true"))
+ cur->isSystemServer = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isSystemServer = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "isOwner")) {
+ cur->isOwnerSet = true;
+ if (!strcasecmp(value, "true"))
+ cur->isOwner = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isOwner = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "user")) {
+ if (cur->user.str) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->user.str = strdup(value);
+ if (!cur->user.str) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ cur->user.len = strlen(cur->user.str);
+ if (cur->user.str[cur->user.len-1] == '*')
+ cur->user.is_prefix = 1;
+ } else if (!strcasecmp(name, "seinfo")) {
+ if (cur->seinfo) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->seinfo = strdup(value);
+ if (!cur->seinfo) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ if (strstr(value, ":")) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "name")) {
+ if (cur->name.str) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->name.str = strdup(value);
+ if (!cur->name.str) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ cur->name.len = strlen(cur->name.str);
+ if (cur->name.str[cur->name.len-1] == '*')
+ cur->name.is_prefix = 1;
+ } else if (!strcasecmp(name, "domain")) {
+ if (cur->domain) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->domain = strdup(value);
+ if (!cur->domain) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ } else if (!strcasecmp(name, "type")) {
+ if (cur->type) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->type = strdup(value);
+ if (!cur->type) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ } else if (!strcasecmp(name, "levelFromUid")) {
+ if (cur->levelFrom) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ if (!strcasecmp(value, "true"))
+ cur->levelFrom = LEVELFROM_APP;
+ else if (!strcasecmp(value, "false"))
+ cur->levelFrom = LEVELFROM_NONE;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "levelFrom")) {
+ if (cur->levelFrom) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ if (!strcasecmp(value, "none"))
+ cur->levelFrom = LEVELFROM_NONE;
+ else if (!strcasecmp(value, "app"))
+ cur->levelFrom = LEVELFROM_APP;
+ else if (!strcasecmp(value, "user"))
+ cur->levelFrom = LEVELFROM_USER;
+ else if (!strcasecmp(value, "all"))
+ cur->levelFrom = LEVELFROM_ALL;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "level")) {
+ if (cur->level) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->level = strdup(value);
+ if (!cur->level) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ } else if (!strcasecmp(name, "path")) {
+ if (cur->path.str) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->path.str = strdup(value);
+ if (!cur->path.str) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ cur->path.len = strlen(cur->path.str);
+ if (cur->path.str[cur->path.len-1] == '*')
+ cur->path.is_prefix = 1;
+ } else if (!strcasecmp(name, "isPrivApp")) {
+ cur->isPrivAppSet = true;
+ if (!strcasecmp(value, "true"))
+ cur->isPrivApp = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isPrivApp = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else {
+ free_seapp_context(cur);
+ goto err;
+ }
+
+ token = strtok_r(NULL, " \t", &saveptr);
+ if (!token)
+ break;
+ }
+
+ if (cur->name.str &&
+ (!cur->seinfo || !strcmp(cur->seinfo, "default"))) {
+ selinux_log(SELINUX_ERROR, "%s: No specific seinfo value specified with name=\"%s\", on line %u: insecure configuration!\n",
+ seapp_contexts_file, cur->name.str, lineno);
+ free_seapp_context(cur);
+ goto err;
+ }
+
+ seapp_contexts[nspec] = cur;
+ nspec++;
+ lineno++;
+ }
+
+ qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
+ seapp_context_cmp);
+
+ if (seapp_contexts_dup)
+ goto err;
+
+#if DEBUG
+ {
+ int i;
+ for (i = 0; i < nspec; i++) {
+ cur = seapp_contexts[i];
+ selinux_log(SELINUX_INFO, "%s: isSystemServer=%s isOwner=%s user=%s seinfo=%s name=%s path=%s isPrivApp=%s -> domain=%s type=%s level=%s levelFrom=%s",
+ __FUNCTION__,
+ cur->isSystemServer ? "true" : "false",
+ cur->isOwnerSet ? (cur->isOwner ? "true" : "false") : "null",
+ cur->user.str,
+ cur->seinfo, cur->name.str, cur->path.str,
+ cur->isPrivAppSet ? (cur->isPrivApp ? "true" : "false") : "null",
+ cur->domain, cur->type, cur->level,
+ levelFromName[cur->levelFrom]);
+ }
+ }
+#endif
+
+ ret = 0;
+
+out:
+ fclose(fp);
+ return ret;
+
+err:
+ selinux_log(SELINUX_ERROR, "%s: Invalid entry on line %u\n",
+ seapp_contexts_file, lineno);
+ free_seapp_contexts();
+ ret = -1;
+ goto out;
+oom:
+ selinux_log(SELINUX_ERROR,
+ "%s: Out of memory\n", __FUNCTION__);
+ free_seapp_contexts();
+ ret = -1;
+ goto out;
+}
+
+
+static void seapp_context_init(void)
+{
+ selinux_android_seapp_context_reload();
+}
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+/*
+ * Max id that can be mapped to category set uniquely
+ * using the current scheme.
+ */
+#define CAT_MAPPING_MAX_ID (0x1<<16)
+
+enum seapp_kind {
+ SEAPP_TYPE,
+ SEAPP_DOMAIN
+};
+
+#define PRIVILEGED_APP_STR ":privapp"
+static bool is_app_privileged(const char *seinfo)
+{
+ return strstr(seinfo, PRIVILEGED_APP_STR) != NULL;
+}
+
+static int seinfo_parse(char *dest, const char *src, size_t size)
+{
+ size_t len;
+ char *p;
+
+ if ((p = strchr(src, ':')) != NULL)
+ len = p - src;
+ else
+ len = strlen(src);
+
+ if (len > size - 1)
+ return -1;
+
+ strncpy(dest, src, len);
+ dest[len] = '\0';
+
+ return 0;
+}
+
+static int seapp_context_lookup(enum seapp_kind kind,
+ uid_t uid,
+ bool isSystemServer,
+ const char *seinfo,
+ const char *pkgname,
+ const char *path,
+ context_t ctx)
+{
+ struct passwd *pwd;
+ bool isOwner;
+ const char *username = NULL;
+ struct seapp_context *cur = NULL;
+ int i;
+ size_t n;
+ uid_t userid;
+ uid_t appid;
+ bool isPrivApp = false;
+ char parsedseinfo[BUFSIZ];
+
+ __selinux_once(once, seapp_context_init);
+
+ if (seinfo) {
+ if (seinfo_parse(parsedseinfo, seinfo, BUFSIZ))
+ goto err;
+ isPrivApp = is_app_privileged(seinfo);
+ seinfo = parsedseinfo;
+ }
+
+ userid = uid / AID_USER;
+ isOwner = (userid == 0);
+ appid = uid % AID_USER;
+ if (appid < AID_APP) {
+ /*
+ * This code is Android specific, bionic guarantees that
+ * calls to non-reentrant getpwuid() are thread safe.
+ */
+#ifndef __BIONIC__
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#endif
+ pwd = getpwuid(appid);
+ if (!pwd)
+ goto err;
+
+ username = pwd->pw_name;
+
+ } else if (appid < AID_ISOLATED_START) {
+ username = "_app";
+ appid -= AID_APP;
+ } else {
+ username = "_isolated";
+ appid -= AID_ISOLATED_START;
+ }
+
+ if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
+ goto err;
+
+ for (i = 0; i < nspec; i++) {
+ cur = seapp_contexts[i];
+
+ if (cur->isSystemServer != isSystemServer)
+ continue;
+
+ if (cur->isOwnerSet && cur->isOwner != isOwner)
+ continue;
+
+ if (cur->user.str) {
+ if (cur->user.is_prefix) {
+ if (strncasecmp(username, cur->user.str, cur->user.len-1))
+ continue;
+ } else {
+ if (strcasecmp(username, cur->user.str))
+ continue;
+ }
+ }
+
+ if (cur->seinfo) {
+ if (!seinfo || strcasecmp(seinfo, cur->seinfo))
+ continue;
+ }
+
+ if (cur->name.str) {
+ if(!pkgname)
+ continue;
+
+ if (cur->name.is_prefix) {
+ if (strncasecmp(pkgname, cur->name.str, cur->name.len-1))
+ continue;
+ } else {
+ if (strcasecmp(pkgname, cur->name.str))
+ continue;
+ }
+ }
+
+ if (cur->isPrivAppSet && cur->isPrivApp != isPrivApp)
+ continue;
+
+ if (cur->path.str) {
+ if (!path)
+ continue;
+
+ if (cur->path.is_prefix) {
+ if (strncmp(path, cur->path.str, cur->path.len-1))
+ continue;
+ } else {
+ if (strcmp(path, cur->path.str))
+ continue;
+ }
+ }
+
+ if (kind == SEAPP_TYPE && !cur->type)
+ continue;
+ else if (kind == SEAPP_DOMAIN && !cur->domain)
+ continue;
+
+ if (kind == SEAPP_TYPE) {
+ if (context_type_set(ctx, cur->type))
+ goto oom;
+ } else if (kind == SEAPP_DOMAIN) {
+ if (context_type_set(ctx, cur->domain))
+ goto oom;
+ }
+
+ if (cur->levelFrom != LEVELFROM_NONE) {
+ char level[255];
+ switch (cur->levelFrom) {
+ case LEVELFROM_APP:
+ snprintf(level, sizeof level, "s0:c%u,c%u",
+ appid & 0xff,
+ 256 + (appid>>8 & 0xff));
+ break;
+ case LEVELFROM_USER:
+ snprintf(level, sizeof level, "s0:c%u,c%u",
+ 512 + (userid & 0xff),
+ 768 + (userid>>8 & 0xff));
+ break;
+ case LEVELFROM_ALL:
+ snprintf(level, sizeof level, "s0:c%u,c%u,c%u,c%u",
+ appid & 0xff,
+ 256 + (appid>>8 & 0xff),
+ 512 + (userid & 0xff),
+ 768 + (userid>>8 & 0xff));
+ break;
+ default:
+ goto err;
+ }
+ if (context_range_set(ctx, level))
+ goto oom;
+ } else if (cur->level) {
+ if (context_range_set(ctx, cur->level))
+ goto oom;
+ }
+
+ break;
+ }
+
+ if (kind == SEAPP_DOMAIN && i == nspec) {
+ /*
+ * No match.
+ * Fail to prevent staying in the zygote's context.
+ */
+ selinux_log(SELINUX_ERROR,
+ "%s: No match for app with uid %d, seinfo %s, name %s\n",
+ __FUNCTION__, uid, seinfo, pkgname);
+
+ if (security_getenforce() == 1)
+ goto err;
+ }
+
+ return 0;
+err:
+ return -1;
+oom:
+ return -2;
+}
+
+int selinux_android_setfilecon(const char *pkgdir,
+ const char *pkgname,
+ const char *seinfo,
+ uid_t uid)
+{
+ char *orig_ctx_str = NULL;
+ char *ctx_str = NULL;
+ context_t ctx = NULL;
+ int rc = -1;
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ rc = getfilecon(pkgdir, &ctx_str);
+ if (rc < 0)
+ goto err;
+
+ ctx = context_new(ctx_str);
+ orig_ctx_str = ctx_str;
+ if (!ctx)
+ goto oom;
+
+ rc = seapp_context_lookup(SEAPP_TYPE, uid, 0, seinfo, pkgname, NULL, ctx);
+ if (rc == -1)
+ goto err;
+ else if (rc == -2)
+ goto oom;
+
+ ctx_str = context_str(ctx);
+ if (!ctx_str)
+ goto oom;
+
+ rc = security_check_context(ctx_str);
+ if (rc < 0)
+ goto err;
+
+ if (strcmp(ctx_str, orig_ctx_str)) {
+ rc = setfilecon(pkgdir, ctx_str);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = 0;
+out:
+ freecon(orig_ctx_str);
+ context_free(ctx);
+ return rc;
+err:
+ selinux_log(SELINUX_ERROR, "%s: Error setting context for pkgdir %s, uid %d: %s\n",
+ __FUNCTION__, pkgdir, uid, strerror(errno));
+ rc = -1;
+ goto out;
+oom:
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
+ rc = -1;
+ goto out;
+}
+
+int selinux_android_setcon(const char *con)
+{
+ int ret = setcon(con);
+ if (ret)
+ return ret;
+ /*
+ System properties must be reinitialized after setcon() otherwise the
+ previous property files will be leaked since mmap()'ed regions are not
+ closed as a result of setcon().
+ */
+ return __system_properties_init();
+}
+
+int selinux_android_setcontext(uid_t uid,
+ bool isSystemServer,
+ const char *seinfo,
+ const char *pkgname)
+{
+ char *orig_ctx_str = NULL, *ctx_str;
+ context_t ctx = NULL;
+ int rc = -1;
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ rc = getcon(&ctx_str);
+ if (rc)
+ goto err;
+
+ ctx = context_new(ctx_str);
+ orig_ctx_str = ctx_str;
+ if (!ctx)
+ goto oom;
+
+ rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, NULL, ctx);
+ if (rc == -1)
+ goto err;
+ else if (rc == -2)
+ goto oom;
+
+ ctx_str = context_str(ctx);
+ if (!ctx_str)
+ goto oom;
+
+ rc = security_check_context(ctx_str);
+ if (rc < 0)
+ goto err;
+
+ if (strcmp(ctx_str, orig_ctx_str)) {
+ rc = selinux_android_setcon(ctx_str);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = 0;
+out:
+ freecon(orig_ctx_str);
+ context_free(ctx);
+ avc_netlink_close();
+ return rc;
+err:
+ if (isSystemServer)
+ selinux_log(SELINUX_ERROR,
+ "%s: Error setting context for system server: %s\n",
+ __FUNCTION__, strerror(errno));
+ else
+ selinux_log(SELINUX_ERROR,
+ "%s: Error setting context for app with uid %d, seinfo %s: %s\n",
+ __FUNCTION__, uid, seinfo, strerror(errno));
+
+ rc = -1;
+ goto out;
+oom:
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
+ rc = -1;
+ goto out;
+}
+
+static struct selabel_handle *fc_sehandle = NULL;
+#define FC_DIGEST_SIZE SHA_DIGEST_LENGTH
+static uint8_t fc_digest[FC_DIGEST_SIZE];
+
+static bool compute_file_contexts_hash(uint8_t c_digest[])
+{
+ int fd;
+ struct stat sb;
+ void *map;
+
+ fd = open(seopts.value, O_CLOEXEC | O_RDONLY | O_NOFOLLOW);
+ if (fd < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not open %s: %s\n",
+ seopts.value, strerror(errno));
+ return false;
+ }
+ if (fstat(fd, &sb) < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not stat %s: %s\n",
+ seopts.value, strerror(errno));
+ close(fd);
+ return false;
+ }
+ map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not map %s: %s\n",
+ seopts.value, strerror(errno));
+ close(fd);
+ return false;
+ }
+ SHA1(map, sb.st_size, c_digest);
+ munmap(map, sb.st_size);
+ close(fd);
+
+ return true;
+}
+
+static void file_context_init(void)
+{
+ if (!fc_sehandle)
+ fc_sehandle = selinux_android_file_context_handle();
+}
+
+
+
+static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
+
+#define PKGTAB_SIZE 256
+static struct pkg_info *pkgTab[PKGTAB_SIZE];
+
+static unsigned int pkghash(const char *pkgname)
+{
+ unsigned int h = 7;
+ for (; *pkgname; pkgname++) {
+ h = h * 31 + *pkgname;
+ }
+ return h & (PKGTAB_SIZE - 1);
+}
+
+static bool pkg_parse_callback(pkg_info *info, void *userdata) {
+
+ (void) userdata;
+
+ unsigned int hash = pkghash(info->name);
+ if (pkgTab[hash])
+ info->private_data = pkgTab[hash];
+ pkgTab[hash] = info;
+ return true;
+}
+
+static void package_info_init(void)
+{
+
+ bool rc = packagelist_parse(pkg_parse_callback, NULL);
+ if (!rc) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could NOT parse package list\n");
+ return;
+ }
+
+#if DEBUG
+ {
+ unsigned int hash, buckets, entries, chainlen, longestchain;
+ struct pkg_info *info = NULL;
+
+ buckets = entries = longestchain = 0;
+ for (hash = 0; hash < PKGTAB_SIZE; hash++) {
+ if (pkgTab[hash]) {
+ buckets++;
+ chainlen = 0;
+ for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
+ chainlen++;
+ selinux_log(SELINUX_INFO, "%s: name=%s uid=%u debuggable=%s dataDir=%s seinfo=%s\n",
+ __FUNCTION__,
+ info->name, info->uid, info->debuggable ? "true" : "false", info->data_dir, info->seinfo);
+ }
+ entries += chainlen;
+ if (longestchain < chainlen)
+ longestchain = chainlen;
+ }
+ }
+ selinux_log(SELINUX_INFO, "SELinux: %d pkg entries and %d/%d buckets used, longest chain %d\n", entries, buckets, PKGTAB_SIZE, longestchain);
+ }
+#endif
+
+}
+
+static pthread_once_t pkg_once = PTHREAD_ONCE_INIT;
+
+struct pkg_info *package_info_lookup(const char *name)
+{
+ struct pkg_info *info;
+ unsigned int hash;
+
+ __selinux_once(pkg_once, package_info_init);
+
+ hash = pkghash(name);
+ for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
+ if (!strcmp(name, info->name))
+ return info;
+ }
+ return NULL;
+}
+
+/* The path prefixes of package data directories. */
+#define DATA_DATA_PATH "/data/data"
+#define DATA_USER_PATH "/data/user"
+#define DATA_USER_DE_PATH "/data/user_de"
+#define EXPAND_USER_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user"
+#define EXPAND_USER_DE_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user_de"
+#define DATA_DATA_PREFIX DATA_DATA_PATH "/"
+#define DATA_USER_PREFIX DATA_USER_PATH "/"
+#define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/"
+
+static int pkgdir_selabel_lookup(const char *pathname,
+ const char *seinfo,
+ uid_t uid,
+ char **secontextp)
+{
+ char *pkgname = NULL, *end = NULL;
+ struct pkg_info *info = NULL;
+ char *secontext = *secontextp;
+ context_t ctx = NULL;
+ int rc = 0;
+
+ /* Skip directory prefix before package name. */
+ if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1)) {
+ pathname += sizeof(DATA_DATA_PREFIX) - 1;
+ } else if (!strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1)) {
+ pathname += sizeof(DATA_USER_PREFIX) - 1;
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else if (!strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1)) {
+ pathname += sizeof(DATA_USER_DE_PREFIX) - 1;
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else if (!fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+ pathname += sizeof(EXPAND_USER_PATH);
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else if (!fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+ pathname += sizeof(EXPAND_USER_DE_PATH);
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else
+ return 0;
+
+ if (!(*pathname))
+ return 0;
+
+ pkgname = strdup(pathname);
+ if (!pkgname)
+ return -1;
+
+ for (end = pkgname; *end && *end != '/'; end++)
+ ;
+ pathname = end;
+ if (*end)
+ pathname++;
+ *end = '\0';
+
+ if (!seinfo) {
+ info = package_info_lookup(pkgname);
+ if (!info) {
+ selinux_log(SELINUX_WARNING, "SELinux: Could not look up information for package %s, cannot restorecon %s.\n",
+ pkgname, pathname);
+ free(pkgname);
+ return -1;
+ }
+ }
+
+ ctx = context_new(secontext);
+ if (!ctx)
+ goto err;
+
+ rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0,
+ info ? info->seinfo : seinfo, info ? info->name : pkgname, pathname, ctx);
+ if (rc < 0)
+ goto err;
+
+ secontext = context_str(ctx);
+ if (!secontext)
+ goto err;
+
+ if (!strcmp(secontext, *secontextp))
+ goto out;
+
+ rc = security_check_context(secontext);
+ if (rc < 0)
+ goto err;
+
+ freecon(*secontextp);
+ *secontextp = strdup(secontext);
+ if (!(*secontextp))
+ goto err;
+
+ rc = 0;
+
+out:
+ free(pkgname);
+ context_free(ctx);
+ return rc;
+err:
+ selinux_log(SELINUX_ERROR, "%s: Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n",
+ __FUNCTION__, pathname, pkgname, info->seinfo, info->uid, strerror(errno));
+ rc = -1;
+ goto out;
+}
+
+#define RESTORECON_LAST "security.restorecon_last"
+
+static int restorecon_sb(const char *pathname, const struct stat *sb,
+ bool nochange, bool verbose,
+ const char *seinfo, uid_t uid)
+{
+ char *secontext = NULL;
+ char *oldsecontext = NULL;
+ int rc = 0;
+
+ if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
+ return 0; /* no match, but not an error */
+
+ if (lgetfilecon(pathname, &oldsecontext) < 0)
+ goto err;
+
+ /*
+ * For subdirectories of /data/data or /data/user, we ignore selabel_lookup()
+ * and use pkgdir_selabel_lookup() instead. Files within those directories
+ * have different labeling rules, based off of /seapp_contexts, and
+ * installd is responsible for managing these labels instead of init.
+ */
+ if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+ !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
+ !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+ if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
+ goto err;
+ }
+
+ if (strcmp(oldsecontext, secontext) != 0) {
+ if (verbose)
+ selinux_log(SELINUX_INFO,
+ "SELinux: Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);
+ if (!nochange) {
+ if (lsetfilecon(pathname, secontext) < 0)
+ goto err;
+ }
+ }
+
+ rc = 0;
+
+out:
+ freecon(oldsecontext);
+ freecon(secontext);
+ return rc;
+
+err:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Could not set context for %s: %s\n",
+ pathname, strerror(errno));
+ rc = -1;
+ goto out;
+}
+
+#define SYS_PATH "/sys"
+#define SYS_PREFIX SYS_PATH "/"
+
+static int selinux_android_restorecon_common(const char* pathname_orig,
+ const char *seinfo,
+ uid_t uid,
+ unsigned int flags)
+{
+ bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
+ bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
+ bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
+ bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
+ bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
+ bool issys;
+ bool setrestoreconlast = true;
+ struct stat sb;
+ struct statfs sfsb;
+ FTS *fts;
+ FTSENT *ftsent;
+ char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
+ char * paths[2] = { NULL , NULL };
+ int ftsflags = FTS_NOCHDIR | FTS_XDEV | FTS_PHYSICAL;
+ int error, sverrno;
+ char xattr_value[FC_DIGEST_SIZE];
+ ssize_t size;
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ __selinux_once(fc_once, file_context_init);
+
+ if (!fc_sehandle)
+ return 0;
+
+ /*
+ * Convert passed-in pathname to canonical pathname by resolving realpath of
+ * containing dir, then appending last component name.
+ */
+ pathbname = basename(pathname_orig);
+ if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) {
+ pathname = realpath(pathname_orig, NULL);
+ if (!pathname)
+ goto realpatherr;
+ } else {
+ pathdname = dirname(pathname_orig);
+ pathdnamer = realpath(pathdname, NULL);
+ if (!pathdnamer)
+ goto realpatherr;
+ if (!strcmp(pathdnamer, "/"))
+ error = asprintf(&pathname, "/%s", pathbname);
+ else
+ error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname);
+ if (error < 0)
+ goto oom;
+ }
+
+ paths[0] = pathname;
+ issys = (!strcmp(pathname, SYS_PATH)
+ || !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
+
+ if (!recurse) {
+ if (lstat(pathname, &sb) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
+ goto cleanup;
+ }
+
+ /*
+ * Ignore restorecon_last on /data/data or /data/user
+ * since their labeling is based on seapp_contexts and seinfo
+ * assignments rather than file_contexts and is managed by
+ * installd rather than init.
+ */
+ if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+ !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
+ !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME))
+ setrestoreconlast = false;
+
+ /* Also ignore on /sys since it is regenerated on each boot regardless. */
+ if (issys)
+ setrestoreconlast = false;
+
+ /* Ignore files on in-memory filesystems */
+ if (statfs(pathname, &sfsb) == 0) {
+ if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
+ setrestoreconlast = false;
+ }
+
+ if (setrestoreconlast) {
+ size = getxattr(pathname, RESTORECON_LAST, xattr_value, sizeof fc_digest);
+ if (!force && size == sizeof fc_digest && memcmp(fc_digest, xattr_value, sizeof fc_digest) == 0) {
+ selinux_log(SELINUX_INFO,
+ "SELinux: Skipping restorecon_recursive(%s)\n",
+ pathname);
+ error = 0;
+ goto cleanup;
+ }
+ }
+
+ fts = fts_open(paths, ftsflags, NULL);
+ if (!fts) {
+ error = -1;
+ goto cleanup;
+ }
+
+ error = 0;
+ while ((ftsent = fts_read(fts)) != NULL) {
+ switch (ftsent->fts_info) {
+ case FTS_DC:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Directory cycle on %s.\n", ftsent->fts_path);
+ errno = ELOOP;
+ error = -1;
+ goto out;
+ case FTS_DP:
+ continue;
+ case FTS_DNR:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ case FTS_NS:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ case FTS_ERR:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ case FTS_D:
+ if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ }
+
+ if (!datadata &&
+ (!strcmp(ftsent->fts_path, DATA_DATA_PATH) ||
+ !strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+ !strncmp(ftsent->fts_path, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+ !fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME) ||
+ !fnmatch(EXPAND_USER_DE_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) {
+ // Don't label anything below this directory.
+ fts_set(fts, ftsent, FTS_SKIP);
+ // but fall through and make sure we label the directory itself
+ }
+ /* fall through */
+ default:
+ error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
+ break;
+ }
+ }
+
+ // Labeling successful. Mark the top level directory as completed.
+ if (setrestoreconlast && !nochange && !error)
+ setxattr(pathname, RESTORECON_LAST, fc_digest, sizeof fc_digest, 0);
+
+out:
+ sverrno = errno;
+ (void) fts_close(fts);
+ errno = sverrno;
+cleanup:
+ free(pathdnamer);
+ free(pathname);
+ return error;
+oom:
+ sverrno = errno;
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
+ errno = sverrno;
+ error = -1;
+ goto cleanup;
+realpatherr:
+ sverrno = errno;
+ selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n",
+ pathname_orig, strerror(errno));
+ errno = sverrno;
+ error = -1;
+ goto cleanup;
+}
+
+int selinux_android_restorecon(const char *file, unsigned int flags)
+{
+ return selinux_android_restorecon_common(file, NULL, -1, flags);
+}
+
+int selinux_android_restorecon_pkgdir(const char *pkgdir,
+ const char *seinfo,
+ uid_t uid,
+ unsigned int flags)
+{
+ return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA);
+}
+
+struct selabel_handle* selinux_android_file_context_handle(void)
+{
+ char *path = NULL;
+ struct selabel_handle *sehandle;
+ struct selinux_opt fc_opts[] = {
+ { SELABEL_OPT_PATH, path },
+ { SELABEL_OPT_BASEONLY, (char *)1 }
+ };
+
+ fc_opts[0].value = seopts.value;
+
+ sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 2);
+
+ if (!sehandle) {
+ selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",
+ __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+ if (!compute_file_contexts_hash(fc_digest)) {
+ selabel_close(sehandle);
+ return NULL;
+ }
+ selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts contexts from %s.\n",
+ fc_opts[0].value);
+
+ return sehandle;
+}
+
+struct selabel_handle* selinux_android_prop_context_handle(void)
+{
+ struct selabel_handle* sehandle;
+
+ sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP,
+ &seopts_prop, 1);
+ if (!sehandle) {
+ selinux_log(SELINUX_ERROR, "%s: Error getting property context handle (%s)\n",
+ __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+ selinux_log(SELINUX_INFO, "SELinux: Loaded property_contexts from %s.\n",
+ seopts_prop.value);
+
+ return sehandle;
+}
+
+struct selabel_handle* selinux_android_service_context_handle(void)
+{
+ struct selabel_handle* sehandle;
+
+ sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP,
+ &seopts_service, 1);
+
+ if (!sehandle) {
+ selinux_log(SELINUX_ERROR, "%s: Error getting service context handle (%s)\n",
+ __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+ selinux_log(SELINUX_INFO, "SELinux: Loaded service_contexts from %s.\n",
+ seopts_service.value);
+
+ return sehandle;
+}
+
+void selinux_android_set_sehandle(const struct selabel_handle *hndl)
+{
+ fc_sehandle = (struct selabel_handle *) hndl;
+}
+
+int selinux_android_load_policy(void)
+{
+ int fd = -1, rc;
+ struct stat sb;
+ void *map = NULL;
+ static int load_successful = 0;
+
+ /*
+ * Since updating policy at runtime has been abolished
+ * we just check whether a policy has been loaded before
+ * and return if this is the case.
+ * There is no point in reloading policy.
+ */
+ if (load_successful){
+ selinux_log(SELINUX_WARNING, "SELinux: Attempted reload of SELinux policy!/n");
+ return 0;
+ }
+
+ set_selinuxmnt(SELINUXMNT);
+ fd = open(sepolicy_file, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not open sepolicy: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ if (fstat(fd, &sb) < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not stat %s: %s\n",
+ sepolicy_file, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not map %s: %s\n",
+ sepolicy_file, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ rc = security_load_policy(map, sb.st_size);
+ if (rc < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not load policy: %s\n",
+ strerror(errno));
+ munmap(map, sb.st_size);
+ close(fd);
+ return -1;
+ }
+
+ munmap(map, sb.st_size);
+ close(fd);
+ selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", sepolicy_file);
+ load_successful = 1;
+ return 0;
+}
+
+int selinux_log_callback(int type, const char *fmt, ...)
+{
+ va_list ap;
+ int priority;
+ char *strp;
+
+ switch(type) {
+ case SELINUX_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case SELINUX_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ default:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ }
+
+ va_start(ap, fmt);
+ if (vasprintf(&strp, fmt, ap) != -1) {
+ LOG_PRI(priority, "SELinux", "%s", strp);
+ LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);
+ free(strp);
+ }
+ va_end(ap);
+ return 0;
+}
diff --git a/libselinux/src/avc.c b/libselinux/src/avc.c
new file mode 100644
index 0000000..528d897
--- /dev/null
+++ b/libselinux/src/avc.c
@@ -0,0 +1,1121 @@
+/*
+ * Implementation of the userspace access vector cache (AVC).
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ *
+ * Derived from the kernel AVC implementation by
+ * Stephen Smalley <sds@epoch.ncsc.mil> and
+ * James Morris <jmorris@redhat.com>.
+ */
+#include <selinux/avc.h>
+#include "selinux_internal.h"
+#include <assert.h>
+#include "avc_sidtab.h"
+#include "avc_internal.h"
+
+#define AVC_CACHE_SLOTS 512
+#define AVC_CACHE_MAXNODES 410
+
+struct avc_entry {
+ security_id_t ssid;
+ security_id_t tsid;
+ security_class_t tclass;
+ struct av_decision avd;
+ security_id_t create_sid;
+ int used; /* used recently */
+};
+
+struct avc_node {
+ struct avc_entry ae;
+ struct avc_node *next;
+};
+
+struct avc_cache {
+ struct avc_node *slots[AVC_CACHE_SLOTS];
+ uint32_t lru_hint; /* LRU hint for reclaim scan */
+ uint32_t active_nodes;
+ uint32_t latest_notif; /* latest revocation notification */
+};
+
+struct avc_callback_node {
+ int (*callback) (uint32_t event, security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ access_vector_t * out_retained);
+ uint32_t events;
+ security_id_t ssid;
+ security_id_t tsid;
+ security_class_t tclass;
+ access_vector_t perms;
+ struct avc_callback_node *next;
+};
+
+static void *avc_netlink_thread = NULL;
+static void *avc_lock = NULL;
+static void *avc_log_lock = NULL;
+static struct avc_node *avc_node_freelist = NULL;
+static struct avc_cache avc_cache;
+static char *avc_audit_buf = NULL;
+static struct avc_cache_stats cache_stats;
+static struct avc_callback_node *avc_callbacks = NULL;
+static struct sidtab avc_sidtab;
+
+static inline int avc_hash(security_id_t ssid,
+ security_id_t tsid, security_class_t tclass)
+{
+ return ((uintptr_t) ssid ^ ((uintptr_t) tsid << 2) ^ tclass)
+ & (AVC_CACHE_SLOTS - 1);
+}
+
+int avc_context_to_sid(const char * ctx, security_id_t * sid)
+{
+ int rc;
+ /* avc_init needs to be called before this function */
+ assert(avc_running);
+
+ avc_get_lock(avc_lock);
+ rc = sidtab_context_to_sid(&avc_sidtab, ctx, sid);
+ avc_release_lock(avc_lock);
+ return rc;
+}
+
+int avc_sid_to_context(security_id_t sid, char ** ctx)
+{
+ int rc;
+ *ctx = NULL;
+ avc_get_lock(avc_lock);
+ *ctx = strdup(sid->ctx); /* caller must free via freecon */
+ rc = *ctx ? 0 : -1;
+ avc_release_lock(avc_lock);
+ return rc;
+}
+
+int avc_get_initial_sid(const char * name, security_id_t * sid)
+{
+ int rc;
+ char * con;
+
+ rc = security_get_initial_context(name, &con);
+ if (rc < 0)
+ return rc;
+ rc = avc_context_to_sid(con, sid);
+
+ freecon(con);
+
+ return rc;
+}
+
+int avc_open(struct selinux_opt *opts, unsigned nopts)
+{
+ avc_setenforce = 0;
+
+ while (nopts--)
+ switch(opts[nopts].type) {
+ case AVC_OPT_SETENFORCE:
+ avc_setenforce = 1;
+ avc_enforcing = !!opts[nopts].value;
+ break;
+ }
+
+ return avc_init("avc", NULL, NULL, NULL, NULL);
+}
+
+int avc_init(const char *prefix,
+ const struct avc_memory_callback *mem_cb,
+ const struct avc_log_callback *log_cb,
+ const struct avc_thread_callback *thread_cb,
+ const struct avc_lock_callback *lock_cb)
+{
+ struct avc_node *new;
+ int i, rc = 0;
+
+ if (avc_running)
+ return 0;
+
+ if (prefix)
+ strncpy(avc_prefix, prefix, AVC_PREFIX_SIZE - 1);
+
+ set_callbacks(mem_cb, log_cb, thread_cb, lock_cb);
+
+ avc_lock = avc_alloc_lock();
+ avc_log_lock = avc_alloc_lock();
+
+ memset(&cache_stats, 0, sizeof(cache_stats));
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++)
+ avc_cache.slots[i] = 0;
+ avc_cache.lru_hint = 0;
+ avc_cache.active_nodes = 0;
+ avc_cache.latest_notif = 0;
+
+ rc = sidtab_init(&avc_sidtab);
+ if (rc) {
+ avc_log(SELINUX_ERROR,
+ "%s: unable to initialize SID table\n",
+ avc_prefix);
+ goto out;
+ }
+
+ avc_audit_buf = (char *)avc_malloc(AVC_AUDIT_BUFSIZE);
+ if (!avc_audit_buf) {
+ avc_log(SELINUX_ERROR,
+ "%s: unable to allocate audit buffer\n",
+ avc_prefix);
+ rc = -1;
+ goto out;
+ }
+
+ for (i = 0; i < AVC_CACHE_MAXNODES; i++) {
+ new = avc_malloc(sizeof(*new));
+ if (!new) {
+ avc_log(SELINUX_WARNING,
+ "%s: warning: only got %d av entries\n",
+ avc_prefix, i);
+ break;
+ }
+ memset(new, 0, sizeof(*new));
+ new->next = avc_node_freelist;
+ avc_node_freelist = new;
+ }
+
+ if (!avc_setenforce) {
+ rc = security_getenforce();
+ if (rc < 0) {
+ avc_log(SELINUX_ERROR,
+ "%s: could not determine enforcing mode: %s\n",
+ avc_prefix,
+ strerror(errno));
+ goto out;
+ }
+ avc_enforcing = rc;
+ }
+
+ rc = avc_netlink_open(0);
+ if (rc < 0) {
+ avc_log(SELINUX_ERROR,
+ "%s: can't open netlink socket: %d (%s)\n",
+ avc_prefix, errno, strerror(errno));
+ goto out;
+ }
+ if (avc_using_threads) {
+ avc_netlink_thread = avc_create_thread(&avc_netlink_loop);
+ avc_netlink_trouble = 0;
+ }
+ avc_running = 1;
+ out:
+ return rc;
+}
+
+void avc_cache_stats(struct avc_cache_stats *p)
+{
+ memcpy(p, &cache_stats, sizeof(cache_stats));
+}
+
+void avc_sid_stats(void)
+{
+ /* avc_init needs to be called before this function */
+ assert(avc_running);
+ avc_get_lock(avc_log_lock);
+ avc_get_lock(avc_lock);
+ sidtab_sid_stats(&avc_sidtab, avc_audit_buf, AVC_AUDIT_BUFSIZE);
+ avc_release_lock(avc_lock);
+ avc_log(SELINUX_INFO, "%s", avc_audit_buf);
+ avc_release_lock(avc_log_lock);
+}
+
+void avc_av_stats(void)
+{
+ int i, chain_len, max_chain_len, slots_used;
+ struct avc_node *node;
+
+ avc_get_lock(avc_lock);
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ node = avc_cache.slots[i];
+ if (node) {
+ slots_used++;
+ chain_len = 0;
+ while (node) {
+ chain_len++;
+ node = node->next;
+ }
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ avc_release_lock(avc_lock);
+
+ avc_log(SELINUX_INFO, "%s: %d AV entries and %d/%d buckets used, "
+ "longest chain length %d\n", avc_prefix,
+ avc_cache.active_nodes,
+ slots_used, AVC_CACHE_SLOTS, max_chain_len);
+}
+
+hidden_def(avc_av_stats)
+
+static inline struct avc_node *avc_reclaim_node(void)
+{
+ struct avc_node *prev, *cur;
+ int try;
+ uint32_t hvalue;
+
+ hvalue = avc_cache.lru_hint;
+ for (try = 0; try < 2; try++) {
+ do {
+ prev = NULL;
+ cur = avc_cache.slots[hvalue];
+ while (cur) {
+ if (!cur->ae.used)
+ goto found;
+
+ cur->ae.used = 0;
+
+ prev = cur;
+ cur = cur->next;
+ }
+ hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1);
+ } while (hvalue != avc_cache.lru_hint);
+ }
+
+ errno = ENOMEM; /* this was a panic in the kernel... */
+ return NULL;
+
+ found:
+ avc_cache.lru_hint = hvalue;
+
+ if (prev == NULL)
+ avc_cache.slots[hvalue] = cur->next;
+ else
+ prev->next = cur->next;
+
+ return cur;
+}
+
+static inline void avc_clear_avc_entry(struct avc_entry *ae)
+{
+ memset(ae, 0, sizeof(*ae));
+}
+
+static inline struct avc_node *avc_claim_node(security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass)
+{
+ struct avc_node *new;
+ int hvalue;
+
+ if (!avc_node_freelist)
+ avc_cleanup();
+
+ if (avc_node_freelist) {
+ new = avc_node_freelist;
+ avc_node_freelist = avc_node_freelist->next;
+ avc_cache.active_nodes++;
+ } else {
+ new = avc_reclaim_node();
+ if (!new)
+ goto out;
+ }
+
+ hvalue = avc_hash(ssid, tsid, tclass);
+ avc_clear_avc_entry(&new->ae);
+ new->ae.used = 1;
+ new->ae.ssid = ssid;
+ new->ae.tsid = tsid;
+ new->ae.tclass = tclass;
+ new->next = avc_cache.slots[hvalue];
+ avc_cache.slots[hvalue] = new;
+
+ out:
+ return new;
+}
+
+static inline struct avc_node *avc_search_node(security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass,
+ int *probes)
+{
+ struct avc_node *cur;
+ int hvalue;
+ int tprobes = 1;
+
+ hvalue = avc_hash(ssid, tsid, tclass);
+ cur = avc_cache.slots[hvalue];
+ while (cur != NULL &&
+ (ssid != cur->ae.ssid ||
+ tclass != cur->ae.tclass || tsid != cur->ae.tsid)) {
+ tprobes++;
+ cur = cur->next;
+ }
+
+ if (cur == NULL) {
+ /* cache miss */
+ goto out;
+ }
+
+ /* cache hit */
+ if (probes)
+ *probes = tprobes;
+
+ cur->ae.used = 1;
+
+ out:
+ return cur;
+}
+
+/**
+ * avc_lookup - Look up an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @aeref: AVC entry reference
+ *
+ * Look up an AVC entry that is valid for the
+ * @requested permissions between the SID pair
+ * (@ssid, @tsid), interpreting the permissions
+ * based on @tclass. If a valid AVC entry exists,
+ * then this function updates @aeref to refer to the
+ * entry and returns %0. Otherwise, -1 is returned.
+ */
+static int avc_lookup(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass,
+ access_vector_t requested, struct avc_entry_ref *aeref)
+{
+ struct avc_node *node;
+ int probes, rc = 0;
+
+ avc_cache_stats_incr(cav_lookups);
+ node = avc_search_node(ssid, tsid, tclass, &probes);
+
+ if (node && ((node->ae.avd.decided & requested) == requested)) {
+ avc_cache_stats_incr(cav_hits);
+ avc_cache_stats_add(cav_probes, probes);
+ aeref->ae = &node->ae;
+ goto out;
+ }
+
+ avc_cache_stats_incr(cav_misses);
+ rc = -1;
+ out:
+ return rc;
+}
+
+/**
+ * avc_insert - Insert an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @ae: AVC entry
+ * @aeref: AVC entry reference
+ *
+ * Insert an AVC entry for the SID pair
+ * (@ssid, @tsid) and class @tclass.
+ * The access vectors and the sequence number are
+ * normally provided by the security server in
+ * response to a security_compute_av() call. If the
+ * sequence number @ae->avd.seqno is not less than the latest
+ * revocation notification, then the function copies
+ * the access vectors into a cache entry, updates
+ * @aeref to refer to the entry, and returns %0.
+ * Otherwise, this function returns -%1 with @errno set to %EAGAIN.
+ */
+static int avc_insert(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass,
+ struct avc_entry *ae, struct avc_entry_ref *aeref)
+{
+ struct avc_node *node;
+ int rc = 0;
+
+ if (ae->avd.seqno < avc_cache.latest_notif) {
+ avc_log(SELINUX_WARNING,
+ "%s: seqno %d < latest_notif %d\n", avc_prefix,
+ ae->avd.seqno, avc_cache.latest_notif);
+ errno = EAGAIN;
+ rc = -1;
+ goto out;
+ }
+
+ node = avc_claim_node(ssid, tsid, tclass);
+ if (!node) {
+ rc = -1;
+ goto out;
+ }
+
+ memcpy(&node->ae.avd, &ae->avd, sizeof(ae->avd));
+ aeref->ae = &node->ae;
+ out:
+ return rc;
+}
+
+void avc_cleanup(void)
+{
+}
+
+hidden_def(avc_cleanup)
+
+int avc_reset(void)
+{
+ struct avc_callback_node *c;
+ int i, ret, rc = 0, errsave = 0;
+ struct avc_node *node, *tmp;
+ errno = 0;
+
+ if (!avc_running)
+ return 0;
+
+ avc_get_lock(avc_lock);
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ node = avc_cache.slots[i];
+ while (node) {
+ tmp = node;
+ node = node->next;
+ avc_clear_avc_entry(&tmp->ae);
+ tmp->next = avc_node_freelist;
+ avc_node_freelist = tmp;
+ avc_cache.active_nodes--;
+ }
+ avc_cache.slots[i] = 0;
+ }
+ avc_cache.lru_hint = 0;
+
+ avc_release_lock(avc_lock);
+
+ memset(&cache_stats, 0, sizeof(cache_stats));
+
+ for (c = avc_callbacks; c; c = c->next) {
+ if (c->events & AVC_CALLBACK_RESET) {
+ ret = c->callback(AVC_CALLBACK_RESET, 0, 0, 0, 0, 0);
+ if (ret && !rc) {
+ rc = ret;
+ errsave = errno;
+ }
+ }
+ }
+ errno = errsave;
+ return rc;
+}
+
+hidden_def(avc_reset)
+
+void avc_destroy(void)
+{
+ struct avc_callback_node *c;
+ struct avc_node *node, *tmp;
+ int i;
+ /* avc_init needs to be called before this function */
+ assert(avc_running);
+
+ avc_get_lock(avc_lock);
+
+ if (avc_using_threads)
+ avc_stop_thread(avc_netlink_thread);
+ avc_netlink_close();
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ node = avc_cache.slots[i];
+ while (node) {
+ tmp = node;
+ node = node->next;
+ avc_free(tmp);
+ }
+ }
+ while (avc_node_freelist) {
+ tmp = avc_node_freelist;
+ avc_node_freelist = tmp->next;
+ avc_free(tmp);
+ }
+ avc_release_lock(avc_lock);
+
+ while (avc_callbacks) {
+ c = avc_callbacks;
+ avc_callbacks = c->next;
+ avc_free(c);
+ }
+ sidtab_destroy(&avc_sidtab);
+ avc_free_lock(avc_lock);
+ avc_free_lock(avc_log_lock);
+ avc_free(avc_audit_buf);
+ avc_running = 0;
+}
+
+/* ratelimit stuff put aside for now --EFW */
+#if 0
+/*
+ * Copied from net/core/utils.c:net_ratelimit and modified for
+ * use by the AVC audit facility.
+ */
+#define AVC_MSG_COST 5*HZ
+#define AVC_MSG_BURST 10*5*HZ
+
+/*
+ * This enforces a rate limit: not more than one kernel message
+ * every 5secs to make a denial-of-service attack impossible.
+ */
+static int avc_ratelimit(void)
+{
+ static unsigned long toks = 10 * 5 * HZ;
+ static unsigned long last_msg;
+ static int missed, rc = 0;
+ unsigned long now = jiffies;
+ void *ratelimit_lock = avc_alloc_lock();
+
+ avc_get_lock(ratelimit_lock);
+ toks += now - last_msg;
+ last_msg = now;
+ if (toks > AVC_MSG_BURST)
+ toks = AVC_MSG_BURST;
+ if (toks >= AVC_MSG_COST) {
+ int lost = missed;
+ missed = 0;
+ toks -= AVC_MSG_COST;
+ avc_release_lock(ratelimit_lock);
+ if (lost) {
+ avc_log(SELINUX_WARNING,
+ "%s: %d messages suppressed.\n", avc_prefix,
+ lost);
+ }
+ rc = 1;
+ goto out;
+ }
+ missed++;
+ avc_release_lock(ratelimit_lock);
+ out:
+ avc_free_lock(ratelimit_lock);
+ return rc;
+}
+
+static inline int check_avc_ratelimit(void)
+{
+ if (avc_enforcing)
+ return avc_ratelimit();
+ else {
+ /* If permissive, then never suppress messages. */
+ return 1;
+ }
+}
+#endif /* ratelimit stuff */
+
+/**
+ * avc_dump_av - Display an access vector in human-readable form.
+ * @tclass: target security class
+ * @av: access vector
+ */
+static void avc_dump_av(security_class_t tclass, access_vector_t av)
+{
+ const char *permstr;
+ access_vector_t bit = 1;
+
+ if (av == 0) {
+ log_append(avc_audit_buf, " null");
+ return;
+ }
+
+ log_append(avc_audit_buf, " {");
+
+ while (av) {
+ if (av & bit) {
+ permstr = security_av_perm_to_string(tclass, bit);
+ if (!permstr)
+ break;
+ log_append(avc_audit_buf, " %s", permstr);
+ av &= ~bit;
+ }
+ bit <<= 1;
+ }
+
+ if (av)
+ log_append(avc_audit_buf, " 0x%x", av);
+ log_append(avc_audit_buf, " }");
+}
+
+/**
+ * avc_dump_query - Display a SID pair and a class in human-readable form.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ */
+static void avc_dump_query(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass)
+{
+ avc_get_lock(avc_lock);
+
+ log_append(avc_audit_buf, "scontext=%s tcontext=%s",
+ ssid->ctx, tsid->ctx);
+
+ avc_release_lock(avc_lock);
+ log_append(avc_audit_buf, " tclass=%s",
+ security_class_to_string(tclass));
+}
+
+void avc_audit(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t requested,
+ struct av_decision *avd, int result, void *a)
+{
+ access_vector_t denied, audited;
+
+ denied = requested & ~avd->allowed;
+ if (denied)
+ audited = denied & avd->auditdeny;
+ else if (!requested || result)
+ audited = denied = requested;
+ else
+ audited = requested & avd->auditallow;
+ if (!audited)
+ return;
+#if 0
+ if (!check_avc_ratelimit())
+ return;
+#endif
+ /* prevent overlapping buffer writes */
+ avc_get_lock(avc_log_lock);
+ snprintf(avc_audit_buf, AVC_AUDIT_BUFSIZE,
+ "%s: %s ", avc_prefix, (denied || !requested) ? "denied" : "granted");
+ avc_dump_av(tclass, audited);
+ log_append(avc_audit_buf, " for ");
+
+ /* get any extra information printed by the callback */
+ avc_suppl_audit(a, tclass, avc_audit_buf + strlen(avc_audit_buf),
+ AVC_AUDIT_BUFSIZE - strlen(avc_audit_buf));
+
+ log_append(avc_audit_buf, " ");
+ avc_dump_query(ssid, tsid, tclass);
+
+ /* append permissive=0|1 like the kernel at the end */
+ if (denied || !requested)
+ log_append(avc_audit_buf, " permissive=%d", !result);
+
+ log_append(avc_audit_buf, "\n");
+ avc_log(SELINUX_AVC, "%s", avc_audit_buf);
+
+ avc_release_lock(avc_log_lock);
+}
+
+hidden_def(avc_audit)
+
+
+static void avd_init(struct av_decision *avd)
+{
+ avd->allowed = 0;
+ avd->auditallow = 0;
+ avd->auditdeny = 0xffffffff;
+ avd->seqno = avc_cache.latest_notif;
+ avd->flags = 0;
+}
+
+int avc_has_perm_noaudit(security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass,
+ access_vector_t requested,
+ struct avc_entry_ref *aeref, struct av_decision *avd)
+{
+ struct avc_entry *ae;
+ int rc = 0;
+ struct avc_entry entry;
+ access_vector_t denied;
+ struct avc_entry_ref ref;
+
+ if (avd)
+ avd_init(avd);
+
+ if (!avc_using_threads && !avc_app_main_loop) {
+ (void)avc_netlink_check_nb();
+ }
+
+ if (!aeref) {
+ avc_entry_ref_init(&ref);
+ aeref = &ref;
+ }
+
+ avc_get_lock(avc_lock);
+ avc_cache_stats_incr(entry_lookups);
+ ae = aeref->ae;
+ if (ae) {
+ if (ae->ssid == ssid &&
+ ae->tsid == tsid &&
+ ae->tclass == tclass &&
+ ((ae->avd.decided & requested) == requested)) {
+ avc_cache_stats_incr(entry_hits);
+ ae->used = 1;
+ } else {
+ avc_cache_stats_incr(entry_discards);
+ ae = 0;
+ }
+ }
+
+ if (!ae) {
+ avc_cache_stats_incr(entry_misses);
+ rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
+ if (rc) {
+ rc = security_compute_av(ssid->ctx, tsid->ctx,
+ tclass, requested,
+ &entry.avd);
+ if (rc && errno == EINVAL && !avc_enforcing) {
+ rc = errno = 0;
+ goto out;
+ }
+ if (rc)
+ goto out;
+ rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
+ if (rc)
+ goto out;
+ }
+ ae = aeref->ae;
+ }
+
+ if (avd)
+ memcpy(avd, &ae->avd, sizeof(*avd));
+
+ denied = requested & ~(ae->avd.allowed);
+
+ if (!requested || denied) {
+ if (!avc_enforcing ||
+ (ae->avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE))
+ ae->avd.allowed |= requested;
+ else {
+ errno = EACCES;
+ rc = -1;
+ }
+ }
+
+ out:
+ avc_release_lock(avc_lock);
+ return rc;
+}
+
+hidden_def(avc_has_perm_noaudit)
+
+int avc_has_perm(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t requested,
+ struct avc_entry_ref *aeref, void *auditdata)
+{
+ struct av_decision avd;
+ int errsave, rc;
+
+ rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
+ errsave = errno;
+ avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+ errno = errsave;
+ return rc;
+}
+
+int avc_compute_create(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, security_id_t *newsid)
+{
+ int rc;
+ struct avc_entry_ref aeref;
+ struct avc_entry entry;
+ char * ctx;
+
+ *newsid = NULL;
+ avc_entry_ref_init(&aeref);
+
+ avc_get_lock(avc_lock);
+
+ /* check for a cached entry */
+ rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
+ if (rc) {
+ /* need to make a cache entry for this tuple */
+ rc = security_compute_av(ssid->ctx, tsid->ctx,
+ tclass, 0, &entry.avd);
+ if (rc)
+ goto out;
+ rc = avc_insert(ssid, tsid, tclass, &entry, &aeref);
+ if (rc)
+ goto out;
+ }
+
+ /* check for a saved compute_create value */
+ if (!aeref.ae->create_sid) {
+ /* need to query the kernel policy */
+ rc = security_compute_create(ssid->ctx, tsid->ctx, tclass,
+ &ctx);
+ if (rc)
+ goto out;
+ rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
+ freecon(ctx);
+ if (rc)
+ goto out;
+
+ aeref.ae->create_sid = *newsid;
+ } else {
+ /* found saved value */
+ *newsid = aeref.ae->create_sid;
+ }
+
+ rc = 0;
+out:
+ avc_release_lock(avc_lock);
+ return rc;
+}
+
+int avc_add_callback(int (*callback) (uint32_t event, security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass,
+ access_vector_t perms,
+ access_vector_t * out_retained),
+ uint32_t events, security_id_t ssid,
+ security_id_t tsid,
+ security_class_t tclass, access_vector_t perms)
+{
+ struct avc_callback_node *c;
+ int rc = 0;
+
+ c = avc_malloc(sizeof(*c));
+ if (!c) {
+ rc = -1;
+ goto out;
+ }
+
+ c->callback = callback;
+ c->events = events;
+ c->ssid = ssid;
+ c->tsid = tsid;
+ c->tclass = tclass;
+ c->perms = perms;
+ c->next = avc_callbacks;
+ avc_callbacks = c;
+ out:
+ return rc;
+}
+
+static inline int avc_sidcmp(security_id_t x, security_id_t y)
+{
+ return (x == y || x == SECSID_WILD || y == SECSID_WILD);
+}
+
+static inline void avc_update_node(uint32_t event, struct avc_node *node,
+ access_vector_t perms)
+{
+ switch (event) {
+ case AVC_CALLBACK_GRANT:
+ node->ae.avd.allowed |= perms;
+ break;
+ case AVC_CALLBACK_TRY_REVOKE:
+ case AVC_CALLBACK_REVOKE:
+ node->ae.avd.allowed &= ~perms;
+ break;
+ case AVC_CALLBACK_AUDITALLOW_ENABLE:
+ node->ae.avd.auditallow |= perms;
+ break;
+ case AVC_CALLBACK_AUDITALLOW_DISABLE:
+ node->ae.avd.auditallow &= ~perms;
+ break;
+ case AVC_CALLBACK_AUDITDENY_ENABLE:
+ node->ae.avd.auditdeny |= perms;
+ break;
+ case AVC_CALLBACK_AUDITDENY_DISABLE:
+ node->ae.avd.auditdeny &= ~perms;
+ break;
+ }
+}
+
+static int avc_update_cache(uint32_t event, security_id_t ssid,
+ security_id_t tsid, security_class_t tclass,
+ access_vector_t perms)
+{
+ struct avc_node *node;
+ int i;
+
+ avc_get_lock(avc_lock);
+
+ if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
+ /* apply to all matching nodes */
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ for (node = avc_cache.slots[i]; node; node = node->next) {
+ if (avc_sidcmp(ssid, node->ae.ssid) &&
+ avc_sidcmp(tsid, node->ae.tsid) &&
+ tclass == node->ae.tclass) {
+ avc_update_node(event, node, perms);
+ }
+ }
+ }
+ } else {
+ /* apply to one node */
+ node = avc_search_node(ssid, tsid, tclass, 0);
+ if (node) {
+ avc_update_node(event, node, perms);
+ }
+ }
+
+ avc_release_lock(avc_lock);
+
+ return 0;
+}
+
+/* avc_control - update cache and call callbacks
+ *
+ * This should not be called directly; use the individual event
+ * functions instead.
+ */
+static int avc_control(uint32_t event, security_id_t ssid,
+ security_id_t tsid, security_class_t tclass,
+ access_vector_t perms,
+ uint32_t seqno, access_vector_t * out_retained)
+{
+ struct avc_callback_node *c;
+ access_vector_t tretained = 0, cretained = 0;
+ int ret, rc = 0, errsave = 0;
+ errno = 0;
+
+ /*
+ * try_revoke only removes permissions from the cache
+ * state if they are not retained by the object manager.
+ * Hence, try_revoke must wait until after the callbacks have
+ * been invoked to update the cache state.
+ */
+ if (event != AVC_CALLBACK_TRY_REVOKE)
+ avc_update_cache(event, ssid, tsid, tclass, perms);
+
+ for (c = avc_callbacks; c; c = c->next) {
+ if ((c->events & event) &&
+ avc_sidcmp(c->ssid, ssid) &&
+ avc_sidcmp(c->tsid, tsid) &&
+ c->tclass == tclass && (c->perms & perms)) {
+ cretained = 0;
+ ret = c->callback(event, ssid, tsid, tclass,
+ (c->perms & perms), &cretained);
+ if (ret && !rc) {
+ rc = ret;
+ errsave = errno;
+ }
+ if (!ret)
+ tretained |= cretained;
+ }
+ }
+
+ if (event == AVC_CALLBACK_TRY_REVOKE) {
+ /* revoke any unretained permissions */
+ perms &= ~tretained;
+ avc_update_cache(event, ssid, tsid, tclass, perms);
+ *out_retained = tretained;
+ }
+
+ avc_get_lock(avc_lock);
+ if (seqno > avc_cache.latest_notif)
+ avc_cache.latest_notif = seqno;
+ avc_release_lock(avc_lock);
+
+ errno = errsave;
+ return rc;
+}
+
+/**
+ * avc_ss_grant - Grant previously denied permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ */
+int avc_ss_grant(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno)
+{
+ return avc_control(AVC_CALLBACK_GRANT,
+ ssid, tsid, tclass, perms, seqno, 0);
+}
+
+/**
+ * avc_ss_try_revoke - Try to revoke previously granted permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ * @out_retained: subset of @perms that are retained
+ *
+ * Try to revoke previously granted permissions, but
+ * only if they are not retained as migrated permissions.
+ * Return the subset of permissions that are retained via @out_retained.
+ */
+int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass,
+ access_vector_t perms, uint32_t seqno,
+ access_vector_t * out_retained)
+{
+ return avc_control(AVC_CALLBACK_TRY_REVOKE,
+ ssid, tsid, tclass, perms, seqno, out_retained);
+}
+
+/**
+ * avc_ss_revoke - Revoke previously granted permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ *
+ * Revoke previously granted permissions, even if
+ * they are retained as migrated permissions.
+ */
+int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno)
+{
+ return avc_control(AVC_CALLBACK_REVOKE,
+ ssid, tsid, tclass, perms, seqno, 0);
+}
+
+/**
+ * avc_ss_reset - Flush the cache and revalidate migrated permissions.
+ * @seqno: policy sequence number
+ */
+int avc_ss_reset(uint32_t seqno)
+{
+ int rc;
+
+ rc = avc_reset();
+
+ avc_get_lock(avc_lock);
+ if (seqno > avc_cache.latest_notif)
+ avc_cache.latest_notif = seqno;
+ avc_release_lock(avc_lock);
+
+ return rc;
+}
+
+/**
+ * avc_ss_set_auditallow - Enable or disable auditing of granted permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ * @enable: enable flag.
+ */
+int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno, uint32_t enable)
+{
+ if (enable)
+ return avc_control(AVC_CALLBACK_AUDITALLOW_ENABLE,
+ ssid, tsid, tclass, perms, seqno, 0);
+ else
+ return avc_control(AVC_CALLBACK_AUDITALLOW_DISABLE,
+ ssid, tsid, tclass, perms, seqno, 0);
+}
+
+/**
+ * avc_ss_set_auditdeny - Enable or disable auditing of denied permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ * @enable: enable flag.
+ */
+int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno, uint32_t enable)
+{
+ if (enable)
+ return avc_control(AVC_CALLBACK_AUDITDENY_ENABLE,
+ ssid, tsid, tclass, perms, seqno, 0);
+ else
+ return avc_control(AVC_CALLBACK_AUDITDENY_DISABLE,
+ ssid, tsid, tclass, perms, seqno, 0);
+}
diff --git a/libselinux/src/avc_internal.c b/libselinux/src/avc_internal.c
new file mode 100644
index 0000000..c89a886
--- /dev/null
+++ b/libselinux/src/avc_internal.c
@@ -0,0 +1,292 @@
+/*
+ * Callbacks for user-supplied memory allocation, supplemental
+ * auditing, and locking routines.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ *
+ * Netlink code derived in part from sample code by
+ * James Morris <jmorris@redhat.com>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include "callbacks.h"
+#include "selinux_netlink.h"
+#include "avc_internal.h"
+
+#ifndef NETLINK_SELINUX
+#define NETLINK_SELINUX 7
+#endif
+
+/* callback pointers */
+void *(*avc_func_malloc) (size_t) = NULL;
+void (*avc_func_free) (void *) = NULL;
+
+void (*avc_func_log) (const char *, ...) = NULL;
+void (*avc_func_audit) (void *, security_class_t, char *, size_t) = NULL;
+
+int avc_using_threads = 0;
+int avc_app_main_loop = 0;
+void *(*avc_func_create_thread) (void (*)(void)) = NULL;
+void (*avc_func_stop_thread) (void *) = NULL;
+
+void *(*avc_func_alloc_lock) (void) = NULL;
+void (*avc_func_get_lock) (void *) = NULL;
+void (*avc_func_release_lock) (void *) = NULL;
+void (*avc_func_free_lock) (void *) = NULL;
+
+/* message prefix string and avc enforcing mode */
+char avc_prefix[AVC_PREFIX_SIZE] = "uavc";
+int avc_running = 0;
+int avc_enforcing = 1;
+int avc_setenforce = 0;
+int avc_netlink_trouble = 0;
+
+/* netlink socket code */
+static int fd = -1;
+
+int avc_netlink_open(int blocking)
+{
+ int len, rc = 0;
+ struct sockaddr_nl addr;
+
+ fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SELINUX);
+ if (fd < 0) {
+ rc = fd;
+ goto out;
+ }
+
+ if (!blocking && fcntl(fd, F_SETFL, O_NONBLOCK)) {
+ close(fd);
+ fd = -1;
+ rc = -1;
+ goto out;
+ }
+
+ len = sizeof(addr);
+
+ memset(&addr, 0, len);
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = SELNL_GRP_AVC;
+
+ if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
+ close(fd);
+ fd = -1;
+ rc = -1;
+ goto out;
+ }
+ out:
+ return rc;
+}
+
+void avc_netlink_close(void)
+{
+ if (fd >= 0)
+ close(fd);
+ fd = -1;
+}
+
+static int avc_netlink_receive(char *buf, unsigned buflen, int blocking)
+{
+ int rc;
+ struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof nladdr;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+
+ do {
+ rc = poll(&pfd, 1, (blocking ? -1 : 0));
+ } while (rc < 0 && errno == EINTR);
+
+ if (rc == 0 && !blocking) {
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ else if (rc < 1) {
+ avc_log(SELINUX_ERROR, "%s: netlink poll: error %d\n",
+ avc_prefix, errno);
+ return rc;
+ }
+
+ rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
+ &nladdrlen);
+ if (rc < 0)
+ return rc;
+
+ if (nladdrlen != sizeof nladdr) {
+ avc_log(SELINUX_WARNING,
+ "%s: warning: netlink address truncated, len %d?\n",
+ avc_prefix, nladdrlen);
+ return -1;
+ }
+
+ if (nladdr.nl_pid) {
+ avc_log(SELINUX_WARNING,
+ "%s: warning: received spoofed netlink packet from: %d\n",
+ avc_prefix, nladdr.nl_pid);
+ return -1;
+ }
+
+ if (rc == 0) {
+ avc_log(SELINUX_WARNING,
+ "%s: warning: received EOF on netlink socket\n",
+ avc_prefix);
+ errno = EBADFD;
+ return -1;
+ }
+
+ if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
+ avc_log(SELINUX_WARNING,
+ "%s: warning: incomplete netlink message\n",
+ avc_prefix);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int avc_netlink_process(char *buf)
+{
+ int rc;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+
+ switch (nlh->nlmsg_type) {
+ case NLMSG_ERROR:{
+ struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+ /* Netlink ack */
+ if (err->error == 0)
+ break;
+
+ errno = -err->error;
+ avc_log(SELINUX_ERROR,
+ "%s: netlink error: %d\n", avc_prefix, errno);
+ return -1;
+ }
+
+ case SELNL_MSG_SETENFORCE:{
+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+ avc_log(SELINUX_INFO,
+ "%s: received setenforce notice (enforcing=%d)\n",
+ avc_prefix, msg->val);
+ if (avc_setenforce)
+ break;
+ avc_enforcing = msg->val;
+ if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
+ avc_log(SELINUX_ERROR,
+ "%s: cache reset returned %d (errno %d)\n",
+ avc_prefix, rc, errno);
+ return rc;
+ }
+ rc = selinux_netlink_setenforce(msg->val);
+ if (rc < 0)
+ return rc;
+ break;
+ }
+
+ case SELNL_MSG_POLICYLOAD:{
+ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
+ avc_log(SELINUX_INFO,
+ "%s: received policyload notice (seqno=%d)\n",
+ avc_prefix, msg->seqno);
+ rc = avc_ss_reset(msg->seqno);
+ if (rc < 0) {
+ avc_log(SELINUX_ERROR,
+ "%s: cache reset returned %d (errno %d)\n",
+ avc_prefix, rc, errno);
+ return rc;
+ }
+ rc = selinux_netlink_policyload(msg->seqno);
+ if (rc < 0)
+ return rc;
+ break;
+ }
+
+ default:
+ avc_log(SELINUX_WARNING,
+ "%s: warning: unknown netlink message %d\n",
+ avc_prefix, nlh->nlmsg_type);
+ }
+ return 0;
+}
+
+int avc_netlink_check_nb(void)
+{
+ int rc;
+ char buf[1024] __attribute__ ((aligned));
+
+ while (1) {
+ errno = 0;
+ rc = avc_netlink_receive(buf, sizeof(buf), 0);
+ if (rc < 0) {
+ if (errno == EWOULDBLOCK)
+ return 0;
+ if (errno == 0 || errno == EINTR)
+ continue;
+ else {
+ avc_log(SELINUX_ERROR,
+ "%s: netlink recvfrom: error %d\n",
+ avc_prefix, errno);
+ return rc;
+ }
+ }
+
+ (void)avc_netlink_process(buf);
+ }
+ return 0;
+}
+
+/* run routine for the netlink listening thread */
+void avc_netlink_loop(void)
+{
+ int rc;
+ char buf[1024] __attribute__ ((aligned));
+
+ while (1) {
+ errno = 0;
+ rc = avc_netlink_receive(buf, sizeof(buf), 1);
+ if (rc < 0) {
+ if (errno == 0 || errno == EINTR)
+ continue;
+ else {
+ avc_log(SELINUX_ERROR,
+ "%s: netlink recvfrom: error %d\n",
+ avc_prefix, errno);
+ break;
+ }
+ }
+
+ rc = avc_netlink_process(buf);
+ if (rc < 0)
+ break;
+ }
+
+ close(fd);
+ fd = -1;
+ avc_netlink_trouble = 1;
+ avc_log(SELINUX_ERROR,
+ "%s: netlink thread: errors encountered, terminating\n",
+ avc_prefix);
+}
+
+int avc_netlink_acquire_fd(void)
+{
+ avc_app_main_loop = 1;
+
+ return fd;
+}
+
+void avc_netlink_release_fd(void)
+{
+ avc_app_main_loop = 0;
+}
diff --git a/libselinux/src/avc_internal.h b/libselinux/src/avc_internal.h
new file mode 100644
index 0000000..53610e8
--- /dev/null
+++ b/libselinux/src/avc_internal.h
@@ -0,0 +1,182 @@
+/*
+ * This file describes the internal interface used by the AVC
+ * for calling the user-supplied memory allocation, supplemental
+ * auditing, and locking routine, as well as incrementing the
+ * statistics fields.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_INTERNAL_H_
+#define _SELINUX_AVC_INTERNAL_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <selinux/avc.h>
+#include "callbacks.h"
+#include "dso.h"
+
+/* callback pointers */
+extern void *(*avc_func_malloc) (size_t) hidden;
+extern void (*avc_func_free) (void *)hidden;
+
+extern void (*avc_func_log) (const char *, ...)hidden;
+extern void (*avc_func_audit) (void *, security_class_t, char *, size_t)hidden;
+
+extern int avc_using_threads hidden;
+extern int avc_app_main_loop hidden;
+extern void *(*avc_func_create_thread) (void (*)(void))hidden;
+extern void (*avc_func_stop_thread) (void *)hidden;
+
+extern void *(*avc_func_alloc_lock) (void)hidden;
+extern void (*avc_func_get_lock) (void *)hidden;
+extern void (*avc_func_release_lock) (void *)hidden;
+extern void (*avc_func_free_lock) (void *)hidden;
+
+static inline void set_callbacks(const struct avc_memory_callback *mem_cb,
+ const struct avc_log_callback *log_cb,
+ const struct avc_thread_callback *thread_cb,
+ const struct avc_lock_callback *lock_cb)
+{
+ if (mem_cb) {
+ avc_func_malloc = mem_cb->func_malloc;
+ avc_func_free = mem_cb->func_free;
+ }
+ if (log_cb) {
+ avc_func_log = log_cb->func_log;
+ avc_func_audit = log_cb->func_audit;
+ }
+ if (thread_cb) {
+ avc_using_threads = 1;
+ avc_func_create_thread = thread_cb->func_create_thread;
+ avc_func_stop_thread = thread_cb->func_stop_thread;
+ }
+ if (lock_cb) {
+ avc_func_alloc_lock = lock_cb->func_alloc_lock;
+ avc_func_get_lock = lock_cb->func_get_lock;
+ avc_func_release_lock = lock_cb->func_release_lock;
+ avc_func_free_lock = lock_cb->func_free_lock;
+ }
+}
+
+/* message prefix and enforcing mode*/
+#define AVC_PREFIX_SIZE 16
+extern char avc_prefix[AVC_PREFIX_SIZE] hidden;
+extern int avc_running hidden;
+extern int avc_enforcing hidden;
+extern int avc_setenforce hidden;
+
+/* user-supplied callback interface for avc */
+static inline void *avc_malloc(size_t size)
+{
+ return avc_func_malloc ? avc_func_malloc(size) : malloc(size);
+}
+
+static inline void avc_free(void *ptr)
+{
+ if (avc_func_free)
+ avc_func_free(ptr);
+ else
+ free(ptr);
+}
+
+/* this is a macro in order to use the variadic capability. */
+#define avc_log(type, format...) \
+ if (avc_func_log) \
+ avc_func_log(format); \
+ else \
+ selinux_log(type, format);
+
+static inline void avc_suppl_audit(void *ptr, security_class_t class,
+ char *buf, size_t len)
+{
+ if (avc_func_audit)
+ avc_func_audit(ptr, class, buf, len);
+ else
+ selinux_audit(ptr, class, buf, len);
+}
+
+static inline void *avc_create_thread(void (*run) (void))
+{
+ return avc_func_create_thread ? avc_func_create_thread(run) : NULL;
+}
+
+static inline void avc_stop_thread(void *thread)
+{
+ if (avc_func_stop_thread)
+ avc_func_stop_thread(thread);
+}
+
+static inline void *avc_alloc_lock(void)
+{
+ return avc_func_alloc_lock ? avc_func_alloc_lock() : NULL;
+}
+
+static inline void avc_get_lock(void *lock)
+{
+ if (avc_func_get_lock)
+ avc_func_get_lock(lock);
+}
+
+static inline void avc_release_lock(void *lock)
+{
+ if (avc_func_release_lock)
+ avc_func_release_lock(lock);
+}
+
+static inline void avc_free_lock(void *lock)
+{
+ if (avc_func_free_lock)
+ avc_func_free_lock(lock);
+}
+
+/* statistics helper routines */
+#ifdef AVC_CACHE_STATS
+
+#define avc_cache_stats_incr(field) \
+ cache_stats.field ++;
+#define avc_cache_stats_add(field, num) \
+ cache_stats.field += num;
+
+#else
+
+#define avc_cache_stats_incr(field)
+#define avc_cache_stats_add(field, num)
+
+#endif
+
+/* logging helper routines */
+#define AVC_AUDIT_BUFSIZE 1024
+
+/* again, we need the variadic capability here */
+#define log_append(buf,format...) \
+ snprintf(buf+strlen(buf), AVC_AUDIT_BUFSIZE-strlen(buf), format)
+
+/* internal callbacks */
+int avc_ss_grant(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno) hidden;
+int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass,
+ access_vector_t perms, uint32_t seqno,
+ access_vector_t * out_retained) hidden;
+int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno) hidden;
+int avc_ss_reset(uint32_t seqno) hidden;
+int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno, uint32_t enable) hidden;
+int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
+ security_class_t tclass, access_vector_t perms,
+ uint32_t seqno, uint32_t enable) hidden;
+
+/* netlink kernel message code */
+extern int avc_netlink_trouble hidden;
+
+hidden_proto(avc_av_stats)
+ hidden_proto(avc_cleanup)
+ hidden_proto(avc_reset)
+ hidden_proto(avc_audit)
+ hidden_proto(avc_has_perm_noaudit)
+#endif /* _SELINUX_AVC_INTERNAL_H_ */
diff --git a/libselinux/src/avc_sidtab.c b/libselinux/src/avc_sidtab.c
new file mode 100644
index 0000000..52f21df
--- /dev/null
+++ b/libselinux/src/avc_sidtab.c
@@ -0,0 +1,152 @@
+/*
+ * Implementation of the userspace SID hashtable.
+ *
+ * Author : Eamon Walsh, <ewalsh@epoch.ncsc.mil>
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <selinux/avc.h>
+#include "avc_sidtab.h"
+#include "avc_internal.h"
+
+static inline unsigned sidtab_hash(const char * key)
+{
+ char *p, *keyp;
+ unsigned int size;
+ unsigned int val;
+
+ val = 0;
+ keyp = (char *)key;
+ size = strlen(keyp);
+ for (p = keyp; (unsigned int)(p - keyp) < size; p++)
+ val =
+ (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
+ return val & (SIDTAB_SIZE - 1);
+}
+
+int sidtab_init(struct sidtab *s)
+{
+ int i, rc = 0;
+
+ s->htable = (struct sidtab_node **)avc_malloc
+ (sizeof(struct sidtab_node *) * SIDTAB_SIZE);
+
+ if (!s->htable) {
+ rc = -1;
+ goto out;
+ }
+ for (i = 0; i < SIDTAB_SIZE; i++)
+ s->htable[i] = NULL;
+ s->nel = 0;
+ out:
+ return rc;
+}
+
+int sidtab_insert(struct sidtab *s, const char * ctx)
+{
+ int hvalue, rc = 0;
+ struct sidtab_node *newnode;
+ char * newctx;
+
+ newnode = (struct sidtab_node *)avc_malloc(sizeof(*newnode));
+ if (!newnode) {
+ rc = -1;
+ goto out;
+ }
+ newctx = (char *) strdup(ctx);
+ if (!newctx) {
+ rc = -1;
+ avc_free(newnode);
+ goto out;
+ }
+
+ hvalue = sidtab_hash(newctx);
+ newnode->next = s->htable[hvalue];
+ newnode->sid_s.ctx = newctx;
+ newnode->sid_s.refcnt = 1; /* unused */
+ s->htable[hvalue] = newnode;
+ s->nel++;
+ out:
+ return rc;
+}
+
+int
+sidtab_context_to_sid(struct sidtab *s,
+ const char * ctx, security_id_t * sid)
+{
+ int hvalue, rc = 0;
+ struct sidtab_node *cur;
+
+ *sid = NULL;
+ hvalue = sidtab_hash(ctx);
+
+ loop:
+ cur = s->htable[hvalue];
+ while (cur != NULL && strcmp(cur->sid_s.ctx, ctx))
+ cur = cur->next;
+
+ if (cur == NULL) { /* need to make a new entry */
+ rc = sidtab_insert(s, ctx);
+ if (rc)
+ goto out;
+ goto loop; /* find the newly inserted node */
+ }
+
+ *sid = &cur->sid_s;
+ out:
+ return rc;
+}
+
+void sidtab_sid_stats(struct sidtab *h, char *buf, int buflen)
+{
+ int i, chain_len, slots_used, max_chain_len;
+ struct sidtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ snprintf(buf, buflen,
+ "%s: %d SID entries and %d/%d buckets used, longest "
+ "chain length %d\n", avc_prefix, h->nel, slots_used,
+ SIDTAB_SIZE, max_chain_len);
+}
+
+void sidtab_destroy(struct sidtab *s)
+{
+ int i;
+ struct sidtab_node *cur, *temp;
+
+ if (!s)
+ return;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur != NULL) {
+ temp = cur;
+ cur = cur->next;
+ freecon(temp->sid_s.ctx);
+ avc_free(temp);
+ }
+ s->htable[i] = NULL;
+ }
+ avc_free(s->htable);
+ s->htable = NULL;
+}
diff --git a/libselinux/src/avc_sidtab.h b/libselinux/src/avc_sidtab.h
new file mode 100644
index 0000000..bce9b87
--- /dev/null
+++ b/libselinux/src/avc_sidtab.h
@@ -0,0 +1,36 @@
+/*
+ * A security identifier table (sidtab) is a hash table
+ * of security context structures indexed by SID value.
+ */
+#ifndef _SELINUX_AVC_SIDTAB_H_
+#define _SELINUX_AVC_SIDTAB_H_
+
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include "dso.h"
+
+struct sidtab_node {
+ struct security_id sid_s;
+ struct sidtab_node *next;
+};
+
+#define SIDTAB_HASH_BITS 7
+#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
+#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
+#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
+
+struct sidtab {
+ struct sidtab_node **htable;
+ unsigned nel;
+};
+
+int sidtab_init(struct sidtab *s) hidden;
+int sidtab_insert(struct sidtab *s, const char * ctx) hidden;
+
+int sidtab_context_to_sid(struct sidtab *s,
+ const char * ctx, security_id_t * sid) hidden;
+
+void sidtab_sid_stats(struct sidtab *s, char *buf, int buflen) hidden;
+void sidtab_destroy(struct sidtab *s) hidden;
+
+#endif /* _SELINUX_AVC_SIDTAB_H_ */
diff --git a/libselinux/src/booleans.c b/libselinux/src/booleans.c
new file mode 100644
index 0000000..17e0ad8
--- /dev/null
+++ b/libselinux/src/booleans.c
@@ -0,0 +1,263 @@
+/*
+ * Author: Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Modified:
+ * Dan Walsh <dwalsh@redhat.com> - Added security_load_booleans().
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "selinux_internal.h"
+#include "policy.h"
+
+#define SELINUX_BOOL_DIR "/booleans/"
+
+static int filename_select(const struct dirent *d)
+{
+ if (d->d_name[0] == '.'
+ && (d->d_name[1] == '\0'
+ || (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ return 0;
+ return 1;
+}
+
+int security_get_boolean_names(char ***names, int *len)
+{
+ char path[PATH_MAX];
+ int i, rc;
+ struct dirent **namelist;
+ char **n;
+
+ assert(len);
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s%s", selinux_mnt, SELINUX_BOOL_DIR);
+ *len = scandir(path, &namelist, &filename_select, alphasort);
+ if (*len <= 0) {
+ return -1;
+ }
+
+ n = (char **)malloc(sizeof(char *) * *len);
+ if (!n) {
+ rc = -1;
+ goto bad;
+ }
+
+ for (i = 0; i < *len; i++) {
+ n[i] = strdup(namelist[i]->d_name);
+ if (!n[i]) {
+ rc = -1;
+ goto bad_freen;
+ }
+ }
+ rc = 0;
+ *names = n;
+ out:
+ for (i = 0; i < *len; i++) {
+ free(namelist[i]);
+ }
+ free(namelist);
+ return rc;
+ bad_freen:
+ for (--i; i >= 0; --i)
+ free(n[i]);
+ free(n);
+ bad:
+ goto out;
+}
+
+hidden_def(security_get_boolean_names)
+#define STRBUF_SIZE 3
+static int get_bool_value(const char *name, char **buf)
+{
+ int fd, len;
+ char *fname = NULL;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ *buf = (char *)malloc(sizeof(char) * (STRBUF_SIZE + 1));
+ if (!*buf)
+ goto out;
+ (*buf)[STRBUF_SIZE] = 0;
+
+ len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
+ fname = (char *)malloc(sizeof(char) * len);
+ if (!fname)
+ goto out;
+ snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name);
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ len = read(fd, *buf, STRBUF_SIZE);
+ close(fd);
+ if (len != STRBUF_SIZE)
+ goto out;
+
+ free(fname);
+ return 0;
+ out:
+ if (*buf)
+ free(*buf);
+ if (fname)
+ free(fname);
+ return -1;
+}
+
+int security_get_boolean_pending(const char *name)
+{
+ char *buf;
+ int val;
+
+ if (get_bool_value(name, &buf))
+ return -1;
+
+ if (atoi(&buf[1]))
+ val = 1;
+ else
+ val = 0;
+ free(buf);
+ return val;
+}
+
+int security_get_boolean_active(const char *name)
+{
+ char *buf;
+ int val;
+
+ if (get_bool_value(name, &buf))
+ return -1;
+
+ buf[1] = '\0';
+ if (atoi(buf))
+ val = 1;
+ else
+ val = 0;
+ free(buf);
+ return val;
+}
+
+hidden_def(security_get_boolean_active)
+
+int security_set_boolean(const char *name, int value)
+{
+ int fd, ret, len;
+ char buf[2], *fname;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (value < 0 || value > 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
+ fname = (char *)malloc(sizeof(char) * len);
+ if (!fname)
+ return -1;
+ snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name);
+
+ fd = open(fname, O_WRONLY);
+ if (fd < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ if (value)
+ buf[0] = '1';
+ else
+ buf[0] = '0';
+ buf[1] = '\0';
+
+ ret = write(fd, buf, 2);
+ close(fd);
+ out:
+ free(fname);
+ if (ret > 0)
+ return 0;
+ else
+ return -1;
+}
+
+hidden_def(security_set_boolean)
+
+int security_commit_booleans(void)
+{
+ int fd, ret;
+ char buf[2];
+ char path[PATH_MAX];
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/commit_pending_bools", selinux_mnt);
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ return -1;
+
+ buf[0] = '1';
+ buf[1] = '\0';
+
+ ret = write(fd, buf, 2);
+ close(fd);
+
+ if (ret > 0)
+ return 0;
+ else
+ return -1;
+}
+
+hidden_def(security_commit_booleans)
+
+static void rollback(SELboolean * boollist, int end)
+{
+ int i;
+
+ for (i = 0; i < end; i++)
+ security_set_boolean(boollist[i].name,
+ security_get_boolean_active(boollist[i].
+ name));
+}
+
+int security_set_boolean_list(size_t boolcnt, SELboolean * const boollist,
+ int permanent __attribute__((unused)))
+{
+
+ size_t i;
+ for (i = 0; i < boolcnt; i++) {
+ if (security_set_boolean(boollist[i].name, boollist[i].value)) {
+ rollback(boollist, i);
+ return -1;
+ }
+ }
+
+ /* OK, let's do the commit */
+ if (security_commit_booleans()) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/libselinux/src/callbacks.c b/libselinux/src/callbacks.c
new file mode 100644
index 0000000..c3cf98b
--- /dev/null
+++ b/libselinux/src/callbacks.c
@@ -0,0 +1,124 @@
+/*
+ * User-supplied callbacks and default implementations.
+ * Class and permission mappings.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+#include "callbacks.h"
+
+/* default implementations */
+static int __attribute__ ((format(printf, 2, 3)))
+default_selinux_log(int type __attribute__((unused)), const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+ va_start(ap, fmt);
+ rc = vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return rc;
+}
+
+static int
+default_selinux_audit(void *ptr __attribute__((unused)),
+ security_class_t cls __attribute__((unused)),
+ char *buf __attribute__((unused)),
+ size_t len __attribute__((unused)))
+{
+ return 0;
+}
+
+static int
+default_selinux_validate(char **ctx)
+{
+ return security_check_context(*ctx);
+}
+
+static int
+default_selinux_setenforce(int enforcing __attribute__((unused)))
+{
+ return 0;
+}
+
+static int
+default_selinux_policyload(int seqno __attribute__((unused)))
+{
+ return 0;
+}
+
+/* callback pointers */
+int __attribute__ ((format(printf, 2, 3)))
+(*selinux_log)(int, const char *, ...) =
+ default_selinux_log;
+
+int
+(*selinux_audit) (void *, security_class_t, char *, size_t) =
+ default_selinux_audit;
+
+int
+(*selinux_validate)(char **ctx) =
+ default_selinux_validate;
+
+int
+(*selinux_netlink_setenforce) (int enforcing) =
+ default_selinux_setenforce;
+
+int
+(*selinux_netlink_policyload) (int seqno) =
+ default_selinux_policyload;
+
+/* callback setting function */
+void
+selinux_set_callback(int type, union selinux_callback cb)
+{
+ switch (type) {
+ case SELINUX_CB_LOG:
+ selinux_log = cb.func_log;
+ break;
+ case SELINUX_CB_AUDIT:
+ selinux_audit = cb.func_audit;
+ break;
+ case SELINUX_CB_VALIDATE:
+ selinux_validate = cb.func_validate;
+ break;
+ case SELINUX_CB_SETENFORCE:
+ selinux_netlink_setenforce = cb.func_setenforce;
+ break;
+ case SELINUX_CB_POLICYLOAD:
+ selinux_netlink_policyload = cb.func_policyload;
+ break;
+ }
+}
+
+/* callback getting function */
+union selinux_callback
+selinux_get_callback(int type)
+{
+ union selinux_callback cb;
+
+ switch (type) {
+ case SELINUX_CB_LOG:
+ cb.func_log = selinux_log;
+ break;
+ case SELINUX_CB_AUDIT:
+ cb.func_audit = selinux_audit;
+ break;
+ case SELINUX_CB_VALIDATE:
+ cb.func_validate = selinux_validate;
+ break;
+ case SELINUX_CB_SETENFORCE:
+ cb.func_setenforce = selinux_netlink_setenforce;
+ break;
+ case SELINUX_CB_POLICYLOAD:
+ cb.func_policyload = selinux_netlink_policyload;
+ break;
+ default:
+ memset(&cb, 0, sizeof(cb));
+ errno = EINVAL;
+ break;
+ }
+ return cb;
+}
diff --git a/libselinux/src/callbacks.h b/libselinux/src/callbacks.h
new file mode 100644
index 0000000..2a572e0
--- /dev/null
+++ b/libselinux/src/callbacks.h
@@ -0,0 +1,30 @@
+/*
+ * This file describes the callbacks passed to selinux_init() and available
+ * for use from the library code. They all have default implementations.
+ */
+#ifndef _SELINUX_CALLBACKS_H_
+#define _SELINUX_CALLBACKS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <selinux/selinux.h>
+#include "dso.h"
+
+/* callback pointers */
+extern int __attribute__ ((format(printf, 2, 3)))
+(*selinux_log) (int type, const char *, ...) hidden;
+
+extern int
+(*selinux_audit) (void *, security_class_t, char *, size_t) hidden;
+
+extern int
+(*selinux_validate)(char **ctx) hidden;
+
+extern int
+(*selinux_netlink_setenforce) (int enforcing) hidden;
+
+extern int
+(*selinux_netlink_policyload) (int seqno) hidden;
+
+#endif /* _SELINUX_CALLBACKS_H_ */
diff --git a/libselinux/src/canonicalize_context.c b/libselinux/src/canonicalize_context.c
new file mode 100644
index 0000000..b8f874f
--- /dev/null
+++ b/libselinux/src/canonicalize_context.c
@@ -0,0 +1,62 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <limits.h>
+
+int security_canonicalize_context(const char * con,
+ char ** canoncon)
+{
+ char path[PATH_MAX];
+ char *buf;
+ size_t size;
+ int fd, ret;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/context", selinux_mnt);
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ size = selinux_page_size;
+ buf = malloc(size);
+ if (!buf) {
+ ret = -1;
+ goto out;
+ }
+ strncpy(buf, con, size);
+
+ ret = write(fd, buf, strlen(buf) + 1);
+ if (ret < 0)
+ goto out2;
+
+ memset(buf, 0, size);
+ ret = read(fd, buf, size - 1);
+ if (ret < 0 && errno == EINVAL) {
+ /* Fall back to the original context for kernels
+ that do not support the extended interface. */
+ strncpy(buf, con, size);
+ }
+
+ *canoncon = strdup(buf);
+ if (!(*canoncon)) {
+ ret = -1;
+ goto out2;
+ }
+ ret = 0;
+ out2:
+ free(buf);
+ out:
+ close(fd);
+ return ret;
+}
+
diff --git a/libselinux/src/checkAccess.c b/libselinux/src/checkAccess.c
new file mode 100644
index 0000000..dc11cf7
--- /dev/null
+++ b/libselinux/src/checkAccess.c
@@ -0,0 +1,62 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "selinux_internal.h"
+#include <selinux/avc.h>
+#include "avc_internal.h"
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static int selinux_enabled;
+
+static void avc_init_once(void)
+{
+ selinux_enabled = is_selinux_enabled();
+ if (selinux_enabled == 1)
+ avc_open(NULL, 0);
+}
+
+int selinux_check_access(const char * scon, const char * tcon, const char *class, const char *perm, void *aux) {
+ int rc;
+ security_id_t scon_id;
+ security_id_t tcon_id;
+ security_class_t sclass;
+ access_vector_t av;
+
+ __selinux_once(once, avc_init_once);
+
+ if (selinux_enabled != 1)
+ return 0;
+
+ rc = avc_context_to_sid(scon, &scon_id);
+ if (rc < 0)
+ return rc;
+
+ rc = avc_context_to_sid(tcon, &tcon_id);
+ if (rc < 0)
+ return rc;
+
+ sclass = string_to_security_class(class);
+ if (sclass == 0) {
+ rc = errno;
+ avc_log(SELINUX_ERROR, "Unknown class %s", class);
+ if (security_deny_unknown() == 0)
+ return 0;
+ errno = rc;
+ return -1;
+ }
+
+ av = string_to_av_perm(sclass, perm);
+ if (av == 0) {
+ rc = errno;
+ avc_log(SELINUX_ERROR, "Unknown permission %s for class %s", perm, class);
+ if (security_deny_unknown() == 0)
+ return 0;
+ errno = rc;
+ return -1;
+ }
+
+ return avc_has_perm (scon_id, tcon_id, sclass, av, NULL, aux);
+}
+
diff --git a/libselinux/src/check_context.c b/libselinux/src/check_context.c
new file mode 100644
index 0000000..7471194
--- /dev/null
+++ b/libselinux/src/check_context.c
@@ -0,0 +1,33 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <limits.h>
+
+int security_check_context(const char * con)
+{
+ char path[PATH_MAX];
+ int fd, ret;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/context", selinux_mnt);
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ ret = write(fd, con, strlen(con) + 1);
+ close(fd);
+ if (ret < 0)
+ return -1;
+ return 0;
+}
+
diff --git a/libselinux/src/compute_av.c b/libselinux/src/compute_av.c
new file mode 100644
index 0000000..d6f76f8
--- /dev/null
+++ b/libselinux/src/compute_av.c
@@ -0,0 +1,72 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+int security_compute_av(const char * scon,
+ const char * tcon,
+ security_class_t tclass,
+ access_vector_t requested,
+ struct av_decision *avd)
+{
+ char path[PATH_MAX];
+ char *buf;
+ size_t len;
+ int fd, ret;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/access", selinux_mnt);
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ len = selinux_page_size;
+ buf = malloc(len);
+ if (!buf) {
+ ret = -1;
+ goto out;
+ }
+
+ snprintf(buf, len, "%s %s %hu %x", scon, tcon,
+ unmap_class(tclass), unmap_perm(tclass, requested));
+
+ ret = write(fd, buf, strlen(buf));
+ if (ret < 0)
+ goto out2;
+
+ memset(buf, 0, len);
+ ret = read(fd, buf, len - 1);
+ if (ret < 0)
+ goto out2;
+
+ ret = sscanf(buf, "%x %x %x %x %u %x",
+ &avd->allowed, &avd->decided,
+ &avd->auditallow, &avd->auditdeny,
+ &avd->seqno, &avd->flags);
+ if (ret < 5) {
+ ret = -1;
+ goto out2;
+ } else if (ret < 6)
+ avd->flags = 0;
+
+ map_decision(tclass, avd);
+
+ ret = 0;
+ out2:
+ free(buf);
+ out:
+ close(fd);
+ return ret;
+}
+
diff --git a/libselinux/src/compute_create.c b/libselinux/src/compute_create.c
new file mode 100644
index 0000000..d3b16c9
--- /dev/null
+++ b/libselinux/src/compute_create.c
@@ -0,0 +1,61 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+int security_compute_create(const char * scon,
+ const char * tcon,
+ security_class_t tclass,
+ char ** newcon)
+{
+ char path[PATH_MAX];
+ char *buf;
+ size_t size;
+ int fd, ret;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/create", selinux_mnt);
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ size = selinux_page_size;
+ buf = malloc(size);
+ if (!buf) {
+ ret = -1;
+ goto out;
+ }
+ snprintf(buf, size, "%s %s %hu", scon, tcon, unmap_class(tclass));
+
+ ret = write(fd, buf, strlen(buf));
+ if (ret < 0)
+ goto out2;
+
+ memset(buf, 0, size);
+ ret = read(fd, buf, size - 1);
+ if (ret < 0)
+ goto out2;
+
+ *newcon = strdup(buf);
+ if (!(*newcon)) {
+ ret = -1;
+ goto out2;
+ }
+ ret = 0;
+ out2:
+ free(buf);
+ out:
+ close(fd);
+ return ret;
+}
diff --git a/libselinux/src/context.c b/libselinux/src/context.c
new file mode 100644
index 0000000..66abea1
--- /dev/null
+++ b/libselinux/src/context.c
@@ -0,0 +1,197 @@
+#include "context_internal.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define COMP_USER 0
+#define COMP_ROLE 1
+#define COMP_TYPE 2
+#define COMP_RANGE 3
+
+typedef struct {
+ char *current_str; /* This is made up-to-date only when needed */
+ char *(component[4]);
+} context_private_t;
+
+/*
+ * Allocate a new context, initialized from str. There must be 3 or
+ * 4 colon-separated components and no whitespace in any component other
+ * than the MLS component.
+ */
+context_t context_new(const char *str)
+{
+ int i, count;
+ errno = 0;
+ context_private_t *n =
+ (context_private_t *) malloc(sizeof(context_private_t));
+ context_t result = (context_t) malloc(sizeof(context_s_t));
+ const char *p, *tok;
+
+ if (result)
+ result->ptr = n;
+ else
+ free(n);
+ if (n == 0 || result == 0) {
+ goto err;
+ }
+ n->current_str = n->component[0] = n->component[1] = n->component[2] =
+ n->component[3] = 0;
+ for (i = count = 0, p = str; *p; p++) {
+ switch (*p) {
+ case ':':
+ count++;
+ break;
+ case '\n':
+ case '\t':
+ case '\r':
+ goto err; /* sanity check */
+ case ' ':
+ if (count < 3)
+ goto err; /* sanity check */
+ }
+ }
+ /*
+ * Could be anywhere from 2 - 5
+ * e.g user:role:type to user:role:type:sens1:cata-sens2:catb
+ */
+ if (count < 2 || count > 5) { /* might not have a range */
+ goto err;
+ }
+
+ n->component[3] = 0;
+ for (i = 0, tok = str; *tok; i++) {
+ if (i < 3)
+ for (p = tok; *p && *p != ':'; p++) { /* empty */
+ } else {
+ /* MLS range is one component */
+ for (p = tok; *p; p++) { /* empty */
+ }
+ }
+ n->component[i] = (char *)malloc(p - tok + 1);
+ if (n->component[i] == 0)
+ goto err;
+ strncpy(n->component[i], tok, p - tok);
+ n->component[i][p - tok] = '\0';
+ tok = *p ? p + 1 : p;
+ }
+ return result;
+ err:
+ if (errno == 0) errno = EINVAL;
+ context_free(result);
+ return 0;
+}
+
+hidden_def(context_new)
+
+static void conditional_free(char **v)
+{
+ if (*v) {
+ free(*v);
+ }
+ *v = 0;
+}
+
+/*
+ * free all storage used by a context. Safe to call with
+ * null pointer.
+ */
+void context_free(context_t context)
+{
+ context_private_t *n;
+ int i;
+ if (context) {
+ n = context->ptr;
+ if (n) {
+ conditional_free(&n->current_str);
+ for (i = 0; i < 4; i++) {
+ conditional_free(&n->component[i]);
+ }
+ free(n);
+ }
+ free(context);
+ }
+}
+
+hidden_def(context_free)
+
+/*
+ * Return a pointer to the string value of the context.
+ */
+char *context_str(context_t context)
+{
+ context_private_t *n = context->ptr;
+ int i;
+ size_t total = 0;
+ conditional_free(&n->current_str);
+ for (i = 0; i < 4; i++) {
+ if (n->component[i]) {
+ total += strlen(n->component[i]) + 1;
+ }
+ }
+ n->current_str = malloc(total);
+ if (n->current_str != 0) {
+ char *cp = n->current_str;
+
+ strcpy(cp, n->component[0]);
+ cp += strlen(cp);
+ for (i = 1; i < 4; i++) {
+ if (n->component[i]) {
+ *cp++ = ':';
+ strcpy(cp, n->component[i]);
+ cp += strlen(cp);
+ }
+ }
+ }
+ return n->current_str;
+}
+
+hidden_def(context_str)
+
+/* Returns nonzero iff failed */
+static int set_comp(context_private_t * n, int idx, const char *str)
+{
+ char *t = NULL;
+ const char *p;
+ if (str) {
+ t = (char *)malloc(strlen(str) + 1);
+ if (!t) {
+ return 1;
+ }
+ for (p = str; *p; p++) {
+ if (*p == '\t' || *p == '\n' || *p == '\r' ||
+ ((*p == ':' || *p == ' ') && idx != COMP_RANGE)) {
+ free(t);
+ errno = EINVAL;
+ return 1;
+ }
+ }
+ strcpy(t, str);
+ }
+ conditional_free(&n->component[idx]);
+ n->component[idx] = t;
+ return 0;
+}
+
+#define def_get(name,tag) \
+const char * context_ ## name ## _get(context_t context) \
+{ \
+ context_private_t *n = context->ptr; \
+ return n->component[tag]; \
+} \
+hidden_def(context_ ## name ## _get)
+
+def_get(type, COMP_TYPE)
+ def_get(user, COMP_USER)
+ def_get(range, COMP_RANGE)
+ def_get(role, COMP_ROLE)
+#define def_set(name,tag) \
+int context_ ## name ## _set(context_t context, const char* str) \
+{ \
+ return set_comp(context->ptr,tag,str);\
+} \
+hidden_def(context_ ## name ## _set)
+ def_set(type, COMP_TYPE)
+ def_set(role, COMP_ROLE)
+ def_set(user, COMP_USER)
+ def_set(range, COMP_RANGE)
diff --git a/libselinux/src/context_internal.h b/libselinux/src/context_internal.h
new file mode 100644
index 0000000..3c71e80
--- /dev/null
+++ b/libselinux/src/context_internal.h
@@ -0,0 +1,14 @@
+#include <selinux/context.h>
+#include "dso.h"
+
+hidden_proto(context_new)
+ hidden_proto(context_free)
+ hidden_proto(context_str)
+ hidden_proto(context_type_set)
+ hidden_proto(context_type_get)
+ hidden_proto(context_role_set)
+ hidden_proto(context_role_get)
+ hidden_proto(context_user_set)
+ hidden_proto(context_user_get)
+ hidden_proto(context_range_set)
+ hidden_proto(context_range_get)
diff --git a/libselinux/src/deny_unknown.c b/libselinux/src/deny_unknown.c
new file mode 100644
index 0000000..c93998a
--- /dev/null
+++ b/libselinux/src/deny_unknown.c
@@ -0,0 +1,40 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_deny_unknown(void)
+{
+ int fd, ret, deny_unknown = 0;
+ char path[PATH_MAX];
+ char buf[20];
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof(path), "%s/deny_unknown", selinux_mnt);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret < 0)
+ return -1;
+
+ if (sscanf(buf, "%d", &deny_unknown) != 1)
+ return -1;
+
+ return deny_unknown;
+}
+
+hidden_def(security_deny_unknown);
diff --git a/libselinux/src/disable.c b/libselinux/src/disable.c
new file mode 100644
index 0000000..dac0f5b
--- /dev/null
+++ b/libselinux/src/disable.c
@@ -0,0 +1,38 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_disable(void)
+{
+ int fd, ret;
+ char path[PATH_MAX];
+ char buf[20];
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/disable", selinux_mnt);
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ return -1;
+
+ buf[0] = '1';
+ buf[1] = '\0';
+ ret = write(fd, buf, strlen(buf));
+ close(fd);
+ if (ret < 0)
+ return -1;
+
+ return 0;
+}
+
+hidden_def(security_disable)
diff --git a/libselinux/src/dso.h b/libselinux/src/dso.h
new file mode 100644
index 0000000..12c3d11
--- /dev/null
+++ b/libselinux/src/dso.h
@@ -0,0 +1,23 @@
+#ifndef _SELINUX_DSO_H
+#define _SELINUX_DSO_H 1
+
+#ifdef SHARED
+# define hidden __attribute__ ((visibility ("hidden")))
+# define hidden_proto(fct) __hidden_proto (fct, fct##_internal)
+# define __hidden_proto(fct, internal) \
+ extern __typeof (fct) internal; \
+ extern __typeof (fct) fct __asm (#internal) hidden;
+# if defined(__alpha__) || defined(__mips__)
+# define hidden_def(fct) \
+ asm (".globl " #fct "\n" #fct " = " #fct "_internal");
+# else
+# define hidden_def(fct) \
+ asm (".globl " #fct "\n.set " #fct ", " #fct "_internal");
+#endif
+#else
+# define hidden
+# define hidden_proto(fct)
+# define hidden_def(fct)
+#endif
+
+#endif
diff --git a/libselinux/src/enabled.c b/libselinux/src/enabled.c
new file mode 100644
index 0000000..c60eb19
--- /dev/null
+++ b/libselinux/src/enabled.c
@@ -0,0 +1,54 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include "policy.h"
+
+int is_selinux_enabled(void)
+{
+ /* init_selinuxmnt() gets called before this function. We
+ * will assume that if a selinux file system is mounted, then
+ * selinux is enabled. */
+ return (selinux_mnt ? 1 : 0);
+}
+
+hidden_def(is_selinux_enabled)
+
+/*
+ * Function: is_selinux_mls_enabled()
+ * Return: 1 on success
+ * 0 on failure
+ */
+int is_selinux_mls_enabled(void)
+{
+ char buf[20], path[PATH_MAX];
+ int fd, ret, enabled = 0;
+
+ if (!selinux_mnt)
+ return enabled;
+
+ snprintf(path, sizeof path, "%s/mls", selinux_mnt);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return enabled;
+
+ memset(buf, 0, sizeof buf);
+
+ do {
+ ret = read(fd, buf, sizeof buf - 1);
+ } while (ret < 0 && errno == EINTR);
+ close(fd);
+ if (ret < 0)
+ return enabled;
+
+ if (!strcmp(buf, "1"))
+ enabled = 1;
+
+ return enabled;
+}
+
+hidden_def(is_selinux_mls_enabled)
diff --git a/libselinux/src/fgetfilecon.c b/libselinux/src/fgetfilecon.c
new file mode 100644
index 0000000..33cdc27
--- /dev/null
+++ b/libselinux/src/fgetfilecon.c
@@ -0,0 +1,51 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int fgetfilecon(int fd, char ** context)
+{
+ char *buf;
+ ssize_t size;
+ ssize_t ret;
+
+ size = INITCONTEXTLEN + 1;
+ buf = malloc(size);
+ if (!buf)
+ return -1;
+ memset(buf, 0, size);
+
+ ret = fgetxattr(fd, XATTR_NAME_SELINUX, buf, size - 1);
+ if (ret < 0 && errno == ERANGE) {
+ char *newbuf;
+
+ size = fgetxattr(fd, XATTR_NAME_SELINUX, NULL, 0);
+ if (size < 0)
+ goto out;
+
+ size++;
+ newbuf = realloc(buf, size);
+ if (!newbuf)
+ goto out;
+
+ buf = newbuf;
+ memset(buf, 0, size);
+ ret = fgetxattr(fd, XATTR_NAME_SELINUX, buf, size - 1);
+ }
+ out:
+ if (ret == 0) {
+ /* Re-map empty attribute values to errors. */
+ errno = EOPNOTSUPP;
+ ret = -1;
+ }
+ if (ret < 0)
+ free(buf);
+ else
+ *context = buf;
+ return ret;
+}
+
diff --git a/libselinux/src/freecon.c b/libselinux/src/freecon.c
new file mode 100644
index 0000000..5290dfa
--- /dev/null
+++ b/libselinux/src/freecon.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include "selinux_internal.h"
+#include <stdlib.h>
+#include <errno.h>
+
+void freecon(char * con)
+{
+ free(con);
+}
+
+hidden_def(freecon)
diff --git a/libselinux/src/fsetfilecon.c b/libselinux/src/fsetfilecon.c
new file mode 100644
index 0000000..17f8875
--- /dev/null
+++ b/libselinux/src/fsetfilecon.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int fsetfilecon(int fd, const char *context)
+{
+ return fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1,
+ 0);
+}
+
diff --git a/libselinux/src/get_initial_context.c b/libselinux/src/get_initial_context.c
new file mode 100644
index 0000000..64863dd
--- /dev/null
+++ b/libselinux/src/get_initial_context.c
@@ -0,0 +1,55 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <limits.h>
+
+#define SELINUX_INITCON_DIR "/initial_contexts/"
+
+int security_get_initial_context(const char * name, char ** con)
+{
+ char path[PATH_MAX];
+ char *buf;
+ size_t size;
+ int fd, ret;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s%s%s",
+ selinux_mnt, SELINUX_INITCON_DIR, name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ size = selinux_page_size;
+ buf = malloc(size);
+ if (!buf) {
+ ret = -1;
+ goto out;
+ }
+ memset(buf, 0, size);
+ ret = read(fd, buf, size - 1);
+ if (ret < 0)
+ goto out2;
+
+ *con = strdup(buf);
+ if (!(*con)) {
+ ret = -1;
+ goto out2;
+ }
+ ret = 0;
+ out2:
+ free(buf);
+ out:
+ close(fd);
+ return ret;
+}
+
diff --git a/libselinux/src/getenforce.c b/libselinux/src/getenforce.c
new file mode 100644
index 0000000..4fb516a
--- /dev/null
+++ b/libselinux/src/getenforce.c
@@ -0,0 +1,40 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_getenforce(void)
+{
+ int fd, ret, enforce = 0;
+ char path[PATH_MAX];
+ char buf[20];
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ memset(buf, 0, sizeof buf);
+ ret = read(fd, buf, sizeof buf - 1);
+ close(fd);
+ if (ret < 0)
+ return -1;
+
+ if (sscanf(buf, "%d", &enforce) != 1)
+ return -1;
+
+ return enforce;
+}
+
+hidden_def(security_getenforce)
diff --git a/libselinux/src/getfilecon.c b/libselinux/src/getfilecon.c
new file mode 100644
index 0000000..02037de
--- /dev/null
+++ b/libselinux/src/getfilecon.c
@@ -0,0 +1,50 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "policy.h"
+
+int getfilecon(const char *path, char ** context)
+{
+ char *buf;
+ ssize_t size;
+ ssize_t ret;
+
+ size = INITCONTEXTLEN + 1;
+ buf = malloc(size);
+ if (!buf)
+ return -1;
+ memset(buf, 0, size);
+
+ ret = getxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+ if (ret < 0 && errno == ERANGE) {
+ char *newbuf;
+
+ size = getxattr(path, XATTR_NAME_SELINUX, NULL, 0);
+ if (size < 0)
+ goto out;
+
+ size++;
+ newbuf = realloc(buf, size);
+ if (!newbuf)
+ goto out;
+
+ buf = newbuf;
+ memset(buf, 0, size);
+ ret = getxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+ }
+ out:
+ if (ret == 0) {
+ /* Re-map empty attribute values to errors. */
+ errno = EOPNOTSUPP;
+ ret = -1;
+ }
+ if (ret < 0)
+ free(buf);
+ else
+ *context = buf;
+ return ret;
+}
diff --git a/libselinux/src/getpeercon.c b/libselinux/src/getpeercon.c
new file mode 100644
index 0000000..3bd29dc
--- /dev/null
+++ b/libselinux/src/getpeercon.c
@@ -0,0 +1,45 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+#ifndef SO_PEERSEC
+#define SO_PEERSEC 31
+#endif
+
+int getpeercon(int fd, char ** context)
+{
+ char *buf;
+ socklen_t size;
+ ssize_t ret;
+
+ size = INITCONTEXTLEN + 1;
+ buf = malloc(size);
+ if (!buf)
+ return -1;
+ memset(buf, 0, size);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &size);
+ if (ret < 0 && errno == ERANGE) {
+ char *newbuf;
+
+ newbuf = realloc(buf, size);
+ if (!newbuf)
+ goto out;
+
+ buf = newbuf;
+ memset(buf, 0, size);
+ ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &size);
+ }
+ out:
+ if (ret < 0)
+ free(buf);
+ else
+ *context = buf;
+ return ret;
+}
+
diff --git a/libselinux/src/init.c b/libselinux/src/init.c
new file mode 100644
index 0000000..65bc01b
--- /dev/null
+++ b/libselinux/src/init.c
@@ -0,0 +1,123 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#ifdef DARWIN
+#include <sys/param.h>
+#include <sys/mount.h>
+#else
+#include <sys/vfs.h>
+#endif
+
+#include <stdint.h>
+#include <limits.h>
+
+#include "dso.h"
+#include "policy.h"
+#include "selinux_internal.h"
+
+char *selinux_mnt = NULL;
+int selinux_page_size = 0;
+
+static void init_selinuxmnt(void)
+{
+ char buf[BUFSIZ], *p;
+ FILE *fp=NULL;
+ struct statfs sfbuf;
+ int rc;
+ char *bufp;
+ int exists = 0;
+
+ if (selinux_mnt)
+ return;
+
+ /* We check to see if the preferred mount point for selinux file
+ * system has a selinuxfs. */
+ do {
+ rc = statfs(SELINUXMNT, &sfbuf);
+ } while (rc < 0 && errno == EINTR);
+ if (rc == 0) {
+ if ((uint32_t)sfbuf.f_type == (uint32_t)SELINUX_MAGIC) {
+ selinux_mnt = strdup(SELINUXMNT);
+ return;
+ }
+ }
+
+ /* Drop back to detecting it the long way. */
+ fp = fopen("/proc/filesystems", "r");
+ if (!fp)
+ return;
+
+ while ((bufp = fgets(buf, sizeof buf - 1, fp)) != NULL) {
+ if (strstr(buf, "selinuxfs")) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (!exists)
+ goto out;
+
+ fclose(fp);
+
+ /* At this point, the usual spot doesn't have an selinuxfs so
+ * we look around for it */
+ fp = fopen("/proc/mounts", "r");
+ if (!fp)
+ goto out;
+
+ while ((bufp = fgets(buf, sizeof buf - 1, fp)) != NULL) {
+ char *tmp;
+ p = strchr(buf, ' ');
+ if (!p)
+ goto out;
+ p++;
+ tmp = strchr(p, ' ');
+ if (!tmp)
+ goto out;
+ if (!strncmp(tmp + 1, "selinuxfs ", 10)) {
+ *tmp = '\0';
+ break;
+ }
+ }
+
+ /* If we found something, dup it */
+ if (bufp)
+ selinux_mnt = strdup(p);
+
+ out:
+ if (fp)
+ fclose(fp);
+ return;
+}
+
+void fini_selinuxmnt(void)
+{
+ free(selinux_mnt);
+ selinux_mnt = NULL;
+}
+
+void set_selinuxmnt(const char *mnt)
+{
+ selinux_mnt = strdup(mnt);
+}
+
+hidden_def(set_selinuxmnt)
+
+static void init_lib(void) __attribute__ ((constructor));
+static void init_lib(void)
+{
+ selinux_page_size = sysconf(_SC_PAGE_SIZE);
+ init_selinuxmnt();
+}
+
+static void fini_lib(void) __attribute__ ((destructor));
+static void fini_lib(void)
+{
+ fini_selinuxmnt();
+}
diff --git a/libselinux/src/label.c b/libselinux/src/label.c
new file mode 100644
index 0000000..fb8c266
--- /dev/null
+++ b/libselinux/src/label.c
@@ -0,0 +1,172 @@
+/*
+ * Generalized labeling frontend for userspace object managers.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <selinux/selinux.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+typedef int (*selabel_initfunc)(struct selabel_handle *rec,
+ const struct selinux_opt *opts,
+ unsigned nopts);
+
+static selabel_initfunc initfuncs[] = {
+ &selabel_file_init,
+ NULL,
+ NULL,
+ NULL,
+ &selabel_property_init,
+};
+
+/*
+ * Validation functions
+ */
+
+static inline int selabel_is_validate_set(const struct selinux_opt *opts,
+ unsigned n)
+{
+ while (n--)
+ if (opts[n].type == SELABEL_OPT_VALIDATE)
+ return !!opts[n].value;
+
+ return 0;
+}
+
+int selabel_validate(struct selabel_handle *rec,
+ struct selabel_lookup_rec *contexts)
+{
+ int rc = 0;
+
+ if (!rec->validating || contexts->validated)
+ goto out;
+
+ rc = selinux_validate(&contexts->ctx_raw);
+ if (rc < 0)
+ goto out;
+
+ contexts->validated = 1;
+out:
+ return rc;
+}
+
+/*
+ * Public API
+ */
+
+struct selabel_handle *selabel_open(unsigned int backend,
+ const struct selinux_opt *opts,
+ unsigned nopts)
+{
+ struct selabel_handle *rec = NULL;
+
+ if (backend >= ARRAY_SIZE(initfuncs)) {
+ errno = EINVAL;
+ goto out;
+ }
+
+ if (initfuncs[backend] == NULL)
+ goto out;
+
+ rec = (struct selabel_handle *)malloc(sizeof(*rec));
+ if (!rec)
+ goto out;
+
+ memset(rec, 0, sizeof(*rec));
+ rec->backend = backend;
+ rec->validating = selabel_is_validate_set(opts, nopts);
+
+ if ((*initfuncs[backend])(rec, opts, nopts)) {
+ free(rec->spec_file);
+ free(rec);
+ rec = NULL;
+ }
+
+out:
+ return rec;
+}
+
+static struct selabel_lookup_rec *
+selabel_lookup_common(struct selabel_handle *rec,
+ const char *key, int type)
+{
+ struct selabel_lookup_rec *lr;
+ lr = rec->func_lookup(rec, key, type);
+ if (!lr)
+ return NULL;
+
+ return lr;
+}
+
+int selabel_lookup(struct selabel_handle *rec, char **con,
+ const char *key, int type)
+{
+ struct selabel_lookup_rec *lr;
+
+ lr = selabel_lookup_common(rec, key, type);
+ if (!lr)
+ return -1;
+
+ *con = strdup(lr->ctx_raw);
+ return *con ? 0 : -1;
+}
+
+bool selabel_partial_match(struct selabel_handle *rec, const char *key)
+{
+ if (!rec->func_partial_match) {
+ /*
+ * If the label backend does not support partial matching,
+ * then assume a match is possible.
+ */
+ return true;
+ }
+ return rec->func_partial_match(rec, key);
+}
+
+int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
+ const char *key, const char **aliases, int type)
+{
+ struct selabel_lookup_rec *lr;
+
+ if (!rec->func_lookup_best_match) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ lr = rec->func_lookup_best_match(rec, key, aliases, type);
+ if (!lr)
+ return -1;
+
+ *con = strdup(lr->ctx_raw);
+ return *con ? 0 : -1;
+}
+
+enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
+ struct selabel_handle *h2)
+{
+ if (!h1->func_cmp || h1->func_cmp != h2->func_cmp)
+ return SELABEL_INCOMPARABLE;
+
+ return h1->func_cmp(h1, h2);
+}
+
+void selabel_close(struct selabel_handle *rec)
+{
+ rec->func_close(rec);
+ free(rec->spec_file);
+ free(rec);
+}
+
+void selabel_stats(struct selabel_handle *rec)
+{
+ rec->func_stats(rec);
+}
diff --git a/libselinux/src/label_android_property.c b/libselinux/src/label_android_property.c
new file mode 100644
index 0000000..887e32c
--- /dev/null
+++ b/libselinux/src/label_android_property.c
@@ -0,0 +1,299 @@
+/*
+ * Property Service contexts backend for labeling Android
+ * property keys
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+/* A property security context specification. */
+typedef struct spec {
+ struct selabel_lookup_rec lr; /* holds contexts for lookup result */
+ char *property_key; /* property key string */
+} spec_t;
+
+/* Our stored configuration */
+struct saved_data {
+ /*
+ * The array of specifications is sorted for longest
+ * prefix match
+ */
+ spec_t *spec_arr;
+ unsigned int nspec; /* total number of specifications */
+};
+
+static int cmp(const void *A, const void *B)
+{
+ const struct spec *sp1 = A, *sp2 = B;
+
+ if (strncmp(sp1->property_key, "*", 1) == 0)
+ return 1;
+ if (strncmp(sp2->property_key, "*", 1) == 0)
+ return -1;
+
+ size_t L1 = strlen(sp1->property_key);
+ size_t L2 = strlen(sp2->property_key);
+
+ return (L1 < L2) - (L1 > L2);
+}
+
+/*
+ * Warn about duplicate specifications.
+ */
+static int nodups_specs(struct saved_data *data, const char *path)
+{
+ int rc = 0;
+ unsigned int ii, jj;
+ struct spec *curr_spec, *spec_arr = data->spec_arr;
+
+ for (ii = 0; ii < data->nspec; ii++) {
+ curr_spec = &spec_arr[ii];
+ for (jj = ii + 1; jj < data->nspec; jj++) {
+ if (!strcmp(spec_arr[jj].property_key,
+ curr_spec->property_key)) {
+ rc = -1;
+ errno = EINVAL;
+ if (strcmp(spec_arr[jj].lr.ctx_raw,
+ curr_spec->lr.ctx_raw)) {
+ selinux_log
+ (SELINUX_ERROR,
+ "%s: Multiple different specifications for %s (%s and %s).\n",
+ path, curr_spec->property_key,
+ spec_arr[jj].lr.ctx_raw,
+ curr_spec->lr.ctx_raw);
+ } else {
+ selinux_log
+ (SELINUX_ERROR,
+ "%s: Multiple same specifications for %s.\n",
+ path, curr_spec->property_key);
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+static int process_line(struct selabel_handle *rec,
+ const char *path, char *line_buf,
+ int pass, unsigned lineno)
+{
+ int items;
+ char *prop = NULL, *context = NULL;
+ struct saved_data *data = (struct saved_data *)rec->data;
+ spec_t *spec_arr = data->spec_arr;
+ unsigned int nspec = data->nspec;
+ const char *errbuf = NULL;
+
+ items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
+ if (items < 0) {
+ items = errno;
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u error due to: %s\n", path,
+ lineno, errbuf ?: strerror(errno));
+ errno = items;
+ return -1;
+ }
+
+ if (items == 0)
+ return items;
+
+ if (items != 2) {
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u is missing fields\n", path,
+ lineno);
+ free(prop);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (pass == 0) {
+ free(prop);
+ free(context);
+ } else if (pass == 1) {
+ /* On the second pass, process and store the specification in spec. */
+ spec_arr[nspec].property_key = prop;
+ spec_arr[nspec].lr.ctx_raw = context;
+
+ if (rec->validating) {
+ if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u has invalid context %s\n",
+ path, lineno, spec_arr[nspec].lr.ctx_raw);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ }
+
+ data->nspec = ++nspec;
+ return 0;
+}
+
+static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned n)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ const char *path = NULL;
+ FILE *fp;
+ char line_buf[BUFSIZ];
+ unsigned int lineno, maxnspec, pass;
+ int status = -1;
+ struct stat sb;
+
+ /* Process arguments */
+ while (n--)
+ switch (opts[n].type) {
+ case SELABEL_OPT_PATH:
+ path = opts[n].value;
+ break;
+ }
+
+ if (!path)
+ return -1;
+
+ /* Open the specification file. */
+ if ((fp = fopen(path, "r")) == NULL)
+ return -1;
+
+ if (fstat(fileno(fp), &sb) < 0)
+ goto finish;
+ errno = EINVAL;
+ if (!S_ISREG(sb.st_mode))
+ goto finish;
+
+ /*
+ * Two passes of the specification file. First is to get the size.
+ * After the first pass, the spec array is malloced to the appropriate
+ * size. Second pass is to populate the spec array and check for
+ * dups.
+ */
+ maxnspec = UINT_MAX / sizeof(spec_t);
+ for (pass = 0; pass < 2; pass++) {
+ data->nspec = 0;
+ lineno = 0;
+
+ while (fgets(line_buf, sizeof(line_buf) - 1, fp)
+ && data->nspec < maxnspec) {
+ if (process_line(rec, path, line_buf, pass, ++lineno)
+ != 0)
+ goto finish;
+ }
+
+ if (pass == 1) {
+ status = nodups_specs(data, path);
+
+ if (status)
+ goto finish;
+ }
+
+ if (pass == 0) {
+ if (data->nspec == 0) {
+ status = 0;
+ goto finish;
+ }
+
+ if (NULL == (data->spec_arr =
+ malloc(sizeof(spec_t) * data->nspec)))
+ goto finish;
+
+ memset(data->spec_arr, 0, sizeof(spec_t) * data->nspec);
+ maxnspec = data->nspec;
+ rewind(fp);
+ }
+ }
+
+ qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
+
+ status = 0;
+finish:
+ fclose(fp);
+ return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void closef(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct spec *spec;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ spec = &data->spec_arr[i];
+ free(spec->property_key);
+ free(spec->lr.ctx_raw);
+ free(spec->lr.ctx_trans);
+ }
+
+ if (data->spec_arr)
+ free(data->spec_arr);
+
+ free(data);
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
+ const char *key,
+ int __attribute__((unused)) type)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ spec_t *spec_arr = data->spec_arr;
+ unsigned int i;
+ struct selabel_lookup_rec *ret = NULL;
+
+ if (!data->nspec) {
+ errno = ENOENT;
+ goto finish;
+ }
+
+ for (i = 0; i < data->nspec; i++) {
+ if (strncmp(spec_arr[i].property_key, key,
+ strlen(spec_arr[i].property_key)) == 0) {
+ break;
+ }
+ if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
+ break;
+ }
+
+ if (i >= data->nspec) {
+ /* No matching specification. */
+ errno = ENOENT;
+ goto finish;
+ }
+
+ ret = &spec_arr[i].lr;
+
+finish:
+ return ret;
+}
+
+static void stats(struct selabel_handle __attribute__((unused)) *rec)
+{
+ selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
+}
+
+int selabel_property_init(struct selabel_handle *rec,
+ const struct selinux_opt *opts,
+ unsigned nopts)
+{
+ struct saved_data *data;
+
+ data = (struct saved_data *)malloc(sizeof(*data));
+ if (!data)
+ return -1;
+ memset(data, 0, sizeof(*data));
+
+ rec->data = data;
+ rec->func_close = &closef;
+ rec->func_stats = &stats;
+ rec->func_lookup = &lookup;
+
+ return init(rec, opts, nopts);
+}
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
new file mode 100644
index 0000000..d3e67c0
--- /dev/null
+++ b/libselinux/src/label_file.c
@@ -0,0 +1,859 @@
+/*
+ * File contexts backend for labeling system
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ * Author : Stephen Smalley <sds@tycho.nsa.gov>
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "callbacks.h"
+#include "label_internal.h"
+#include "label_file.h"
+
+/*
+ * Internals, mostly moved over from matchpathcon.c
+ */
+
+/* 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 name, returns the index into stem_arr (or -1 if
+ * there is no match - IE for a file in the root directory or a regex that is
+ * too complex for us). Makes buf point to the text AFTER the stem. */
+static int find_stem_from_file(struct saved_data *data, const char **buf)
+{
+ int i;
+ int stem_len = get_stem_from_file_name(*buf);
+
+ if (!stem_len)
+ return -1;
+ for (i = 0; i < data->num_stems; i++) {
+ if (stem_len == data->stem_arr[i].len
+ && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
+ *buf += stem_len;
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Warn about duplicate specifications.
+ */
+static int nodups_specs(struct saved_data *data, const char *path)
+{
+ int rc = 0;
+ unsigned int ii, jj;
+ struct spec *curr_spec, *spec_arr = data->spec_arr;
+
+ for (ii = 0; ii < data->nspec; ii++) {
+ curr_spec = &spec_arr[ii];
+ for (jj = ii + 1; jj < data->nspec; jj++) {
+ if ((!strcmp(spec_arr[jj].regex_str,
+ curr_spec->regex_str))
+ && (!spec_arr[jj].mode || !curr_spec->mode
+ || spec_arr[jj].mode == curr_spec->mode)) {
+ rc = -1;
+ errno = EINVAL;
+ if (strcmp(spec_arr[jj].lr.ctx_raw,
+ curr_spec->lr.ctx_raw)) {
+ selinux_log
+ (SELINUX_ERROR,
+ "%s: Multiple different specifications for %s (%s and %s).\n",
+ path, curr_spec->regex_str,
+ spec_arr[jj].lr.ctx_raw,
+ curr_spec->lr.ctx_raw);
+ } else {
+ selinux_log
+ (SELINUX_ERROR,
+ "%s: Multiple same specifications for %s.\n",
+ path, curr_spec->regex_str);
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+static int load_mmap(struct selabel_handle *rec, const char *path,
+ struct stat *sb, bool isbinary)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ char mmap_path[PATH_MAX + 1];
+ int mmapfd;
+ int rc;
+ struct stat mmap_stat;
+ char *addr, *str_buf;
+ size_t len;
+ int *stem_map;
+ struct mmap_area *mmap_area;
+ uint32_t i, magic, version;
+ uint32_t entry_len, stem_map_len, regex_array_len;
+
+ if (isbinary) {
+ len = strlen(path);
+ if (len >= sizeof(mmap_path))
+ return -1;
+ strcpy(mmap_path, path);
+ } else {
+ rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path);
+ if (rc >= (int)sizeof(mmap_path))
+ return -1;
+ }
+
+ mmapfd = open(mmap_path, O_RDONLY | O_CLOEXEC);
+ if (mmapfd < 0)
+ return -1;
+
+ rc = fstat(mmapfd, &mmap_stat);
+ if (rc < 0) {
+ close(mmapfd);
+ return -1;
+ }
+
+ /* if mmap is old, ignore it */
+ if (mmap_stat.st_mtime < sb->st_mtime) {
+ close(mmapfd);
+ return -1;
+ }
+
+ /* ok, read it in... */
+ len = mmap_stat.st_size;
+ len += (sysconf(_SC_PAGE_SIZE) - 1);
+ len &= ~(sysconf(_SC_PAGE_SIZE) - 1);
+
+ mmap_area = malloc(sizeof(*mmap_area));
+ if (!mmap_area) {
+ close(mmapfd);
+ return -1;
+ }
+
+ addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0);
+ close(mmapfd);
+ if (addr == MAP_FAILED) {
+ free(mmap_area);
+ perror("mmap");
+ return -1;
+ }
+
+ /* save where we mmap'd the file to cleanup on close() */
+ mmap_area->addr = mmap_area->next_addr = addr;
+ mmap_area->len = mmap_area->next_len = len;
+ mmap_area->next = data->mmap_areas;
+ data->mmap_areas = mmap_area;
+
+ /* check if this looks like an fcontext file */
+ rc = next_entry(&magic, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
+ return -1;
+
+ /* check if this version is higher than we understand */
+ rc = next_entry(&version, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
+ return -1;
+
+ if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) {
+ if (!regex_version()) {
+ return -1;
+ }
+ len = strlen(regex_version());
+
+ rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0)
+ return -1;
+
+ /* Check version lengths */
+ if (len != entry_len)
+ return -1;
+
+ /* Check if pcre version mismatch */
+ str_buf = malloc(entry_len + 1);
+ if (!str_buf)
+ return -1;
+
+ rc = next_entry(str_buf, mmap_area, entry_len);
+ if (rc < 0) {
+ free(str_buf);
+ return -1;
+ }
+
+ str_buf[entry_len] = '\0';
+ if ((strcmp(str_buf, regex_version()) != 0)) {
+ free(str_buf);
+ return -1;
+ }
+ free(str_buf);
+ }
+
+ /* allocate the stems_data array */
+ rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || !stem_map_len)
+ return -1;
+
+ /*
+ * map indexed by the stem # in the mmap file and contains the stem
+ * number in the data stem_arr
+ */
+ stem_map = calloc(stem_map_len, sizeof(*stem_map));
+ if (!stem_map)
+ return -1;
+
+ for (i = 0; i < stem_map_len; i++) {
+ char *buf;
+ uint32_t stem_len;
+ int newid;
+
+ /* the length does not inlude the nul */
+ rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || !stem_len) {
+ rc = -1;
+ goto err;
+ }
+
+ /* Check for stem_len wrap around. */
+ if (stem_len < UINT32_MAX) {
+ buf = (char *)mmap_area->next_addr;
+ /* Check if over-run before null check. */
+ rc = next_entry(NULL, mmap_area, (stem_len + 1));
+ if (rc < 0)
+ goto err;
+
+ if (buf[stem_len] != '\0') {
+ rc = -1;
+ goto err;
+ }
+ } else {
+ rc = -1;
+ goto err;
+ }
+
+ /* store the mapping between old and new */
+ newid = find_stem(data, buf, stem_len);
+ if (newid < 0) {
+ newid = store_stem(data, buf, stem_len);
+ if (newid < 0) {
+ rc = newid;
+ goto err;
+ }
+ data->stem_arr[newid].from_mmap = 1;
+ }
+ stem_map[i] = newid;
+ }
+
+ /* allocate the regex array */
+ rc = next_entry(®ex_array_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || !regex_array_len) {
+ rc = -1;
+ goto err;
+ }
+
+ for (i = 0; i < regex_array_len; i++) {
+ struct spec *spec;
+ int32_t stem_id, meta_chars;
+ uint32_t mode = 0, prefix_len = 0;
+
+ rc = grow_specs(data);
+ if (rc < 0)
+ goto err;
+
+ spec = &data->spec_arr[data->nspec];
+ spec->from_mmap = 1;
+ spec->regcomp = 1;
+
+ /* Process context */
+ rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || !entry_len) {
+ rc = -1;
+ goto err;
+ }
+
+ str_buf = malloc(entry_len);
+ if (!str_buf) {
+ rc = -1;
+ goto err;
+ }
+ rc = next_entry(str_buf, mmap_area, entry_len);
+ if (rc < 0)
+ goto err;
+
+ if (str_buf[entry_len - 1] != '\0') {
+ free(str_buf);
+ rc = -1;
+ goto err;
+ }
+ spec->lr.ctx_raw = str_buf;
+
+ if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) {
+ if (selabel_validate(rec, &spec->lr) < 0) {
+ selinux_log(SELINUX_ERROR,
+ "%s: context %s is invalid\n", mmap_path, spec->lr.ctx_raw);
+ goto err;
+ }
+ }
+
+ /* Process regex string */
+ rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || !entry_len) {
+ rc = -1;
+ goto err;
+ }
+
+ spec->regex_str = (char *)mmap_area->next_addr;
+ rc = next_entry(NULL, mmap_area, entry_len);
+ if (rc < 0)
+ goto err;
+
+ if (spec->regex_str[entry_len - 1] != '\0') {
+ rc = -1;
+ goto err;
+ }
+
+ /* Process mode */
+ if (version >= SELINUX_COMPILED_FCONTEXT_MODE)
+ rc = next_entry(&mode, mmap_area, sizeof(uint32_t));
+ else
+ rc = next_entry(&mode, mmap_area, sizeof(mode_t));
+ if (rc < 0)
+ goto err;
+
+ spec->mode = mode;
+
+ /* map the stem id from the mmap file to the data->stem_arr */
+ rc = next_entry(&stem_id, mmap_area, sizeof(int32_t));
+ if (rc < 0)
+ goto err;
+
+ if (stem_id < 0 || stem_id >= (int32_t)stem_map_len)
+ spec->stem_id = -1;
+ else
+ spec->stem_id = stem_map[stem_id];
+
+ /* retrieve the hasMetaChars bit */
+ rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t));
+ if (rc < 0)
+ goto err;
+
+ spec->hasMetaChars = meta_chars;
+ /* and prefix length for use by selabel_lookup_best_match */
+ if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) {
+ rc = next_entry(&prefix_len, mmap_area,
+ sizeof(uint32_t));
+ if (rc < 0)
+ goto err;
+
+ spec->prefix_len = prefix_len;
+ }
+
+ rc = regex_load_mmap(mmap_area, &spec->regex);
+ if (rc < 0)
+ goto err;
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ spec->regcomp = 1;
+#else
+ spec->regcomp = 0;
+#endif
+
+ data->nspec++;
+ }
+
+ /* win */
+ rc = 0;
+err:
+ free(stem_map);
+
+ return rc;
+}
+
+static int process_file(const char *path, const char *suffix,
+ struct selabel_handle *rec, const char *prefix)
+{
+ FILE *fp;
+ struct stat sb;
+ unsigned int lineno;
+ size_t line_len = 0;
+ char *line_buf = NULL;
+ int rc;
+ char stack_path[PATH_MAX + 1];
+ bool isbinary = false;
+ uint32_t magic;
+
+ /* append the path suffix if we have one */
+ if (suffix) {
+ rc = snprintf(stack_path, sizeof(stack_path),
+ "%s.%s", path, suffix);
+ if (rc >= (int)sizeof(stack_path)) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ path = stack_path;
+ }
+
+ /* Open the specification file. */
+ fp = fopen(path, "r");
+ if (fp) {
+ if (fstat(fileno(fp), &sb) < 0)
+ return -1;
+ if (!S_ISREG(sb.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (fread(&magic, sizeof magic, 1, fp) != 1) {
+ errno = EINVAL;
+ fclose(fp);
+ return -1;
+ }
+
+ if (magic == SELINUX_MAGIC_COMPILED_FCONTEXT) {
+ /* file_contexts.bin format */
+ fclose(fp);
+ fp = NULL;
+ isbinary = true;
+ } else {
+ rewind(fp);
+ }
+ } else {
+ /*
+ * Text file does not exist, so clear the timestamp
+ * so that we will always pass the timestamp comparison
+ * with the bin file in load_mmap().
+ */
+ sb.st_mtime = 0;
+ }
+
+ rc = load_mmap(rec, path, &sb, isbinary);
+ if (rc == 0)
+ goto out;
+
+ if (!fp)
+ return -1; /* no text or bin file */
+
+ /*
+ * Then do detailed validation of the input and fill the spec array
+ */
+ lineno = 0;
+ rc = 0;
+ while (getline(&line_buf, &line_len, fp) > 0) {
+ rc = process_line(rec, path, prefix, line_buf, ++lineno);
+ if (rc)
+ goto out;
+ }
+
+out:
+ free(line_buf);
+ if (fp)
+ fclose(fp);
+ return rc;
+}
+
+static void closef(struct selabel_handle *rec);
+
+static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned n)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ const char *path = NULL;
+ const char *prefix = NULL;
+ int status = -1, baseonly = 0;
+
+ /* Process arguments */
+ while (n--)
+ switch(opts[n].type) {
+ case SELABEL_OPT_PATH:
+ path = opts[n].value;
+ break;
+ case SELABEL_OPT_SUBSET:
+ prefix = opts[n].value;
+ break;
+ case SELABEL_OPT_BASEONLY:
+ baseonly = !!opts[n].value;
+ break;
+ }
+
+ rec->spec_file = strdup(path);
+
+ /*
+ * The do detailed validation of the input and fill the spec array
+ */
+ status = process_file(path, NULL, rec, prefix);
+ if (status)
+ goto finish;
+
+ if (rec->validating) {
+ status = nodups_specs(data, path);
+ if (status)
+ goto finish;
+ }
+
+ if (!baseonly) {
+ status = process_file(path, "homedirs", rec, prefix);
+ if (status && errno != ENOENT)
+ goto finish;
+
+ status = process_file(path, "local", rec, prefix);
+ if (status && errno != ENOENT)
+ goto finish;
+ }
+
+ status = sort_specs(data);
+
+finish:
+ if (status)
+ closef(rec);
+
+ return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void closef(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct mmap_area *area, *last_area;
+ struct spec *spec;
+ struct stem *stem;
+ unsigned int i;
+
+ for (i = 0; i < data->nspec; i++) {
+ spec = &data->spec_arr[i];
+ free(spec->lr.ctx_trans);
+ free(spec->lr.ctx_raw);
+ if (spec->from_mmap)
+ continue;
+ free(spec->regex_str);
+ free(spec->type_str);
+ regex_data_free(spec->regex);
+ }
+
+ for (i = 0; i < (unsigned int)data->num_stems; i++) {
+ stem = &data->stem_arr[i];
+ if (stem->from_mmap)
+ continue;
+ free(stem->buf);
+ }
+
+ if (data->spec_arr)
+ free(data->spec_arr);
+ if (data->stem_arr)
+ free(data->stem_arr);
+
+ area = data->mmap_areas;
+ while (area) {
+ munmap(area->addr, area->len);
+ last_area = area;
+ area = area->next;
+ free(last_area);
+ }
+ free(data);
+}
+
+static struct spec *lookup_common(struct selabel_handle *rec,
+ const char *key,
+ int type,
+ bool partial)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct spec *spec_arr = data->spec_arr;
+ int i, rc, file_stem;
+ mode_t mode = (mode_t)type;
+ const char *buf;
+ struct spec *ret = NULL;
+ char *clean_key = NULL;
+ const char *prev_slash, *next_slash;
+ unsigned int sofar = 0;
+ struct regex_error_data regex_error_data;
+
+ if (!data->nspec) {
+ errno = ENOENT;
+ goto finish;
+ }
+
+ /* Remove duplicate slashes */
+ if ((next_slash = strstr(key, "//"))) {
+ clean_key = (char *) malloc(strlen(key) + 1);
+ if (!clean_key)
+ goto finish;
+ prev_slash = key;
+ while (next_slash) {
+ memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
+ sofar += next_slash - prev_slash;
+ prev_slash = next_slash + 1;
+ next_slash = strstr(prev_slash, "//");
+ }
+ strcpy(clean_key + sofar, prev_slash);
+ key = clean_key;
+ }
+
+ buf = key;
+ file_stem = find_stem_from_file(data, &buf);
+ mode &= S_IFMT;
+
+ /*
+ * Check for matching specifications in reverse order, so that
+ * the last matching specification is used.
+ */
+ for (i = data->nspec - 1; i >= 0; i--) {
+ struct spec *spec = &spec_arr[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->stem_id == -1 || spec->stem_id == file_stem) &&
+ (!mode || !spec->mode || mode == spec->mode)) {
+ if (compile_regex(data, spec, ®ex_error_data) < 0)
+ goto finish;
+ if (spec->stem_id == -1)
+ rc = regex_match(spec->regex, key, partial);
+ else
+ rc = regex_match(spec->regex, buf, partial);
+ if (rc == REGEX_MATCH) {
+ spec->matches++;
+ break;
+ } else if (partial && rc == REGEX_MATCH_PARTIAL)
+ break;
+
+ if (rc == REGEX_NO_MATCH)
+ continue;
+
+ errno = ENOENT;
+ /* else it's an error */
+ goto finish;
+ }
+ }
+
+ if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
+ /* No matching specification. */
+ errno = ENOENT;
+ goto finish;
+ }
+
+ errno = 0;
+ ret = &spec_arr[i];
+
+finish:
+ free(clean_key);
+ return ret;
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
+ const char *key, int type)
+{
+ struct spec *spec;
+
+ spec = lookup_common(rec, key, type, false);
+ if (spec)
+ return &spec->lr;
+ return NULL;
+}
+
+static bool partial_match(struct selabel_handle *rec, const char *key)
+{
+ return lookup_common(rec, key, 0, true) ? true : false;
+}
+
+static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
+ const char *key,
+ const char **aliases,
+ int type)
+{
+ size_t n, i;
+ int best = -1;
+ struct spec **specs;
+ size_t prefix_len = 0;
+ struct selabel_lookup_rec *lr = NULL;
+
+ if (!aliases || !aliases[0])
+ return lookup(rec, key, type);
+
+ for (n = 0; aliases[n]; n++)
+ ;
+
+ specs = calloc(n+1, sizeof(struct spec *));
+ if (!specs)
+ return NULL;
+ specs[0] = lookup_common(rec, key, type, false);
+ if (specs[0]) {
+ if (!specs[0]->hasMetaChars) {
+ /* exact match on key */
+ lr = &specs[0]->lr;
+ goto out;
+ }
+ best = 0;
+ prefix_len = specs[0]->prefix_len;
+ }
+ for (i = 1; i <= n; i++) {
+ specs[i] = lookup_common(rec, aliases[i-1], type, false);
+ if (specs[i]) {
+ if (!specs[i]->hasMetaChars) {
+ /* exact match on alias */
+ lr = &specs[i]->lr;
+ goto out;
+ }
+ if (specs[i]->prefix_len > prefix_len) {
+ best = i;
+ prefix_len = specs[i]->prefix_len;
+ }
+ }
+ }
+
+ if (best >= 0) {
+ /* longest fixed prefix match on key or alias */
+ lr = &specs[best]->lr;
+ } else {
+ errno = ENOENT;
+ }
+
+out:
+ free(specs);
+ return lr;
+}
+
+static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j)
+{
+ selinux_log(SELINUX_INFO,
+ "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n",
+ reason,
+ i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw,
+ j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw);
+ return SELABEL_INCOMPARABLE;
+}
+
+static enum selabel_cmp_result cmp(struct selabel_handle *h1,
+ struct selabel_handle *h2)
+{
+ struct saved_data *data1 = (struct saved_data *)h1->data;
+ struct saved_data *data2 = (struct saved_data *)h2->data;
+ unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec;
+ struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr;
+ struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr;
+ bool skipped1 = false, skipped2 = false;
+
+ i = 0;
+ j = 0;
+ while (i < nspec1 && j < nspec2) {
+ struct spec *spec1 = &spec_arr1[i];
+ struct spec *spec2 = &spec_arr2[j];
+
+ /*
+ * Because sort_specs() moves exact pathnames to the
+ * end, we might need to skip over additional regex
+ * entries that only exist in one of the configurations.
+ */
+ if (!spec1->hasMetaChars && spec2->hasMetaChars) {
+ j++;
+ skipped2 = true;
+ continue;
+ }
+
+ if (spec1->hasMetaChars && !spec2->hasMetaChars) {
+ i++;
+ skipped1 = true;
+ continue;
+ }
+
+ if (spec1->regcomp && spec2->regcomp) {
+ if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){
+ return incomp(spec1, spec2, "regex", i, j);
+ }
+ } else {
+ if (strcmp(spec1->regex_str, spec2->regex_str))
+ return incomp(spec1, spec2, "regex_str", i, j);
+ }
+
+ if (spec1->mode != spec2->mode)
+ return incomp(spec1, spec2, "mode", i, j);
+
+ if (spec1->stem_id == -1 && spec2->stem_id != -1)
+ return incomp(spec1, spec2, "stem_id", i, j);
+ if (spec2->stem_id == -1 && spec1->stem_id != -1)
+ return incomp(spec1, spec2, "stem_id", i, j);
+ if (spec1->stem_id != -1 && spec2->stem_id != -1) {
+ struct stem *stem1 = &stem_arr1[spec1->stem_id];
+ struct stem *stem2 = &stem_arr2[spec2->stem_id];
+ if (stem1->len != stem2->len ||
+ strncmp(stem1->buf, stem2->buf, stem1->len))
+ return incomp(spec1, spec2, "stem", i, j);
+ }
+
+ if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw))
+ return incomp(spec1, spec2, "ctx_raw", i, j);
+
+ i++;
+ j++;
+ }
+
+ if ((skipped1 || i < nspec1) && !skipped2)
+ return SELABEL_SUPERSET;
+ if ((skipped2 || j < nspec2) && !skipped1)
+ return SELABEL_SUBSET;
+ if (skipped1 && skipped2)
+ return SELABEL_INCOMPARABLE;
+ return SELABEL_EQUAL;
+}
+
+
+static void stats(struct selabel_handle *rec)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ unsigned int i, nspec = data->nspec;
+ struct spec *spec_arr = data->spec_arr;
+
+ for (i = 0; i < nspec; i++) {
+ if (spec_arr[i].matches == 0) {
+ if (spec_arr[i].type_str) {
+ selinux_log(SELINUX_WARNING,
+ "Warning! No matches for (%s, %s, %s)\n",
+ spec_arr[i].regex_str,
+ spec_arr[i].type_str,
+ spec_arr[i].lr.ctx_raw);
+ } else {
+ selinux_log(SELINUX_WARNING,
+ "Warning! No matches for (%s, %s)\n",
+ spec_arr[i].regex_str,
+ spec_arr[i].lr.ctx_raw);
+ }
+ }
+ }
+}
+
+int selabel_file_init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned nopts)
+{
+ struct saved_data *data;
+
+ data = (struct saved_data *)malloc(sizeof(*data));
+ if (!data)
+ return -1;
+ memset(data, 0, sizeof(*data));
+
+ rec->data = data;
+ rec->func_close = &closef;
+ rec->func_stats = &stats;
+ rec->func_lookup = &lookup;
+ rec->func_partial_match = &partial_match;
+ rec->func_lookup_best_match = &lookup_best_match;
+ rec->func_cmp = &cmp;
+
+ return init(rec, opts, nopts);
+}
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
new file mode 100644
index 0000000..d0ff254
--- /dev/null
+++ b/libselinux/src/label_file.h
@@ -0,0 +1,474 @@
+#ifndef _SELABEL_FILE_H_
+#define _SELABEL_FILE_H_
+
+#include <errno.h>
+#include <string.h>
+
+#include <sys/stat.h>
+
+/*
+ * Android: regex.h/c was introduced to hold all dependencies on the regular
+ * expression back-end when we started supporting PCRE2. regex.h defines a
+ * minimal interface required by libselinux, so that the remaining code
+ * can be agnostic about the underlying implementation.
+ */
+#include "regex.h"
+
+#include "callbacks.h"
+#include "label_internal.h"
+
+#define SELINUX_MAGIC_COMPILED_FCONTEXT 0xf97cff8a
+
+/* Version specific changes */
+#define SELINUX_COMPILED_FCONTEXT_NOPCRE_VERS 1
+#define SELINUX_COMPILED_FCONTEXT_PCRE_VERS 2
+#define SELINUX_COMPILED_FCONTEXT_MODE 3
+#define SELINUX_COMPILED_FCONTEXT_PREFIX_LEN 4
+
+#define SELINUX_COMPILED_FCONTEXT_MAX_VERS SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
+
+/* A file security context specification. */
+struct spec {
+ struct selabel_lookup_rec lr; /* holds contexts for lookup result */
+ char *regex_str; /* regular expession string for diagnostics */
+ char *type_str; /* type string for diagnostic messages */
+ struct regex_data * regex; /* backend dependent regular expression data */
+ mode_t mode; /* mode format value */
+ int matches; /* number of matching pathnames */
+ int stem_id; /* indicates which stem-compression item */
+ char hasMetaChars; /* regular expression has meta-chars */
+ char regcomp; /* regex_str has been compiled to regex */
+ char from_mmap; /* this spec is from an mmap of the data */
+ size_t prefix_len; /* length of fixed path prefix */
+};
+
+/* A regular expression stem */
+struct stem {
+ char *buf;
+ int len;
+ char from_mmap;
+};
+
+/* Where we map the file in during selabel_open() */
+struct mmap_area {
+ void *addr; /* Start addr + len used to release memory at close */
+ size_t len;
+ void *next_addr; /* Incremented by next_entry() */
+ size_t next_len; /* Decremented by next_entry() */
+ struct mmap_area *next;
+};
+
+/* Our stored configuration */
+struct saved_data {
+ /*
+ * The array of specifications, initially in the same order as in
+ * the specification file. Sorting occurs based on hasMetaChars.
+ */
+ struct spec *spec_arr;
+ unsigned int nspec;
+ unsigned int alloc_specs;
+
+ /*
+ * The array of regular expression stems.
+ */
+ struct stem *stem_arr;
+ int num_stems;
+ int alloc_stems;
+ struct mmap_area *mmap_areas;
+};
+
+static inline mode_t string_to_mode(char *mode)
+{
+ size_t len;
+
+ if (!mode)
+ return 0;
+ len = strlen(mode);
+ if (mode[0] != '-' || len != 2)
+ return -1;
+ switch (mode[1]) {
+ case 'b':
+ return S_IFBLK;
+ case 'c':
+ return S_IFCHR;
+ case 'd':
+ return S_IFDIR;
+ case 'p':
+ return S_IFIFO;
+ case 'l':
+ return S_IFLNK;
+ case 's':
+ return S_IFSOCK;
+ case '-':
+ return S_IFREG;
+ default:
+ return -1;
+ }
+ /* impossible to get here */
+ return 0;
+}
+
+static inline int grow_specs(struct saved_data *data)
+{
+ struct spec *specs;
+ size_t new_specs, total_specs;
+
+ if (data->nspec < data->alloc_specs)
+ return 0;
+
+ new_specs = data->nspec + 16;
+ total_specs = data->nspec + new_specs;
+
+ specs = realloc(data->spec_arr, total_specs * sizeof(*specs));
+ if (!specs) {
+ perror("realloc");
+ return -1;
+ }
+
+ /* blank the new entries */
+ memset(&specs[data->nspec], 0, new_specs * sizeof(*specs));
+
+ data->spec_arr = specs;
+ data->alloc_specs = total_specs;
+ return 0;
+}
+
+/* Determine if the regular expression specification has any meta characters. */
+static inline 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;
+ spec->prefix_len = len;
+
+ /* 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;
+ spec->prefix_len = c - spec->regex_str;
+ return;
+ case '\\': /* skip the next character */
+ c++;
+ break;
+ default:
+ break;
+
+ }
+ c++;
+ }
+}
+
+/* Move exact pathname specifications to the end. */
+static inline int sort_specs(struct saved_data *data)
+{
+ struct spec *spec_copy;
+ struct spec spec;
+ unsigned int i;
+ int front, back;
+ size_t len = sizeof(*spec_copy);
+
+ spec_copy = malloc(len * data->nspec);
+ if (!spec_copy)
+ return -1;
+
+ /* first move the exact pathnames to the back */
+ front = 0;
+ back = data->nspec - 1;
+ for (i = 0; i < data->nspec; i++) {
+ if (data->spec_arr[i].hasMetaChars)
+ memcpy(&spec_copy[front++], &data->spec_arr[i], len);
+ else
+ memcpy(&spec_copy[back--], &data->spec_arr[i], len);
+ }
+
+ /*
+ * now the exact pathnames are at the end, but they are in the reverse
+ * order. Since 'front' is now the first of the 'exact' we can run
+ * that part of the array switching the front and back element.
+ */
+ back = data->nspec - 1;
+ while (front < back) {
+ /* save the front */
+ memcpy(&spec, &spec_copy[front], len);
+ /* move the back to the front */
+ memcpy(&spec_copy[front], &spec_copy[back], len);
+ /* put the old front in the back */
+ memcpy(&spec_copy[back], &spec, len);
+ front++;
+ back--;
+ }
+
+ free(data->spec_arr);
+ data->spec_arr = spec_copy;
+
+ return 0;
+}
+
+/* Return the length of the text that can be considered the stem, returns 0
+ * if there is no identifiable stem */
+static inline 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 stemid given a string and a length
+ */
+static inline int find_stem(struct saved_data *data, const char *buf,
+ int stem_len)
+{
+ int i;
+
+ for (i = 0; i < data->num_stems; i++) {
+ if (stem_len == data->stem_arr[i].len &&
+ !strncmp(buf, data->stem_arr[i].buf, stem_len))
+ return i;
+ }
+
+ return -1;
+}
+
+/* returns the index of the new stored object */
+static inline int store_stem(struct saved_data *data, char *buf, int stem_len)
+{
+ int num = data->num_stems;
+
+ if (data->alloc_stems == num) {
+ struct stem *tmp_arr;
+
+ data->alloc_stems = data->alloc_stems * 2 + 16;
+ tmp_arr = realloc(data->stem_arr,
+ sizeof(*tmp_arr) * data->alloc_stems);
+ if (!tmp_arr)
+ return -1;
+ data->stem_arr = tmp_arr;
+ }
+ data->stem_arr[num].len = stem_len;
+ data->stem_arr[num].buf = buf;
+ data->stem_arr[num].from_mmap = 0;
+ data->num_stems++;
+
+ return num;
+}
+
+/* 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). */
+static inline int find_stem_from_spec(struct saved_data *data, const char *buf)
+{
+ int stem_len = get_stem_from_spec(buf);
+ int stemid;
+ char *stem;
+
+ if (!stem_len)
+ return -1;
+
+ stemid = find_stem(data, buf, stem_len);
+ if (stemid >= 0)
+ return stemid;
+
+ /* not found, allocate a new one */
+ stem = strndup(buf, stem_len);
+ if (!stem)
+ return -1;
+
+ return store_stem(data, stem, stem_len);
+}
+
+/* This will always check for buffer over-runs and either read the next entry
+ * if buf != NULL or skip over the entry (as these areas are mapped in the
+ * current buffer). */
+static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
+{
+ if (bytes > fp->next_len)
+ return -1;
+
+ if (buf)
+ memcpy(buf, fp->next_addr, bytes);
+
+ fp->next_addr = (char *)fp->next_addr + bytes;
+ fp->next_len -= bytes;
+ return 0;
+}
+
+static inline int compile_regex(struct saved_data *data, struct spec *spec,
+ struct regex_error_data * error_data)
+{
+ char *reg_buf, *anchored_regex, *cp;
+ struct stem *stem_arr = data->stem_arr;
+ size_t len;
+ int rc;
+
+ if (spec->regcomp)
+ return 0; /* already done */
+
+ /* Skip the fixed stem. */
+ reg_buf = spec->regex_str;
+ if (spec->stem_id >= 0)
+ reg_buf += stem_arr[spec->stem_id].len;
+
+ /* Anchor the regular expression. */
+ len = strlen(reg_buf);
+ cp = anchored_regex = malloc(len + 3);
+ if (!anchored_regex)
+ return -1;
+
+ /* Create ^...$ regexp. */
+ *cp++ = '^';
+ memcpy(cp, reg_buf, len);
+ cp += len;
+ *cp++ = '$';
+ *cp = '\0';
+
+ /* Compile the regular expression. */
+ rc = regex_prepare_data(&spec->regex, anchored_regex, error_data);
+ free(anchored_regex);
+ if (rc < 0) {
+ return -1;
+ }
+
+ /* Done. */
+ spec->regcomp = 1;
+
+ return 0;
+}
+
+/* This service is used by label_file.c process_file() and
+ * utils/sefcontext_compile.c */
+static inline int process_line(struct selabel_handle *rec,
+ const char *path, const char *prefix,
+ char *line_buf, unsigned lineno)
+{
+ int items, len, rc;
+ char *regex = NULL, *type = NULL, *context = NULL;
+ struct saved_data *data = (struct saved_data *)rec->data;
+ struct spec *spec_arr;
+ unsigned int nspec = data->nspec;
+ char const *errbuf;
+ struct regex_error_data error_data;
+
+ items = read_spec_entries(line_buf, &errbuf, 3, ®ex, &type, &context);
+ if (items < 0) {
+ rc = errno;
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u error due to: %s\n", path,
+ lineno, errbuf ?: strerror(errno));
+ errno = rc;
+ return -1;
+ }
+
+ if (items == 0)
+ return items;
+
+ if (items < 2) {
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u is missing fields\n", path,
+ lineno);
+ if (items == 1)
+ free(regex);
+ errno = EINVAL;
+ return -1;
+ } else if (items == 2) {
+ /* The type field is optional. */
+ context = type;
+ type = 0;
+ }
+
+ len = get_stem_from_spec(regex);
+ if (len && prefix && strncmp(prefix, regex, len)) {
+ /* Stem of regex does not match requested prefix, discard. */
+ free(regex);
+ free(type);
+ free(context);
+ return 0;
+ }
+
+ rc = grow_specs(data);
+ if (rc)
+ return rc;
+
+ spec_arr = data->spec_arr;
+
+ /* process and store the specification in spec. */
+ spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
+ spec_arr[nspec].regex_str = regex;
+
+ spec_arr[nspec].type_str = type;
+ spec_arr[nspec].mode = 0;
+
+ spec_arr[nspec].lr.ctx_raw = context;
+
+ /*
+ * bump data->nspecs to cause closef() to cover it in its free
+ * but do not bump nspec since it's used below.
+ */
+ data->nspec++;
+
+ if (rec->validating &&
+ compile_regex(data, &spec_arr[nspec], &error_data)) {
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u has invalid regex %s: %s\n",
+ path, lineno, regex,
+ (errbuf ? errbuf : "out of memory"));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (type) {
+ mode_t mode = string_to_mode(type);
+
+ if (mode == (mode_t)-1) {
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u has invalid file type %s\n",
+ path, lineno, type);
+ errno = EINVAL;
+ return -1;
+ }
+ spec_arr[nspec].mode = mode;
+ }
+
+ /* Determine if specification has
+ * any meta characters in the RE */
+ spec_hasMetaChars(&spec_arr[nspec]);
+
+ if (strcmp(context, "<<none>>") && rec->validating) {
+ if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
+ selinux_log(SELINUX_ERROR,
+ "%s: line %u has invalid context %s\n",
+ path, lineno, spec_arr[nspec].lr.ctx_raw);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+#endif /* _SELABEL_FILE_H_ */
diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h
new file mode 100644
index 0000000..455d948
--- /dev/null
+++ b/libselinux/src/label_internal.h
@@ -0,0 +1,93 @@
+/*
+ * This file describes the internal interface used by the labeler
+ * for calling the user-supplied memory allocation, validation,
+ * and locking routine.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+#ifndef _SELABEL_INTERNAL_H_
+#define _SELABEL_INTERNAL_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include "dso.h"
+
+/*
+ * Installed backends
+ */
+int selabel_file_init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned nopts) hidden;
+int selabel_media_init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned nopts) hidden;
+int selabel_x_init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned nopts) hidden;
+int selabel_db_init(struct selabel_handle *rec,
+ const struct selinux_opt *opts, unsigned nopts) hidden;
+int selabel_property_init(struct selabel_handle *rec,
+ const struct selinux_opt *opts, unsigned nopts) hidden;
+
+/*
+ * Labeling internal structures
+ */
+struct selabel_sub {
+ char *src;
+ int slen;
+ char *dst;
+ struct selabel_sub *next;
+};
+
+struct selabel_lookup_rec {
+ char * ctx_raw;
+ char * ctx_trans;
+ int validated;
+};
+
+struct selabel_handle {
+ /* arguments that were passed to selabel_open */
+ unsigned int backend;
+ int validating;
+
+ /* labeling operations */
+ struct selabel_lookup_rec *(*func_lookup) (struct selabel_handle *h,
+ const char *key, int type);
+ void (*func_close) (struct selabel_handle *h);
+ void (*func_stats) (struct selabel_handle *h);
+ bool (*func_partial_match) (struct selabel_handle *h, const char *key);
+ struct selabel_lookup_rec *(*func_lookup_best_match)
+ (struct selabel_handle *h,
+ const char *key,
+ const char **aliases,
+ int type);
+ enum selabel_cmp_result (*func_cmp)(struct selabel_handle *h1,
+ struct selabel_handle *h2);
+
+ /* supports backend-specific state information */
+ void *data;
+
+ /*
+ * The main spec file used. Note for file contexts the local and/or
+ * homedirs could also have been used to resolve a context.
+ */
+ char *spec_file;
+
+ /* substitution support */
+ struct selabel_sub *dist_subs;
+ struct selabel_sub *subs;
+};
+
+/*
+ * Validation function
+ */
+extern int
+selabel_validate(struct selabel_handle *rec,
+ struct selabel_lookup_rec *contexts) hidden;
+
+/*
+ * The read_spec_entries function may be used to
+ * replace sscanf to read entries from spec files.
+ */
+extern int read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...);
+
+#endif /* _SELABEL_INTERNAL_H_ */
diff --git a/libselinux/src/label_support.c b/libselinux/src/label_support.c
new file mode 100644
index 0000000..e226d51
--- /dev/null
+++ b/libselinux/src/label_support.c
@@ -0,0 +1,117 @@
+/*
+ * This file contains helper functions for labeling support.
+ *
+ * Author : Richard Haines <richard_c_haines@btinternet.com>
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "label_internal.h"
+
+/*
+ * The read_spec_entries and read_spec_entry functions may be used to
+ * replace sscanf to read entries from spec files. The file and
+ * property services now use these.
+ */
+
+/*
+ * Read an entry from a spec file (e.g. file_contexts)
+ * entry - Buffer to allocate for the entry.
+ * ptr - current location of the line to be processed.
+ * returns - 0 on success and *entry is set to be a null
+ * terminated value. On Error it returns -1 and
+ errno will be set.
+ *
+ */
+static inline int read_spec_entry(char **entry, char **ptr, int *len, const char **errbuf)
+{
+ *entry = NULL;
+ char *tmp_buf = NULL;
+
+ while (isspace(**ptr) && **ptr != '\0')
+ (*ptr)++;
+
+ tmp_buf = *ptr;
+ *len = 0;
+
+ while (!isspace(**ptr) && **ptr != '\0') {
+ if (!isascii(**ptr)) {
+ errno = EINVAL;
+ *errbuf = "Non-ASCII characters found";
+ return -1;
+ }
+ (*ptr)++;
+ (*len)++;
+ }
+
+ if (*len) {
+ *entry = strndup(tmp_buf, *len);
+ if (!*entry)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * line_buf - Buffer containing the spec entries .
+ * errbuf - Double pointer used for passing back specific error messages.
+ * num_args - The number of spec parameter entries to process.
+ * ... - A 'char **spec_entry' for each parameter.
+ * returns - The number of items processed. On error, it returns -1 with errno
+ * set and may set errbuf to a specific error message.
+ *
+ * This function calls read_spec_entry() to do the actual string processing.
+ * As such, can return anything from that function as well.
+ */
+int hidden read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...)
+{
+ char **spec_entry, *buf_p;
+ int len, rc, items, entry_len = 0;
+ va_list ap;
+
+ *errbuf = NULL;
+
+ len = strlen(line_buf);
+ if (line_buf[len - 1] == '\n')
+ line_buf[len - 1] = '\0';
+ else
+ /* Handle case if line not \n terminated by bumping
+ * the len for the check below (as the line is NUL
+ * terminated by getline(3)) */
+ len++;
+
+ buf_p = line_buf;
+ while (isspace(*buf_p))
+ buf_p++;
+
+ /* Skip comment lines and empty lines. */
+ if (*buf_p == '#' || *buf_p == '\0')
+ return 0;
+
+ /* Process the spec file entries */
+ va_start(ap, num_args);
+
+ items = 0;
+ while (items < num_args) {
+ spec_entry = va_arg(ap, char **);
+
+ if (len - 1 == buf_p - line_buf) {
+ va_end(ap);
+ return items;
+ }
+
+ rc = read_spec_entry(spec_entry, &buf_p, &entry_len, errbuf);
+ if (rc < 0) {
+ va_end(ap);
+ return rc;
+ }
+ if (entry_len)
+ items++;
+ }
+ va_end(ap);
+ return items;
+}
diff --git a/libselinux/src/lgetfilecon.c b/libselinux/src/lgetfilecon.c
new file mode 100644
index 0000000..22851a4
--- /dev/null
+++ b/libselinux/src/lgetfilecon.c
@@ -0,0 +1,50 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int lgetfilecon(const char *path, char ** context)
+{
+ char *buf;
+ ssize_t size;
+ ssize_t ret;
+
+ size = INITCONTEXTLEN + 1;
+ buf = malloc(size);
+ if (!buf)
+ return -1;
+ memset(buf, 0, size);
+
+ ret = lgetxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+ if (ret < 0 && errno == ERANGE) {
+ char *newbuf;
+
+ size = lgetxattr(path, XATTR_NAME_SELINUX, NULL, 0);
+ if (size < 0)
+ goto out;
+
+ size++;
+ newbuf = realloc(buf, size);
+ if (!newbuf)
+ goto out;
+
+ buf = newbuf;
+ memset(buf, 0, size);
+ ret = lgetxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+ }
+ out:
+ if (ret == 0) {
+ /* Re-map empty attribute values to errors. */
+ errno = EOPNOTSUPP;
+ ret = -1;
+ }
+ if (ret < 0)
+ free(buf);
+ else
+ *context = buf;
+ return ret;
+}
diff --git a/libselinux/src/load_policy.c b/libselinux/src/load_policy.c
new file mode 100644
index 0000000..51a178a
--- /dev/null
+++ b/libselinux/src/load_policy.c
@@ -0,0 +1,41 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "selinux_internal.h"
+#include <dlfcn.h>
+#include "policy.h"
+#include <limits.h>
+#include "callbacks.h"
+
+int security_load_policy(void *data, size_t len)
+{
+ char path[PATH_MAX];
+ int fd, ret;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/load", selinux_mnt);
+ fd = open(path, O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return -1;
+
+ ret = write(fd, data, len);
+ close(fd);
+ if (ret < 0)
+ return -1;
+ return 0;
+}
+
+hidden_def(security_load_policy)
diff --git a/libselinux/src/lsetfilecon.c b/libselinux/src/lsetfilecon.c
new file mode 100644
index 0000000..7147f9e
--- /dev/null
+++ b/libselinux/src/lsetfilecon.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int lsetfilecon(const char *path, const char *context)
+{
+ return lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,
+ 0);
+}
+
diff --git a/libselinux/src/mapping.c b/libselinux/src/mapping.c
new file mode 100644
index 0000000..f205804
--- /dev/null
+++ b/libselinux/src/mapping.c
@@ -0,0 +1,210 @@
+/*
+ * Class and permission mappings.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include "mapping.h"
+
+/*
+ * Class and permission mappings
+ */
+
+struct selinux_mapping {
+ security_class_t value; /* real, kernel value */
+ unsigned num_perms;
+ access_vector_t perms[sizeof(access_vector_t) * 8];
+};
+
+static struct selinux_mapping *current_mapping = NULL;
+static security_class_t current_mapping_size = 0;
+
+/*
+ * Mapping setting function
+ */
+
+int
+selinux_set_mapping(struct security_class_mapping *map)
+{
+ size_t size = sizeof(struct selinux_mapping);
+ security_class_t i, j;
+ unsigned k;
+
+ free(current_mapping);
+ current_mapping = NULL;
+ current_mapping_size = 0;
+
+ if (avc_reset() < 0)
+ goto err;
+
+ /* Find number of classes in the input mapping */
+ if (!map) {
+ errno = EINVAL;
+ goto err;
+ }
+ i = 0;
+ while (map[i].name)
+ i++;
+
+ /* Allocate space for the class records, plus one for class zero */
+ current_mapping = (struct selinux_mapping *)calloc(++i, size);
+ if (!current_mapping)
+ goto err;
+
+ /* Store the raw class and permission values */
+ j = 0;
+ while (map[j].name) {
+ struct security_class_mapping *p_in = map + (j++);
+ struct selinux_mapping *p_out = current_mapping + j;
+
+ p_out->value = string_to_security_class(p_in->name);
+ if (!p_out->value)
+ goto err2;
+
+ k = 0;
+ while (p_in->perms[k]) {
+ /* An empty permission string skips ahead */
+ if (!*p_in->perms[k]) {
+ k++;
+ continue;
+ }
+ p_out->perms[k] = string_to_av_perm(p_out->value,
+ p_in->perms[k]);
+ if (!p_out->perms[k])
+ goto err2;
+ k++;
+ }
+ p_out->num_perms = k;
+ }
+
+ /* Set the mapping size here so the above lookups are "raw" */
+ current_mapping_size = i;
+ return 0;
+err2:
+ free(current_mapping);
+ current_mapping = NULL;
+ current_mapping_size = 0;
+err:
+ return -1;
+}
+
+/*
+ * Get real, kernel values from mapped values
+ */
+
+security_class_t
+unmap_class(security_class_t tclass)
+{
+ if (tclass < current_mapping_size)
+ return current_mapping[tclass].value;
+
+ /* If here no mapping set or the class requested is not valid. */
+ if (current_mapping_size != 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ return tclass;
+}
+
+access_vector_t
+unmap_perm(security_class_t tclass, access_vector_t tperm)
+{
+ if (tclass < current_mapping_size) {
+ unsigned i;
+ access_vector_t kperm = 0;
+
+ for (i=0; i<current_mapping[tclass].num_perms; i++)
+ if (tperm & (1<<i)) {
+ kperm |= current_mapping[tclass].perms[i];
+ tperm &= ~(1<<i);
+ }
+ return kperm;
+ }
+
+ /* If here no mapping set or the perm requested is not valid. */
+ if (current_mapping_size != 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ return tperm;
+}
+
+/*
+ * Get mapped values from real, kernel values
+ */
+
+security_class_t
+map_class(security_class_t kclass)
+{
+ security_class_t i;
+
+ for (i=0; i<current_mapping_size; i++)
+ if (current_mapping[i].value == kclass)
+ return i;
+
+/* If here no mapping set or the class requested is not valid. */
+ if (current_mapping_size != 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ return kclass;
+}
+
+access_vector_t
+map_perm(security_class_t tclass, access_vector_t kperm)
+{
+ if (tclass < current_mapping_size) {
+ unsigned i;
+ access_vector_t tperm = 0;
+
+ for (i=0; i<current_mapping[tclass].num_perms; i++)
+ if (kperm & current_mapping[tclass].perms[i]) {
+ tperm |= 1<<i;
+ kperm &= ~current_mapping[tclass].perms[i];
+ }
+
+ if (tperm == 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ return tperm;
+ }
+ return kperm;
+}
+
+void
+map_decision(security_class_t tclass, struct av_decision *avd)
+{
+ if (tclass < current_mapping_size) {
+ unsigned i;
+ access_vector_t result;
+
+ for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+ if (avd->allowed & current_mapping[tclass].perms[i])
+ result |= 1<<i;
+ avd->allowed = result;
+
+ for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+ if (avd->decided & current_mapping[tclass].perms[i])
+ result |= 1<<i;
+ avd->decided = result;
+
+ for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+ if (avd->auditallow & current_mapping[tclass].perms[i])
+ result |= 1<<i;
+ avd->auditallow = result;
+
+ for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+ if (avd->auditdeny & current_mapping[tclass].perms[i])
+ result |= 1<<i;
+ avd->auditdeny = result;
+ }
+}
diff --git a/libselinux/src/mapping.h b/libselinux/src/mapping.h
new file mode 100644
index 0000000..b96756b
--- /dev/null
+++ b/libselinux/src/mapping.h
@@ -0,0 +1,41 @@
+/*
+ * This file describes the class and permission mappings used to
+ * hide the kernel numbers from userspace by allowing userspace object
+ * managers to specify a list of classes and permissions.
+ */
+#ifndef _SELINUX_MAPPING_H_
+#define _SELINUX_MAPPING_H_
+
+#include <selinux/selinux.h>
+
+/*
+ * Get real, kernel values from mapped values
+ */
+
+extern security_class_t
+unmap_class(security_class_t tclass);
+
+extern access_vector_t
+unmap_perm(security_class_t tclass, access_vector_t tperm);
+
+/*
+ * Get mapped values from real, kernel values
+ */
+
+extern security_class_t
+map_class(security_class_t kclass);
+
+extern access_vector_t
+map_perm(security_class_t tclass, access_vector_t kperm);
+
+extern void
+map_decision(security_class_t tclass, struct av_decision *avd);
+
+/*mapping is not used for embedded build*/
+#ifdef DISABLE_AVC
+#define unmap_perm(x,y) y
+#define unmap_class(x) x
+#define map_decision(x,y)
+#endif
+
+#endif /* _SELINUX_MAPPING_H_ */
diff --git a/libselinux/src/policy.h b/libselinux/src/policy.h
new file mode 100644
index 0000000..92a416e
--- /dev/null
+++ b/libselinux/src/policy.h
@@ -0,0 +1,28 @@
+#ifndef _POLICY_H_
+#define _POLICY_H_
+
+/* Private definitions used internally by libselinux. */
+
+/* xattr name for SELinux attributes. */
+#define XATTR_NAME_SELINUX "security.selinux"
+
+/* Initial length guess for getting contexts. */
+#define INITCONTEXTLEN 255
+
+/* selinuxfs magic number */
+#define SELINUX_MAGIC 0xf97cff8c
+
+/* Preferred selinuxfs mount point directory paths. */
+#define SELINUXMNT "/sys/fs/selinux"
+#define OLDSELINUXMNT "/selinux"
+
+/* selinuxfs filesystem type string. */
+#define SELINUXFS "selinuxfs"
+
+/* selinuxfs mount point determined at runtime */
+extern char *selinux_mnt;
+
+/* First version of policy supported in mainline Linux. */
+#define DEFAULT_POLICY_VERSION 15
+
+#endif
diff --git a/libselinux/src/policyvers.c b/libselinux/src/policyvers.c
new file mode 100644
index 0000000..284a7f7
--- /dev/null
+++ b/libselinux/src/policyvers.c
@@ -0,0 +1,45 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <stdio.h>
+#include "policy.h"
+#include "dso.h"
+#include <limits.h>
+
+int security_policyvers(void)
+{
+ int fd, ret;
+ char path[PATH_MAX];
+ char buf[20];
+ unsigned vers = DEFAULT_POLICY_VERSION;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/policyvers", selinux_mnt);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return vers;
+ else
+ return -1;
+ }
+ memset(buf, 0, sizeof buf);
+ ret = read(fd, buf, sizeof buf - 1);
+ close(fd);
+ if (ret < 0)
+ return -1;
+
+ if (sscanf(buf, "%u", &vers) != 1)
+ return -1;
+
+ return vers;
+}
+
+hidden_def(security_policyvers)
diff --git a/libselinux/src/procattr.c b/libselinux/src/procattr.c
new file mode 100644
index 0000000..74c0012
--- /dev/null
+++ b/libselinux/src/procattr.c
@@ -0,0 +1,176 @@
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+#ifdef HOST
+static pid_t gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+#endif
+
+static int openattr(pid_t pid, const char *attr, int flags)
+{
+ int fd, rc;
+ char *path;
+ pid_t tid;
+
+ if (pid > 0) {
+ rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
+ } else if (pid == 0) {
+ rc = asprintf(&path, "/proc/thread-self/attr/%s", attr);
+ if (rc < 0)
+ return -1;
+ fd = open(path, flags | O_CLOEXEC);
+ if (fd >= 0 || errno != ENOENT)
+ goto out;
+ free(path);
+ tid = gettid();
+ rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ if (rc < 0)
+ return -1;
+
+ fd = open(path, flags | O_CLOEXEC);
+out:
+ free(path);
+ return fd;
+}
+
+static int getprocattrcon(char ** context,
+ pid_t pid, const char *attr)
+{
+ char *buf;
+ size_t size;
+ int fd;
+ ssize_t ret;
+ int errno_hold;
+
+ fd = openattr(pid, attr, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ size = selinux_page_size;
+ buf = malloc(size);
+ if (!buf) {
+ ret = -1;
+ goto out;
+ }
+ memset(buf, 0, size);
+
+ do {
+ ret = read(fd, buf, size - 1);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0)
+ goto out2;
+
+ if (ret == 0) {
+ *context = NULL;
+ goto out2;
+ }
+
+ *context = strdup(buf);
+ if (!(*context)) {
+ ret = -1;
+ goto out2;
+ }
+ ret = 0;
+ out2:
+ free(buf);
+ out:
+ errno_hold = errno;
+ close(fd);
+ errno = errno_hold;
+ return ret;
+}
+
+static int setprocattrcon(const char * context,
+ pid_t pid, const char *attr)
+{
+ int fd;
+ ssize_t ret;
+ int errno_hold;
+
+ fd = openattr(pid, attr, O_RDWR);
+ if (fd < 0)
+ return -1;
+ if (context)
+ do {
+ ret = write(fd, context, strlen(context) + 1);
+ } while (ret < 0 && errno == EINTR);
+ else
+ do {
+ ret = write(fd, NULL, 0); /* clear */
+ } while (ret < 0 && errno == EINTR);
+ errno_hold = errno;
+ close(fd);
+ errno = errno_hold;
+ if (ret < 0)
+ return -1;
+ else
+ return 0;
+}
+
+#define getselfattr_def(fn, attr) \
+ int get##fn(char **c) \
+ { \
+ return getprocattrcon(c, 0, #attr); \
+ }
+
+#define setselfattr_def(fn, attr) \
+ int set##fn(const char * c) \
+ { \
+ return setprocattrcon(c, 0, #attr); \
+ }
+
+#define all_selfattr_def(fn, attr) \
+ getselfattr_def(fn, attr) \
+ setselfattr_def(fn, attr)
+
+#define getpidattr_def(fn, attr) \
+ int get##fn(pid_t pid, char **c) \
+ { \
+ if (pid <= 0) { \
+ errno = EINVAL; \
+ return -1; \
+ } else { \
+ return getprocattrcon(c, pid, #attr); \
+ } \
+ }
+
+all_selfattr_def(con, current)
+ getpidattr_def(pidcon, current)
+ getselfattr_def(prevcon, prev)
+ all_selfattr_def(execcon, exec)
+ all_selfattr_def(fscreatecon, fscreate)
+ all_selfattr_def(sockcreatecon, sockcreate)
+ all_selfattr_def(keycreatecon, keycreate)
+
+ hidden_def(getcon_raw)
+ hidden_def(getcon)
+ hidden_def(getexeccon_raw)
+ hidden_def(getfilecon_raw)
+ hidden_def(getfilecon)
+ hidden_def(getfscreatecon_raw)
+ hidden_def(getkeycreatecon_raw)
+ hidden_def(getpeercon_raw)
+ hidden_def(getpidcon_raw)
+ hidden_def(getprevcon_raw)
+ hidden_def(getprevcon)
+ hidden_def(getsockcreatecon_raw)
+ hidden_def(setcon_raw)
+ hidden_def(setexeccon_raw)
+ hidden_def(setexeccon)
+ hidden_def(setfilecon_raw)
+ hidden_def(setfscreatecon_raw)
+ hidden_def(setkeycreatecon_raw)
+ hidden_def(setsockcreatecon_raw)
diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
new file mode 100644
index 0000000..4223b02
--- /dev/null
+++ b/libselinux/src/regex.c
@@ -0,0 +1,390 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "regex.h"
+#include "label_file.h"
+
+int regex_prepare_data(struct regex_data ** regex, char const * pattern_string,
+ struct regex_error_data * errordata) {
+ memset(errordata, 0, sizeof(struct regex_error_data));
+ *regex = regex_data_create();
+ if (!(*regex))
+ return -1;
+#ifdef USE_PCRE2
+ (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
+ PCRE2_ZERO_TERMINATED,
+ PCRE2_DOTALL,
+ &errordata->error_code,
+ &errordata->error_offset, NULL);
+#else
+ (*regex)->regex = pcre_compile(pattern_string, PCRE_DOTALL,
+ &errordata->error_buffer,
+ &errordata->error_offset, NULL);
+#endif
+ if (!(*regex)->regex) {
+ goto err;
+ }
+
+#ifdef USE_PCRE2
+ (*regex)->match_data =
+ pcre2_match_data_create_from_pattern((*regex)->regex, NULL);
+ if (!(*regex)->match_data) {
+ goto err;
+ }
+#else
+ (*regex)->sd = pcre_study((*regex)->regex, 0, &errordata->error_buffer);
+ if (!(*regex)->sd && errordata->error_buffer) {
+ goto err;
+ }
+ (*regex)->extra_owned = !!(*regex)->sd;
+#endif
+ return 0;
+
+err: regex_data_free(*regex);
+ *regex = NULL;
+ return -1;
+}
+
+char const * regex_version() {
+#ifdef USE_PCRE2
+ static int initialized = 0;
+ static char * version_string = NULL;
+ size_t version_string_len;
+ if (!initialized) {
+ version_string_len = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
+ version_string = (char*) malloc(version_string_len);
+ if (!version_string) {
+ return NULL;
+ }
+ pcre2_config(PCRE2_CONFIG_VERSION, version_string);
+ initialized = 1;
+ }
+ return version_string;
+#else
+ return pcre_version();
+#endif
+}
+
+int regex_load_mmap(struct mmap_area * mmap_area, struct regex_data ** regex) {
+ int rc;
+ size_t entry_len, info_len;
+
+ rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
+#ifdef USE_PCRE2
+ if (rc < 0)
+ return -1;
+
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ /* this should yield exactly one because we store one pattern at a time
+ */
+ rc = pcre2_serialize_get_number_of_codes(mmap_area->next_addr);
+ if (rc != 1)
+ return -1;
+
+ *regex = regex_data_create();
+ if (!*regex)
+ return -1;
+
+ rc = pcre2_serialize_decode(&(*regex)->regex, 1,
+ (PCRE2_SPTR)mmap_area->next_addr, NULL);
+ if (rc != 1)
+ goto err;
+
+ (*regex)->match_data =
+ pcre2_match_data_create_from_pattern((*regex)->regex, NULL);
+ if (!(*regex)->match_data)
+ goto err;
+
+#endif /* NO_PERSISTENTLY_STORED_PATTERNS */
+ /* and skip the decoded bit */
+ rc = next_entry(NULL, mmap_area, entry_len);
+ if (rc < 0)
+ goto err;
+
+ return 0;
+#else
+ if (rc < 0 || !entry_len) {
+ rc = -1;
+ return -1;
+ }
+ *regex = regex_data_create();
+ if (!(*regex))
+ return -1;
+
+ (*regex)->regex = (pcre *) mmap_area->next_addr;
+ rc = next_entry(NULL, mmap_area, entry_len);
+ if (rc < 0)
+ goto err;
+
+ /* Check that regex lengths match. pcre_fullinfo()
+ * also validates its magic number. */
+ rc = pcre_fullinfo((*regex)->regex, NULL, PCRE_INFO_SIZE, &info_len);
+ if (rc < 0 || info_len != entry_len) {
+ goto err;
+ }
+
+ rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
+ if (rc < 0 || !entry_len) {
+ goto err;
+ }
+ (*regex)->lsd.study_data = (void *) mmap_area->next_addr;
+ (*regex)->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
+ rc = next_entry(NULL, mmap_area, entry_len);
+ if (rc < 0)
+ goto err;
+
+ /* Check that study data lengths match. */
+ rc = pcre_fullinfo((*regex)->regex, &(*regex)->lsd,
+ PCRE_INFO_STUDYSIZE,
+ &info_len);
+ if (rc < 0 || info_len != entry_len) {
+ goto err;
+ }
+ (*regex)->extra_owned = 0;
+ return 0;
+#endif
+ err: regex_data_free(*regex);
+ *regex = NULL;
+ return -1;
+}
+
+int regex_writef(struct regex_data * regex, FILE * fp) {
+ int rc;
+ size_t len;
+#ifdef USE_PCRE2
+ PCRE2_UCHAR * bytes;
+ PCRE2_SIZE to_write;
+
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ /* encode the patter for serialization */
+ rc = pcre2_serialize_encode(®ex->regex, 1, &bytes, &to_write, NULL);
+ if (rc != 1)
+ return -1;
+
+#else
+ to_write = 0;
+#endif
+ /* write serialized pattern's size */
+ len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
+ if (len != 1) {
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ pcre2_serialize_free(bytes);
+#endif
+ return -1;
+ }
+
+#ifndef NO_PERSISTENTLY_STORED_PATTERNS
+ /* write serialized pattern */
+ len = fwrite(bytes, 1, to_write, fp);
+ if (len != to_write) {
+ pcre2_serialize_free(bytes);
+ return -1;
+ }
+ pcre2_serialize_free(bytes);
+#endif
+#else
+ uint32_t to_write;
+ size_t size;
+ pcre_extra * sd = regex->extra_owned ? regex->sd : ®ex->lsd;
+
+ /* determine the size of the pcre data in bytes */
+ rc = pcre_fullinfo(regex->regex, NULL, PCRE_INFO_SIZE, &size);
+ if (rc < 0)
+ return -1;
+
+ /* write the number of bytes in the pcre data */
+ to_write = size;
+ len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
+ if (len != 1)
+ return -1;
+
+ /* write the actual pcre data as a char array */
+ len = fwrite(regex->regex, 1, to_write, fp);
+ if (len != to_write)
+ return -1;
+
+ /* determine the size of the pcre study info */
+ rc = pcre_fullinfo(regex->regex, sd, PCRE_INFO_STUDYSIZE, &size);
+ if (rc < 0)
+ return -1;
+
+ /* write the number of bytes in the pcre study data */
+ to_write = size;
+ len = fwrite(&to_write, sizeof(uint32_t), 1, fp);
+ if (len != 1)
+ return -1;
+
+ /* write the actual pcre study data as a char array */
+ len = fwrite(sd->study_data, 1, to_write, fp);
+ if (len != to_write)
+ return -1;
+#endif
+ return 0;
+}
+
+struct regex_data * regex_data_create() {
+ struct regex_data * dummy = (struct regex_data*) malloc(
+ sizeof(struct regex_data));
+ if (dummy) {
+ memset(dummy, 0, sizeof(struct regex_data));
+ }
+ return dummy;
+}
+
+void regex_data_free(struct regex_data * regex) {
+ if (regex) {
+#ifdef USE_PCRE2
+ if (regex->regex) {
+ pcre2_code_free(regex->regex);
+ }
+ if (regex->match_data) {
+ pcre2_match_data_free(regex->match_data);
+ }
+#else
+ if (regex->regex)
+ pcre_free(regex->regex);
+ if (regex->extra_owned && regex->sd) {
+ pcre_free_study(regex->sd);
+ }
+#endif
+ free(regex);
+ }
+}
+
+int regex_match(struct regex_data * regex, char const * subject, int partial) {
+ int rc;
+#ifdef USE_PCRE2
+ rc = pcre2_match(regex->regex,
+ (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0,
+ partial ? PCRE2_PARTIAL_SOFT : 0, regex->match_data,
+ NULL);
+ if (rc > 0)
+ return REGEX_MATCH;
+ switch (rc) {
+ case PCRE2_ERROR_PARTIAL:
+ return REGEX_MATCH_PARTIAL;
+ case PCRE2_ERROR_NOMATCH:
+ return REGEX_NO_MATCH;
+ default:
+ return REGEX_ERROR;
+ }
+#else
+ rc = pcre_exec(regex->regex,
+ regex->extra_owned ? regex->sd : ®ex->lsd, subject,
+ strlen(subject), 0, partial ? PCRE_PARTIAL_SOFT : 0,
+ NULL,
+ 0);
+ switch (rc) {
+ case 0:
+ return REGEX_MATCH;
+ case PCRE_ERROR_PARTIAL:
+ return REGEX_MATCH_PARTIAL;
+ case PCRE_ERROR_NOMATCH:
+ return REGEX_NO_MATCH;
+ default:
+ return REGEX_ERROR;
+ }
+#endif
+}
+
+/* TODO Replace this compare function with something that actually compares the
+ * regular expressions.
+ * This compare function basically just compares the binary representations of
+ * the automatons, and because this representation contains pointers and
+ * metadata, it can only return a match if regex1 == regex2.
+ * Preferably, this function would be replaced with an algorithm that computes
+ * the equivalence of the automatons systematically.
+ */
+int regex_cmp(struct regex_data * regex1, struct regex_data * regex2) {
+ int rc;
+ size_t len1, len2;
+#ifdef USE_PCRE2
+ rc = pcre2_pattern_info(regex1->regex, PCRE2_INFO_SIZE, &len1);
+ assert(rc == 0);
+ rc = pcre2_pattern_info(regex2->regex, PCRE2_INFO_SIZE, &len2);
+ assert(rc == 0);
+ if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1))
+ return SELABEL_INCOMPARABLE;
+#else
+ rc = pcre_fullinfo(regex1->regex, NULL, PCRE_INFO_SIZE, &len1);
+ assert(rc == 0);
+ rc = pcre_fullinfo(regex2->regex, NULL, PCRE_INFO_SIZE, &len2);
+ assert(rc == 0);
+ if (len1 != len2 || memcmp(regex1->regex, regex2->regex, len1))
+ return SELABEL_INCOMPARABLE;
+#endif
+ return SELABEL_EQUAL;
+}
+
+void regex_format_error(struct regex_error_data const * error_data,
+ char * buffer, size_t buf_size) {
+ unsigned the_end_length = buf_size > 4 ? 4 : buf_size;
+ char * ptr = &buffer[buf_size - the_end_length];
+ int rc = 0;
+ size_t pos = 0;
+ if (!buffer || !buf_size)
+ return;
+ rc = snprintf(buffer, buf_size, "REGEX back-end error: ");
+ if (rc < 0)
+ /* If snprintf fails it constitutes a logical error that needs
+ * fixing.
+ */
+ abort();
+
+ pos += rc;
+ if (pos >= buf_size)
+ goto truncated;
+
+ if (error_data->error_offset > 0) {
+#ifdef USE_PCRE2
+ rc = snprintf(buffer + pos, buf_size - pos, "At offset %lu: ",
+ error_data->error_offset);
+#else
+ rc = snprintf(buffer + pos, buf_size - pos, "At offset %d: ",
+ error_data->error_offset);
+#endif
+ if (rc < 0)
+ abort();
+
+ }
+ pos += rc;
+ if (pos >= buf_size)
+ goto truncated;
+
+#ifdef USE_PCRE2
+ rc = pcre2_get_error_message(error_data->error_code,
+ (PCRE2_UCHAR*)(buffer + pos),
+ buf_size - pos);
+ if (rc == PCRE2_ERROR_NOMEMORY)
+ goto truncated;
+#else
+ rc = snprintf(buffer + pos, buf_size - pos, "%s",
+ error_data->error_buffer);
+ if (rc < 0)
+ abort();
+
+ if ((size_t)rc < strlen(error_data->error_buffer))
+ goto truncated;
+#endif
+
+ return;
+
+truncated:
+ /* replace end of string with "..." to indicate that it was truncated */
+ switch (the_end_length) {
+ /* no break statements, fall-through is intended */
+ case 4:
+ *ptr++ = '.';
+ case 3:
+ *ptr++ = '.';
+ case 2:
+ *ptr++ = '.';
+ case 1:
+ *ptr++ = '\0';
+ default:
+ break;
+ }
+ return;
+}
diff --git a/libselinux/src/regex.h b/libselinux/src/regex.h
new file mode 100644
index 0000000..7b74f32
--- /dev/null
+++ b/libselinux/src/regex.h
@@ -0,0 +1,166 @@
+#ifndef SRC_REGEX_H_
+#define SRC_REGEX_H_
+
+#include <stdio.h>
+
+#ifdef USE_PCRE2
+#include <pcre2.h>
+#else
+#include <pcre.h>
+#endif
+
+enum {
+ REGEX_MATCH,
+ REGEX_MATCH_PARTIAL,
+ REGEX_NO_MATCH,
+ REGEX_ERROR = -1,
+};
+
+#ifdef USE_PCRE2
+struct regex_data {
+ pcre2_code * regex; /* compiled regular expression */
+ pcre2_match_data * match_data; /* match data block required for the compiled
+ pattern in regex2 */
+};
+
+struct regex_error_data {
+ int error_code;
+ PCRE2_SIZE error_offset;
+};
+
+/* ^^^^^^ USE_PCRE2 ^^^^^^ */
+#else
+/* vvvvvv USE_PCRE vvvvvv */
+
+/* Prior to version 8.20, libpcre did not have pcre_free_study() */
+#if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20))
+#define pcre_free_study pcre_free
+#endif
+
+struct regex_data {
+ pcre *regex; /* compiled regular expression */
+ int extra_owned;
+ union {
+ pcre_extra *sd; /* pointer to extra compiled stuff */
+ pcre_extra lsd; /* used to hold the mmap'd version */
+ };
+};
+
+struct regex_error_data {
+ char const * error_buffer;
+ int error_offset;
+};
+
+#endif /* USE_PCRE2 */
+
+struct mmap_area;
+
+/**
+ * regex_verison returns the version string of the underlying regular
+ * regular expressions library. In the case of PCRE it just returns the
+ * result of pcre_version(). In the case of PCRE2, the very first time this
+ * function is called it allocates a buffer large enough to hold the version
+ * string and reads the PCRE2_CONFIG_VERSION option to fill the buffer.
+ * The allocated buffer will linger in memory until the calling process is being
+ * reaped.
+ *
+ * It may return NULL on error.
+ */
+char const * regex_version();
+/**
+ * This constructor function allocates a buffer for a regex_data structure.
+ * The buffer is being initialized with zeroes.
+ */
+struct regex_data * regex_data_create();
+/**
+ * This complementary destructor function frees the a given regex_data buffer.
+ * It also frees any non NULL member pointers with the appropriate pcreX_X_free
+ * function. For PCRE this function respects the extra_owned field and frees
+ * the pcre_extra data conditionally. Calling this function on a NULL pointer is
+ * save.
+ */
+void regex_data_free(struct regex_data * regex);
+/**
+ * This function compiles the regular expression. Additionally, it prepares
+ * data structures required by the different underlying engines. For PCRE
+ * it calls pcre_study to generate optional data required for optimized
+ * execution of the compiled pattern. In the case of PCRE2, it allocates
+ * a pcre2_match_data structure of appropriate size to hold all possible
+ * matches created by the pattern.
+ *
+ * @arg regex If successful, the structure returned through *regex was allocated
+ * with regex_data_create and must be freed with regex_data_free.
+ * @arg pattern_string The pattern string that is to be compiled.
+ * @arg errordata A pointer to a regex_error_data structure must be passed
+ * to this function. This structure depends on the underlying
+ * implementation. It can be passed to regex_format_error
+ * to generate a human readable error message.
+ * @retval 0 on success
+ * @retval -1 on error
+ */
+int regex_prepare_data(struct regex_data ** regex, char const * pattern_string,
+ struct regex_error_data * errordata);
+/**
+ * This function loads a serialized precompiled pattern from a contiguous
+ * data region given by map_area.
+ *
+ * @arg map_area Description of the memory region holding a serialized
+ * representation of the precompiled pattern.
+ * @arg regex If successful, the structure returned through *regex was allocated
+ * with regex_data_create and must be freed with regex_data_free.
+ *
+ * @retval 0 on success
+ * @retval -1 on error
+ */
+int regex_load_mmap(struct mmap_area * map_area, struct regex_data ** regex);
+/**
+ * This function stores a precompiled regular expression to a file.
+ * In the case of PCRE, it just dumps the binary representation of the
+ * precomplied pattern into a file. In the case of PCRE2, it uses the
+ * serialization function provided by the library.
+ *
+ * @arg regex The precomplied regular expression data.
+ * @arg fp A file stream specifying the output file.
+ */
+int regex_writef(struct regex_data * regex, FILE * fp);
+/**
+ * This function applies a precompiled pattern to a subject string and
+ * returns whether or not a match was found.
+ *
+ * @arg regex The precompiled pattern.
+ * @arg subject The subject string.
+ * @arg partial Boolean indicating if partial matches are wanted. A nonzero
+ * value is equivalent to specifying PCRE[2]_PARTIAL_SOFT as
+ * option to pcre_exec of pcre2_match.
+ * @retval REGEX_MATCH if a match was found
+ * @retval REGEX_MATCH_PARTIAL if a partial match was found
+ * @retval REGEX_NO_MATCH if no match was found
+ * @retval REGEX_ERROR if an error was encountered during the execution of the
+ * regular expression
+ */
+int regex_match(struct regex_data * regex, char const * subject, int partial);
+/**
+ * This function compares two compiled regular expressions (regex1 and regex2).
+ * It compares the binary representations of the compiled patterns. It is a very
+ * crude approximation because the binary representation holds data like
+ * reference counters, that has nothing to do with the actual state machine.
+ *
+ * @retval SELABEL_EQUAL if the pattern's binary representations are exactly
+ * the same
+ * @retval SELABEL_INCOMPARABLE otherwise
+ */
+int regex_cmp(struct regex_data * regex1, struct regex_data * regex2);
+/**
+ * This function takes the error data returned by regex_prepare_data and turns
+ * it in to a human readable error message.
+ * If the buffer given to hold the error message is to small it truncates the
+ * message and indicates the truncation with an ellipsis ("...") at the end of
+ * the buffer.
+ *
+ * @arg error_data Error data as returned by regex_prepare_data.
+ * @arg buffer String buffer to hold the formated error string.
+ * @arg buf_size Total size of the given bufer in bytes.
+ */
+void regex_format_error(struct regex_error_data const * error_data,
+ char * buffer, size_t buf_size);
+#endif /* SRC_REGEX_H_ */
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
new file mode 100644
index 0000000..5087bb6
--- /dev/null
+++ b/libselinux/src/selinux_internal.h
@@ -0,0 +1,132 @@
+#include <selinux/selinux.h>
+#include <pthread.h>
+#include "dso.h"
+
+hidden_proto(selinux_mkload_policy)
+ hidden_proto(set_selinuxmnt)
+ hidden_proto(security_disable)
+ hidden_proto(security_policyvers)
+ hidden_proto(security_load_policy)
+ hidden_proto(security_get_boolean_active)
+ hidden_proto(security_get_boolean_names)
+ hidden_proto(security_set_boolean)
+ hidden_proto(security_commit_booleans)
+ hidden_proto(security_check_context)
+ hidden_proto(security_check_context_raw)
+ hidden_proto(security_canonicalize_context)
+ hidden_proto(security_canonicalize_context_raw)
+ hidden_proto(security_compute_av)
+ hidden_proto(security_compute_av_raw)
+ hidden_proto(security_compute_av_flags)
+ hidden_proto(security_compute_av_flags_raw)
+ hidden_proto(security_compute_user)
+ hidden_proto(security_compute_user_raw)
+ hidden_proto(security_compute_create)
+ hidden_proto(security_compute_create_raw)
+ hidden_proto(security_compute_member_raw)
+ hidden_proto(security_compute_relabel_raw)
+ hidden_proto(is_selinux_enabled)
+ hidden_proto(is_selinux_mls_enabled)
+ hidden_proto(freecon)
+ hidden_proto(freeconary)
+ hidden_proto(getprevcon)
+ hidden_proto(getprevcon_raw)
+ hidden_proto(getcon)
+ hidden_proto(getcon_raw)
+ hidden_proto(setcon_raw)
+ hidden_proto(getpeercon_raw)
+ hidden_proto(getpidcon_raw)
+ hidden_proto(getexeccon_raw)
+ hidden_proto(getfilecon)
+ hidden_proto(getfilecon_raw)
+ hidden_proto(lgetfilecon_raw)
+ hidden_proto(fgetfilecon_raw)
+ hidden_proto(setfilecon_raw)
+ hidden_proto(lsetfilecon_raw)
+ hidden_proto(fsetfilecon_raw)
+ hidden_proto(setexeccon)
+ hidden_proto(setexeccon_raw)
+ hidden_proto(getfscreatecon_raw)
+ hidden_proto(getkeycreatecon_raw)
+ hidden_proto(getsockcreatecon_raw)
+ hidden_proto(setfscreatecon_raw)
+ hidden_proto(setkeycreatecon_raw)
+ hidden_proto(setsockcreatecon_raw)
+ hidden_proto(security_getenforce)
+ hidden_proto(security_setenforce)
+ hidden_proto(security_deny_unknown)
+ hidden_proto(selinux_binary_policy_path)
+ hidden_proto(selinux_default_context_path)
+ hidden_proto(selinux_securetty_types_path)
+ hidden_proto(selinux_failsafe_context_path)
+ hidden_proto(selinux_removable_context_path)
+ hidden_proto(selinux_virtual_domain_context_path)
+ hidden_proto(selinux_virtual_image_context_path)
+ hidden_proto(selinux_file_context_path)
+ hidden_proto(selinux_file_context_homedir_path)
+ hidden_proto(selinux_file_context_local_path)
+ hidden_proto(selinux_file_context_subs_path)
+ hidden_proto(selinux_netfilter_context_path)
+ hidden_proto(selinux_homedir_context_path)
+ hidden_proto(selinux_user_contexts_path)
+ hidden_proto(selinux_booleans_path)
+ hidden_proto(selinux_customizable_types_path)
+ hidden_proto(selinux_media_context_path)
+ hidden_proto(selinux_x_context_path)
+ hidden_proto(selinux_sepgsql_context_path)
+ hidden_proto(selinux_path)
+ hidden_proto(selinux_check_passwd_access)
+ hidden_proto(selinux_check_securetty_context)
+ hidden_proto(matchpathcon_init_prefix)
+ hidden_proto(selinux_users_path)
+ hidden_proto(selinux_usersconf_path);
+hidden_proto(selinux_translations_path);
+hidden_proto(selinux_colors_path);
+hidden_proto(selinux_getenforcemode);
+hidden_proto(selinux_getpolicytype);
+hidden_proto(selinux_raw_to_trans_context);
+hidden_proto(selinux_trans_to_raw_context);
+ hidden_proto(selinux_raw_context_to_color);
+hidden_proto(security_get_initial_context);
+hidden_proto(security_get_initial_context_raw);
+hidden_proto(selinux_reset_config);
+
+extern int selinux_page_size hidden;
+
+/* Make pthread_once optional */
+#pragma weak pthread_once
+#pragma weak pthread_key_create
+#pragma weak pthread_key_delete
+#pragma weak pthread_setspecific
+
+/* Call handler iff the first call. */
+#define __selinux_once(ONCE_CONTROL, INIT_FUNCTION) \
+ do { \
+ if (pthread_once != NULL) \
+ pthread_once (&(ONCE_CONTROL), (INIT_FUNCTION)); \
+ else if ((ONCE_CONTROL) == PTHREAD_ONCE_INIT) { \
+ INIT_FUNCTION (); \
+ (ONCE_CONTROL) = 2; \
+ } \
+ } while (0)
+
+/* Pthread key macros */
+#define __selinux_key_create(KEY, DESTRUCTOR) \
+ do { \
+ if (pthread_key_create != NULL) \
+ pthread_key_create(KEY, DESTRUCTOR); \
+ } while (0)
+
+#define __selinux_key_delete(KEY) \
+ do { \
+ if (pthread_key_delete != NULL) \
+ pthread_key_delete(KEY); \
+ } while (0)
+
+#define __selinux_setspecific(KEY, VALUE) \
+ do { \
+ if (pthread_setspecific != NULL) \
+ pthread_setspecific(KEY, VALUE); \
+ } while (0)
+
+
diff --git a/libselinux/src/selinux_netlink.h b/libselinux/src/selinux_netlink.h
new file mode 100644
index 0000000..88ef551
--- /dev/null
+++ b/libselinux/src/selinux_netlink.h
@@ -0,0 +1,31 @@
+/*
+ * Netlink event notifications for SELinux.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ */
+#ifndef _LINUX_SELINUX_NETLINK_H
+#define _LINUX_SELINUX_NETLINK_H
+
+/* Message types. */
+#define SELNL_MSG_BASE 0x10
+enum {
+ SELNL_MSG_SETENFORCE = SELNL_MSG_BASE,
+ SELNL_MSG_POLICYLOAD,
+ SELNL_MSG_MAX
+};
+
+/* Multicast groups */
+#define SELNL_GRP_NONE 0x00000000
+#define SELNL_GRP_AVC 0x00000001 /* AVC notifications */
+#define SELNL_GRP_ALL 0xffffffff
+
+/* Message structures */
+struct selnl_msg_setenforce {
+ int32_t val;
+};
+
+struct selnl_msg_policyload {
+ uint32_t seqno;
+};
+
+#endif /* _LINUX_SELINUX_NETLINK_H */
diff --git a/libselinux/src/sestatus.c b/libselinux/src/sestatus.c
new file mode 100644
index 0000000..ed29dc5
--- /dev/null
+++ b/libselinux/src/sestatus.c
@@ -0,0 +1,349 @@
+/*
+ * sestatus.c
+ *
+ * APIs to reference SELinux kernel status page (/selinux/status)
+ *
+ * Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ */
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "avc_internal.h"
+#include "policy.h"
+
+/*
+ * copied from the selinux/include/security.h
+ */
+struct selinux_status_t
+{
+ uint32_t version; /* version number of thie structure */
+ uint32_t sequence; /* sequence number of seqlock logic */
+ uint32_t enforcing; /* current setting of enforcing mode */
+ uint32_t policyload; /* times of policy reloaded */
+ uint32_t deny_unknown; /* current setting of deny_unknown */
+ /* version > 0 support above status */
+} __attribute((packed));
+
+/*
+ * `selinux_status'
+ *
+ * NULL : not initialized yet
+ * MAP_FAILED : opened, but fallback-mode
+ * Valid Pointer : opened and mapped correctly
+ */
+static struct selinux_status_t *selinux_status = NULL;
+static int selinux_status_fd;
+static uint32_t last_seqno;
+
+static uint32_t fallback_sequence;
+static int fallback_enforcing;
+static int fallback_policyload;
+
+/*
+ * read_sequence
+ *
+ * A utility routine to reference kernel status page according to
+ * seqlock logic. Since selinux_status->sequence is an odd value during
+ * the kernel status page being updated, we try to synchronize completion
+ * of this updating, but we assume it is rare.
+ * The sequence is almost even number.
+ *
+ * __sync_synchronize is a portable memory barrier for various kind
+ * of architecture that is supported by GCC.
+ */
+static inline uint32_t read_sequence(struct selinux_status_t *status)
+{
+ uint32_t seqno = 0;
+
+ do {
+ /*
+ * No need for sched_yield() in the first trial of
+ * this loop.
+ */
+ if (seqno & 0x0001)
+ sched_yield();
+
+ seqno = status->sequence;
+
+ __sync_synchronize();
+
+ } while (seqno & 0x0001);
+
+ return seqno;
+}
+
+/*
+ * selinux_status_updated
+ *
+ * It returns whether something has been happened since the last call.
+ * Because `selinux_status->sequence' shall be always incremented on
+ * both of setenforce/policyreload events, so differences from the last
+ * value informs us something has been happened.
+ */
+int selinux_status_updated(void)
+{
+ uint32_t curr_seqno;
+ int result = 0;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED) {
+ if (avc_netlink_check_nb() < 0)
+ return -1;
+
+ curr_seqno = fallback_sequence;
+ } else {
+ curr_seqno = read_sequence(selinux_status);
+ }
+
+ /*
+ * `curr_seqno' is always even-number, so it does not match with
+ * `last_seqno' being initialized to odd-number in the first call.
+ * We never return 'something was updated' in the first call,
+ * because this function focuses on status-updating since the last
+ * invocation.
+ */
+ if (last_seqno & 0x0001)
+ last_seqno = curr_seqno;
+
+ if (last_seqno != curr_seqno)
+ {
+ last_seqno = curr_seqno;
+ result = 1;
+ }
+ return result;
+}
+
+/*
+ * selinux_status_getenforce
+ *
+ * It returns the current performing mode of SELinux.
+ * 1 means currently we run in enforcing mode, or 0 means permissive mode.
+ */
+int selinux_status_getenforce(void)
+{
+ uint32_t seqno;
+ uint32_t enforcing;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED) {
+ if (avc_netlink_check_nb() < 0)
+ return -1;
+
+ return fallback_enforcing;
+ }
+
+ /* sequence must not be changed during references */
+ do {
+ seqno = read_sequence(selinux_status);
+
+ enforcing = selinux_status->enforcing;
+
+ } while (seqno != read_sequence(selinux_status));
+
+ return enforcing ? 1 : 0;
+}
+
+/*
+ * selinux_status_policyload
+ *
+ * It returns times of policy reloaded on the running system.
+ * Note that it is not a reliable value on fallback-mode until it receives
+ * the first event message via netlink socket, so, a correct usage of this
+ * value is to compare it with the previous value to detect policy reloaded
+ * event.
+ */
+int selinux_status_policyload(void)
+{
+ uint32_t seqno;
+ uint32_t policyload;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED) {
+ if (avc_netlink_check_nb() < 0)
+ return -1;
+
+ return fallback_policyload;
+ }
+
+ /* sequence must not be changed during references */
+ do {
+ seqno = read_sequence(selinux_status);
+
+ policyload = selinux_status->policyload;
+
+ } while (seqno != read_sequence(selinux_status));
+
+ return policyload;
+}
+
+/*
+ * selinux_status_deny_unknown
+ *
+ * It returns a guideline to handle undefined object classes or permissions.
+ * 0 means SELinux treats policy queries on undefined stuff being allowed,
+ * however, 1 means such queries are denied.
+ */
+int selinux_status_deny_unknown(void)
+{
+ uint32_t seqno;
+ uint32_t deny_unknown;
+
+ if (selinux_status == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (selinux_status == MAP_FAILED)
+ return security_deny_unknown();
+
+ /* sequence must not be changed during references */
+ do {
+ seqno = read_sequence(selinux_status);
+
+ deny_unknown = selinux_status->deny_unknown;
+
+ } while (seqno != read_sequence(selinux_status));
+
+ return deny_unknown ? 1 : 0;
+}
+
+/*
+ * callback routines for fallback case using netlink socket
+ */
+static int fallback_cb_setenforce(int enforcing)
+{
+ fallback_sequence += 2;
+ fallback_enforcing = enforcing;
+
+ return 0;
+}
+
+static int fallback_cb_policyload(int policyload)
+{
+ fallback_sequence += 2;
+ fallback_policyload = policyload;
+
+ return 0;
+}
+
+/*
+ * selinux_status_open
+ *
+ * It tries to open and mmap kernel status page (/selinux/status).
+ * Since Linux 2.6.37 or later supports this feature, we may run
+ * fallback routine using a netlink socket on older kernels, if
+ * the supplied `fallback' is not zero.
+ * It returns 0 on success, or -1 on error.
+ */
+int selinux_status_open(int fallback)
+{
+ int fd;
+ char path[PATH_MAX];
+ long pagesize;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 0)
+ return -1;
+
+ snprintf(path, sizeof(path), "%s/status", selinux_mnt);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ goto error;
+
+ selinux_status = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (selinux_status == MAP_FAILED) {
+ close(fd);
+ goto error;
+ }
+ selinux_status_fd = fd;
+ last_seqno = (uint32_t)(-1);
+
+ return 0;
+
+error:
+ /*
+ * If caller wants fallback routine, we try to provide
+ * an equivalent functionality using existing netlink
+ * socket, although it needs system call invocation to
+ * receive event notification.
+ */
+ if (fallback && avc_netlink_open(0) == 0) {
+ union selinux_callback cb;
+
+ /* register my callbacks */
+ cb.func_setenforce = fallback_cb_setenforce;
+ selinux_set_callback(SELINUX_CB_SETENFORCE, cb);
+ cb.func_policyload = fallback_cb_policyload;
+ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb);
+
+ /* mark as fallback mode */
+ selinux_status = MAP_FAILED;
+ selinux_status_fd = avc_netlink_acquire_fd();
+ last_seqno = (uint32_t)(-1);
+
+ fallback_sequence = 0;
+ fallback_enforcing = security_getenforce();
+ fallback_policyload = 0;
+
+ return 1;
+ }
+ selinux_status = NULL;
+
+ return -1;
+}
+
+/*
+ * selinux_status_close
+ *
+ * It unmap and close the kernel status page, or close netlink socket
+ * if fallback mode.
+ */
+void selinux_status_close(void)
+{
+ long pagesize;
+
+ /* not opened */
+ if (selinux_status == NULL)
+ return;
+
+ /* fallback-mode */
+ if (selinux_status == MAP_FAILED)
+ {
+ avc_netlink_release_fd();
+ avc_netlink_close();
+ selinux_status = NULL;
+ return;
+ }
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ /* not much we can do other than leak memory */
+ if (pagesize > 0)
+ munmap(selinux_status, pagesize);
+ selinux_status = NULL;
+
+ close(selinux_status_fd);
+ selinux_status_fd = -1;
+ last_seqno = (uint32_t)(-1);
+}
diff --git a/libselinux/src/setenforce.c b/libselinux/src/setenforce.c
new file mode 100644
index 0000000..e5e7612
--- /dev/null
+++ b/libselinux/src/setenforce.c
@@ -0,0 +1,37 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_setenforce(int value)
+{
+ int fd, ret;
+ char path[PATH_MAX];
+ char buf[20];
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ snprintf(buf, sizeof buf, "%d", value);
+ ret = write(fd, buf, strlen(buf));
+ close(fd);
+ if (ret < 0)
+ return -1;
+
+ return 0;
+}
+
+hidden_def(security_setenforce)
diff --git a/libselinux/src/setfilecon.c b/libselinux/src/setfilecon.c
new file mode 100644
index 0000000..81322f8
--- /dev/null
+++ b/libselinux/src/setfilecon.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int setfilecon(const char *path, const char *context)
+{
+ return setxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,
+ 0);
+}
+
diff --git a/libselinux/src/stringrep.c b/libselinux/src/stringrep.c
new file mode 100644
index 0000000..c867222
--- /dev/null
+++ b/libselinux/src/stringrep.c
@@ -0,0 +1,277 @@
+/*
+ * String representation support for classes and permissions.
+ */
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+#define MAXVECTORS 8*sizeof(access_vector_t)
+
+struct discover_class_node {
+ char *name;
+ security_class_t value;
+ char **perms;
+
+ struct discover_class_node *next;
+};
+
+static struct discover_class_node *discover_class_cache = NULL;
+
+static struct discover_class_node * get_class_cache_entry_name(const char *s)
+{
+ struct discover_class_node *node = discover_class_cache;
+
+ for (; node != NULL && strcmp(s,node->name) != 0; node = node->next);
+
+ return node;
+}
+
+static struct discover_class_node * get_class_cache_entry_value(security_class_t c)
+{
+ struct discover_class_node *node = discover_class_cache;
+
+ for (; node != NULL && c != node->value; node = node->next);
+
+ return node;
+}
+
+static struct discover_class_node * discover_class(const char *s)
+{
+ int fd, ret;
+ char path[PATH_MAX];
+ char buf[20];
+ DIR *dir;
+ struct dirent *dentry;
+ size_t i;
+
+ struct discover_class_node *node;
+
+ if (!selinux_mnt) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /* allocate a node */
+ node = malloc(sizeof(struct discover_class_node));
+ if (node == NULL)
+ return NULL;
+
+ /* allocate array for perms */
+ node->perms = calloc(MAXVECTORS,sizeof(char*));
+ if (node->perms == NULL)
+ goto err1;
+
+ /* load up the name */
+ node->name = strdup(s);
+ if (node->name == NULL)
+ goto err2;
+
+ /* load up class index */
+ snprintf(path, sizeof path, "%s/class/%s/index", selinux_mnt,s);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto err3;
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret < 0)
+ goto err3;
+
+ if (sscanf(buf, "%hu", &node->value) != 1)
+ goto err3;
+
+ /* load up permission indicies */
+ snprintf(path, sizeof path, "%s/class/%s/perms",selinux_mnt,s);
+ dir = opendir(path);
+ if (dir == NULL)
+ goto err3;
+
+ dentry = readdir(dir);
+ while (dentry != NULL) {
+ unsigned int value;
+ struct stat m;
+
+ snprintf(path, sizeof path, "%s/class/%s/perms/%s", selinux_mnt,s,dentry->d_name);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ goto err4;
+
+ if (fstat(fd, &m) < 0) {
+ close(fd);
+ goto err4;
+ }
+
+ if (m.st_mode & S_IFDIR) {
+ close(fd);
+ dentry = readdir(dir);
+ continue;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret < 0)
+ goto err4;
+
+ if (sscanf(buf, "%u", &value) != 1)
+ goto err4;
+
+ if (value == 0 || value > MAXVECTORS)
+ goto err4;
+
+ node->perms[value-1] = strdup(dentry->d_name);
+ if (node->perms[value-1] == NULL)
+ goto err4;
+
+ dentry = readdir(dir);
+ }
+ closedir(dir);
+
+ node->next = discover_class_cache;
+ discover_class_cache = node;
+
+ return node;
+
+err4:
+ closedir(dir);
+ for (i=0; i<MAXVECTORS; i++)
+ free(node->perms[i]);
+err3:
+ free(node->name);
+err2:
+ free(node->perms);
+err1:
+ free(node);
+ return NULL;
+}
+
+security_class_t string_to_security_class(const char *s)
+{
+ struct discover_class_node *node;
+
+ node = get_class_cache_entry_name(s);
+ if (node == NULL) {
+ node = discover_class(s);
+
+ if (node == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ }
+
+ return map_class(node->value);
+}
+
+access_vector_t string_to_av_perm(security_class_t tclass, const char *s)
+{
+ struct discover_class_node *node;
+ security_class_t kclass = unmap_class(tclass);
+
+ node = get_class_cache_entry_value(kclass);
+ if (node != NULL) {
+ size_t i;
+ for (i=0; i<MAXVECTORS && node->perms[i] != NULL; i++)
+ if (strcmp(node->perms[i],s) == 0)
+ return map_perm(tclass, 1<<i);
+ }
+
+ errno = EINVAL;
+ return 0;
+}
+
+const char *security_class_to_string(security_class_t tclass)
+{
+ struct discover_class_node *node;
+
+ tclass = unmap_class(tclass);
+
+ node = get_class_cache_entry_value(tclass);
+ if (node)
+ return node->name;
+ return NULL;
+}
+
+const char *security_av_perm_to_string(security_class_t tclass,
+ access_vector_t av)
+{
+ struct discover_class_node *node;
+ size_t i;
+
+ av = unmap_perm(tclass, av);
+ tclass = unmap_class(tclass);
+
+ node = get_class_cache_entry_value(tclass);
+ if (av && node)
+ for (i = 0; i<MAXVECTORS; i++)
+ if ((1<<i) & av)
+ return node->perms[i];
+
+ 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;
+}
diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
new file mode 100644
index 0000000..f88c756
--- /dev/null
+++ b/libselinux/utils/sefcontext_compile.c
@@ -0,0 +1,363 @@
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <limits.h>
+#include <selinux/selinux.h>
+
+#include "../src/label_file.h"
+#include "../src/regex.h"
+
+static int validate_context(char __attribute__ ((unused)) **ctx)
+{
+ return 0;
+}
+
+static int process_file(struct selabel_handle *rec, const char *filename)
+{
+ unsigned int line_num;
+ int rc;
+ char *line_buf = NULL;
+ size_t line_len = 0;
+ FILE *context_file;
+ const char *prefix = NULL;
+
+ context_file = fopen(filename, "r");
+ if (!context_file) {
+ fprintf(stderr, "Error opening %s: %s\n",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ line_num = 0;
+ rc = 0;
+ while (getline(&line_buf, &line_len, context_file) > 0) {
+ rc = process_line(rec, filename, prefix, line_buf, ++line_num);
+ if (rc)
+ goto out;
+ }
+out:
+ free(line_buf);
+ fclose(context_file);
+ return rc;
+}
+
+/*
+ * File Format
+ *
+ * u32 - magic number
+ * u32 - version
+ * u32 - length of pcre version EXCLUDING nul
+ * char - pcre version string EXCLUDING nul
+ * u32 - number of stems
+ * ** Stems
+ * u32 - length of stem EXCLUDING nul
+ * char - stem char array INCLUDING nul
+ * u32 - number of regexs
+ * ** Regexes
+ * u32 - length of upcoming context INCLUDING nul
+ * char - char array of the raw context
+ * u32 - length of the upcoming regex_str
+ * char - char array of the original regex string including the stem.
+ * u32 - mode bits for >= SELINUX_COMPILED_FCONTEXT_MODE
+ * mode_t for <= SELINUX_COMPILED_FCONTEXT_PCRE_VERS
+ * s32 - stemid associated with the regex
+ * u32 - spec has meta characters
+ * u32 - The specs prefix_len if >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
+ * u32 - data length of the pcre regex
+ * char - a bufer holding the raw pcre regex info
+ * u32 - data length of the pcre regex study daya
+ * char - a buffer holding the raw pcre regex study data
+ */
+static int write_binary_file(struct saved_data *data, int fd)
+{
+ struct spec *specs = data->spec_arr;
+ FILE *bin_file;
+ size_t len;
+ uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
+ uint32_t section_len;
+ uint32_t i;
+ int rc;
+
+ bin_file = fdopen(fd, "w");
+ if (!bin_file) {
+ perror("fopen output_file");
+ exit(EXIT_FAILURE);
+ }
+
+ /* write some magic number */
+ len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* write the version */
+ section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
+ len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* write version of the regex back-end */
+ if (!regex_version())
+ goto err;
+ section_len = strlen(regex_version());
+ len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+ len = fwrite(regex_version(), sizeof(char), section_len, bin_file);
+ if (len != section_len)
+ goto err;
+
+ /* write the number of stems coming */
+ section_len = data->num_stems;
+ len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ for (i = 0; i < section_len; i++) {
+ char *stem = data->stem_arr[i].buf;
+ uint32_t stem_len = data->stem_arr[i].len;
+
+ /* write the strlen (aka no nul) */
+ len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* include the nul in the file */
+ stem_len += 1;
+ len = fwrite(stem, sizeof(char), stem_len, bin_file);
+ if (len != stem_len)
+ goto err;
+ }
+
+ /* write the number of regexes coming */
+ section_len = data->nspec;
+ len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ for (i = 0; i < section_len; i++) {
+ char *context = specs[i].lr.ctx_raw;
+ char *regex_str = specs[i].regex_str;
+ mode_t mode = specs[i].mode;
+ size_t prefix_len = specs[i].prefix_len;
+ int32_t stem_id = specs[i].stem_id;
+ struct regex_data *re = specs[i].regex;
+ uint32_t to_write;
+ size_t size;
+
+ /* length of the context string (including nul) */
+ to_write = strlen(context) + 1;
+ len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* original context strin (including nul) */
+ len = fwrite(context, sizeof(char), to_write, bin_file);
+ if (len != to_write)
+ goto err;
+
+ /* length of the original regex string (including nul) */
+ to_write = strlen(regex_str) + 1;
+ len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* original regex string */
+ len = fwrite(regex_str, sizeof(char), to_write, bin_file);
+ if (len != to_write)
+ goto err;
+
+ /* binary F_MODE bits */
+ to_write = mode;
+ len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* stem for this regex (could be -1) */
+ len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* does this spec have a metaChar? */
+ to_write = specs[i].hasMetaChars;
+ len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
+ to_write = prefix_len;
+ len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
+ if (len != 1)
+ goto err;
+
+ /* Write regex related data */
+ rc = regex_writef(re, bin_file);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = 0;
+out:
+ fclose(bin_file);
+ return rc;
+err:
+ rc = -1;
+ goto out;
+}
+
+static void free_specs(struct saved_data *data)
+{
+ struct spec *specs = data->spec_arr;
+ unsigned int num_entries = data->nspec;
+ unsigned int i;
+
+ for (i = 0; i < num_entries; i++) {
+ free(specs[i].lr.ctx_raw);
+ free(specs[i].lr.ctx_trans);
+ free(specs[i].regex_str);
+ free(specs[i].type_str);
+ regex_data_free(specs[i].regex);
+ }
+ free(specs);
+
+ num_entries = data->num_stems;
+ for (i = 0; i < num_entries; i++)
+ free(data->stem_arr[i].buf);
+ free(data->stem_arr);
+
+ memset(data, 0, sizeof(*data));
+}
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+ "usage: %s [-o out_file] fc_file\n"
+ "Where:\n\t"
+ "-o Optional file name of the PCRE formatted binary\n\t"
+ " file to be output. If not specified the default\n\t"
+ " will be fc_file with the .bin suffix appended.\n\t"
+ "fc_file The text based file contexts file to be processed.\n",
+ progname);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *path = NULL;
+ const char *out_file = NULL;
+ char stack_path[PATH_MAX + 1];
+ char *tmp = NULL;
+ int fd, rc, opt;
+ struct stat buf;
+ struct selabel_handle *rec = NULL;
+ struct saved_data *data = NULL;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ while ((opt = getopt(argc, argv, "o:")) > 0) {
+ switch (opt) {
+ case 'o':
+ out_file = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if (optind >= argc)
+ usage(argv[0]);
+
+ path = argv[optind];
+ if (stat(path, &buf) < 0) {
+ fprintf(stderr, "Can not stat: %s: %m\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Generate dummy handle for process_line() function */
+ rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
+ if (!rec) {
+ fprintf(stderr, "Failed to calloc handle\n");
+ exit(EXIT_FAILURE);
+ }
+ rec->backend = SELABEL_CTX_FILE;
+
+ /* Need to set validation on to get the bin file generated by the
+ * process_line function, however as the bin file being generated
+ * may not be related to the currently loaded policy (that it
+ * would be validated against), then set callback to ignore any
+ * validation. */
+ rec->validating = 1;
+ selinux_set_callback(SELINUX_CB_VALIDATE,
+ (union selinux_callback)&validate_context);
+
+ data = (struct saved_data *)calloc(1, sizeof(*data));
+ if (!data) {
+ fprintf(stderr, "Failed to calloc saved_data\n");
+ free(rec);
+ exit(EXIT_FAILURE);
+ }
+
+ rec->data = data;
+
+ rc = process_file(rec, path);
+ if (rc < 0)
+ goto err;
+
+ rc = sort_specs(data);
+ if (rc)
+ goto err;
+
+ if (out_file)
+ rc = snprintf(stack_path, sizeof(stack_path), "%s", out_file);
+ else
+ rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
+
+ if (rc < 0 || rc >= (int)sizeof(stack_path))
+ goto err;
+
+ tmp = malloc(strlen(stack_path) + 7);
+ if (!tmp)
+ goto err;
+
+ rc = sprintf(tmp, "%sXXXXXX", stack_path);
+ if (rc < 0)
+ goto err;
+
+ fd = mkstemp(tmp);
+ if (fd < 0)
+ goto err;
+
+ rc = fchmod(fd, buf.st_mode);
+ if (rc < 0) {
+ perror("fchmod failed to set permission on compiled regexs");
+ goto err_unlink;
+ }
+
+ rc = write_binary_file(data, fd);
+ if (rc < 0)
+ goto err_unlink;
+
+ rc = rename(tmp, stack_path);
+ if (rc < 0)
+ goto err_unlink;
+
+ rc = 0;
+out:
+ free_specs(data);
+ free(rec);
+ free(data);
+ free(tmp);
+ return rc;
+
+err_unlink:
+ unlink(tmp);
+err:
+ rc = -1;
+ goto out;
+}