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 + + Used some ideas/code from the go-ini packages https://github.com/vaughan0 + By Vaughan Newton +*/ + +// #cgo pkg-config: libselinux +// #include +// #include +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 +#include + +#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 +.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 +.br +.B #include +.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 +.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 +.br +.B #include +.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 . .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 +#include + #include #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, ®ex, &type, &context); - if (items <= 0) + 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) { 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 #include #include +#include #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 #include #include +#include #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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}