Blob Blame History Raw
diff --git libselinux-2.5-rc1/ChangeLog libselinux-2.5-rc1/ChangeLog
index 993158f..2994ded 100644
--- libselinux-2.5-rc1/ChangeLog
+++ libselinux-2.5-rc1/ChangeLog
@@ -1,4 +1,6 @@
 2.5-rc1 2016-01-07
+	* Add selinux_restorecon function, from Richard Haines.
+	* read_spec_entry: fail on non-ascii, from William Roberts.
 	* Add man information about thread specific functions, from Dan Waslh.
 	* Don't wrap rpm_execcon with DISABLE_RPM with SWIG, from Petr Lautrbach.
 	* Correct line count for property and service context files, from Richard Haines.
diff --git libselinux-2.5-rc1/Makefile libselinux-2.5-rc1/Makefile
index 6142b60..bdf9de8 100644
--- libselinux-2.5-rc1/Makefile
+++ libselinux-2.5-rc1/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = src include utils man
+SUBDIRS = src include utils man golang
 
 DISABLE_AVC ?= n
 DISABLE_SETRANS ?= n
diff --git libselinux-2.5-rc1/golang/Makefile libselinux-2.5-rc1/golang/Makefile
new file mode 100644
index 0000000..b75677b
--- /dev/null
+++ libselinux-2.5-rc1/golang/Makefile
@@ -0,0 +1,22 @@
+# Installation directories.
+PREFIX ?= $(DESTDIR)/usr
+LIBDIR ?= $(DESTDIR)/usr/lib
+GODIR ?= $(LIBDIR)/golang/src/pkg/github.com/selinux
+all:
+
+install: 
+	[ -d $(GODIR) ] || mkdir -p $(GODIR)
+	install -m 644 selinux.go $(GODIR)
+
+test:
+	@mkdir selinux
+	@cp selinux.go selinux
+	GOPATH=$(pwd) go run test.go 
+	@rm -rf selinux
+
+clean:
+	@rm -f *~
+	@rm -rf selinux
+indent:
+
+relabel:
diff --git libselinux-2.5-rc1/golang/selinux.go libselinux-2.5-rc1/golang/selinux.go
new file mode 100644
index 0000000..34bf6bb
--- /dev/null
+++ libselinux-2.5-rc1/golang/selinux.go
@@ -0,0 +1,412 @@
+package selinux
+
+/*
+ The selinux package is a go bindings to libselinux required to add selinux
+ support to docker.
+
+ Author Dan Walsh <dwalsh@redhat.com>
+
+ Used some ideas/code from the go-ini packages https://github.com/vaughan0
+ By Vaughan Newton
+*/
+
+// #cgo pkg-config: libselinux
+// #include <selinux/selinux.h>
+// #include <stdlib.h>
+import "C"
+import (
+	"bufio"
+	"crypto/rand"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"os"
+	"path"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"unsafe"
+)
+
+var (
+	assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
+	mcsList     = make(map[string]bool)
+)
+
+func Matchpathcon(path string, mode os.FileMode) (string, error) {
+	var con C.security_context_t
+	var scon string
+	rc, err := C.matchpathcon(C.CString(path), C.mode_t(mode), &con)
+	if rc == 0 {
+		scon = C.GoString(con)
+		C.free(unsafe.Pointer(con))
+	}
+	return scon, err
+}
+
+func Setfilecon(path, scon string) (int, error) {
+	rc, err := C.lsetfilecon(C.CString(path), C.CString(scon))
+	return int(rc), err
+}
+
+func Getfilecon(path string) (string, error) {
+	var scon C.security_context_t
+	var fcon string
+	rc, err := C.lgetfilecon(C.CString(path), &scon)
+	if rc >= 0 {
+		fcon = C.GoString(scon)
+		err = nil
+	}
+	return fcon, err
+}
+
+func Setfscreatecon(scon string) (int, error) {
+	var (
+		rc  C.int
+		err error
+	)
+	if scon != "" {
+		rc, err = C.setfscreatecon(C.CString(scon))
+	} else {
+		rc, err = C.setfscreatecon(nil)
+	}
+	return int(rc), err
+}
+
+func Getfscreatecon() (string, error) {
+	var scon C.security_context_t
+	var fcon string
+	rc, err := C.getfscreatecon(&scon)
+	if rc >= 0 {
+		fcon = C.GoString(scon)
+		err = nil
+		C.freecon(scon)
+	}
+	return fcon, err
+}
+
+func Getcon() string {
+	var pcon C.security_context_t
+	C.getcon(&pcon)
+	scon := C.GoString(pcon)
+	C.freecon(pcon)
+	return scon
+}
+
+func Getpidcon(pid int) (string, error) {
+	var pcon C.security_context_t
+	var scon string
+	rc, err := C.getpidcon(C.pid_t(pid), &pcon)
+	if rc >= 0 {
+		scon = C.GoString(pcon)
+		C.freecon(pcon)
+		err = nil
+	}
+	return scon, err
+}
+
+func Getpeercon(socket int) (string, error) {
+	var pcon C.security_context_t
+	var scon string
+	rc, err := C.getpeercon(C.int(socket), &pcon)
+	if rc >= 0 {
+		scon = C.GoString(pcon)
+		C.freecon(pcon)
+		err = nil
+	}
+	return scon, err
+}
+
+func Setexeccon(scon string) error {
+	var val *C.char
+	if !SelinuxEnabled() {
+		return nil
+	}
+	if scon != "" {
+		val = C.CString(scon)
+	} else {
+		val = nil
+	}
+	_, err := C.setexeccon(val)
+	return err
+}
+
+type Context struct {
+	con []string
+}
+
+func (c *Context) SetUser(user string) {
+	c.con[0] = user
+}
+func (c *Context) GetUser() string {
+	return c.con[0]
+}
+func (c *Context) SetRole(role string) {
+	c.con[1] = role
+}
+func (c *Context) GetRole() string {
+	return c.con[1]
+}
+func (c *Context) SetType(setype string) {
+	c.con[2] = setype
+}
+func (c *Context) GetType() string {
+	return c.con[2]
+}
+func (c *Context) SetLevel(mls string) {
+	c.con[3] = mls
+}
+func (c *Context) GetLevel() string {
+	return c.con[3]
+}
+func (c *Context) Get() string {
+	return strings.Join(c.con, ":")
+}
+func (c *Context) Set(scon string) {
+	c.con = strings.SplitN(scon, ":", 4)
+}
+func NewContext(scon string) Context {
+	var con Context
+	con.Set(scon)
+	return con
+}
+
+func SelinuxEnabled() bool {
+	b := C.is_selinux_enabled()
+	if b > 0 {
+		return true
+	}
+	return false
+}
+
+const (
+	Enforcing  = 1
+	Permissive = 0
+	Disabled   = -1
+)
+
+func SelinuxGetEnforce() int {
+	return int(C.security_getenforce())
+}
+
+func SelinuxGetEnforceMode() int {
+	var enforce C.int
+	C.selinux_getenforcemode(&enforce)
+	return int(enforce)
+}
+
+func mcsAdd(mcs string) {
+	mcsList[mcs] = true
+}
+
+func mcsDelete(mcs string) {
+	mcsList[mcs] = false
+}
+
+func mcsExists(mcs string) bool {
+	return mcsList[mcs]
+}
+
+func IntToMcs(id int, catRange uint32) string {
+	if (id < 1) || (id > 523776) {
+		return ""
+	}
+
+	SETSIZE := int(catRange)
+	TIER := SETSIZE
+
+	ORD := id
+	for ORD > TIER {
+		ORD = ORD - TIER
+		TIER -= 1
+	}
+	TIER = SETSIZE - TIER
+	ORD = ORD + TIER
+	return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
+}
+
+func uniqMcs(catRange uint32) string {
+	var n uint32
+	var c1, c2 uint32
+	var mcs string
+	for {
+		binary.Read(rand.Reader, binary.LittleEndian, &n)
+		c1 = n % catRange
+		binary.Read(rand.Reader, binary.LittleEndian, &n)
+		c2 = n % catRange
+		if c1 == c2 {
+			continue
+		} else {
+			if c1 > c2 {
+				t := c1
+				c1 = c2
+				c2 = t
+			}
+		}
+		mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
+		if mcsExists(mcs) {
+			continue
+		}
+		mcsAdd(mcs)
+		break
+	}
+	return mcs
+}
+func freeContext(processLabel string) {
+	var scon Context
+	scon = NewContext(processLabel)
+	mcsDelete(scon.GetLevel())
+}
+
+func GetLxcContexts() (processLabel string, fileLabel string) {
+	var val, key string
+	var bufin *bufio.Reader
+	if !SelinuxEnabled() {
+		return
+	}
+	lxcPath := C.GoString(C.selinux_lxc_contexts_path())
+	fileLabel = "system_u:object_r:svirt_sandbox_file_t:s0"
+	processLabel = "system_u:system_r:svirt_lxc_net_t:s0"
+
+	in, err := os.Open(lxcPath)
+	if err != nil {
+		goto exit
+	}
+
+	defer in.Close()
+	bufin = bufio.NewReader(in)
+
+	for done := false; !done; {
+		var line string
+		if line, err = bufin.ReadString('\n'); err != nil {
+			if err == io.EOF {
+				done = true
+			} else {
+				goto exit
+			}
+		}
+		line = strings.TrimSpace(line)
+		if len(line) == 0 {
+			// Skip blank lines
+			continue
+		}
+		if line[0] == ';' || line[0] == '#' {
+			// Skip comments
+			continue
+		}
+		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
+			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
+			if key == "process" {
+				processLabel = strings.Trim(val, "\"")
+			}
+			if key == "file" {
+				fileLabel = strings.Trim(val, "\"")
+			}
+		}
+	}
+exit:
+	var scon Context
+	mcs := IntToMcs(os.Getpid(), 1024)
+	scon = NewContext(processLabel)
+	scon.SetLevel(mcs)
+	processLabel = scon.Get()
+	scon = NewContext(fileLabel)
+	scon.SetLevel(mcs)
+	fileLabel = scon.Get()
+	return processLabel, fileLabel
+}
+
+func CopyLevel(src, dest string) (string, error) {
+	if !SelinuxEnabled() {
+		return "", nil
+	}
+	if src == "" {
+		return "", nil
+	}
+	rc, err := C.security_check_context(C.CString(src))
+	if rc != 0 {
+		return "", err
+	}
+	rc, err = C.security_check_context(C.CString(dest))
+	if rc != 0 {
+		return "", err
+	}
+	scon := NewContext(src)
+	tcon := NewContext(dest)
+	tcon.SetLevel(scon.GetLevel())
+	return tcon.Get(), nil
+}
+
+func RestoreCon(fpath string, recurse bool) error {
+	var flabel string
+	var err error
+	var fs os.FileInfo
+
+	if !SelinuxEnabled() {
+		return nil
+	}
+
+	if recurse {
+		var paths []string
+		var err error
+
+		if paths, err = filepath.Glob(path.Join(fpath, "**", "*")); err != nil {
+			return fmt.Errorf("Unable to find directory %v: %v", fpath, err)
+		}
+
+		for _, fpath := range paths {
+			if err = RestoreCon(fpath, false); err != nil {
+				return fmt.Errorf("Unable to restore selinux context for %v: %v", fpath, err)
+			}
+		}
+		return nil
+	}
+	if fs, err = os.Stat(fpath); err != nil {
+		return fmt.Errorf("Unable stat %v: %v", fpath, err)
+	}
+
+	if flabel, err = Matchpathcon(fpath, fs.Mode()); flabel == "" {
+		return fmt.Errorf("Unable to get context for %v: %v", fpath, err)
+	}
+
+	if rc, err := Setfilecon(fpath, flabel); rc != 0 {
+		return fmt.Errorf("Unable to set selinux context for %v: %v", fpath, err)
+	}
+
+	return nil
+}
+
+func Test() {
+	var plabel, flabel string
+	if !SelinuxEnabled() {
+		return
+	}
+
+	plabel, flabel = GetLxcContexts()
+	fmt.Println(plabel)
+	fmt.Println(flabel)
+	freeContext(plabel)
+	plabel, flabel = GetLxcContexts()
+	fmt.Println(plabel)
+	fmt.Println(flabel)
+	freeContext(plabel)
+	if SelinuxEnabled() {
+		fmt.Println("Enabled")
+	} else {
+		fmt.Println("Disabled")
+	}
+	fmt.Println("getenforce ", SelinuxGetEnforce())
+	fmt.Println("getenforcemode ", SelinuxGetEnforceMode())
+	flabel, _ = Matchpathcon("/home/dwalsh/.emacs", 0)
+	fmt.Println(flabel)
+	pid := os.Getpid()
+	fmt.Printf("PID:%d MCS:%s\n", pid, IntToMcs(pid, 1023))
+	fmt.Println(Getcon())
+	fmt.Println(Getfilecon("/etc/passwd"))
+	fmt.Println(Getpidcon(1))
+	Setfscreatecon("unconfined_u:unconfined_r:unconfined_t:s0")
+	fmt.Println(Getfscreatecon())
+	Setfscreatecon("")
+	fmt.Println(Getfscreatecon())
+	fmt.Println(Getpidcon(1))
+}
diff --git libselinux-2.5-rc1/golang/test.go libselinux-2.5-rc1/golang/test.go
new file mode 100644
index 0000000..fed6de8
--- /dev/null
+++ libselinux-2.5-rc1/golang/test.go
@@ -0,0 +1,9 @@
+package main
+
+import (
+	"./selinux"
+)
+
+func main() {
+	selinux.Test()
+}
diff --git libselinux-2.5-rc1/include/selinux/restorecon.h libselinux-2.5-rc1/include/selinux/restorecon.h
new file mode 100644
index 0000000..ba1232e
--- /dev/null
+++ libselinux-2.5-rc1/include/selinux/restorecon.h
@@ -0,0 +1,79 @@
+#ifndef _RESTORECON_H_
+#define _RESTORECON_H_
+
+#include <sys/types.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * selinux_restorecon - Relabel files.
+ * @pathname: specifies file/directory to relabel.
+ * @restorecon_flags: specifies the actions to be performed when relabeling.
+ *
+ * selinux_restorecon(3) will automatically call
+ * selinux_restorecon_default_handle(3) and selinux_restorecon_set_sehandle(3)
+ * first time through to set the selabel_open(3) parameters to use the
+ * currently loaded policy file_contexts and request their computed digest.
+ *
+ * Should other selabel_open(3) parameters be required see
+ * selinux_restorecon_set_sehandle(3).
+ */
+extern int selinux_restorecon(const char *pathname,
+				    unsigned int restorecon_flags);
+/*
+ * restorecon_flags options
+ */
+/* Force the checking of labels even if the stored SHA1
+ * digest matches the specfiles SHA1 digest. */
+#define SELINUX_RESTORECON_IGNORE_DIGEST		1
+/* Do not change file labels */
+#define SELINUX_RESTORECON_NOCHANGE			2
+/* If set set change file label to that in spec file.
+ * If not only change type component to that in spec file. */
+#define SELINUX_RESTORECON_SET_SPECFILE_CTX		4
+/* Recursively descend directories */
+#define SELINUX_RESTORECON_RECURSE			8
+/* Log changes to selinux log. Note that if VERBOSE and
+ * PROGRESS are set, then PROGRESS will take precedence. */
+#define SELINUX_RESTORECON_VERBOSE			16
+/* Show progress by printing * to stdout every 1000 files */
+#define SELINUX_RESTORECON_PROGRESS			32
+/* Convert passed-in pathname to canonical pathname */
+#define SELINUX_RESTORECON_REALPATH			64
+/* Prevent descending into directories that have a different
+ * device number than the pathname from which the descent began */
+#define SELINUX_RESTORECON_XDEV				128
+
+/**
+ * selinux_restorecon_set_sehandle - Set the global fc handle.
+ * @handle: specifies handle to set as the global fc handle.
+ *
+ * Called by a process that has already called selabel_open(3) with it's
+ * required parameters, or if selinux_restorecon_default_handle(3) has been
+ * called to set the default selabel_open(3) parameters.
+ */
+extern void selinux_restorecon_set_sehandle(struct selabel_handle *hndl);
+
+/**
+ * selinux_restorecon_default_handle - Sets default selabel_open(3) parameters
+ *				       to use the currently loaded policy and
+ *				       file_contexts, also requests the digest.
+ */
+extern struct selabel_handle *selinux_restorecon_default_handle(void);
+
+/**
+ * selinux_restorecon_set_exclude_list - Add a list of files or
+ *					 directories that are to be excluded
+ *					 from relabeling.
+ * @exclude_list: containing a NULL terminated list of one or more
+ *		  directories or files not to be relabeled.
+ */
+extern void selinux_restorecon_set_exclude_list(const char **exclude_list);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git libselinux-2.5-rc1/man/man3/selinux_restorecon.3 libselinux-2.5-rc1/man/man3/selinux_restorecon.3
new file mode 100644
index 0000000..152b29c
--- /dev/null
+++ libselinux-2.5-rc1/man/man3/selinux_restorecon.3
@@ -0,0 +1,195 @@
+.TH "selinux_restorecon" "3" "20 Oct 2015" "Security Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon \- restore file(s) default SELinux security contexts
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.sp
+.BI "int selinux_restorecon(const char **" pathname ,
+.in +\w'int selinux_restorecon('u
+.br
+.BI "unsigned int " restorecon_flags ");"
+.in
+.
+.SH "DESCRIPTION"
+.BR selinux_restorecon ()
+restores file default security contexts based on:
+.sp
+.RS
+.IR pathname
+containing a directory or file to be relabeled.
+.br
+If this is a directory and the
+.IR restorecon_flags
+.B SELINUX_RESTORECON_RECURSE
+has been set (for decending through directories), then
+.BR selinux_restorecon ()
+will write an SHA1 digest of the combined specfiles (see the
+.B NOTES
+section for details) to an extended attribute of
+.IR security.restorecon_last
+once the relabeling has been completed successfully. This digest will be
+checked should
+.BR selinux_restorecon ()
+be rerun
+with the
+.IR restorecon_flags
+.B SELINUX_RESTORECON_RECURSE
+flag set. If any of the specfiles had been updated, the digest
+will also be updated. However if the digest is the same, no relabeling checks
+will take place (unless the
+.B SELINUX_RESTORECON_IGNORE_DIGEST
+is set).
+.sp
+.IR restorecon_flags
+contains the labeling option/rules as follows:
+.sp
+.RS
+.sp
+.B SELINUX_RESTORECON_IGNORE_DIGEST
+force the checking of labels even if the stored SHA1 digest matches the
+specfiles SHA1 digest. The specfiles digest will be written to the
+.IR security.restorecon_last
+extended attribute once relabeling has been completed successfully provided the
+.B SELINUX_RESTORECON_NOCHANGE
+has not been set.
+.sp
+.B SELINUX_RESTORECON_NOCHANGE
+don't change any file labels (passive check) or update the digest in the
+.IR security.restorecon_last
+extended attribute.
+.sp
+.B SELINUX_RESTORECON_SET_SPECFILE_CTX
+If set, reset the files label to match the default specfile context.
+if not set only reset the files "type" component of the context to match the
+default specfile context.
+.br
+
+.sp
+.B SELINUX_RESTORECON_RECURSE
+change file and directory labels recursively (descend directories)
+and if successful write an SHA1 digest of the combined specfiles to an
+extended attribute as described in the
+.B NOTES
+section.
+.sp
+.B SELINUX_RESTORECON_VERBOSE
+log file label changes.
+.RS
+Note that if
+.B SELINUX_RESTORECON_VERBOSE
+and
+.B SELINUX_RESTORECON_PROGRESS
+are set, then
+.B SELINUX_RESTORECON_PROGRESS
+will take precedence.
+.RE
+.sp
+.B SELINUX_RESTORECON_PROGRESS
+show progress by printing * to stdout every 1000 files.
+.sp
+.B SELINUX_RESTORECON_REALPATH
+convert passed-in
+.I pathname
+to the canonical pathname using
+.BR realpath (3).
+.sp
+.B SELINUX_RESTORECON_XDEV
+prevent descending into directories that have a different device number than
+the
+.I pathname
+entry from which the descent began.
+.RE
+.sp
+The behavior regarding the checking and updating of the SHA1 digest described
+above is the default behavior. It is possible to change this by first calling
+.BR selabel_open (3)
+and not enabling the
+.B SELABEL_OPT_DIGEST
+option, then calling
+.BR selinux_restorecon_set_sehandle (3)
+to set the handle to be used by
+.BR selinux_restorecon (3).
+.sp
+If the
+.I pathname
+is a directory path, then it is possible to set files/directories to be exluded
+from the path by calling
+.BR selinux_restorecon_set_exclude_list (3)
+with a
+.B NULL
+terminated list before calling
+.BR selinux_restorecon (3).
+.RE
+.
+.SH "RETURN VALUE"
+On success, zero is returned.  On error, \-1 is returned and
+.I errno
+is set appropriately.
+.
+.SH "NOTES"
+To improve performance when relabeling file systems recursively (e.g. the
+.IR restorecon_flags
+.B SELINUX_RESTORECON_RECURSE
+flag is set)
+.BR selinux_restorecon ()
+will write an SHA1 digest of the specfiles that are processed by
+.BR selabel_open (3)
+to an extended attribute named
+.IR security.restorecon_last
+to the directory specified in the
+.IR pathname .
+.sp
+To check the extended attribute entry use
+.BR getfattr (1) ,
+for example:
+.sp
+.RS
+getfattr -e hex -n security.restorecon_last /
+.RE
+.sp
+The SHA1 digest is calculated by
+.BR selabel_open (3)
+concatenating the specfiles it reads during initialisation with the
+resulting digest and list of specfiles being retrieved by
+.BR selabel_digest (3).
+.sp
+The specfiles consist of the mandatory
+.I file_contexts
+file plus any subs, subs_dist, local and homedir entries (text or binary versions)
+as determined by any
+.BR selabel_open (3)
+options e.g.
+.BR SELABEL_OPT_BASEONLY .
+.sp
+Should any of the specfiles have changed, then when
+.BR selinux_restorecon ()
+is run again with the
+.B SELINUX_RESTORECON_RECURSE
+flag set, a new SHA1 digest will be calculated and all files will be automatically
+relabeled depending on the settings of the
+.B SELINUX_RESTORECON_SET_SPECFILE_CTX
+flag (provided
+.B SELINUX_RESTORECON_NOCHANGE
+is not set).
+.sp
+.B /sys
+and in-memory filesystems do not support the
+.IR security.restorecon_last
+extended attribute.
+.sp
+.BR selinux_restorecon ()
+does not check whether the mounted filesystems support the
+.B seclabel
+option. These should be set by the caller by
+.BR selinux_restorecon_set_exclude_list (3)
+in the
+.IR exclude_list .
+.
+.SH "SEE ALSO"
+.BR selinux_restorecon_set_sehandle (3),
+.br
+.BR selinux_restorecon_default_handle (3),
+.br
+.BR selinux_restorecon_set_exclude_list (3),
diff --git libselinux-2.5-rc1/man/man3/selinux_restorecon_default_handle.3 libselinux-2.5-rc1/man/man3/selinux_restorecon_default_handle.3
new file mode 100644
index 0000000..0f1e737
--- /dev/null
+++ libselinux-2.5-rc1/man/man3/selinux_restorecon_default_handle.3
@@ -0,0 +1,59 @@
+.TH "selinux_restorecon_default_handle" "3" "20 Oct 2015" "Security Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon_default_handle \- sets default parameters for
+.BR selinux_restorecon (3)
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.br
+.B #include <selinux/label.h>
+.sp
+.B "struct selabel_handle *selinux_restorecon_default_handle(void);"
+.
+.SH "DESCRIPTION"
+.BR selinux_restorecon_default_handle ()
+sets default parameters for
+.BR selinux_restorecon (3)
+by calling
+.BR selabel_open (3)
+with the
+.B SELABEL_OPT_DIGEST
+option only. This will enable a digest to be calculated on the currently
+loaded policy
+.BR file_contexts (5)
+set of files as described in the
+.B NOTES
+section of
+.BR selinux_restorecon (3).
+.sp
+Calling
+.BR selinux_restorecon_default_handle ()
+is optional, however if used then
+.BR selinux_restorecon_set_sehandle (3)
+should be called with the returned handle to set this for use by
+.BR selinux_restorecon (3).
+.sp
+.BR selinux_restorecon_default_handle ()
+is optional as
+.BR selinux_restorecon (3)
+will automatically call this and
+.BR selinux_restorecon_set_sehandle (3)
+provided a handle has not already been set, for
+example by
+.BR selinux_restorecon_set_sehandle (3)
+to set customised
+.BR selabel_open (3)
+parameters.
+.
+.SH "RETURN VALUE"
+A non\-NULL handle value is returned on success. On error, NULL is returned and
+.I errno
+is set appropriately.
+.
+.SH "SEE ALSO"
+.BR selinux_restorecon (3),
+.br
+.BR selinux_restorecon_set_sehandle (3),
+.br
+.BR selinux_restorecon_set_exclude_list (3)
diff --git libselinux-2.5-rc1/man/man3/selinux_restorecon_set_exclude_list.3 libselinux-2.5-rc1/man/man3/selinux_restorecon_set_exclude_list.3
new file mode 100644
index 0000000..ea1fb78
--- /dev/null
+++ libselinux-2.5-rc1/man/man3/selinux_restorecon_set_exclude_list.3
@@ -0,0 +1,32 @@
+.TH "selinux_restorecon_set_exclude_list" "3" "20 Oct 2015" "Security Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon_set_exclude_list \- set list of files/directories to be
+excluded from relabeling.
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.sp
+.BI "void selinux_restorecon_set_exclude_list(const char **" exclude_list ");"
+.in +\w'void selinux_restorecon_set_exclude_list('u
+.
+.SH "DESCRIPTION"
+.BR selinux_restorecon_set_exclude_list ()
+passes to
+.BR selinux_restorecon (3)
+a pointer containing a
+.B NULL
+terminated list of one or more directories or files that are not to be
+relabeled in
+.IR exclude_list .
+.sp
+.BR selinux_restorecon_set_exclude_list ()
+must be called prior to
+.BR selinux_restorecon (3).
+.
+.SH "SEE ALSO"
+.BR selinux_restorecon (3),
+.br
+.BR selinux_restorecon_set_sehandle (3),
+.br
+.BR selinux_restorecon_default_handle (3)
diff --git libselinux-2.5-rc1/man/man3/selinux_restorecon_set_sehandle.3 libselinux-2.5-rc1/man/man3/selinux_restorecon_set_sehandle.3
new file mode 100644
index 0000000..6182f54
--- /dev/null
+++ libselinux-2.5-rc1/man/man3/selinux_restorecon_set_sehandle.3
@@ -0,0 +1,39 @@
+.TH "selinux_restorecon_set_sehandle" "3" "20 Oct 2015" "Security Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon_set_sehandle \- set a labeling handle for use by
+.BR selinux_restorecon (3)
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.br
+.B #include <selinux/label.h>
+.sp
+.BI "void selinux_restorecon_set_sehandle(struct selabel_handle *" handle ");"
+.in +\w'void selinux_restorecon_set_sehandle('u
+.
+.SH "DESCRIPTION"
+.BR selinux_restorecon_set_sehandle ()
+sets the
+.I handle
+to be use by
+.BR selinux_restorecon (3)
+when relabeling files.
+.sp
+.BR selinux_restorecon_set_sehandle ()
+is generally used when customised
+.BR selabel_open (3)
+parameters are required to perform relabeling operations with
+.BR selinux_restorecon (3).
+.sp
+.BR selinux_restorecon_set_sehandle ()
+will output to the default SELinux log information regarding whether a digest
+is available or not. If it were available, the message will contain the SHA1
+digest and a list of specfiles used to compute the digest.
+.
+.SH "SEE ALSO"
+.BR selinux_restorecon (3),
+.br
+.BR selinux_restorecon_set_exclude_list (3),
+.br
+.BR selinux_restorecon_default_handle (3)
diff --git libselinux-2.5-rc1/man/man8/selinux.8 libselinux-2.5-rc1/man/man8/selinux.8
index 6f1034b..c9f188c 100644
--- libselinux-2.5-rc1/man/man8/selinux.8
+++ libselinux-2.5-rc1/man/man8/selinux.8
@@ -91,11 +91,13 @@ This manual page was written by Dan Walsh <dwalsh@redhat.com>.
 .BR sepolicy (8),
 .BR system-config-selinux (8),
 .BR togglesebool (8),
