diff mbox

[2/3] libselinux: Add setfiles support to selinux_restorecon(3)

Message ID 1462893798-9559-1-git-send-email-richard_c_haines@btinternet.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Richard Haines May 10, 2016, 3:23 p.m. UTC
Add additional error handling, flags, xdev and alt_rootpath
support for setfiles(8) functionality.

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 libselinux/include/selinux/restorecon.h            |  17 +++
 libselinux/man/man3/selinux_restorecon.3           |  13 +++
 .../man/man3/selinux_restorecon_set_alt_rootpath.3 |  34 ++++++
 libselinux/src/selinux_restorecon.c                | 126 +++++++++++++++++----
 libselinux/utils/selinux_restorecon.c              |  28 ++++-
 5 files changed, 197 insertions(+), 21 deletions(-)
 create mode 100644 libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3

Comments

Stephen Smalley May 20, 2016, 5:03 p.m. UTC | #1
On 05/10/2016 11:23 AM, Richard Haines wrote:
> Add additional error handling, flags, xdev and alt_rootpath
> support for setfiles(8) functionality.
> 
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> ---
>  libselinux/include/selinux/restorecon.h            |  17 +++
>  libselinux/man/man3/selinux_restorecon.3           |  13 +++
>  .../man/man3/selinux_restorecon_set_alt_rootpath.3 |  34 ++++++
>  libselinux/src/selinux_restorecon.c                | 126 +++++++++++++++++----
>  libselinux/utils/selinux_restorecon.c              |  28 ++++-
>  5 files changed, 197 insertions(+), 21 deletions(-)
>  create mode 100644 libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
> 
> diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
> index 0b93b0c..6b3e0f1 100644
> --- a/libselinux/include/selinux/restorecon.h
> +++ b/libselinux/include/selinux/restorecon.h
> @@ -50,6 +50,14 @@ extern int selinux_restorecon(const char *pathname,
>   * If there is a different context that matched the inode,
>   * then use the first context that matched. */
>  #define SELINUX_RESTORECON_ADD_ASSOC			256
> +/* Abort on errors during the file tree walk. */
> +#define SELINUX_RESTORECON_ABORT_ON_ERROR		512
> +/* Log any label changes to syslog. */
> +#define SELINUX_RESTORECON_SYSLOG_CHANGES		1024
> +/* Log what spec matched each file. */
> +#define SELINUX_RESTORECON_LOG_MATCHES			2048
> +/* Ignore files that do not exist. */
> +#define SELINUX_RESTORECON_IGNORE_NOENTRY		4096

Maybe we should express these as hex values since they are flags.

> diff --git a/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
> new file mode 100644
> index 0000000..79223eb
> --- /dev/null
> +++ b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
> @@ -0,0 +1,34 @@
> +.TH "selinux_restorecon_set_alt_rootpath" "3" "28 April 2016" "Security Enhanced Linux" "SELinux API documentation"
> +
> +.SH "NAME"
> +selinux_restorecon_set_alt_rootpath \- set an alternate rootpath.
> +.
> +.SH "SYNOPSIS"
> +.B #include <selinux/restorecon.h>
> +.sp
> +.BI "void selinux_restorecon_set_alt_rootpath(const char *" alt_rootpath ");"
> +.in +\w'void selinux_restorecon_set_alt_rootpath('u
> +.
> +.SH "DESCRIPTION"
> +.BR selinux_restorecon_set_alt_rootpath ()
> +passes to
> +.BR selinux_restorecon (3)
> +a pointer containing an alternate rootpath
> +.IR alt_rootpath .
> +.br
> +The path MUST NOT be terminated with a trailing '/'.
> +.br
> +The path MUST NOT be '/' (i.e. the root path).

Any particular reason we can't just handle this in the implementation?

> diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
> index 2794659..11ae8a0 100644
> --- a/libselinux/src/selinux_restorecon.c
> +++ b/libselinux/src/selinux_restorecon.c
> @@ -22,6 +22,7 @@
>  #include <sys/vfs.h>
>  #include <linux/magic.h>
>  #include <libgen.h>
> +#include <syslog.h>
>  #include <selinux/selinux.h>
>  #include <selinux/context.h>
>  #include <selinux/label.h>
> @@ -39,6 +40,8 @@ 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 const char *rootpath = NULL;
> +static int rootpathlen;
>  static size_t fc_count = 0;
>  #define STAR_COUNT 1000
>  
> @@ -47,12 +50,16 @@ struct rest_flags {
>  	bool nochange;
>  	bool verbose;
>  	bool progress;
> -	bool specctx;
> +	bool set_specctx;
>  	bool add_assoc;
> -	bool ignore;
> +	bool ignore_digest;
>  	bool recurse;
>  	bool userealpath;
>  	bool xdev;
> +	bool abort_on_error;
> +	bool syslog_changes;
> +	bool log_matches;
> +	bool ignore_enoent;
>  };
>  
>  static void restorecon_init(void)
> @@ -67,13 +74,15 @@ static void restorecon_init(void)
>  
>  static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
>  
> -
>  static int check_excluded(const char *file)
>  {
>  	int i;
> +	size_t len;
>  
>  	for (i = 0; fc_exclude_list[i]; i++) {
> -		if (strcmp(file, fc_exclude_list[i]) == 0)
> +		len = strlen(fc_exclude_list[i]);
> +		/* Check if 'file' is in an excluded directory. */
> +		if (strncmp(file, fc_exclude_list[i], len) == 0)
>  				return 1;

Simple prefix match could give you a false positive, e.g. if /foo/bar is
on the exclude list and there is a file /foo/barfoo.
Any particular reason you aren't using setfiles restore.c exclude() logic?
However, is it even possible to reach /foo/bar/baz if we are checking
the exclude list on each component and pruning the tree walk on a match
of /foo/bar?


>  	}
>  	return 0;
> @@ -360,10 +369,29 @@ static int restorecon_sb(const char *pathname, const struct stat *sb,
>  	char *newcon = NULL;
>  	char *curcon = NULL;
>  	char *newtypecon = NULL;
> -	int rc = 0;
> +	int rc;
>  	bool updated = false;
> +	const char *tmp_pathname = pathname;

Maybe call it lookup_path or something more descriptive.

> +
> +	if (rootpath) {
> +		if (strncmp(rootpath, tmp_pathname, rootpathlen) != 0) {
> +			selinux_log(SELINUX_ERROR,
> +				    "%s is not located in alt_rootpath %s\n",
> +				    tmp_pathname, rootpath);
> +			return -1;
> +		}
> +		tmp_pathname += rootpathlen;
> +	}
>  
> -	if (selabel_lookup_raw(fc_sehandle, &newcon, pathname, sb->st_mode) < 0)
> +	if (rootpath != NULL && tmp_pathname[0] == '\0')
> +		/* this is actually the root dir of the alt root. */
> +		rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
> +						    sb->st_mode);
> +	else
> +		rc = selabel_lookup_raw(fc_sehandle, &newcon, tmp_pathname,
> +						    sb->st_mode);
> +
> +	if (rc < 0)
>  		return 0; /* no match, but not an error */
>  
>  	if (flags->add_assoc) {


> @@ -436,6 +468,16 @@ static int restorecon_sb(const char *pathname, const struct stat *sb,
>  				    "%s %s from %s to %s\n",
>  				    updated ? "Relabeled" : "Would relabel",
>  				    pathname, curcon, newcon);
> +
> +		if (flags->syslog_changes && !flags->nochange) {
> +			if (curcon)
> +				syslog(LOG_INFO,
> +					    "relabeling %s from %s to %s\n",
> +					    pathname, curcon, newcon);
> +			else
> +				syslog(LOG_INFO, "labeling %s to %s\n",
> +					    pathname, newcon);
> +		}

Wondering if this could be handled by the caller just specifying a log
callback that calls syslog rather than doing it here, but probably would
conflict with other logging.

> @@ -589,13 +649,22 @@ int selinux_restorecon(const char *pathname_orig,
>  		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
>  
>  	fts = fts_open(paths, fts_flags, NULL);
> -	if (!fts) {
> -		error = -1;
> -		goto cleanup;
> -	}
> +	if (!fts)
> +		goto fts_err;
> +
> +	ftsent = fts_read(fts);
> +	if (!ftsent)
> +		goto fts_err;
> +
> +	/* Keep the inode of the first device. */
> +	dev_num = ftsent->fts_statp->st_dev;
>  
>  	error = 0;
> -	while ((ftsent = fts_read(fts)) != NULL) {
> +	do {
> +		/* If the XDEV flag is set and the device is different */
> +		if (flags.xdev && ftsent->fts_statp->st_dev != dev_num)
> +			continue;

Why is this necessary given that we already set FTS_XDEV above?

> +
>  		switch (ftsent->fts_info) {
>  		case FTS_DC:
>  			selinux_log(SELINUX_ERROR,
> @@ -644,9 +713,12 @@ int selinux_restorecon(const char *pathname_orig,
>  
>  			error |= restorecon_sb(ftsent->fts_path,
>  					       ftsent->fts_statp, &flags);
> +
> +			if (error && flags.abort_on_error)
> +				goto out;
>  			break;
>  		}
> -	}
> +	} while ((ftsent = fts_read(fts)) != NULL);
>  
>  	/* Labeling successful. Mark the top level directory as completed. */
>  	if (setrestoreconlast && !flags.nochange && !error) {
> @@ -672,12 +744,14 @@ cleanup:
>  	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,
> @@ -686,6 +760,13 @@ realpatherr:
>  	errno = sverrno;
>  	error = -1;
>  	goto cleanup;
> +
> +fts_err:
> +	selinux_log(SELINUX_ERROR,
> +		    "fts error while labeling %s: %s\n",
> +		    paths[0], strerror(errno));
> +	error = -1;
> +	goto cleanup;
>  }
>  
>  /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
> @@ -757,3 +838,10 @@ void selinux_restorecon_set_exclude_list(const char **exclude_list)
>  {
>  	fc_exclude_list = exclude_list;
>  }
> +
> +/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
> +void selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
> +{
> +	rootpath = alt_rootpath;

Should we be strdup()'ing this in case the caller is passing a local
variable or other transient memory that won't stay around necessarily
for later restorecon() calls?

> +	rootpathlen = strlen(rootpath);
> +}
> diff --git a/libselinux/utils/selinux_restorecon.c b/libselinux/utils/selinux_restorecon.c
> index 2552d63..da59fcd 100644
> --- a/libselinux/utils/selinux_restorecon.c
> +++ b/libselinux/utils/selinux_restorecon.c
> @@ -37,7 +37,7 @@ static int validate_context(char **contextp)
>  static void usage(const char *progname)
>  {
>  	fprintf(stderr,
> -		"\nusage: %s [-FCnRrdeia] [-v|-P] [-p policy] [-f specfile] "
> +		"\nusage: %s [-FCnRrdeiIaAsl] [-v|-P] [-x alt_rootpath] [-p policy] [-f specfile] "
>  		"pathname ...\n"
>  		"Where:\n\t"
>  		"-F  Set the label to that in specfile.\n\t"
> @@ -57,9 +57,14 @@ static void usage(const char *progname)
>  		"-e  Exclude this file/directory (add multiple -e entries).\n\t"
>  		"-i  Do not set SELABEL_OPT_DIGEST option when calling "
>  		" selabel_open(3).\n\t"
> +		"-I  Ignore files that do not exist.\n\t"
>  		"-a  Add an association between an inode and a context.\n\t"
>  		"    If there is a different context that matched the inode,\n\t"
>  		"    then use the first context that matched.\n\t"
> +		"-A  Abort on errors during the file tree walk.\n\t"
> +		"-s  Log any label changes to syslog(3).\n\t"
> +		"-l  Log what specfile context matched each file.\n\t"
> +		"-x  Set alternate rootpath.\n\t"
>  		"-p  Optional binary policy file (also sets validate context "
>  		"option).\n\t"
>  		"-f  Optional file contexts file.\n\t"
> @@ -101,6 +106,7 @@ int main(int argc, char **argv)
>  	int opt, i;
>  	unsigned int restorecon_flags = 0;
>  	char *path = NULL, *digest = NULL, *validate = NULL;
> +	char *alt_rootpath = NULL;
>  	FILE *policystream;
>  	bool ignore_digest = false, require_selinux = true;
>  	bool verbose = false, progress = false;
> @@ -118,7 +124,7 @@ int main(int argc, char **argv)
>  	exclude_list = NULL;
>  	exclude_count = 0;
>  
> -	while ((opt = getopt(argc, argv, "iFCnRvPrdae:f:p:")) > 0) {
> +	while ((opt = getopt(argc, argv, "iIFCnRvPrdaAsle:f:p:x:")) > 0) {
>  		switch (opt) {
>  		case 'F':
>  			restorecon_flags |=
> @@ -190,9 +196,24 @@ int main(int argc, char **argv)
>  		case 'i':
>  			ignore_digest = true;
>  			break;
> +		case 'I':
> +			restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY;
> +			break;
>  		case 'a':
>  			restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC;
>  			break;
> +		case 'A':
> +			restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR;
> +			break;
> +		case 's':
> +			restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES;
> +			break;
> +		case 'l':
> +			restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES;
> +			break;
> +		case 'x':
> +			alt_rootpath = optarg;
> +			break;
>  		default:
>  			usage(argv[0]);
>  		}
> @@ -247,6 +268,9 @@ int main(int argc, char **argv)
>  		selinux_restorecon_set_exclude_list
>  						 ((const char **)exclude_list);
>  
> +	if (alt_rootpath)
> +		selinux_restorecon_set_alt_rootpath(alt_rootpath);
> +
>  	/* Call restorecon for each path in list */
>  	for (i = optind; i < argc; i++) {
>  		if (selinux_restorecon(argv[i], restorecon_flags) < 0) {
>
Richard Haines May 31, 2016, 3:21 p.m. UTC | #2
> On Friday, 20 May 2016, 18:01, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> > On 05/10/2016 11:23 AM, Richard Haines wrote:
>>  Add additional error handling, flags, xdev and alt_rootpath
>>  support for setfiles(8) functionality.
>> 
>>  Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
>>  ---
>>   libselinux/include/selinux/restorecon.h            |  17 +++
>>   libselinux/man/man3/selinux_restorecon.3           |  13 +++
>>   .../man/man3/selinux_restorecon_set_alt_rootpath.3 |  34 ++++++
>>   libselinux/src/selinux_restorecon.c                | 126 
> +++++++++++++++++----
>>   libselinux/utils/selinux_restorecon.c              |  28 ++++-
>>   5 files changed, 197 insertions(+), 21 deletions(-)
>>   create mode 100644 
> libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
>> 
>>  diff --git a/libselinux/include/selinux/restorecon.h 
> b/libselinux/include/selinux/restorecon.h
>>  index 0b93b0c..6b3e0f1 100644
>>  --- a/libselinux/include/selinux/restorecon.h
>>  +++ b/libselinux/include/selinux/restorecon.h
>>  @@ -50,6 +50,14 @@ extern int selinux_restorecon(const char *pathname,
>>    * If there is a different context that matched the inode,
>>    * then use the first context that matched. */
>>   #define SELINUX_RESTORECON_ADD_ASSOC            256
>>  +/* Abort on errors during the file tree walk. */
>>  +#define SELINUX_RESTORECON_ABORT_ON_ERROR        512
>>  +/* Log any label changes to syslog. */
>>  +#define SELINUX_RESTORECON_SYSLOG_CHANGES        1024
>>  +/* Log what spec matched each file. */
>>  +#define SELINUX_RESTORECON_LOG_MATCHES            2048
>>  +/* Ignore files that do not exist. */
>>  +#define SELINUX_RESTORECON_IGNORE_NOENTRY        4096
> 

> Maybe we should express these as hex values since they are flags.
Fixed.
> 
>>  diff --git a/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 
> b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
>>  new file mode 100644
>>  index 0000000..79223eb
>>  --- /dev/null
>>  +++ b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
>>  @@ -0,0 +1,34 @@
>>  +.TH "selinux_restorecon_set_alt_rootpath" "3" "28 
> April 2016" "Security Enhanced Linux" "SELinux API 
> documentation"
>>  +
>>  +.SH "NAME"
>>  +selinux_restorecon_set_alt_rootpath \- set an alternate rootpath.
>>  +.
>>  +.SH "SYNOPSIS"
>>  +.B #include <selinux/restorecon.h>
>>  +.sp
>>  +.BI "void selinux_restorecon_set_alt_rootpath(const char *" 
> alt_rootpath ");"
>>  +.in +\w'void selinux_restorecon_set_alt_rootpath('u
>>  +.
>>  +.SH "DESCRIPTION"
>>  +.BR selinux_restorecon_set_alt_rootpath ()
>>  +passes to
>>  +.BR selinux_restorecon (3)
>>  +a pointer containing an alternate rootpath
>>  +.IR alt_rootpath .
>>  +.br
>>  +The path MUST NOT be terminated with a trailing '/'.
>>  +.br
>>  +The path MUST NOT be '/' (i.e. the root path).
> 

> Any particular reason we can't just handle this in the implementation?
Used code from setfiles.c to handle this now.
> 
>>  diff --git a/libselinux/src/selinux_restorecon.c 
> b/libselinux/src/selinux_restorecon.c
>>  index 2794659..11ae8a0 100644
>>  --- a/libselinux/src/selinux_restorecon.c
>>  +++ b/libselinux/src/selinux_restorecon.c
>>  @@ -22,6 +22,7 @@
>>   #include <sys/vfs.h>
>>   #include <linux/magic.h>
>>   #include <libgen.h>
>>  +#include <syslog.h>
>>   #include <selinux/selinux.h>
>>   #include <selinux/context.h>
>>   #include <selinux/label.h>
>>  @@ -39,6 +40,8 @@ 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 const char *rootpath = NULL;
>>  +static int rootpathlen;
>>   static size_t fc_count = 0;
>>   #define STAR_COUNT 1000
>>   
>>  @@ -47,12 +50,16 @@ struct rest_flags {
>>       bool nochange;
>>       bool verbose;
>>       bool progress;
>>  -    bool specctx;
>>  +    bool set_specctx;
>>       bool add_assoc;
>>  -    bool ignore;
>>  +    bool ignore_digest;
>>       bool recurse;
>>       bool userealpath;
>>       bool xdev;
>>  +    bool abort_on_error;
>>  +    bool syslog_changes;
>>  +    bool log_matches;
>>  +    bool ignore_enoent;
>>   };
>>   
>>   static void restorecon_init(void)
>>  @@ -67,13 +74,15 @@ static void restorecon_init(void)
>>   
>>   static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
>>   
>>  -
>>   static int check_excluded(const char *file)
>>   {
>>       int i;
>>  +    size_t len;
>>   
>>       for (i = 0; fc_exclude_list[i]; i++) {
>>  -        if (strcmp(file, fc_exclude_list[i]) == 0)
>>  +        len = strlen(fc_exclude_list[i]);
>>  +        /* Check if 'file' is in an excluded directory. */
>>  +        if (strncmp(file, fc_exclude_list[i], len) == 0)
>>                   return 1;
> 
> Simple prefix match could give you a false positive, e.g. if /foo/bar is
> on the exclude list and there is a file /foo/barfoo.
> Any particular reason you aren't using setfiles restore.c exclude() logic?
> However, is it even possible to reach /foo/bar/baz if we are checking
> the exclude list on each component and pruning the tree walk on a match

> of /foo/bar?

Adding exclude_non_seclabel_mounts(), add_exclude() and remove_exclude()
to selinux_restorecon to handle this.
> 
> 
>>       }
>>       return 0;
>>  @@ -360,10 +369,29 @@ static int restorecon_sb(const char *pathname, const 
> struct stat *sb,
>>       char *newcon = NULL;
>>       char *curcon = NULL;
>>       char *newtypecon = NULL;
>>  -    int rc = 0;
>>  +    int rc;
>>       bool updated = false;
>>  +    const char *tmp_pathname = pathname;
> 

> Maybe call it lookup_path or something more descriptive.
Fixed.
> 
>>  +
>>  +    if (rootpath) {
>>  +        if (strncmp(rootpath, tmp_pathname, rootpathlen) != 0) {
>>  +            selinux_log(SELINUX_ERROR,
>>  +                    "%s is not located in alt_rootpath 
> %s\n",
>>  +                    tmp_pathname, rootpath);
>>  +            return -1;
>>  +        }
>>  +        tmp_pathname += rootpathlen;
>>  +    }
>>   
>>  -    if (selabel_lookup_raw(fc_sehandle, &newcon, pathname, 
> sb->st_mode) < 0)
>>  +    if (rootpath != NULL && tmp_pathname[0] == '\0')
>>  +        /* this is actually the root dir of the alt root. */
>>  +        rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
>>  +                            sb->st_mode);
>>  +    else
>>  +        rc = selabel_lookup_raw(fc_sehandle, &newcon, tmp_pathname,
>>  +                            sb->st_mode);
>>  +
>>  +    if (rc < 0)
>>           return 0; /* no match, but not an error */
>>   
>>       if (flags->add_assoc) {
> 
> 
>>  @@ -436,6 +468,16 @@ static int restorecon_sb(const char *pathname, const 
> struct stat *sb,
>>                       "%s %s from %s to %s\n",
>>                       updated ? "Relabeled" : "Would 
> relabel",
>>                       pathname, curcon, newcon);
>>  +
>>  +        if (flags->syslog_changes && !flags->nochange) 
> {
>>  +            if (curcon)
>>  +                syslog(LOG_INFO,
>>  +                        "relabeling %s from %s to %s\n",
>>  +                        pathname, curcon, newcon);
>>  +            else
>>  +                syslog(LOG_INFO, "labeling %s to %s\n",
>>  +                        pathname, newcon);
>>  +        }
> 
> Wondering if this could be handled by the caller just specifying a log
> callback that calls syslog rather than doing it here, but probably would

> conflict with other logging.

I've left this as is at present as I would need time to experiment with
callbacks. Could review later ???
> 
>>  @@ -589,13 +649,22 @@ int selinux_restorecon(const char *pathname_orig,
>>           fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
>>   
>>       fts = fts_open(paths, fts_flags, NULL);
>>  -    if (!fts) {
>>  -        error = -1;
>>  -        goto cleanup;
>>  -    }
>>  +    if (!fts)
>>  +        goto fts_err;
>>  +
>>  +    ftsent = fts_read(fts);
>>  +    if (!ftsent)
>>  +        goto fts_err;
>>  +
>>  +    /* Keep the inode of the first device. */
>>  +    dev_num = ftsent->fts_statp->st_dev;
>>   
>>       error = 0;
>>  -    while ((ftsent = fts_read(fts)) != NULL) {
>>  +    do {
>>  +        /* If the XDEV flag is set and the device is different */
>>  +        if (flags.xdev && ftsent->fts_statp->st_dev != 
> dev_num)
>>  +            continue;
> 

> Why is this necessary given that we already set FTS_XDEV above?

After much testing I found this did not work as I expected so implemented
the current setfiles logic that works. I've now found the original
patch from http://marc.info/?l=selinux&m=124688830500777&w=2 and added the
following text from this to the updated code:

/*
* Keep the inode of the first device. This is because the FTS_XDEV
* flag tells fts not to descend into directories with different
* device numbers, but fts will still give back the actual directory.
* By saving the device number of the directory that was passed to
* selinux_restorecon() and then skipping all actions on any
* directories with a different device number when the FTS_XDEV flag
* is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
*/
> 
>>  +
>>           switch (ftsent->fts_info) {
>>           case FTS_DC:
>>               selinux_log(SELINUX_ERROR,
>>  @@ -644,9 +713,12 @@ int selinux_restorecon(const char *pathname_orig,
>>   
>>               error |= restorecon_sb(ftsent->fts_path,
>>                              ftsent->fts_statp, &flags);
>>  +
>>  +            if (error && flags.abort_on_error)
>>  +                goto out;
>>               break;
>>           }
>>  -    }
>>  +    } while ((ftsent = fts_read(fts)) != NULL);
>>   
>>       /* Labeling successful. Mark the top level directory as completed. */
>>       if (setrestoreconlast && !flags.nochange && !error) 
> {
>>  @@ -672,12 +744,14 @@ cleanup:
>>       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,
>>  @@ -686,6 +760,13 @@ realpatherr:
>>       errno = sverrno;
>>       error = -1;
>>       goto cleanup;
>>  +
>>  +fts_err:
>>  +    selinux_log(SELINUX_ERROR,
>>  +            "fts error while labeling %s: %s\n",
>>  +            paths[0], strerror(errno));
>>  +    error = -1;
>>  +    goto cleanup;
>>   }
>>   
>>   /* selinux_restorecon_set_sehandle(3) is called to set the global fc 
> handle */
>>  @@ -757,3 +838,10 @@ void selinux_restorecon_set_exclude_list(const char 
> **exclude_list)
>>   {
>>       fc_exclude_list = exclude_list;
>>   }
>>  +
>>  +/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
>>  +void selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
>>  +{
>>  +    rootpath = alt_rootpath;
> 
> Should we be strdup()'ing this in case the caller is passing a local
> variable or other transient memory that won't stay around necessarily

> for later restorecon() calls?

Used code from setfiles.c to handle this now that dups entry.
> 
> 
>>  +    rootpathlen = strlen(rootpath);
>>  +}
>>  diff --git a/libselinux/utils/selinux_restorecon.c 
> b/libselinux/utils/selinux_restorecon.c
>>  index 2552d63..da59fcd 100644
>>  --- a/libselinux/utils/selinux_restorecon.c
>>  +++ b/libselinux/utils/selinux_restorecon.c
>>  @@ -37,7 +37,7 @@ static int validate_context(char **contextp)
>>   static void usage(const char *progname)
>>   {
>>       fprintf(stderr,
>>  -        "\nusage: %s [-FCnRrdeia] [-v|-P] [-p policy] [-f 
> specfile] "
>>  +        "\nusage: %s [-FCnRrdeiIaAsl] [-v|-P] [-x alt_rootpath] 
> [-p policy] [-f specfile] "
>>           "pathname ...\n"
>>           "Where:\n\t"
>>           "-F  Set the label to that in specfile.\n\t"
>>  @@ -57,9 +57,14 @@ static void usage(const char *progname)
>>           "-e  Exclude this file/directory (add multiple -e 
> entries).\n\t"
>>           "-i  Do not set SELABEL_OPT_DIGEST option when calling "
>>           " selabel_open(3).\n\t"
>>  +        "-I  Ignore files that do not exist.\n\t"
>>           "-a  Add an association between an inode and a 
> context.\n\t"
>>           "    If there is a different context that matched the 
> inode,\n\t"
>>           "    then use the first context that 
> matched.\n\t"
>>  +        "-A  Abort on errors during the file tree 
> walk.\n\t"
>>  +        "-s  Log any label changes to syslog(3).\n\t"
>>  +        "-l  Log what specfile context matched each 
> file.\n\t"
>>  +        "-x  Set alternate rootpath.\n\t"
>>           "-p  Optional binary policy file (also sets validate context 
> "
>>           "option).\n\t"
>>           "-f  Optional file contexts file.\n\t"
>>  @@ -101,6 +106,7 @@ int main(int argc, char **argv)
>>       int opt, i;
>>       unsigned int restorecon_flags = 0;
>>       char *path = NULL, *digest = NULL, *validate = NULL;
>>  +    char *alt_rootpath = NULL;
>>       FILE *policystream;
>>       bool ignore_digest = false, require_selinux = true;
>>       bool verbose = false, progress = false;
>>  @@ -118,7 +124,7 @@ int main(int argc, char **argv)
>>       exclude_list = NULL;
>>       exclude_count = 0;
>>   
>>  -    while ((opt = getopt(argc, argv, "iFCnRvPrdae:f:p:")) > 
> 0) {
>>  +    while ((opt = getopt(argc, argv, "iIFCnRvPrdaAsle:f:p:x:")) 
>>  0) {
>>           switch (opt) {
>>           case 'F':
>>               restorecon_flags |=
>>  @@ -190,9 +196,24 @@ int main(int argc, char **argv)
>>           case 'i':
>>               ignore_digest = true;
>>               break;
>>  +        case 'I':
>>  +            restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY;
>>  +            break;
>>           case 'a':
>>               restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC;
>>               break;
>>  +        case 'A':
>>  +            restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR;
>>  +            break;
>>  +        case 's':
>>  +            restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES;
>>  +            break;
>>  +        case 'l':
>>  +            restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES;
>>  +            break;
>>  +        case 'x':
>>  +            alt_rootpath = optarg;
>>  +            break;
>>           default:
>>               usage(argv[0]);
>>           }
>>  @@ -247,6 +268,9 @@ int main(int argc, char **argv)
>>           selinux_restorecon_set_exclude_list
>>                            ((const char **)exclude_list);
>>   
>>  +    if (alt_rootpath)
>>  +        selinux_restorecon_set_alt_rootpath(alt_rootpath);
>>  +
>>       /* Call restorecon for each path in list */
>>       for (i = optind; i < argc; i++) {
>>           if (selinux_restorecon(argv[i], restorecon_flags) < 0) {
>> 
>
diff mbox

Patch

diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
index 0b93b0c..6b3e0f1 100644
--- a/libselinux/include/selinux/restorecon.h
+++ b/libselinux/include/selinux/restorecon.h
@@ -50,6 +50,14 @@  extern int selinux_restorecon(const char *pathname,
  * If there is a different context that matched the inode,
  * then use the first context that matched. */
 #define SELINUX_RESTORECON_ADD_ASSOC			256
+/* Abort on errors during the file tree walk. */
+#define SELINUX_RESTORECON_ABORT_ON_ERROR		512
+/* Log any label changes to syslog. */
+#define SELINUX_RESTORECON_SYSLOG_CHANGES		1024
+/* Log what spec matched each file. */
+#define SELINUX_RESTORECON_LOG_MATCHES			2048
+/* Ignore files that do not exist. */
+#define SELINUX_RESTORECON_IGNORE_NOENTRY		4096
 
 /**
  * selinux_restorecon_set_sehandle - Set the global fc handle.
@@ -77,6 +85,15 @@  extern struct selabel_handle *selinux_restorecon_default_handle(void);
  */
 extern void selinux_restorecon_set_exclude_list(const char **exclude_list);
 
+/**
+ * selinux_restorecon_set_alt_rootpath - Use alternate rootpath.
+ *					 directories that are to be excluded
+ *					 from relabeling.
+ * @alt_rootpath: containing the alternate rootpath to be used. The path MUST
+ *		  NOT be terminated with a '/'. The path MUST NOT be only '/'.
+ */
+extern void selinux_restorecon_set_alt_rootpath(const char *alt_rootpath);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3
index bbb6721..1d79f91 100644
--- a/libselinux/man/man3/selinux_restorecon.3
+++ b/libselinux/man/man3/selinux_restorecon.3
@@ -106,6 +106,19 @@  entry from which the descent began.
 .B SELINUX_RESTORECON_ADD_ASSOC
 attempt to add an association between an inode and a context. If there is a
 different context that matched the inode, then use the first context that matched.
+.sp
+.B SELINUX_RESTORECON_ABORT_ON_ERROR
+abort on errors during the file tree walk.
+.sp
+.B SELINUX_RESTORECON_SYSLOG_CHANGES
+log any label changes to
+.BR syslog (3).
+.sp
+.B SELINUX_RESTORECON_LOG_MATCHES
+log what specfile context matched each file.
+.sp
+.B SELINUX_RESTORECON_IGNORE_NOENTRY
+ignore files that do not exist.
 .RE
 .sp
 The behavior regarding the checking and updating of the SHA1 digest described
diff --git a/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
new file mode 100644
index 0000000..79223eb
--- /dev/null
+++ b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
@@ -0,0 +1,34 @@ 
+.TH "selinux_restorecon_set_alt_rootpath" "3" "28 April 2016" "Security Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon_set_alt_rootpath \- set an alternate rootpath.
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.sp
+.BI "void selinux_restorecon_set_alt_rootpath(const char *" alt_rootpath ");"
+.in +\w'void selinux_restorecon_set_alt_rootpath('u
+.
+.SH "DESCRIPTION"
+.BR selinux_restorecon_set_alt_rootpath ()
+passes to
+.BR selinux_restorecon (3)
+a pointer containing an alternate rootpath
+.IR alt_rootpath .
+.br
+The path MUST NOT be terminated with a trailing '/'.
+.br
+The path MUST NOT be '/' (i.e. the root path).
+.sp
+.BR selinux_restorecon_set_alt_rootpath ()
+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),
+.br
+.BR selinux_restorecon_set_exclude_list (3)
diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
index 2794659..11ae8a0 100644
--- a/libselinux/src/selinux_restorecon.c
+++ b/libselinux/src/selinux_restorecon.c
@@ -22,6 +22,7 @@ 
 #include <sys/vfs.h>
 #include <linux/magic.h>
 #include <libgen.h>
+#include <syslog.h>
 #include <selinux/selinux.h>
 #include <selinux/context.h>
 #include <selinux/label.h>
@@ -39,6 +40,8 @@  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 const char *rootpath = NULL;
+static int rootpathlen;
 static size_t fc_count = 0;
 #define STAR_COUNT 1000
 
@@ -47,12 +50,16 @@  struct rest_flags {
 	bool nochange;
 	bool verbose;
 	bool progress;
-	bool specctx;
+	bool set_specctx;
 	bool add_assoc;
-	bool ignore;
+	bool ignore_digest;
 	bool recurse;
 	bool userealpath;
 	bool xdev;
+	bool abort_on_error;
+	bool syslog_changes;
+	bool log_matches;
+	bool ignore_enoent;
 };
 
 static void restorecon_init(void)
@@ -67,13 +74,15 @@  static void restorecon_init(void)
 
 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
 
-
 static int check_excluded(const char *file)
 {
 	int i;
+	size_t len;
 
 	for (i = 0; fc_exclude_list[i]; i++) {
-		if (strcmp(file, fc_exclude_list[i]) == 0)
+		len = strlen(fc_exclude_list[i]);
+		/* Check if 'file' is in an excluded directory. */
+		if (strncmp(file, fc_exclude_list[i], len) == 0)
 				return 1;
 	}
 	return 0;
@@ -360,10 +369,29 @@  static int restorecon_sb(const char *pathname, const struct stat *sb,
 	char *newcon = NULL;
 	char *curcon = NULL;
 	char *newtypecon = NULL;
-	int rc = 0;
+	int rc;
 	bool updated = false;
+	const char *tmp_pathname = pathname;
+
+	if (rootpath) {
+		if (strncmp(rootpath, tmp_pathname, rootpathlen) != 0) {
+			selinux_log(SELINUX_ERROR,
+				    "%s is not located in alt_rootpath %s\n",
+				    tmp_pathname, rootpath);
+			return -1;
+		}
+		tmp_pathname += rootpathlen;
+	}
 
-	if (selabel_lookup_raw(fc_sehandle, &newcon, pathname, sb->st_mode) < 0)
+	if (rootpath != NULL && tmp_pathname[0] == '\0')
+		/* this is actually the root dir of the alt root. */
+		rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
+						    sb->st_mode);
+	else
+		rc = selabel_lookup_raw(fc_sehandle, &newcon, tmp_pathname,
+						    sb->st_mode);
+
+	if (rc < 0)
 		return 0; /* no match, but not an error */
 
 	if (flags->add_assoc) {
@@ -385,6 +413,10 @@  static int restorecon_sb(const char *pathname, const struct stat *sb,
 		}
 	}
 
+	if (flags->log_matches)
+		selinux_log(SELINUX_INFO, "%s matched by %s\n",
+			    pathname, newcon);
+
 	if (lgetfilecon_raw(pathname, &curcon) < 0) {
 		if (errno != ENODATA)
 			goto err;
@@ -401,7 +433,7 @@  static int restorecon_sb(const char *pathname, const struct stat *sb,
 	}
 
 	if (strcmp(curcon, newcon) != 0) {
-		if (!flags->specctx && curcon &&
+		if (!flags->set_specctx && curcon &&
 				    (is_context_customizable(curcon) > 0)) {
 			if (flags->verbose) {
 				selinux_log(SELINUX_INFO,
@@ -411,7 +443,7 @@  static int restorecon_sb(const char *pathname, const struct stat *sb,
 			}
 		}
 
-		if (!flags->specctx && curcon) {
+		if (!flags->set_specctx && curcon) {
 			/* If types different then update newcon. */
 			rc = compare_types(curcon, newcon, &newtypecon);
 			if (rc)
@@ -436,6 +468,16 @@  static int restorecon_sb(const char *pathname, const struct stat *sb,
 				    "%s %s from %s to %s\n",
 				    updated ? "Relabeled" : "Would relabel",
 				    pathname, curcon, newcon);
+
+		if (flags->syslog_changes && !flags->nochange) {
+			if (curcon)
+				syslog(LOG_INFO,
+					    "relabeling %s from %s to %s\n",
+					    pathname, curcon, newcon);
+			else
+				syslog(LOG_INFO, "labeling %s to %s\n",
+					    pathname, newcon);
+		}
 	}
 
 out:
@@ -468,11 +510,11 @@  int selinux_restorecon(const char *pathname_orig,
 		    SELINUX_RESTORECON_VERBOSE) ? true : false;
 	flags.progress = (restorecon_flags &
 		    SELINUX_RESTORECON_PROGRESS) ? true : false;
-	flags.specctx = (restorecon_flags &
+	flags.set_specctx = (restorecon_flags &
 		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
 	flags.add_assoc = (restorecon_flags &
 		   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
-	flags.ignore = (restorecon_flags &
+	flags.ignore_digest = (restorecon_flags &
 		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
 	flags.recurse = (restorecon_flags &
 		    SELINUX_RESTORECON_RECURSE) ? true : false;
@@ -480,6 +522,14 @@  int selinux_restorecon(const char *pathname_orig,
 		   SELINUX_RESTORECON_REALPATH) ? true : false;
 	flags.xdev = (restorecon_flags &
 		   SELINUX_RESTORECON_XDEV) ? true : false;
+	flags.abort_on_error = (restorecon_flags &
+		   SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
+	flags.syslog_changes = (restorecon_flags &
+		   SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
+	flags.log_matches = (restorecon_flags &
+		   SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
+	flags.ignore_enoent = (restorecon_flags &
+		   SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
 
 	bool issys;
 	bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST
@@ -494,6 +544,7 @@  int selinux_restorecon(const char *pathname_orig,
 	int error, sverrno;
 	char *xattr_value = NULL;
 	ssize_t size;
+	dev_t dev_num = 0;
 
 	if (flags.verbose && flags.progress)
 		flags.verbose = false;
@@ -545,8 +596,17 @@  int selinux_restorecon(const char *pathname_orig,
 			    sizeof(SYS_PREFIX) - 1)) ? true : false;
 
 	if (lstat(pathname, &sb) < 0) {
-		error = -1;
-		goto cleanup;
+		if (flags.ignore_enoent && errno == ENOENT) {
+			free(pathdnamer);
+			free(pathname);
+			return 0;
+		} else {
+			selinux_log(SELINUX_ERROR,
+				    "lstat(%s) failed: %s\n",
+				    pathname, strerror(errno));
+			error = -1;
+			goto cleanup;
+		}
 	}
 
 	/* Ignore restoreconlast if not a directory */
@@ -572,7 +632,7 @@  int selinux_restorecon(const char *pathname_orig,
 		size = getxattr(pathname, RESTORECON_LAST, xattr_value,
 							    fc_digest_len);
 
-		if (!flags.ignore && size == fc_digest_len &&
+		if (!flags.ignore_digest && size == fc_digest_len &&
 			    memcmp(fc_digest, xattr_value, fc_digest_len)
 								    == 0) {
 			selinux_log(SELINUX_INFO,
@@ -589,13 +649,22 @@  int selinux_restorecon(const char *pathname_orig,
 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
 
 	fts = fts_open(paths, fts_flags, NULL);
-	if (!fts) {
-		error = -1;
-		goto cleanup;
-	}
+	if (!fts)
+		goto fts_err;
+
+	ftsent = fts_read(fts);
+	if (!ftsent)
+		goto fts_err;
+
+	/* Keep the inode of the first device. */
+	dev_num = ftsent->fts_statp->st_dev;
 
 	error = 0;
-	while ((ftsent = fts_read(fts)) != NULL) {
+	do {
+		/* If the XDEV flag is set and the device is different */
+		if (flags.xdev && ftsent->fts_statp->st_dev != dev_num)
+			continue;
+
 		switch (ftsent->fts_info) {
 		case FTS_DC:
 			selinux_log(SELINUX_ERROR,
@@ -644,9 +713,12 @@  int selinux_restorecon(const char *pathname_orig,
 
 			error |= restorecon_sb(ftsent->fts_path,
 					       ftsent->fts_statp, &flags);
+
+			if (error && flags.abort_on_error)
+				goto out;
 			break;
 		}
-	}
+	} while ((ftsent = fts_read(fts)) != NULL);
 
 	/* Labeling successful. Mark the top level directory as completed. */
 	if (setrestoreconlast && !flags.nochange && !error) {
@@ -672,12 +744,14 @@  cleanup:
 	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,
@@ -686,6 +760,13 @@  realpatherr:
 	errno = sverrno;
 	error = -1;
 	goto cleanup;
+
+fts_err:
+	selinux_log(SELINUX_ERROR,
+		    "fts error while labeling %s: %s\n",
+		    paths[0], strerror(errno));
+	error = -1;
+	goto cleanup;
 }
 
 /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
@@ -757,3 +838,10 @@  void selinux_restorecon_set_exclude_list(const char **exclude_list)
 {
 	fc_exclude_list = exclude_list;
 }
+
+/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
+void selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
+{
+	rootpath = alt_rootpath;
+	rootpathlen = strlen(rootpath);
+}
diff --git a/libselinux/utils/selinux_restorecon.c b/libselinux/utils/selinux_restorecon.c
index 2552d63..da59fcd 100644
--- a/libselinux/utils/selinux_restorecon.c
+++ b/libselinux/utils/selinux_restorecon.c
@@ -37,7 +37,7 @@  static int validate_context(char **contextp)
 static void usage(const char *progname)
 {
 	fprintf(stderr,
-		"\nusage: %s [-FCnRrdeia] [-v|-P] [-p policy] [-f specfile] "
+		"\nusage: %s [-FCnRrdeiIaAsl] [-v|-P] [-x alt_rootpath] [-p policy] [-f specfile] "
 		"pathname ...\n"
 		"Where:\n\t"
 		"-F  Set the label to that in specfile.\n\t"
@@ -57,9 +57,14 @@  static void usage(const char *progname)
 		"-e  Exclude this file/directory (add multiple -e entries).\n\t"
 		"-i  Do not set SELABEL_OPT_DIGEST option when calling "
 		" selabel_open(3).\n\t"
+		"-I  Ignore files that do not exist.\n\t"
 		"-a  Add an association between an inode and a context.\n\t"
 		"    If there is a different context that matched the inode,\n\t"
 		"    then use the first context that matched.\n\t"
+		"-A  Abort on errors during the file tree walk.\n\t"
+		"-s  Log any label changes to syslog(3).\n\t"
+		"-l  Log what specfile context matched each file.\n\t"
+		"-x  Set alternate rootpath.\n\t"
 		"-p  Optional binary policy file (also sets validate context "
 		"option).\n\t"
 		"-f  Optional file contexts file.\n\t"
@@ -101,6 +106,7 @@  int main(int argc, char **argv)
 	int opt, i;
 	unsigned int restorecon_flags = 0;
 	char *path = NULL, *digest = NULL, *validate = NULL;
+	char *alt_rootpath = NULL;
 	FILE *policystream;
 	bool ignore_digest = false, require_selinux = true;
 	bool verbose = false, progress = false;
@@ -118,7 +124,7 @@  int main(int argc, char **argv)
 	exclude_list = NULL;
 	exclude_count = 0;
 
-	while ((opt = getopt(argc, argv, "iFCnRvPrdae:f:p:")) > 0) {
+	while ((opt = getopt(argc, argv, "iIFCnRvPrdaAsle:f:p:x:")) > 0) {
 		switch (opt) {
 		case 'F':
 			restorecon_flags |=
@@ -190,9 +196,24 @@  int main(int argc, char **argv)
 		case 'i':
 			ignore_digest = true;
 			break;
+		case 'I':
+			restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY;
+			break;
 		case 'a':
 			restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC;
 			break;
+		case 'A':
+			restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR;
+			break;
+		case 's':
+			restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES;
+			break;
+		case 'l':
+			restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES;
+			break;
+		case 'x':
+			alt_rootpath = optarg;
+			break;
 		default:
 			usage(argv[0]);
 		}
@@ -247,6 +268,9 @@  int main(int argc, char **argv)
 		selinux_restorecon_set_exclude_list
 						 ((const char **)exclude_list);
 
+	if (alt_rootpath)
+		selinux_restorecon_set_alt_rootpath(alt_rootpath);
+
 	/* Call restorecon for each path in list */
 	for (i = optind; i < argc; i++) {
 		if (selinux_restorecon(argv[i], restorecon_flags) < 0) {