mirror of
https://github.com/SELinuxProject/selinux
synced 2025-04-19 22:05:20 +00:00
libselinux: Save digest of all partial matches for directory
We used to hash the file_context and skip the restorecon on the top level directory if the hash doesn't change. But the file_context might change after an OTA update; and some users experienced long restorecon time as they have lots of files under directories like /data/media. This CL tries to hash all the partial match entries in the file_context for each directory; and skips the restorecon if that digest stays the same, regardless of the changes to the other parts of file_context. This is a version ported from Android that was originally written by: xunchang <xunchang@google.com> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
This commit is contained in:
parent
c00ed59281
commit
e016502c0a
@ -106,6 +106,11 @@ int selabel_lookup_raw(struct selabel_handle *handle, char **con,
|
|||||||
|
|
||||||
bool selabel_partial_match(struct selabel_handle *handle, const char *key);
|
bool selabel_partial_match(struct selabel_handle *handle, const char *key);
|
||||||
|
|
||||||
|
bool selabel_get_digests_all_partial_matches(struct selabel_handle *rec,
|
||||||
|
const char *key,
|
||||||
|
uint8_t **calculated_digest,
|
||||||
|
uint8_t **xattr_digest,
|
||||||
|
size_t *digest_len);
|
||||||
bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
|
bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
|
||||||
const char *key, uint8_t* digest);
|
const char *key, uint8_t* digest);
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ extern int selinux_restorecon(const char *pathname,
|
|||||||
* restorecon_flags options
|
* restorecon_flags options
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Force the checking of labels even if the stored SHA1
|
* Force the checking of labels even if the stored SHA1 digest
|
||||||
* digest matches the specfiles SHA1 digest.
|
* matches the specfiles SHA1 digest (requires CAP_SYS_ADMIN).
|
||||||
*/
|
*/
|
||||||
#define SELINUX_RESTORECON_IGNORE_DIGEST 0x0001
|
#define SELINUX_RESTORECON_IGNORE_DIGEST 0x0001
|
||||||
/*
|
/*
|
||||||
@ -96,12 +96,17 @@ extern int selinux_restorecon(const char *pathname,
|
|||||||
* See SELINUX_RESTORECON_PROGRESS flag for details.
|
* See SELINUX_RESTORECON_PROGRESS flag for details.
|
||||||
*/
|
*/
|
||||||
#define SELINUX_RESTORECON_MASS_RELABEL 0x4000
|
#define SELINUX_RESTORECON_MASS_RELABEL 0x4000
|
||||||
|
/*
|
||||||
|
* Set if no digest is to be read or written (as only processes
|
||||||
|
* running with CAP_SYS_ADMIN can read/write digests).
|
||||||
|
*/
|
||||||
|
#define SELINUX_RESTORECON_SKIP_DIGEST 0x8000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* selinux_restorecon_set_sehandle - Set the global fc handle.
|
* selinux_restorecon_set_sehandle - Set the global fc handle.
|
||||||
* @hndl: specifies handle to set as the global fc handle.
|
* @hndl: specifies handle to set as the global fc handle.
|
||||||
*
|
*
|
||||||
* Called by a process that has already called selabel_open(3) with it's
|
* Called by a process that has already called selabel_open(3) with its
|
||||||
* required parameters, or if selinux_restorecon_default_handle(3) has been
|
* required parameters, or if selinux_restorecon_default_handle(3) has been
|
||||||
* called to set the default selabel_open(3) parameters.
|
* called to set the default selabel_open(3) parameters.
|
||||||
*/
|
*/
|
||||||
@ -110,7 +115,7 @@ extern void selinux_restorecon_set_sehandle(struct selabel_handle *hndl);
|
|||||||
/**
|
/**
|
||||||
* selinux_restorecon_default_handle - Sets default selabel_open(3) parameters
|
* selinux_restorecon_default_handle - Sets default selabel_open(3) parameters
|
||||||
* to use the currently loaded policy and
|
* to use the currently loaded policy and
|
||||||
* file_contexts, also requests the digest.
|
* file_contexts.
|
||||||
*
|
*
|
||||||
* Return value is the created handle on success or NULL with @errno set on
|
* Return value is the created handle on success or NULL with @errno set on
|
||||||
* failure.
|
* failure.
|
||||||
@ -134,12 +139,12 @@ extern void selinux_restorecon_set_exclude_list(const char **exclude_list);
|
|||||||
extern int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath);
|
extern int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* selinux_restorecon_xattr - Read/remove RESTORECON_LAST xattr entries.
|
* selinux_restorecon_xattr - Read/remove security.sehash xattr entries.
|
||||||
* @pathname: specifies directory path to check.
|
* @pathname: specifies directory path to check.
|
||||||
* @xattr_flags: specifies the actions to be performed.
|
* @xattr_flags: specifies the actions to be performed.
|
||||||
* @xattr_list: a linked list of struct dir_xattr structures containing
|
* @xattr_list: a linked list of struct dir_xattr structures containing
|
||||||
* the directory, digest and result of the action on the
|
* the directory, digest and result of the action on the
|
||||||
* RESTORECON_LAST entry.
|
* security.sehash entry.
|
||||||
*
|
*
|
||||||
* selinux_restorecon_xattr(3) will automatically call
|
* selinux_restorecon_xattr(3) will automatically call
|
||||||
* selinux_restorecon_default_handle(3) and selinux_restorecon_set_sehandle(3)
|
* selinux_restorecon_default_handle(3) and selinux_restorecon_set_sehandle(3)
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
.TH "selabel_get_digests_all_partial_matches" "3" "14 April 2019" "SELinux API documentation"
|
||||||
|
|
||||||
|
.SH "NAME"
|
||||||
|
selabel_get_digests_all_partial_matches \- retrieve the partial matches digest
|
||||||
|
and the xattr digest that applies to the supplied path \- Only supported
|
||||||
|
on file backend.
|
||||||
|
.
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B #include <stdbool.h>
|
||||||
|
.br
|
||||||
|
.B #include <selinux/selinux.h>
|
||||||
|
.br
|
||||||
|
.B #include <selinux/label.h>
|
||||||
|
.sp
|
||||||
|
.BI "bool selabel_get_digests_all_partial_matches("
|
||||||
|
.in +\w'selabel_get_digests_all_partial_matches('u
|
||||||
|
.BI "struct selabel_handle *" hnd ,
|
||||||
|
.br
|
||||||
|
.BI "const char *" key ,
|
||||||
|
.br
|
||||||
|
.BI "uint8_t **" calculated_digest ,
|
||||||
|
.br
|
||||||
|
.BI "uint8_t **" xattr_digest ,
|
||||||
|
.br
|
||||||
|
.BI "size_t *" digest_len ");"
|
||||||
|
.in
|
||||||
|
.
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
.BR selabel_get_digests_all_partial_matches ()
|
||||||
|
retrieves the file_contexts partial matches digest and the xattr digest that
|
||||||
|
applies to the supplied path on the handle
|
||||||
|
.IR hnd .
|
||||||
|
.br
|
||||||
|
The
|
||||||
|
.IR key
|
||||||
|
parameter is the path to retrieve the digests.
|
||||||
|
.br
|
||||||
|
The
|
||||||
|
.IR calculated_digest
|
||||||
|
is a pointer to the
|
||||||
|
.IR key
|
||||||
|
calculated file_contexts digest of all applicable partial matches, or NULL if
|
||||||
|
none exist. The caller must
|
||||||
|
.BR free (3)
|
||||||
|
the buffer.
|
||||||
|
.br
|
||||||
|
The
|
||||||
|
.IR xattr_digest
|
||||||
|
is a pointer to the
|
||||||
|
.IR key
|
||||||
|
.BR xattr (7)
|
||||||
|
stored digest, or NULL if it does not exist.
|
||||||
|
The caller must
|
||||||
|
.BR free (3)
|
||||||
|
the buffer.
|
||||||
|
.br
|
||||||
|
The
|
||||||
|
.IR digest_len
|
||||||
|
is the length of the digests that will always be returned (even if both are
|
||||||
|
NULL). Note that if both digests are returned, they will always be the same length.
|
||||||
|
.sp
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
TRUE if the digests match or FALSE if they do not or either or both are missing.
|
||||||
|
.sp
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR selinux_restorecon (3),
|
||||||
|
.BR selabel_partial_match (3),
|
||||||
|
.BR selabel_open (3),
|
||||||
|
.BR selinux (8),
|
||||||
|
.BR selabel_file (5)
|
@ -28,39 +28,53 @@ If this is a directory and the
|
|||||||
.B SELINUX_RESTORECON_RECURSE
|
.B SELINUX_RESTORECON_RECURSE
|
||||||
has been set (for descending through directories), then
|
has been set (for descending through directories), then
|
||||||
.BR selinux_restorecon ()
|
.BR selinux_restorecon ()
|
||||||
will write an SHA1 digest of the combined specfiles (see the
|
will write an SHA1 digest of specfile entries caculated by
|
||||||
|
.BR selabel_get_digests_all_partial_matches (3)
|
||||||
|
to an extended attribute of
|
||||||
|
.IR security.sehash
|
||||||
|
once the relabeling has been completed successfully (see the
|
||||||
.B NOTES
|
.B NOTES
|
||||||
section for details) to an extended attribute of
|
section for details).
|
||||||
.IR security.restorecon_last
|
.br
|
||||||
once the relabeling has been completed successfully. This digest will be
|
These digests will be checked should
|
||||||
checked should
|
|
||||||
.BR selinux_restorecon ()
|
.BR selinux_restorecon ()
|
||||||
be rerun
|
be rerun with the
|
||||||
with the
|
|
||||||
.IR restorecon_flags
|
.IR restorecon_flags
|
||||||
.B SELINUX_RESTORECON_RECURSE
|
.B SELINUX_RESTORECON_RECURSE
|
||||||
flag set. If any of the specfiles had been updated, the digest
|
flag set. If any of the specfile entries had been updated, the digest
|
||||||
will also be updated. However if the digest is the same, no relabeling checks
|
will also be updated. However if the digest is the same, no relabeling checks
|
||||||
will take place (unless the
|
will take place.
|
||||||
|
.br
|
||||||
|
The
|
||||||
|
.IR restorecon_flags
|
||||||
|
that can be used to manage the usage of the SHA1 digest are:
|
||||||
|
.RS
|
||||||
|
.B SELINUX_RESTORECON_SKIP_DIGEST
|
||||||
|
.br
|
||||||
.B SELINUX_RESTORECON_IGNORE_DIGEST
|
.B SELINUX_RESTORECON_IGNORE_DIGEST
|
||||||
flag is set).
|
.RE
|
||||||
.sp
|
.sp
|
||||||
.IR restorecon_flags
|
.IR restorecon_flags
|
||||||
contains the labeling option/rules as follows:
|
contains the labeling option/rules as follows:
|
||||||
.sp
|
.sp
|
||||||
.RS
|
.RS
|
||||||
.sp
|
.sp
|
||||||
|
.B SELINUX_RESTORECON_SKIP_DIGEST
|
||||||
|
Do not check or update any extended attribute
|
||||||
|
.IR security.sehash
|
||||||
|
entries.
|
||||||
|
.sp
|
||||||
.B SELINUX_RESTORECON_IGNORE_DIGEST
|
.B SELINUX_RESTORECON_IGNORE_DIGEST
|
||||||
force the checking of labels even if the stored SHA1 digest matches the
|
force the checking of labels even if the stored SHA1 digest matches the
|
||||||
specfiles SHA1 digest. The specfiles digest will be written to the
|
specfile entries SHA1 digest. The specfile entries digest will be written to the
|
||||||
.IR security.restorecon_last
|
.IR security.sehash
|
||||||
extended attribute once relabeling has been completed successfully provided the
|
extended attribute once relabeling has been completed successfully provided the
|
||||||
.B SELINUX_RESTORECON_NOCHANGE
|
.B SELINUX_RESTORECON_NOCHANGE
|
||||||
flag has not been set.
|
flag has not been set.
|
||||||
.sp
|
.sp
|
||||||
.B SELINUX_RESTORECON_NOCHANGE
|
.B SELINUX_RESTORECON_NOCHANGE
|
||||||
don't change any file labels (passive check) or update the digest in the
|
don't change any file labels (passive check) or update the digest in the
|
||||||
.IR security.restorecon_last
|
.IR security.sehash
|
||||||
extended attribute.
|
extended attribute.
|
||||||
.sp
|
.sp
|
||||||
.B SELINUX_RESTORECON_SET_SPECFILE_CTX
|
.B SELINUX_RESTORECON_SET_SPECFILE_CTX
|
||||||
@ -70,7 +84,7 @@ default specfile context.
|
|||||||
.sp
|
.sp
|
||||||
.B SELINUX_RESTORECON_RECURSE
|
.B SELINUX_RESTORECON_RECURSE
|
||||||
change file and directory labels recursively (descend directories)
|
change file and directory labels recursively (descend directories)
|
||||||
and if successful write an SHA1 digest of the combined specfiles to an
|
and if successful write an SHA1 digest of the specfile entries to an
|
||||||
extended attribute as described in the
|
extended attribute as described in the
|
||||||
.B NOTES
|
.B NOTES
|
||||||
section.
|
section.
|
||||||
@ -182,12 +196,13 @@ To improve performance when relabeling file systems recursively (e.g. the
|
|||||||
.B SELINUX_RESTORECON_RECURSE
|
.B SELINUX_RESTORECON_RECURSE
|
||||||
flag is set)
|
flag is set)
|
||||||
.BR selinux_restorecon ()
|
.BR selinux_restorecon ()
|
||||||
will write an SHA1 digest of the specfiles that are processed by
|
will write a caculated SHA1 digest of the specfile entries returned by
|
||||||
.BR selabel_open (3)
|
.BR selabel_get_digests_all_partial_matches (3)
|
||||||
to an extended attribute named
|
to an extended attribute named
|
||||||
.IR security.restorecon_last
|
.IR security.sehash
|
||||||
to the directory specified in the
|
for each directory in the
|
||||||
.IR pathname .
|
.IR pathname
|
||||||
|
path.
|
||||||
.IP "2." 4
|
.IP "2." 4
|
||||||
To check the extended attribute entry use
|
To check the extended attribute entry use
|
||||||
.BR getfattr (1) ,
|
.BR getfattr (1) ,
|
||||||
@ -195,40 +210,26 @@ for example:
|
|||||||
.sp
|
.sp
|
||||||
.RS
|
.RS
|
||||||
.RS
|
.RS
|
||||||
getfattr -e hex -n security.restorecon_last /
|
getfattr -e hex -n security.sehash /
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.IP "3." 4
|
.IP "3." 4
|
||||||
The SHA1 digest is calculated by
|
Should any of the specfile entries have changed, then when
|
||||||
.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).
|
|
||||||
.IP "4." 4
|
|
||||||
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 ()
|
.BR selinux_restorecon ()
|
||||||
is run again with the
|
is run again with the
|
||||||
.B SELINUX_RESTORECON_RECURSE
|
.B SELINUX_RESTORECON_RECURSE
|
||||||
flag set, a new SHA1 digest will be calculated and all files will be automatically
|
flag set, new SHA1 digests will be calculated and all files automatically
|
||||||
relabeled depending on the settings of the
|
relabeled depending on the settings of the
|
||||||
.B SELINUX_RESTORECON_SET_SPECFILE_CTX
|
.B SELINUX_RESTORECON_SET_SPECFILE_CTX
|
||||||
flag (provided
|
flag (provided
|
||||||
.B SELINUX_RESTORECON_NOCHANGE
|
.B SELINUX_RESTORECON_NOCHANGE
|
||||||
is not set).
|
is not set).
|
||||||
.IP "5." 4
|
.IP "4." 4
|
||||||
.B /sys
|
.B /sys
|
||||||
and in-memory filesystems do not support the
|
and in-memory filesystems do not support the
|
||||||
.IR security.restorecon_last
|
.IR security.sehash
|
||||||
extended attribute and are automatically excluded from any relabeling checks.
|
extended attribute and are automatically excluded from any relabeling checks.
|
||||||
.IP "6." 4
|
.IP "5." 4
|
||||||
By default
|
By default
|
||||||
.B stderr
|
.B stderr
|
||||||
is used to log output messages and errors. This may be changed by calling
|
is used to log output messages and errors. This may be changed by calling
|
||||||
@ -239,6 +240,8 @@ with the
|
|||||||
option.
|
option.
|
||||||
.
|
.
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
|
.BR selabel_get_digests_all_partial_matches (3),
|
||||||
|
.br
|
||||||
.BR selinux_restorecon_set_sehandle (3),
|
.BR selinux_restorecon_set_sehandle (3),
|
||||||
.br
|
.br
|
||||||
.BR selinux_restorecon_default_handle (3),
|
.BR selinux_restorecon_default_handle (3),
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
selinux_restorecon_xattr \- manage default
|
selinux_restorecon_xattr \- manage default
|
||||||
.I security.restorecon_last
|
.I security.sehash
|
||||||
extended attribute entries added by
|
extended attribute entries added by
|
||||||
.BR selinux_restorecon (3),
|
.BR selinux_restorecon (3),
|
||||||
.BR setfiles (8)
|
.BR setfiles (8)
|
||||||
@ -29,7 +29,7 @@ structures containing information described below based on:
|
|||||||
.RS
|
.RS
|
||||||
.IR pathname
|
.IR pathname
|
||||||
containing a directory tree to be searched for
|
containing a directory tree to be searched for
|
||||||
.I security.restorecon_last
|
.I security.sehash
|
||||||
extended attribute entries.
|
extended attribute entries.
|
||||||
.sp
|
.sp
|
||||||
.IR xattr_flags
|
.IR xattr_flags
|
||||||
@ -119,7 +119,7 @@ By default
|
|||||||
.BR selinux_restorecon_xattr (3)
|
.BR selinux_restorecon_xattr (3)
|
||||||
will use the default set of specfiles described in
|
will use the default set of specfiles described in
|
||||||
.BR files_contexts (5)
|
.BR files_contexts (5)
|
||||||
to calculate the initial SHA1 digest to be used for comparison.
|
to calculate the SHA1 digests to be used for comparison.
|
||||||
To change this default behavior
|
To change this default behavior
|
||||||
.BR selabel_open (3)
|
.BR selabel_open (3)
|
||||||
must be called specifying the required
|
must be called specifying the required
|
||||||
@ -143,7 +143,7 @@ flag has been set.
|
|||||||
and
|
and
|
||||||
.B TMPFS
|
.B TMPFS
|
||||||
filesystems do not support the
|
filesystems do not support the
|
||||||
.IR security.restorecon_last
|
.IR security.sehash
|
||||||
extended attribute and are automatically excluded from searches.
|
extended attribute and are automatically excluded from searches.
|
||||||
.IP "4." 4
|
.IP "4." 4
|
||||||
By default
|
By default
|
||||||
|
@ -274,6 +274,21 @@ bool selabel_partial_match(struct selabel_handle *rec, const char *key)
|
|||||||
return rec->func_partial_match(rec, key);
|
return rec->func_partial_match(rec, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool selabel_get_digests_all_partial_matches(struct selabel_handle *rec,
|
||||||
|
const char *key,
|
||||||
|
uint8_t **calculated_digest,
|
||||||
|
uint8_t **xattr_digest,
|
||||||
|
size_t *digest_len)
|
||||||
|
{
|
||||||
|
if (!rec->func_get_digests_all_partial_matches)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return rec->func_get_digests_all_partial_matches(rec, key,
|
||||||
|
calculated_digest,
|
||||||
|
xattr_digest,
|
||||||
|
digest_len);
|
||||||
|
}
|
||||||
|
|
||||||
bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
|
bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
|
||||||
const char *key, uint8_t *digest) {
|
const char *key, uint8_t *digest) {
|
||||||
if (!rec->func_hash_all_partial_matches) {
|
if (!rec->func_hash_all_partial_matches) {
|
||||||
|
@ -972,6 +972,55 @@ static struct spec *lookup_common(struct selabel_handle *rec,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the digest of all partial matched contexts is the same as
|
||||||
|
* the one saved by setxattr, otherwise returns false. The length of the SHA1
|
||||||
|
* digest will always be returned. The caller must free any returned digests.
|
||||||
|
*/
|
||||||
|
static bool get_digests_all_partial_matches(struct selabel_handle *rec,
|
||||||
|
const char *pathname,
|
||||||
|
uint8_t **calculated_digest,
|
||||||
|
uint8_t **xattr_digest,
|
||||||
|
size_t *digest_len)
|
||||||
|
{
|
||||||
|
uint8_t read_digest[SHA1_HASH_SIZE];
|
||||||
|
ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
|
||||||
|
read_digest, SHA1_HASH_SIZE);
|
||||||
|
uint8_t hash_digest[SHA1_HASH_SIZE];
|
||||||
|
bool status = selabel_hash_all_partial_matches(rec, pathname,
|
||||||
|
hash_digest);
|
||||||
|
|
||||||
|
*xattr_digest = NULL;
|
||||||
|
*calculated_digest = NULL;
|
||||||
|
*digest_len = SHA1_HASH_SIZE;
|
||||||
|
|
||||||
|
if (read_size == SHA1_HASH_SIZE) {
|
||||||
|
*xattr_digest = calloc(1, SHA1_HASH_SIZE + 1);
|
||||||
|
if (!*xattr_digest)
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
memcpy(*xattr_digest, read_digest, SHA1_HASH_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
*calculated_digest = calloc(1, SHA1_HASH_SIZE + 1);
|
||||||
|
if (!*calculated_digest)
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
memcpy(*calculated_digest, hash_digest, SHA1_HASH_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status && read_size == SHA1_HASH_SIZE &&
|
||||||
|
memcmp(read_digest, hash_digest, SHA1_HASH_SIZE) == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
|
static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
|
||||||
{
|
{
|
||||||
assert(digest);
|
assert(digest);
|
||||||
@ -1200,6 +1249,8 @@ int selabel_file_init(struct selabel_handle *rec,
|
|||||||
rec->func_stats = &stats;
|
rec->func_stats = &stats;
|
||||||
rec->func_lookup = &lookup;
|
rec->func_lookup = &lookup;
|
||||||
rec->func_partial_match = &partial_match;
|
rec->func_partial_match = &partial_match;
|
||||||
|
rec->func_get_digests_all_partial_matches =
|
||||||
|
&get_digests_all_partial_matches;
|
||||||
rec->func_hash_all_partial_matches = &hash_all_partial_matches;
|
rec->func_hash_all_partial_matches = &hash_all_partial_matches;
|
||||||
rec->func_lookup_best_match = &lookup_best_match;
|
rec->func_lookup_best_match = &lookup_best_match;
|
||||||
rec->func_cmp = &cmp;
|
rec->func_cmp = &cmp;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* regex.h/c were introduced to hold all dependencies on the regular
|
* regex.h/c were introduced to hold all dependencies on the regular
|
||||||
@ -31,6 +32,9 @@
|
|||||||
#define SELINUX_COMPILED_FCONTEXT_MAX_VERS \
|
#define SELINUX_COMPILED_FCONTEXT_MAX_VERS \
|
||||||
SELINUX_COMPILED_FCONTEXT_REGEX_ARCH
|
SELINUX_COMPILED_FCONTEXT_REGEX_ARCH
|
||||||
|
|
||||||
|
/* Required selinux_restorecon and selabel_get_digests_all_partial_matches() */
|
||||||
|
#define RESTORECON_PARTIAL_MATCH_DIGEST "security.sehash"
|
||||||
|
|
||||||
struct selabel_sub {
|
struct selabel_sub {
|
||||||
char *src;
|
char *src;
|
||||||
int slen;
|
int slen;
|
||||||
|
@ -87,6 +87,11 @@ struct selabel_handle {
|
|||||||
void (*func_close) (struct selabel_handle *h);
|
void (*func_close) (struct selabel_handle *h);
|
||||||
void (*func_stats) (struct selabel_handle *h);
|
void (*func_stats) (struct selabel_handle *h);
|
||||||
bool (*func_partial_match) (struct selabel_handle *h, const char *key);
|
bool (*func_partial_match) (struct selabel_handle *h, const char *key);
|
||||||
|
bool (*func_get_digests_all_partial_matches) (struct selabel_handle *h,
|
||||||
|
const char *key,
|
||||||
|
uint8_t **calculated_digest,
|
||||||
|
uint8_t **xattr_digest,
|
||||||
|
size_t *digest_len);
|
||||||
bool (*func_hash_all_partial_matches) (struct selabel_handle *h,
|
bool (*func_hash_all_partial_matches) (struct selabel_handle *h,
|
||||||
const char *key, uint8_t *digest);
|
const char *key, uint8_t *digest);
|
||||||
struct selabel_lookup_rec *(*func_lookup_best_match)
|
struct selabel_lookup_rec *(*func_lookup_best_match)
|
||||||
|
@ -36,17 +36,13 @@
|
|||||||
|
|
||||||
#include "callbacks.h"
|
#include "callbacks.h"
|
||||||
#include "selinux_internal.h"
|
#include "selinux_internal.h"
|
||||||
|
#include "label_file.h"
|
||||||
#define RESTORECON_LAST "security.restorecon_last"
|
#include "sha1.h"
|
||||||
|
|
||||||
#define SYS_PATH "/sys"
|
|
||||||
#define SYS_PREFIX SYS_PATH "/"
|
|
||||||
|
|
||||||
#define STAR_COUNT 1024
|
#define STAR_COUNT 1024
|
||||||
|
|
||||||
static struct selabel_handle *fc_sehandle = NULL;
|
static struct selabel_handle *fc_sehandle = NULL;
|
||||||
static unsigned char *fc_digest = NULL;
|
static bool selabel_no_digest;
|
||||||
static size_t fc_digest_len = 0;
|
|
||||||
static char *rootpath = NULL;
|
static char *rootpath = NULL;
|
||||||
static int rootpathlen;
|
static int rootpathlen;
|
||||||
|
|
||||||
@ -77,7 +73,6 @@ struct rest_flags {
|
|||||||
bool mass_relabel;
|
bool mass_relabel;
|
||||||
bool set_specctx;
|
bool set_specctx;
|
||||||
bool add_assoc;
|
bool add_assoc;
|
||||||
bool ignore_digest;
|
|
||||||
bool recurse;
|
bool recurse;
|
||||||
bool userealpath;
|
bool userealpath;
|
||||||
bool set_xdev;
|
bool set_xdev;
|
||||||
@ -299,57 +294,60 @@ static int add_xattr_entry(const char *directory, bool delete_nonmatch,
|
|||||||
bool delete_all)
|
bool delete_all)
|
||||||
{
|
{
|
||||||
char *sha1_buf = NULL;
|
char *sha1_buf = NULL;
|
||||||
unsigned char *xattr_value = NULL;
|
size_t i, digest_len = 0;
|
||||||
ssize_t xattr_size;
|
|
||||||
size_t i;
|
|
||||||
int rc, digest_result;
|
int rc, digest_result;
|
||||||
struct dir_xattr *new_entry;
|
struct dir_xattr *new_entry;
|
||||||
|
uint8_t *xattr_digest = NULL;
|
||||||
|
uint8_t *calculated_digest = NULL;
|
||||||
|
|
||||||
if (!directory) {
|
if (!directory) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
xattr_value = malloc(fc_digest_len);
|
selabel_get_digests_all_partial_matches(fc_sehandle, directory,
|
||||||
if (!xattr_value)
|
&calculated_digest,
|
||||||
goto oom;
|
&xattr_digest, &digest_len);
|
||||||
|
|
||||||
xattr_size = getxattr(directory, RESTORECON_LAST, xattr_value,
|
if (!xattr_digest) {
|
||||||
fc_digest_len);
|
free(calculated_digest);
|
||||||
if (xattr_size < 0) {
|
|
||||||
free(xattr_value);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert entry to a hex encoded string. */
|
/* Convert entry to a hex encoded string. */
|
||||||
sha1_buf = malloc(xattr_size * 2 + 1);
|
sha1_buf = malloc(digest_len * 2 + 1);
|
||||||
if (!sha1_buf) {
|
if (!sha1_buf) {
|
||||||
free(xattr_value);
|
free(xattr_digest);
|
||||||
|
free(calculated_digest);
|
||||||
goto oom;
|
goto oom;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < (size_t)xattr_size; i++)
|
for (i = 0; i < digest_len; i++)
|
||||||
sprintf((&sha1_buf[i * 2]), "%02x", xattr_value[i]);
|
sprintf((&sha1_buf[i * 2]), "%02x", xattr_digest[i]);
|
||||||
|
|
||||||
rc = memcmp(fc_digest, xattr_value, fc_digest_len);
|
rc = memcmp(calculated_digest, xattr_digest, digest_len);
|
||||||
digest_result = rc ? NOMATCH : MATCH;
|
digest_result = rc ? NOMATCH : MATCH;
|
||||||
|
|
||||||
if ((delete_nonmatch && rc != 0) || delete_all) {
|
if ((delete_nonmatch && rc != 0) || delete_all) {
|
||||||
digest_result = rc ? DELETED_NOMATCH : DELETED_MATCH;
|
digest_result = rc ? DELETED_NOMATCH : DELETED_MATCH;
|
||||||
rc = removexattr(directory, RESTORECON_LAST);
|
rc = removexattr(directory, RESTORECON_PARTIAL_MATCH_DIGEST);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
selinux_log(SELINUX_ERROR,
|
selinux_log(SELINUX_ERROR,
|
||||||
"Error: %s removing xattr \"%s\" from: %s\n",
|
"Error: %s removing xattr \"%s\" from: %s\n",
|
||||||
strerror(errno), RESTORECON_LAST, directory);
|
strerror(errno),
|
||||||
|
RESTORECON_PARTIAL_MATCH_DIGEST, directory);
|
||||||
digest_result = ERROR;
|
digest_result = ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(xattr_value);
|
free(xattr_digest);
|
||||||
|
free(calculated_digest);
|
||||||
|
|
||||||
/* Now add entries to link list. */
|
/* Now add entries to link list. */
|
||||||
new_entry = malloc(sizeof(struct dir_xattr));
|
new_entry = malloc(sizeof(struct dir_xattr));
|
||||||
if (!new_entry)
|
if (!new_entry) {
|
||||||
|
free(sha1_buf);
|
||||||
goto oom;
|
goto oom;
|
||||||
|
}
|
||||||
new_entry->next = NULL;
|
new_entry->next = NULL;
|
||||||
|
|
||||||
new_entry->directory = strdup(directory);
|
new_entry->directory = strdup(directory);
|
||||||
@ -736,6 +734,68 @@ err:
|
|||||||
goto out1;
|
goto out1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct dir_hash_node {
|
||||||
|
char *path;
|
||||||
|
uint8_t digest[SHA1_HASH_SIZE];
|
||||||
|
struct dir_hash_node *next;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
* Returns true if the digest of all partial matched contexts is the same as
|
||||||
|
* the one saved by setxattr. Otherwise returns false and constructs a
|
||||||
|
* dir_hash_node with the newly calculated digest.
|
||||||
|
*/
|
||||||
|
static bool check_context_match_for_dir(const char *pathname,
|
||||||
|
struct dir_hash_node **new_node,
|
||||||
|
int error)
|
||||||
|
{
|
||||||
|
bool status;
|
||||||
|
size_t digest_len = 0;
|
||||||
|
uint8_t *read_digest = NULL;
|
||||||
|
uint8_t *calculated_digest = NULL;
|
||||||
|
|
||||||
|
if (!new_node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*new_node = NULL;
|
||||||
|
|
||||||
|
/* status = true if digests match, false otherwise. */
|
||||||
|
status = selabel_get_digests_all_partial_matches(fc_sehandle, pathname,
|
||||||
|
&calculated_digest,
|
||||||
|
&read_digest,
|
||||||
|
&digest_len);
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
goto free;
|
||||||
|
|
||||||
|
/* Save digest of all matched contexts for the current directory. */
|
||||||
|
if (!error && calculated_digest) {
|
||||||
|
*new_node = calloc(1, sizeof(struct dir_hash_node));
|
||||||
|
|
||||||
|
if (!*new_node)
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
(*new_node)->path = strdup(pathname);
|
||||||
|
|
||||||
|
if (!(*new_node)->path) {
|
||||||
|
free(*new_node);
|
||||||
|
*new_node = NULL;
|
||||||
|
goto oom;
|
||||||
|
}
|
||||||
|
memcpy((*new_node)->digest, calculated_digest, digest_len);
|
||||||
|
(*new_node)->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free:
|
||||||
|
free(calculated_digest);
|
||||||
|
free(read_digest);
|
||||||
|
return status;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Public API
|
* Public API
|
||||||
*/
|
*/
|
||||||
@ -746,8 +806,6 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
{
|
{
|
||||||
struct rest_flags flags;
|
struct rest_flags flags;
|
||||||
|
|
||||||
flags.ignore_digest = (restorecon_flags &
|
|
||||||
SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
|
|
||||||
flags.nochange = (restorecon_flags &
|
flags.nochange = (restorecon_flags &
|
||||||
SELINUX_RESTORECON_NOCHANGE) ? true : false;
|
SELINUX_RESTORECON_NOCHANGE) ? true : false;
|
||||||
flags.verbose = (restorecon_flags &
|
flags.verbose = (restorecon_flags &
|
||||||
@ -777,10 +835,10 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
flags.warnonnomatch = true;
|
flags.warnonnomatch = true;
|
||||||
ignore_mounts = (restorecon_flags &
|
ignore_mounts = (restorecon_flags &
|
||||||
SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
|
SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
|
||||||
|
bool ignore_digest = (restorecon_flags &
|
||||||
|
SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
|
||||||
|
bool setrestorecondigest = true;
|
||||||
|
|
||||||
bool issys;
|
|
||||||
bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST
|
|
||||||
* FALSE = don't use xattr */
|
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
struct statfs sfsb;
|
struct statfs sfsb;
|
||||||
FTS *fts;
|
FTS *fts;
|
||||||
@ -788,9 +846,9 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
|
char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
|
||||||
char *paths[2] = { NULL, NULL };
|
char *paths[2] = { NULL, NULL };
|
||||||
int fts_flags, error, sverrno;
|
int fts_flags, error, sverrno;
|
||||||
char *xattr_value = NULL;
|
|
||||||
ssize_t size;
|
|
||||||
dev_t dev_num = 0;
|
dev_t dev_num = 0;
|
||||||
|
struct dir_hash_node *current = NULL;
|
||||||
|
struct dir_hash_node *head = NULL;
|
||||||
|
|
||||||
if (flags.verbose && flags.progress)
|
if (flags.verbose && flags.progress)
|
||||||
flags.verbose = false;
|
flags.verbose = false;
|
||||||
@ -800,11 +858,13 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
if (!fc_sehandle)
|
if (!fc_sehandle)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (fc_digest_len) {
|
/*
|
||||||
xattr_value = malloc(fc_digest_len);
|
* If selabel_no_digest = true then no digest has been requested by
|
||||||
if (!xattr_value)
|
* an external selabel_open(3) call.
|
||||||
return -1;
|
*/
|
||||||
}
|
if (selabel_no_digest ||
|
||||||
|
(restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST))
|
||||||
|
setrestorecondigest = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert passed-in pathname to canonical pathname by resolving
|
* Convert passed-in pathname to canonical pathname by resolving
|
||||||
@ -853,13 +913,9 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
paths[0] = pathname;
|
paths[0] = pathname;
|
||||||
issys = (!strcmp(pathname, SYS_PATH) ||
|
|
||||||
!strncmp(pathname, SYS_PREFIX,
|
|
||||||
sizeof(SYS_PREFIX) - 1)) ? true : false;
|
|
||||||
|
|
||||||
if (lstat(pathname, &sb) < 0) {
|
if (lstat(pathname, &sb) < 0) {
|
||||||
if (flags.ignore_noent && errno == ENOENT) {
|
if (flags.ignore_noent && errno == ENOENT) {
|
||||||
free(xattr_value);
|
|
||||||
free(pathdnamer);
|
free(pathdnamer);
|
||||||
free(pathname);
|
free(pathname);
|
||||||
return 0;
|
return 0;
|
||||||
@ -872,9 +928,9 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore restoreconlast if not a directory */
|
/* Skip digest if not a directory */
|
||||||
if ((sb.st_mode & S_IFDIR) != S_IFDIR)
|
if ((sb.st_mode & S_IFDIR) != S_IFDIR)
|
||||||
setrestoreconlast = false;
|
setrestorecondigest = false;
|
||||||
|
|
||||||
if (!flags.recurse) {
|
if (!flags.recurse) {
|
||||||
if (check_excluded(pathname)) {
|
if (check_excluded(pathname)) {
|
||||||
@ -886,30 +942,19 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore restoreconlast on /sys */
|
/* Obtain fs type */
|
||||||
if (issys)
|
if (statfs(pathname, &sfsb) < 0) {
|
||||||
setrestoreconlast = false;
|
selinux_log(SELINUX_ERROR,
|
||||||
|
"statfs(%s) failed: %s\n",
|
||||||
/* Ignore restoreconlast on in-memory filesystems */
|
pathname, strerror(errno));
|
||||||
if (setrestoreconlast && statfs(pathname, &sfsb) == 0) {
|
error = -1;
|
||||||
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 (!flags.ignore_digest && (size_t)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;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/* Skip digest on in-memory filesystems and /sys */
|
||||||
|
if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC ||
|
||||||
|
sfsb.f_type == SYSFS_MAGIC)
|
||||||
|
setrestorecondigest = false;
|
||||||
|
|
||||||
if (flags.set_xdev)
|
if (flags.set_xdev)
|
||||||
fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
|
fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
|
||||||
@ -973,7 +1018,8 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
fts_set(fts, ftsent, FTS_SKIP);
|
fts_set(fts, ftsent, FTS_SKIP);
|
||||||
continue;
|
continue;
|
||||||
case FTS_D:
|
case FTS_D:
|
||||||
if (issys && !selabel_partial_match(fc_sehandle,
|
if (sfsb.f_type == SYSFS_MAGIC &&
|
||||||
|
!selabel_partial_match(fc_sehandle,
|
||||||
ftsent->fts_path)) {
|
ftsent->fts_path)) {
|
||||||
fts_set(fts, ftsent, FTS_SKIP);
|
fts_set(fts, ftsent, FTS_SKIP);
|
||||||
continue;
|
continue;
|
||||||
@ -983,6 +1029,31 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
fts_set(fts, ftsent, FTS_SKIP);
|
fts_set(fts, ftsent, FTS_SKIP);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setrestorecondigest) {
|
||||||
|
struct dir_hash_node *new_node = NULL;
|
||||||
|
|
||||||
|
if (check_context_match_for_dir(ftsent->fts_path,
|
||||||
|
&new_node,
|
||||||
|
error) &&
|
||||||
|
!ignore_digest) {
|
||||||
|
selinux_log(SELINUX_INFO,
|
||||||
|
"Skipping restorecon on directory(%s)\n",
|
||||||
|
ftsent->fts_path);
|
||||||
|
fts_set(fts, ftsent, FTS_SKIP);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_node && !error) {
|
||||||
|
if (!current) {
|
||||||
|
current = new_node;
|
||||||
|
head = current;
|
||||||
|
} else {
|
||||||
|
current->next = new_node;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
error |= restorecon_sb(ftsent->fts_path,
|
error |= restorecon_sb(ftsent->fts_path,
|
||||||
@ -995,13 +1066,24 @@ int selinux_restorecon(const char *pathname_orig,
|
|||||||
}
|
}
|
||||||
} while ((ftsent = fts_read(fts)) != NULL);
|
} while ((ftsent = fts_read(fts)) != NULL);
|
||||||
|
|
||||||
/* Labeling successful. Mark the top level directory as completed. */
|
/*
|
||||||
if (setrestoreconlast && !flags.nochange && !error && fc_digest) {
|
* Labeling successful. Write partial match digests for subdirectories.
|
||||||
error = setxattr(pathname, RESTORECON_LAST, fc_digest,
|
* TODO: Write digest upon FTS_DP if no error occurs in its descents.
|
||||||
fc_digest_len, 0);
|
*/
|
||||||
if (!error && flags.verbose)
|
if (setrestorecondigest && !flags.nochange && !error) {
|
||||||
selinux_log(SELINUX_INFO,
|
current = head;
|
||||||
"Updated digest for: %s\n", pathname);
|
while (current != NULL) {
|
||||||
|
if (setxattr(current->path,
|
||||||
|
RESTORECON_PARTIAL_MATCH_DIGEST,
|
||||||
|
current->digest,
|
||||||
|
SHA1_HASH_SIZE, 0) < 0) {
|
||||||
|
selinux_log(SELINUX_ERROR,
|
||||||
|
"setxattr failed: %s: %s\n",
|
||||||
|
current->path,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@ -1019,7 +1101,15 @@ cleanup:
|
|||||||
}
|
}
|
||||||
free(pathdnamer);
|
free(pathdnamer);
|
||||||
free(pathname);
|
free(pathname);
|
||||||
free(xattr_value);
|
|
||||||
|
current = head;
|
||||||
|
while (current != NULL) {
|
||||||
|
struct dir_hash_node *next = current->next;
|
||||||
|
|
||||||
|
free(current->path);
|
||||||
|
free(current);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
oom:
|
oom:
|
||||||
@ -1050,20 +1140,20 @@ fts_err:
|
|||||||
void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
|
void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
|
||||||
{
|
{
|
||||||
char **specfiles;
|
char **specfiles;
|
||||||
size_t num_specfiles;
|
unsigned char *fc_digest;
|
||||||
|
size_t num_specfiles, fc_digest_len;
|
||||||
|
|
||||||
fc_sehandle = (struct selabel_handle *) hndl;
|
fc_sehandle = (struct selabel_handle *) hndl;
|
||||||
|
|
||||||
/*
|
/* Check if digest requested in selabel_open(3), if so use it. */
|
||||||
* Read digest if requested in selabel_open(3) and set global params.
|
|
||||||
*/
|
|
||||||
if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len,
|
if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len,
|
||||||
&specfiles, &num_specfiles) < 0) {
|
&specfiles, &num_specfiles) < 0)
|
||||||
fc_digest = NULL;
|
selabel_no_digest = true;
|
||||||
fc_digest_len = 0;
|
else
|
||||||
}
|
selabel_no_digest = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* selinux_restorecon_default_handle(3) is called to set the global restorecon
|
* selinux_restorecon_default_handle(3) is called to set the global restorecon
|
||||||
* handle by a process if the default params are required.
|
* handle by a process if the default params are required.
|
||||||
@ -1085,6 +1175,7 @@ struct selabel_handle *selinux_restorecon_default_handle(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selabel_no_digest = false;
|
||||||
return sehandle;
|
return sehandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1134,7 +1225,9 @@ int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* selinux_restorecon_xattr(3) - Find RESTORECON_LAST entries. */
|
/* selinux_restorecon_xattr(3)
|
||||||
|
* Find RESTORECON_PARTIAL_MATCH_DIGEST entries.
|
||||||
|
*/
|
||||||
int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
|
int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
|
||||||
struct dir_xattr ***xattr_list)
|
struct dir_xattr ***xattr_list)
|
||||||
{
|
{
|
||||||
@ -1157,7 +1250,7 @@ int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
|
|||||||
|
|
||||||
__selinux_once(fc_once, restorecon_init);
|
__selinux_once(fc_once, restorecon_init);
|
||||||
|
|
||||||
if (!fc_sehandle || !fc_digest_len)
|
if (!fc_sehandle)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (lstat(pathname, &sb) < 0) {
|
if (lstat(pathname, &sb) < 0) {
|
||||||
|
1
libselinux/utils/.gitignore
vendored
1
libselinux/utils/.gitignore
vendored
@ -15,6 +15,7 @@ matchpathcon
|
|||||||
policyvers
|
policyvers
|
||||||
sefcontext_compile
|
sefcontext_compile
|
||||||
selabel_digest
|
selabel_digest
|
||||||
|
selabel_get_digests_all_partial_matches
|
||||||
selabel_lookup
|
selabel_lookup
|
||||||
selabel_lookup_best_match
|
selabel_lookup_best_match
|
||||||
selabel_partial_match
|
selabel_partial_match
|
||||||
|
170
libselinux/utils/selabel_get_digests_all_partial_matches.c
Normal file
170
libselinux/utils/selabel_get_digests_all_partial_matches.c
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <fts.h>
|
||||||
|
#include <selinux/selinux.h>
|
||||||
|
#include <selinux/label.h>
|
||||||
|
|
||||||
|
#include "../src/label_file.h"
|
||||||
|
|
||||||
|
static __attribute__ ((__noreturn__)) void usage(const char *progname)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"usage: %s [-vr] [-f file] path\n\n"
|
||||||
|
"Where:\n\t"
|
||||||
|
"-v Validate file_contxts entries against loaded policy.\n\t"
|
||||||
|
"-r Recursively descend directories.\n\t"
|
||||||
|
"-f Optional file_contexts file (defaults to current policy).\n\t"
|
||||||
|
"path Path to check current SHA1 digest against file_contexts entries.\n\n"
|
||||||
|
"This will check the directory selinux.sehash SHA1 digest for "
|
||||||
|
"<path> against\na newly generated digest based on the "
|
||||||
|
"file_context entries for that node\n(using the regx, mode "
|
||||||
|
"and path entries).\n", progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int opt, fts_flags;
|
||||||
|
size_t i, digest_len;
|
||||||
|
bool status, recurse = false;
|
||||||
|
FTS *fts;
|
||||||
|
FTSENT *ftsent;
|
||||||
|
char *validate = NULL, *file = NULL;
|
||||||
|
char *paths[2] = { NULL, NULL };
|
||||||
|
uint8_t *xattr_digest = NULL;
|
||||||
|
uint8_t *calculated_digest = NULL;
|
||||||
|
char *sha1_buf = NULL;
|
||||||
|
|
||||||
|
struct selabel_handle *hnd;
|
||||||
|
struct selinux_opt selabel_option[] = {
|
||||||
|
{ SELABEL_OPT_PATH, file },
|
||||||
|
{ SELABEL_OPT_VALIDATE, validate }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
usage(argv[0]);
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "f:rv")) > 0) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'f':
|
||||||
|
file = optarg;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
recurse = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
validate = (char *)1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc) {
|
||||||
|
fprintf(stderr, "No pathname specified\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
paths[0] = argv[optind];
|
||||||
|
|
||||||
|
selabel_option[0].value = file;
|
||||||
|
selabel_option[1].value = validate;
|
||||||
|
|
||||||
|
hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 2);
|
||||||
|
if (!hnd) {
|
||||||
|
fprintf(stderr, "ERROR: selabel_open - Could not obtain "
|
||||||
|
"handle.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
|
||||||
|
fts = fts_open(paths, fts_flags, NULL);
|
||||||
|
if (!fts) {
|
||||||
|
printf("fts error on %s: %s\n",
|
||||||
|
paths[0], strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ftsent = fts_read(fts)) != NULL) {
|
||||||
|
switch (ftsent->fts_info) {
|
||||||
|
case FTS_DP:
|
||||||
|
continue;
|
||||||
|
case FTS_D: {
|
||||||
|
|
||||||
|
xattr_digest = NULL;
|
||||||
|
calculated_digest = NULL;
|
||||||
|
digest_len = 0;
|
||||||
|
|
||||||
|
status = selabel_get_digests_all_partial_matches(hnd,
|
||||||
|
ftsent->fts_path,
|
||||||
|
&calculated_digest,
|
||||||
|
&xattr_digest,
|
||||||
|
&digest_len);
|
||||||
|
|
||||||
|
sha1_buf = calloc(1, digest_len * 2 + 1);
|
||||||
|
if (!sha1_buf) {
|
||||||
|
fprintf(stderr, "Could not calloc buffer ERROR: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) { /* They match */
|
||||||
|
printf("xattr and file_contexts SHA1 digests match for: %s\n",
|
||||||
|
ftsent->fts_path);
|
||||||
|
|
||||||
|
if (calculated_digest) {
|
||||||
|
for (i = 0; i < digest_len; i++)
|
||||||
|
sprintf((&sha1_buf[i * 2]),
|
||||||
|
"%02x",
|
||||||
|
calculated_digest[i]);
|
||||||
|
printf("SHA1 digest: %s\n", sha1_buf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!calculated_digest) {
|
||||||
|
printf("No SHA1 digest available for: %s\n",
|
||||||
|
ftsent->fts_path);
|
||||||
|
printf("as file_context entry is \"<<none>>\"\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("The file_context entries for: %s\n",
|
||||||
|
ftsent->fts_path);
|
||||||
|
|
||||||
|
for (i = 0; i < digest_len; i++)
|
||||||
|
sprintf((&sha1_buf[i * 2]), "%02x",
|
||||||
|
calculated_digest[i]);
|
||||||
|
printf("generated SHA1 digest: %s\n", sha1_buf);
|
||||||
|
|
||||||
|
if (!xattr_digest) {
|
||||||
|
printf("however there is no selinux.sehash xattr entry.\n");
|
||||||
|
} else {
|
||||||
|
printf("however it does NOT match the current entry of:\n");
|
||||||
|
for (i = 0; i < digest_len; i++)
|
||||||
|
sprintf((&sha1_buf[i * 2]),
|
||||||
|
"%02x",
|
||||||
|
xattr_digest[i]);
|
||||||
|
printf("%s\n", sha1_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(xattr_digest);
|
||||||
|
free(calculated_digest);
|
||||||
|
free(sha1_buf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recurse)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) fts_close(fts);
|
||||||
|
(void) selabel_close(hnd);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user