-.BR restorecon (8),
 .BR fixfiles (8),
+.BR restorecon (8),
 .BR setfiles (8),
 .BR semanage (8),
-.BR sepolicy(8)
+.BR sepolicy(8),
+.BR seinfo(8),
+.BR sesearch(8)
 
 Every confined service on the system has a man page in the following format:
 .br
diff --git libselinux-2.5-rc1/src/Makefile libselinux-2.5-rc1/src/Makefile
index 79d50d2..d0021ae 100644
--- libselinux-2.5-rc1/src/Makefile
+++ libselinux-2.5-rc1/src/Makefile
@@ -73,7 +73,7 @@ CFLAGS ?= -O -Wall -W -Wundef -Wformat-y2k -Wformat-security -Winit-self -Wmissi
           -fipa-pure-const -Wno-suggest-attribute=pure -Wno-suggest-attribute=const \
           -Werror -Wno-aggregate-return -Wno-redundant-decls
 
-override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 $(EMFLAGS)
+override CFLAGS += -I../include -I$(INCLUDEDIR) -D_GNU_SOURCE $(EMFLAGS)
 
 SWIG_CFLAGS += -Wno-error -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-parameter \
 		-Wno-shadow -Wno-uninitialized -Wno-missing-prototypes -Wno-missing-declarations
diff --git libselinux-2.5-rc1/src/avc_sidtab.c libselinux-2.5-rc1/src/avc_sidtab.c
index 9669264..c775430 100644
--- libselinux-2.5-rc1/src/avc_sidtab.c
+++ libselinux-2.5-rc1/src/avc_sidtab.c
@@ -81,6 +81,11 @@ sidtab_context_to_sid(struct sidtab *s,
 	int hvalue, rc = 0;
 	struct sidtab_node *cur;
 
+	if (! ctx) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	*sid = NULL;
 	hvalue = sidtab_hash(ctx);
 
diff --git libselinux-2.5-rc1/src/canonicalize_context.c libselinux-2.5-rc1/src/canonicalize_context.c
index 7cf3139..364a746 100644
--- libselinux-2.5-rc1/src/canonicalize_context.c
+++ libselinux-2.5-rc1/src/canonicalize_context.c
@@ -17,6 +17,11 @@ int security_canonicalize_context_raw(const char * con,
 	size_t size;
 	int fd, ret;
 
+	if (! con) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	if (!selinux_mnt) {
 		errno = ENOENT;
 		return -1;
diff --git libselinux-2.5-rc1/src/check_context.c libselinux-2.5-rc1/src/check_context.c
index 52063fa..234749c 100644
--- libselinux-2.5-rc1/src/check_context.c
+++ libselinux-2.5-rc1/src/check_context.c
@@ -14,6 +14,11 @@ int security_check_context_raw(const char * con)
 	char path[PATH_MAX];
 	int fd, ret;
 
+	if (! con) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	if (!selinux_mnt) {
 		errno = ENOENT;
 		return -1;
diff --git libselinux-2.5-rc1/src/compute_av.c libselinux-2.5-rc1/src/compute_av.c
index 937e5c3..35ace7f 100644
--- libselinux-2.5-rc1/src/compute_av.c
+++ libselinux-2.5-rc1/src/compute_av.c
@@ -26,6 +26,11 @@ int security_compute_av_flags_raw(const char * scon,
 		return -1;
 	}
 
+	if ((! scon) || (! tcon)) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	snprintf(path, sizeof path, "%s/access", selinux_mnt);
 	fd = open(path, O_RDWR);
 	if (fd < 0)
diff --git libselinux-2.5-rc1/src/compute_create.c libselinux-2.5-rc1/src/compute_create.c
index 9559d42..14a65d1 100644
--- libselinux-2.5-rc1/src/compute_create.c
+++ libselinux-2.5-rc1/src/compute_create.c
@@ -64,6 +64,11 @@ int security_compute_create_name_raw(const char * scon,
 		return -1;
 	}
 
+	if ((! scon) || (! tcon)) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	snprintf(path, sizeof path, "%s/create", selinux_mnt);
 	fd = open(path, O_RDWR);
 	if (fd < 0)
diff --git libselinux-2.5-rc1/src/compute_member.c libselinux-2.5-rc1/src/compute_member.c
index 1fc7e41..065d996 100644
--- libselinux-2.5-rc1/src/compute_member.c
+++ libselinux-2.5-rc1/src/compute_member.c
@@ -25,6 +25,11 @@ int security_compute_member_raw(const char * scon,
 		return -1;
 	}
 
+	if ((! scon) || (! tcon)) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	snprintf(path, sizeof path, "%s/member", selinux_mnt);
 	fd = open(path, O_RDWR);
 	if (fd < 0)
diff --git libselinux-2.5-rc1/src/compute_relabel.c libselinux-2.5-rc1/src/compute_relabel.c
index 4615aee..cc77f36 100644
--- libselinux-2.5-rc1/src/compute_relabel.c
+++ libselinux-2.5-rc1/src/compute_relabel.c
@@ -25,6 +25,11 @@ int security_compute_relabel_raw(const char * scon,
 		return -1;
 	}
 
+	if ((! scon) || (! tcon)) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	snprintf(path, sizeof path, "%s/relabel", selinux_mnt);
 	fd = open(path, O_RDWR);
 	if (fd < 0)
diff --git libselinux-2.5-rc1/src/compute_user.c libselinux-2.5-rc1/src/compute_user.c
index b37c5d3..7703c26 100644
--- libselinux-2.5-rc1/src/compute_user.c
+++ libselinux-2.5-rc1/src/compute_user.c
@@ -24,6 +24,11 @@ int security_compute_user_raw(const char * scon,
 		return -1;
 	}
 
+	if (! scon) {
+		errno=EINVAL;
+		return -1;
+	}
+
 	snprintf(path, sizeof path, "%s/user", selinux_mnt);
 	fd = open(path, O_RDWR);
 	if (fd < 0)
diff --git libselinux-2.5-rc1/src/fsetfilecon.c libselinux-2.5-rc1/src/fsetfilecon.c
index 52707d0..0cbe12d 100644
--- libselinux-2.5-rc1/src/fsetfilecon.c
+++ libselinux-2.5-rc1/src/fsetfilecon.c
@@ -9,8 +9,12 @@
 
 int fsetfilecon_raw(int fd, const char * context)
 {
-	int rc = fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1,
-			 0);
+	int rc;
+	if (! context) {
+		errno=EINVAL;
+		return -1;
+	}
+	rc = fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0);
 	if (rc < 0 && errno == ENOTSUP) {
 		char * ccontext = NULL;
 		int err = errno;
diff --git libselinux-2.5-rc1/src/label_android_property.c libselinux-2.5-rc1/src/label_android_property.c
index fea1f8f..290b438 100644
--- libselinux-2.5-rc1/src/label_android_property.c
+++ libselinux-2.5-rc1/src/label_android_property.c
@@ -89,10 +89,21 @@ static int process_line(struct selabel_handle *rec,
 	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, 2, &prop, &context);
-	if (items <= 0)
+	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,
diff --git libselinux-2.5-rc1/src/label_file.h libselinux-2.5-rc1/src/label_file.h
index beb1fc2..72fed1f 100644
--- libselinux-2.5-rc1/src/label_file.h
+++ libselinux-2.5-rc1/src/label_file.h
@@ -1,6 +1,9 @@
 #ifndef _SELABEL_FILE_H_
 #define _SELABEL_FILE_H_
 
+#include <errno.h>
+#include <string.h>
+
 #include <sys/stat.h>
 
 #include "callbacks.h"
@@ -390,8 +393,17 @@ static inline int process_line(struct selabel_handle *rec,
 	unsigned int nspec = data->nspec;
 	const char *errbuf = NULL;
 
-	items = read_spec_entries(line_buf, 3, &regex, &type, &context);
-	if (items <= 0)
+	items = read_spec_entries(line_buf, &errbuf, 3, &regex, &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) {
diff --git libselinux-2.5-rc1/src/label_internal.h libselinux-2.5-rc1/src/label_internal.h
index cefa80b..aa48fff 100644
--- libselinux-2.5-rc1/src/label_internal.h
+++ libselinux-2.5-rc1/src/label_internal.h
@@ -140,6 +140,6 @@ compat_validate(struct selabel_handle *rec,
  * 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, int num_args, ...);
+extern int read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...);
 
 #endif				/* _SELABEL_INTERNAL_H_ */
diff --git libselinux-2.5-rc1/src/label_support.c libselinux-2.5-rc1/src/label_support.c
index 324dc51..26f9ef1 100644
--- libselinux-2.5-rc1/src/label_support.c
+++ libselinux-2.5-rc1/src/label_support.c
@@ -10,14 +10,19 @@
 #include <string.h>
 #include <stdio.h>
 #include <errno.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)
+static inline int read_spec_entry(char **entry, char **ptr, int *len, const char **errbuf)
 {
 	*entry = NULL;
 	char *tmp_buf = NULL;
@@ -29,6 +34,11 @@ static inline int read_spec_entry(char **entry, char **ptr, int *len)
 	*len = 0;
 
 	while (!isspace(**ptr) && **ptr != '\0') {
+		if (!isascii(**ptr)) {
+			errno = EINVAL;
+			*errbuf = "Non-ASCII characters found";
+			return -1;
+		}
 		(*ptr)++;
 		(*len)++;
 	}
@@ -44,18 +54,23 @@ static inline int read_spec_entry(char **entry, char **ptr, int *len)
 
 /*
  * 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.
+ * 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, int num_args, ...)
+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';
@@ -85,7 +100,7 @@ int hidden read_spec_entries(char *line_buf, int num_args, ...)
 			return items;
 		}
 
-		rc = read_spec_entry(spec_entry, &buf_p, &entry_len);
+		rc = read_spec_entry(spec_entry, &buf_p, &entry_len, errbuf);
 		if (rc < 0) {
 			va_end(ap);
 			return rc;
diff --git libselinux-2.5-rc1/src/lsetfilecon.c libselinux-2.5-rc1/src/lsetfilecon.c
index 1d3b28a..ea6d70b 100644
--- libselinux-2.5-rc1/src/lsetfilecon.c
+++ libselinux-2.5-rc1/src/lsetfilecon.c
@@ -9,8 +9,13 @@
 
 int lsetfilecon_raw(const char *path, const char * context)
 {
-	int rc = lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,
-			 0);
+	int rc;
+	if (! context) {
+		errno=EINVAL;
+		return -1;
+	}
+
+	rc = lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0);
 	if (rc < 0 && errno == ENOTSUP) {
 		char * ccontext = NULL;
 		int err = errno;
diff --git libselinux-2.5-rc1/src/matchpathcon.c libselinux-2.5-rc1/src/matchpathcon.c
index 5b495a0..3868711 100644
--- libselinux-2.5-rc1/src/matchpathcon.c
+++ libselinux-2.5-rc1/src/matchpathcon.c
@@ -2,6 +2,7 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
+#include <syslog.h>
 #include "selinux_internal.h"
 #include "label_internal.h"
 #include "callbacks.h"
@@ -62,7 +63,7 @@ static void
 {
 	va_list ap;
 	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
+	vsyslog(LOG_ERR, fmt, ap);
 	va_end(ap);
 }
 
diff --git libselinux-2.5-rc1/src/selinux_internal.h libselinux-2.5-rc1/src/selinux_internal.h
index 46566f6..3d3fecf 100644
--- libselinux-2.5-rc1/src/selinux_internal.h
+++ libselinux-2.5-rc1/src/selinux_internal.h
@@ -82,6 +82,7 @@ hidden_proto(selinux_mkload_policy)
     hidden_proto(selinux_customizable_types_path)
     hidden_proto(selinux_media_context_path)
     hidden_proto(selinux_x_context_path)
+    hidden_proto(selinux_openssh_contexts_path)
     hidden_proto(selinux_sepgsql_context_path)
     hidden_proto(selinux_openssh_contexts_path)
     hidden_proto(selinux_systemd_contexts_path)
diff --git libselinux-2.5-rc1/src/selinux_restorecon.c libselinux-2.5-rc1/src/selinux_restorecon.c
new file mode 100644
index 0000000..17ed6fe
--- /dev/null
+++ libselinux-2.5-rc1/src/selinux_restorecon.c
@@ -0,0 +1,486 @@
+/*
+ * The majority of this code is from Android's
+ * external/libselinux/src/android.c and upstream
+ * selinux/policycoreutils/setfiles/restorecon.c
+ *
+ * See selinux_restorecon(3) for details.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <libgen.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/label.h>
+#include <selinux/restorecon.h>
+
+#include "callbacks.h"
+#include "selinux_internal.h"
+
+#define RESTORECON_LAST "security.restorecon_last"
+
+#define SYS_PATH "/sys"
+#define SYS_PREFIX SYS_PATH "/"
+
+static struct selabel_handle *fc_sehandle = NULL;
+static unsigned char *fc_digest = NULL;
+static size_t fc_digest_len = 0;
+static const char **fc_exclude_list = NULL;
+static size_t fc_count = 0;
+#define STAR_COUNT 1000
+
+static void restorecon_init(void)
+{
+	struct selabel_handle *sehandle = NULL;
+
+	if (!fc_sehandle) {
+		sehandle = selinux_restorecon_default_handle();
+		selinux_restorecon_set_sehandle(sehandle);
+	}
+}
+
+static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
+
+
+static int check_excluded(const char *file)
+{
+	int i;
+
+	for (i = 0; fc_exclude_list[i]; i++) {
+		if (strcmp(file, fc_exclude_list[i]) == 0)
+				return 1;
+	}
+	return 0;
+}
+
+/* Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if
+ * the type components differ, updating newtypecon if so. */
+static int compare_types(char *curcon, char *newcon, char **newtypecon)
+{
+	int types_differ = 0;
+	context_t cona;
+	context_t conb;
+	int rc = 0;
+
+	cona = context_new(curcon);
+	if (!cona) {
+		rc = -1;
+		goto out;
+	}
+	conb = context_new(newcon);
+	if (!conb) {
+		context_free(cona);
+		rc = -1;
+		goto out;
+	}
+
+	types_differ = strcmp(context_type_get(cona), context_type_get(conb));
+	if (types_differ) {
+		rc |= context_user_set(conb, context_user_get(cona));
+		rc |= context_role_set(conb, context_role_get(cona));
+		rc |= context_range_set(conb, context_range_get(cona));
+		if (!rc) {
+			*newtypecon = strdup(context_str(conb));
+			if (!*newtypecon) {
+				rc = -1;
+				goto err;
+			}
+		}
+	}
+
+err:
+	context_free(cona);
+	context_free(conb);
+out:
+	return rc;
+}
+
+static int restorecon_sb(const char *pathname, const struct stat *sb,
+					    bool nochange, bool verbose,
+					    bool progress, bool specctx)
+{
+	char *newcon = NULL;
+	char *curcon = NULL;
+	char *newtypecon = NULL;
+	int rc = 0;
+	bool updated = false;
+
+	if (selabel_lookup_raw(fc_sehandle, &newcon, pathname, sb->st_mode) < 0)
+		return 0; /* no match, but not an error */
+
+	if (lgetfilecon_raw(pathname, &curcon) < 0) {
+		if (errno != ENODATA)
+			goto err;
+
+		curcon = NULL;
+	}
+
+	if (progress) {
+		fc_count++;
+		if (fc_count % STAR_COUNT == 0) {
+			fprintf(stdout, "*");
+			fflush(stdout);
+		}
+	}
+
+	if (strcmp(curcon, newcon) != 0) {
+		if (!specctx && curcon &&
+				    (is_context_customizable(curcon) > 0)) {
+			if (verbose) {
+				selinux_log(SELINUX_INFO,
+				 "%s not reset as customized by admin to %s\n",
+							    pathname, curcon);
+				goto out;
+			}
+		}
+
+		if (!specctx && curcon) {
+			/* If types different then update newcon. */
+			rc = compare_types(curcon, newcon, &newtypecon);
+			if (rc)
+				goto err;
+
+			if (newtypecon) {
+				freecon(newcon);
+				newcon = newtypecon;
+			} else {
+				goto out;
+			}
+		}
+
+		if (!nochange) {
+			if (lsetfilecon(pathname, newcon) < 0)
+				goto err;
+			updated = true;
+		}
+
+		if (verbose)
+			selinux_log(SELINUX_INFO,
+				    "%s %s from %s to %s\n",
+				    updated ? "Relabeled" : "Would relabel",
+				    pathname, curcon, newcon);
+	}
+
+out:
+	rc = 0;
+out1:
+	freecon(curcon);
+	freecon(newcon);
+	return rc;
+err:
+	selinux_log(SELINUX_ERROR,
+		    "Could not set context for %s:  %s\n",
+		    pathname, strerror(errno));
+	rc = -1;
+	goto out1;
+}
+
+/*
+ * Public API
+ */
+
+/* selinux_restorecon(3) - Main function that is responsible for labeling */
+int selinux_restorecon(const char *pathname_orig,
+				    unsigned int restorecon_flags)
+{
+	bool ignore = (restorecon_flags &
+		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
+	bool nochange = (restorecon_flags &
+		    SELINUX_RESTORECON_NOCHANGE) ? true : false;
+	bool verbose = (restorecon_flags &
+		    SELINUX_RESTORECON_VERBOSE) ? true : false;
+	bool progress = (restorecon_flags &
+		    SELINUX_RESTORECON_PROGRESS) ? true : false;
+	bool recurse = (restorecon_flags &
+		    SELINUX_RESTORECON_RECURSE) ? true : false;
+	bool specctx = (restorecon_flags &
+		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
+	bool userealpath = (restorecon_flags &
+		   SELINUX_RESTORECON_REALPATH) ? true : false;
+	bool xdev = (restorecon_flags &
+		   SELINUX_RESTORECON_XDEV) ? true : false;
+	bool issys;
+	bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST
+					* FALSE = don't use xattr */
+	struct stat sb;
+	struct statfs sfsb;
+	FTS *fts;
+	FTSENT *ftsent;
+	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
+	char *paths[2] = { NULL , NULL };
+	int fts_flags;
+	int error, sverrno;
+	char *xattr_value = NULL;
+	ssize_t size;
+
+	if (verbose && progress)
+		verbose = false;
+
+	__selinux_once(fc_once, restorecon_init);
+
+	if (!fc_sehandle)
+		return -1;
+
+	if (fc_digest_len) {
+		xattr_value = malloc(fc_digest_len);
+		if (!xattr_value)
+			return -1;
+	}
+
+	/*
+	 * Convert passed-in pathname to canonical pathname by resolving
+	 * realpath of containing dir, then appending last component name.
+	 */
+	if (userealpath) {
+		pathbname = basename((char *)pathname_orig);
+		if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") ||
+					    !strcmp(pathbname, "..")) {
+			pathname = realpath(pathname_orig, NULL);
+			if (!pathname)
+				goto realpatherr;
+		} else {
+			pathdname = dirname((char *)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;
+		}
+	} else {
+		pathname = strdup(pathname_orig);
+		if (!pathname)
+			goto oom;
+	}
+
+	paths[0] = pathname;
+	issys = (!strcmp(pathname, SYS_PATH) ||
+			    !strncmp(pathname, SYS_PREFIX,
+			    sizeof(SYS_PREFIX) - 1)) ? true : false;
+
+	if (lstat(pathname, &sb) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	/* Ignore restoreconlast if not a directory */
+	if ((sb.st_mode & S_IFDIR) != S_IFDIR)
+		setrestoreconlast = false;
+
+	if (!recurse) {
+		error = restorecon_sb(pathname, &sb, nochange, verbose,
+						    progress, specctx);
+		goto cleanup;
+	}
+
+	/* Ignore restoreconlast on /sys */
+	if (issys)
+		setrestoreconlast = false;
+
+	/* Ignore restoreconlast 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,
+							    fc_digest_len);
+
+		if (!ignore && size == fc_digest_len &&
+			    memcmp(fc_digest, xattr_value, fc_digest_len)
+								    == 0) {
+			selinux_log(SELINUX_INFO,
+			    "Skipping restorecon as matching digest on: %s\n",
+				    pathname);
+			error = 0;
+			goto cleanup;
+		}
+	}
+
+	if (xdev)
+		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
+	else
+		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
+
+	fts = fts_open(paths, fts_flags, 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,
+				    "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,
+				    "Could not read %s: %s.\n",
+				    ftsent->fts_path,
+						  strerror(ftsent->fts_errno));
+			fts_set(fts, ftsent, FTS_SKIP);
+			continue;
+		case FTS_NS:
+			selinux_log(SELINUX_ERROR,
+				    "Could not stat %s: %s.\n",
+				    ftsent->fts_path,
+						  strerror(ftsent->fts_errno));
+			fts_set(fts, ftsent, FTS_SKIP);
+			continue;
+		case FTS_ERR:
+			selinux_log(SELINUX_ERROR,
+				    "Error on %s: %s.\n",
+				    ftsent->fts_path,
+						  strerror(ftsent->fts_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;
+			}
+			/* fall through */
+		default:
+			if (fc_exclude_list) {
+				if (check_excluded(ftsent->fts_path)) {
+					fts_set(fts, ftsent, FTS_SKIP);
+					continue;
+				}
+			}
+
+			error |= restorecon_sb(ftsent->fts_path,
+				    ftsent->fts_statp, nochange,
+				    verbose, progress, specctx);
+			break;
+		}
+	}
+
+	/* Labeling successful. Mark the top level directory as completed. */
+	if (setrestoreconlast && !nochange && !error) {
+		error = setxattr(pathname, RESTORECON_LAST, fc_digest,
+						    fc_digest_len, 0);
+		if (!error && verbose)
+			selinux_log(SELINUX_INFO,
+				   "Updated digest for: %s\n", pathname);
+	}
+
+out:
+	sverrno = errno;
+	(void) fts_close(fts);
+	errno = sverrno;
+cleanup:
+	free(pathdnamer);
+	free(pathname);
+	free(xattr_value);
+	return error;
+oom:
+	sverrno = errno;
+	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
+	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;
+}
+
+/* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
+void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
+{
+	char **specfiles, *sha1_buf = NULL;
+	size_t num_specfiles, i;
+
+	fc_sehandle = (struct selabel_handle *) hndl;
+
+	/* Read digest if requested in selabel_open(3).
+	 * If not the set global params. */
+	if (selabel_digest(hndl, &fc_digest, &fc_digest_len,
+				   &specfiles, &num_specfiles) < 0) {
+		fc_digest = NULL;
+		fc_digest_len = 0;
+		selinux_log(SELINUX_INFO, "Digest not requested.\n");
+		return;
+	}
+
+	sha1_buf = malloc(fc_digest_len * 2 + 1);
+	if (!sha1_buf) {
+		selinux_log(SELINUX_ERROR,
+			    "Error allocating digest buffer: %s\n",
+						    strerror(errno));
+		return;
+	}
+
+	for (i = 0; i < fc_digest_len; i++)
+		sprintf((&sha1_buf[i * 2]), "%02x", fc_digest[i]);
+
+	selinux_log(SELINUX_INFO,
+		    "specfiles SHA1 digest: %s\n", sha1_buf);
+	selinux_log(SELINUX_INFO,
+		    "calculated using the following specfile(s):\n");
+	if (specfiles) {
+		for (i = 0; i < num_specfiles; i++)
+			selinux_log(SELINUX_INFO,
+				    "%s\n", specfiles[i]);
+	}
+	free(sha1_buf);
+}
+
+/* selinux_restorecon_default_handle(3) is called to set the global restorecon
+ * handle by a process if the default params are required. */
+struct selabel_handle *selinux_restorecon_default_handle(void)
+{
+	struct selabel_handle *sehandle;
+
+	struct selinux_opt fc_opts[] = {
+		{ SELABEL_OPT_DIGEST, (char *)1 }
+	};
+
+	sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1);
+
+	if (!sehandle) {
+		selinux_log(SELINUX_ERROR,
+			    "Error obtaining file context handle: %s\n",
+						    strerror(errno));
+		return NULL;
+	}
+
+	return sehandle;
+}
+
+/* selinux_restorecon_set_exclude_list(3) is called to set a NULL terminated
+ * list of files/directories to exclude. */
+void selinux_restorecon_set_exclude_list(const char **exclude_list)
+{
+	fc_exclude_list = exclude_list;
+}
diff --git libselinux-2.5-rc1/src/setfilecon.c libselinux-2.5-rc1/src/setfilecon.c
index d05969c..3f0200e 100644
--- libselinux-2.5-rc1/src/setfilecon.c
+++ libselinux-2.5-rc1/src/setfilecon.c
@@ -9,8 +9,12 @@
 
 int setfilecon_raw(const char *path, const char * context)
 {
-	int rc = setxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,
-			0);
+	int rc;
+	if (! context) {
+		errno=EINVAL;
+		return -1;
+	}
+	rc = setxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0);
 	if (rc < 0 && errno == ENOTSUP) {
 		char * ccontext = NULL;
 		int err = errno;
diff --git libselinux-2.5-rc1/utils/Makefile libselinux-2.5-rc1/utils/Makefile
index cac85c7..cf7af52 100644
--- libselinux-2.5-rc1/utils/Makefile
+++ libselinux-2.5-rc1/utils/Makefile
@@ -30,6 +30,8 @@ TARGETS=$(patsubst %.c,%,$(wildcard *.c))
 
 sefcontext_compile: LDLIBS += -lpcre ../src/libselinux.a -lsepol
 
+selinux_restorecon: LDLIBS += -lsepol
+
 ifeq ($(DISABLE_AVC),y)
 	UNUSED_TARGETS+=compute_av compute_create compute_member compute_relabel
 endif
diff --git libselinux-2.5-rc1/utils/selinux_restorecon.c libselinux-2.5-rc1/utils/selinux_restorecon.c
new file mode 100644
index 0000000..52352c5
--- /dev/null
+++ libselinux-2.5-rc1/utils/selinux_restorecon.c
@@ -0,0 +1,263 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sepol/sepol.h>
+#include <selinux/label.h>
+#include <selinux/restorecon.h>
+
+static char *policyfile;
+
+static char **exclude_list;
+static int exclude_count;
+
+static int validate_context(char **contextp)
+{
+	char *context = *contextp, *tmpcon;
+
+	if (policyfile) {
+		if (sepol_check_context(context) < 0) {
+			fprintf(stderr, "Invalid context %s\n", context);
+			exit(-1);
+		}
+	} else if (security_canonicalize_context_raw(context, &tmpcon) == 0) {
+		free(context);
+		*contextp = tmpcon;
+	} else if (errno != ENOENT) {
+		fprintf(stderr, "Validate context error: %s\n",
+						    strerror(errno));
+		exit(-1);
+	}
+
+	return 0;
+}
+
+static void usage(const char *progname)
+{
+	fprintf(stderr,
+		"\nusage: %s [-FCnRrdei] [-v|-P] [-p policy] [-f specfile] "
+		"pathname ...\n"
+		"Where:\n\t"
+		"-F  Set the label to that in specfile.\n\t"
+		"    If not set then reset the \"type\" component of the "
+		"label to that\n\t    in the specfile.\n\t"
+		"-C  Check labels even if the stored SHA1 digest matches\n\t"
+		"    the specfiles SHA1 digest.\n\t"
+		"-n  Don't change any file labels (passive check).\n\t"
+		"-R  Recursively change file and directory labels.\n\t"
+		"-v  Show changes in file labels (-v and -P are mutually "
+		" exclusive).\n\t"
+		"-P  Show progress by printing \"*\" to stdout every 1000 files.\n\t"
+		"-r  Use realpath(3) to convert pathnames to canonical form.\n\t"
+		"-d  Prevent descending into directories that have a "
+		"different\n\t    device number than the pathname from  which "
+		"the descent began.\n\t"
+		"-e  Exclude this file/directory (add multiple -e entries).\n\t"
+		"-i  Do not set SELABEL_OPT_VALIDATE option in selabel_open(3)"
+		" then call\n\t    selinux_restorecon_set_sehandle(3).\n\t"
+		"-p  Optional binary policy file (also sets validate context "
+		"option).\n\t"
+		"-f  Optional file contexts file.\n\t"
+		"pathname  One or more paths to relabel.\n\n",
+		progname);
+	exit(-1);
+}
+
+static void add_exclude(const char *directory)
+{
+	char **tmp_list;
+
+	if (directory == NULL || directory[0] != '/') {
+		fprintf(stderr, "Full path required for exclude: %s.\n",
+			directory);
+		exit(-1);
+	}
+
+	/* Add another two entries, one for directory, and the other to
+	 * terminate the list */
+	tmp_list = realloc(exclude_list, sizeof(char *) * (exclude_count + 2));
+	if (!tmp_list) {
+		fprintf(stderr, "ERROR: realloc failed.\n");
+		exit(-1);
+	}
+	exclude_list = tmp_list;
+
+	exclude_list[exclude_count] = strdup(directory);
+	if (!exclude_list[exclude_count]) {
+		fprintf(stderr, "ERROR: strdup failed.\n");
+		exit(-1);
+	}
+	exclude_count++;
+	exclude_list[exclude_count] = NULL;
+}
+
+int main(int argc, char **argv)
+{
+	int opt, i;
+	unsigned int restorecon_flags = 0;
+	char *path = NULL, *digest = NULL, *validate = NULL;
+	FILE *policystream;
+	bool ignore_digest = false, require_selinux = true;
+	bool verbose = false, progress = false;
+
+	struct selabel_handle *hnd = NULL;
+	struct selinux_opt selabel_option[] = {
+		{ SELABEL_OPT_PATH, path },
+		{ SELABEL_OPT_DIGEST, digest },
+		{ SELABEL_OPT_VALIDATE, validate }
+	};
+
+	if (argc < 2)
+		usage(argv[0]);
+
+	exclude_list = NULL;
+	exclude_count = 0;
+
+	while ((opt = getopt(argc, argv, "iFCnRvPrde:f:p:")) > 0) {
+		switch (opt) {
+		case 'F':
+			restorecon_flags |=
+					SELINUX_RESTORECON_SET_SPECFILE_CTX;
+			break;
+		case 'C':
+			restorecon_flags |=
+					SELINUX_RESTORECON_IGNORE_DIGEST;
+			break;
+		case 'n':
+			restorecon_flags |= SELINUX_RESTORECON_NOCHANGE;
+			break;
+		case 'R':
+			restorecon_flags |= SELINUX_RESTORECON_RECURSE;
+			break;
+		case 'v':
+			if (progress) {
+				fprintf(stderr,
+					"Progress and Verbose are mutually exclusive\n");
+				exit(-1);
+			}
+			verbose = true;
+			restorecon_flags |=  SELINUX_RESTORECON_VERBOSE;
+			break;
+		case 'P':
+			if (verbose) {
+				fprintf(stderr,
+					"Progress and Verbose are mutually exclusive\n");
+				exit(-1);
+			}
+			progress = true;
+			restorecon_flags |=  SELINUX_RESTORECON_PROGRESS;
+			break;
+		case 'r':
+			restorecon_flags |= SELINUX_RESTORECON_REALPATH;
+			break;
+		case 'd':
+			restorecon_flags |= SELINUX_RESTORECON_XDEV;
+			break;
+		case 'e':
+			add_exclude(optarg);
+			break;
+		case 'p':
+			policyfile = optarg;
+
+			policystream = fopen(policyfile, "r");
+			if (!policystream) {
+				fprintf(stderr,
+					"ERROR: opening %s: %s\n",
+					policyfile, strerror(errno));
+				exit(-1);
+			}
+
+			if (sepol_set_policydb_from_file(policystream) < 0) {
+				fprintf(stderr,
+					"ERROR: reading policy %s: %s\n",
+					policyfile, strerror(errno));
+				exit(-1);
+			}
+			fclose(policystream);
+
+			selinux_set_callback(SELINUX_CB_VALIDATE,
+				    (union selinux_callback)&validate_context);
+			require_selinux = false;
+			break;
+		case 'f':
+			path = optarg;
+			break;
+		case 'i':
+			ignore_digest = true;
+			break;
+		default:
+			usage(argv[0]);
+		}
+	}
+
+	if (require_selinux && (is_selinux_enabled() <= 0)) {
+		fprintf(stderr,
+		    "SELinux must be enabled to perform this operation.\n");
+		exit(-1);
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "No pathname specified\n");
+		exit(-1);
+	}
+
+	/* If any of these set then do our own selabel_open and pass
+	 * handle to selinux_restorecon */
+	if (ignore_digest || path || policyfile) {
+		if (path)
+			selabel_option[0].value = path;
+		else
+			selabel_option[0].value = NULL;
+
+		if (ignore_digest)
+			selabel_option[1].value = NULL;
+		else
+			selabel_option[1].value = (char *)1;
+
+		if (policyfile) /* Validate */
+			selabel_option[2].value = (char *)1;
+		else
+			selabel_option[2].value = NULL;
+
+		hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 3);
+		if (!hnd) {
+			switch (errno) {
+			case EOVERFLOW:
+				fprintf(stderr, "ERROR: Number of specfiles or"
+				    " specfile buffer caused an overflow.\n");
+				break;
+			default:
+				fprintf(stderr, "ERROR: selabel_open: %s\n",
+							    strerror(errno));
+			}
+			exit(-1);
+		}
+		selinux_restorecon_set_sehandle(hnd);
+	}
+
+	if (exclude_list)
+		selinux_restorecon_set_exclude_list
+						 ((const char **)exclude_list);
+
+	/* Call restorecon for each path in list */
+	for (i = optind; i < argc; i++) {
+		if (selinux_restorecon(argv[i], restorecon_flags) < 0) {
+			fprintf(stderr, "ERROR: selinux_restorecon: %s\n",
+					    strerror(errno));
+			exit(-1);
+		}
+	}
+
+	if (exclude_list) {
+		for (i = 0; exclude_list[i]; i++)
+			free(exclude_list[i]);
+		free(exclude_list);
+	}
+
+	if (hnd)
+		selabel_close(hnd);
+
+	return 0;
+}