From patchwork Mon Sep 26 15:29:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Haines X-Patchwork-Id: 9350873 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 956B96077B for ; Mon, 26 Sep 2016 15:32:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8566728821 for ; Mon, 26 Sep 2016 15:32:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 78E4528D51; Mon, 26 Sep 2016 15:32:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from emsm-gh1-uea11.nsa.gov (emsm-gh1-uea11.nsa.gov [8.44.101.9]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0B38E28821 for ; Mon, 26 Sep 2016 15:32:11 +0000 (UTC) X-IronPort-AV: E=Sophos;i="5.30,400,1470700800"; d="scan'208";a="19550412" IronPort-PHdr: =?us-ascii?q?9a23=3AG3lGBhNF35HoP7xomfAl6mtUPXoX/o7sNwtQ0KIM?= =?us-ascii?q?zox0KPr5rarrMEGX3/hxlliBBdydsKMezbON+PC5EUU7or+5+EgYd5JNUxJXwe?= =?us-ascii?q?43pCcHRPC/NEvgMfTxZDY7FskRHHVs/nW8LFQHUJ2mPw6anHS+4HYoFwnlMkIt?= =?us-ascii?q?f6KuS9SU1p/8h7n60qaQSj0AvCC6b7J2IUf+hiTqne5Sv7FfLL0swADCuHpCdr?= =?us-ascii?q?ce72ppIVWOg0S0vZ/or9Ze6SAYh9YNv44FCP27LOwESulDATAnNX0lzNH6vhnE?= =?us-ascii?q?Cw2U7z0TVXtFvABPBl3/7Rr6V439+gvzt+xww2HOJ8z9TbkuVQOp2KdiSRnlkw?= =?us-ascii?q?8NKz8/7GzNjMFsyqlcpUTy9FRE34fIbdTNZ7JFdaTHcIZfHDJM?= X-IPAS-Result: =?us-ascii?q?A2H5AgDLPulX/wHyM5BdHAEBBAEBCgEBGAEFAQsBgxIBAQE?= =?us-ascii?q?BAR6BU6VElQE2JIYFgWBMAQEBAQEBAQECAQJbJ4IyBAMTBQU5CjIBAQEBAQEBA?= =?us-ascii?q?QEBAQEBAQEaAghIAQEhAiQTBgENIAwCAwkCBRIpCAgDAS0VHwsFGASIDwEDFwS?= =?us-ascii?q?2RQWBAoRaglcDCIQniSuBTxEBNYVFBYgvhgWBOooIj2qBbIgPDIViSIZBhWKDf?= =?us-ascii?q?FSCSQwBNQ4cgVFxhUd4gScBAQE?= Received: from unknown (HELO tarius.tycho.ncsc.mil) ([144.51.242.1]) by emsm-gh1-uea11.nsa.gov with ESMTP; 26 Sep 2016 15:31:57 +0000 Received: from prometheus.infosec.tycho.ncsc.mil (prometheus [192.168.25.40]) by tarius.tycho.ncsc.mil (8.14.4/8.14.4) with ESMTP id u8QFVqIn000780; Mon, 26 Sep 2016 11:31:54 -0400 Received: from tarius.tycho.ncsc.mil (tarius.infosec.tycho.ncsc.mil [144.51.242.1]) by prometheus.infosec.tycho.ncsc.mil (8.15.2/8.15.2) with ESMTP id u8QFUBTr253084 for ; Mon, 26 Sep 2016 11:30:11 -0400 Received: from goalie.tycho.ncsc.mil (goalie [144.51.242.250]) by tarius.tycho.ncsc.mil (8.14.4/8.14.4) with ESMTP id u8QFUAgj000683 for ; Mon, 26 Sep 2016 11:30:10 -0400 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A1CrAAAiPulXh38AFEFdHAEBBAEBCgEBgz0BAQEBAYFxpTAEEJEvhBOGHoFgTAECAQEBAQECEwEBAQoLCQkZhRQqGQE4ARWBO4gwAQMXBLZFBYEChFqCMSYDCIQniSuESQuDBwWIL4YFgTqKCI9qgWyIG4ViSIZBhWKDfIMdQg4RC4FRcYdmAQEB X-IPAS-Result: A1CrAAAiPulXh38AFEFdHAEBBAEBCgEBgz0BAQEBAYFxpTAEEJEvhBOGHoFgTAECAQEBAQECEwEBAQoLCQkZhRQqGQE4ARWBO4gwAQMXBLZFBYEChFqCMSYDCIQniSuESQuDBwWIL4YFgTqKCI9qgWyIG4ViSIZBhWKDfIMdQg4RC4FRcYdmAQEB X-IronPort-AV: E=Sophos;i="5.30,400,1470715200"; d="scan'208";a="5728971" Received: from emsm-gh1-uea10.corp.nsa.gov (HELO emsm-gh1-uea10.nsa.gov) ([10.208.41.36]) by goalie.tycho.ncsc.mil with ESMTP; 26 Sep 2016 11:30:09 -0400 IronPort-PHdr: =?us-ascii?q?9a23=3AXY2znxL48PzM4TURS9mcpTZWNBhigK39O0sv0rFi?= =?us-ascii?q?tYgULvvxwZ3uMQTl6Ol3ixeRBMOAuqgC0rGd4/mocFdDyK7JiGoFfp1IWk1Nou?= =?us-ascii?q?QttCtkPvS4D1bmJuXhdS0wEZcKflZk+3amLRodQ56mNBXsq3G/pQQfBg/4fVIs?= =?us-ascii?q?YL+kQMiL1I/njqibwN76W01wnj2zYLd/fl2djD76kY0ou7ZkMbs70RDTo3FFKK?= =?us-ascii?q?x8zGJsIk+PzV6nvp/jtM0rzyMFoP8l9shdQY3mbq84SvpeFz1gPGcrt+PxshyW?= =?us-ascii?q?dwqE5nIGXi02mxtODhONuAv7VZf4qCfNvd190SicMNbeR6w1Xyiv9aFmVFnjjy?= =?us-ascii?q?JRZG1xy33elsEl1PETmxmmvREqm4M=3D?= X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: =?us-ascii?q?A0EaAQCLPulXh38AFEFdHAEBBAEBCgEBG?= =?us-ascii?q?AEFAQsBgxIBAQEBAYFxpTAEEJEvg1JBhh6BYEwBAQEBAQEBAQIBAhABAQEKCwk?= =?us-ascii?q?JGS+CMhgMOQoyAQEBAQEBAQEBAQEBAQEBGgIISAEBHSoZATgBFYE7iDABAxcEt?= =?us-ascii?q?kcFgQKEWoIxJgMIhCeJK4RJC4MHBYgvhgWBOooIj2qBbIgbhWJIhkGFYoN8gx1?= =?us-ascii?q?CDhELgVFxh2YBAQE?= X-IPAS-Result: =?us-ascii?q?A0EaAQCLPulXh38AFEFdHAEBBAEBCgEBGAEFAQsBgxIBAQE?= =?us-ascii?q?BAYFxpTAEEJEvg1JBhh6BYEwBAQEBAQEBAQIBAhABAQEKCwkJGS+CMhgMOQoyA?= =?us-ascii?q?QEBAQEBAQEBAQEBAQEBGgIISAEBHSoZATgBFYE7iDABAxcEtkcFgQKEWoIxJgM?= =?us-ascii?q?IhCeJK4RJC4MHBYgvhgWBOooIj2qBbIgbhWJIhkGFYoN8gx1CDhELgVFxh2YBA?= =?us-ascii?q?QE?= X-IronPort-AV: E=Sophos;i="5.30,400,1470700800"; d="scan'208";a="17967132" Received: from rgout0107.bt.lon5.cpcloud.co.uk ([65.20.0.127]) by emsm-gh1-uea10.nsa.gov with ESMTP; 26 Sep 2016 15:30:05 +0000 X-OWM-Source-IP: 81.132.47.164 (GB) X-OWM-Env-Sender: richard_c_haines@btinternet.com X-Junkmail-Premium-Raw: score=8/50, refid=2.7.2:2016.9.26.140317:17:8.129, ip=81.132.47.164, rules=__HAS_FROM, __FRAUD_WEBMAIL_FROM, __TO_MALFORMED_2, __TO_NO_NAME, __HAS_CC_HDR, __CC_NAME, __CC_NAME_DIFF_FROM_ACC, __SUBJ_ALPHA_END, __HAS_MSGID, __SANE_MSGID, __HAS_X_MAILER, __FROM_DOMAIN_IN_ANY_CC1, __ANY_URI, __FRAUD_BODY_WEBMAIL, __URI_NO_WWW, __LINES_OF_YELLING, BODY_SIZE_10000_PLUS, __MIME_TEXT_P1, __MIME_TEXT_ONLY, LINES_OF_YELLING_3, RDNS_GENERIC_POOLED, HTML_00_01, HTML_00_10, RDNS_SUSP_GENERIC, __FRAUD_WEBMAIL, __PHISH_SPEAR_STRUCTURE_1, __FROM_DOMAIN_IN_RCPT, RDNS_SUSP, __MIME_TEXT_P, NO_URI_HTTPS, __CC_REAL_NAMES Received: from localhost.localdomain (81.132.47.164) by rgout01.bt.lon5.cpcloud.co.uk (8.6.122.06) (authenticated as richard_c_haines@btinternet.com) id 57E90F4000068E7D; Mon, 26 Sep 2016 16:30:02 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=btinternet.com; s=btcpcloud; t=1474903806; bh=IX0imdeyZoFTL3qHsoEFkkSPztRNoju0kgfz8YP0VWo=; h=From:To:Cc:Subject:Date:Message-Id:X-Mailer; b=FFxkaIFNbpV3yWRirlB8dMg279nzfdXSiLXqBCITR2UdtT0LgohvlETC4XhWTjz1X4pvM5AkPS3A2LsgZkB1dCgnmBQNRvwvxwvIrjXtbmOMpcbanEvcT/zC32GOvmHTwUiR4kJOHkDXOuuYC3l5Lnt1KdzFZlsNRjFS9q8daO0= From: Richard Haines To: selinux@tycho.nsa.gov Subject: [PATCH 1/2] libselinux: Add function to find security.restorecon_last entries Date: Mon, 26 Sep 2016 16:29:56 +0100 Message-Id: <1474903796-4991-1-git-send-email-richard_c_haines@btinternet.com> X-Mailer: git-send-email 2.7.4 X-BeenThere: selinux@tycho.nsa.gov X-Mailman-Version: 2.1.20 Precedence: list List-Id: "Security-Enhanced Linux \(SELinux\) mailing list" List-Post: List-Help: MIME-Version: 1.0 Errors-To: selinux-bounces@tycho.nsa.gov Sender: "Selinux" X-Virus-Scanned: ClamAV using ClamSMTP This patch adds a new selinux_restorecon_xattr(3) function to find and/or remove security.restorecon_last entries added by setfiles(8) or restorecon(8). Also review and update the man pages. Signed-off-by: Richard Haines --- libselinux/include/selinux/restorecon.h | 48 +++++ libselinux/man/man3/selinux_restorecon.3 | 2 + .../man/man3/selinux_restorecon_default_handle.3 | 6 +- .../man/man3/selinux_restorecon_set_alt_rootpath.3 | 4 +- .../man/man3/selinux_restorecon_set_exclude_list.3 | 4 +- .../man/man3/selinux_restorecon_set_sehandle.3 | 9 +- libselinux/man/man3/selinux_restorecon_xattr.3 | 170 +++++++++++++++++ libselinux/src/selinux_restorecon.c | 207 +++++++++++++++++++++ 8 files changed, 441 insertions(+), 9 deletions(-) create mode 100644 libselinux/man/man3/selinux_restorecon_xattr.3 diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h index e6db8f9..7cfdee1 100644 --- a/libselinux/include/selinux/restorecon.h +++ b/libselinux/include/selinux/restorecon.h @@ -128,6 +128,54 @@ extern void selinux_restorecon_set_exclude_list(const char **exclude_list); */ extern int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath); +/** + * selinux_restorecon_xattr - Read/remove RESTORECON_LAST xattr entries. + * @pathname: specifies directory path to check. + * @xattr_flags: specifies the actions to be performed. + * @xattr_list: a linked list of struct dir_xattr structures containing + * the directory, digest and result of the action on the + * RESTORECON_LAST entry. + * + * selinux_restorecon_xattr(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), however note that a file_contexts + * computed digest is required for selinux_restorecon_xattr(). + */ +enum digest_result { + MATCH = 0, + NOMATCH, + DELETED_MATCH, + DELETED_NOMATCH, + ERROR +}; + +struct dir_xattr { + char *directory; + char *digest; /* A hex encoded string that can be printed. */ + enum digest_result result; + struct dir_xattr *next; +}; + +extern int selinux_restorecon_xattr(const char *pathname, + unsigned int xattr_flags, + struct dir_xattr ***xattr_list); + +/* + * xattr_flags options + */ +/* Recursively descend directories. */ +#define SELINUX_RESTORECON_XATTR_RECURSE 0x0001 +/* Delete non-matching digests from each directory in pathname. */ +#define SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS 0x0002 +/* Delete all digests found in pathname. */ +#define SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS 0x0004 +/* Do not read /proc/mounts. */ +#define SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS 0x0008 + #ifdef __cplusplus } #endif diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3 index ad8acdc..2d8274b 100644 --- a/libselinux/man/man3/selinux_restorecon.3 +++ b/libselinux/man/man3/selinux_restorecon.3 @@ -239,4 +239,6 @@ option. .br .BR selinux_restorecon_set_alt_rootpath (3), .br +.BR selinux_restorecon_xattr (3), +.br .BR selinux_set_callback (3) diff --git a/libselinux/man/man3/selinux_restorecon_default_handle.3 b/libselinux/man/man3/selinux_restorecon_default_handle.3 index 0f1e737..52f5ad3 100644 --- a/libselinux/man/man3/selinux_restorecon_default_handle.3 +++ b/libselinux/man/man3/selinux_restorecon_default_handle.3 @@ -56,4 +56,8 @@ is set appropriately. .br .BR selinux_restorecon_set_sehandle (3), .br -.BR selinux_restorecon_set_exclude_list (3) +.BR selinux_restorecon_set_exclude_list (3), +.br +.BR selinux_restorecon_set_alt_rootpath (3), +.br +.BR selinux_restorecon_xattr (3) diff --git a/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 index 6a33421..13a804a 100644 --- a/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 +++ b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 @@ -32,4 +32,6 @@ is set appropriately. .br .BR selinux_restorecon_default_handle (3), .br -.BR selinux_restorecon_set_exclude_list (3) +.BR selinux_restorecon_set_exclude_list (3), +.br +.BR selinux_restorecon_xattr (3) diff --git a/libselinux/man/man3/selinux_restorecon_set_exclude_list.3 b/libselinux/man/man3/selinux_restorecon_set_exclude_list.3 index 20c9d8d..373deec 100644 --- a/libselinux/man/man3/selinux_restorecon_set_exclude_list.3 +++ b/libselinux/man/man3/selinux_restorecon_set_exclude_list.3 @@ -30,4 +30,6 @@ must be called prior to .br .BR selinux_restorecon_default_handle (3), .br -.BR selinux_restorecon_set_alt_rootpath (3) +.BR selinux_restorecon_set_alt_rootpath (3), +.br +.BR selinux_restorecon_xattr (3) diff --git a/libselinux/man/man3/selinux_restorecon_set_sehandle.3 b/libselinux/man/man3/selinux_restorecon_set_sehandle.3 index 30e0ad5..978945e 100644 --- a/libselinux/man/man3/selinux_restorecon_set_sehandle.3 +++ b/libselinux/man/man3/selinux_restorecon_set_sehandle.3 @@ -25,11 +25,6 @@ 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), @@ -38,4 +33,6 @@ digest and a list of specfiles used to compute the digest. .br .BR selinux_restorecon_default_handle (3), .br -.BR selinux_restorecon_set_alt_rootpath (3) +.BR selinux_restorecon_set_alt_rootpath (3), +.br +.BR selinux_restorecon_xattr (3) diff --git a/libselinux/man/man3/selinux_restorecon_xattr.3 b/libselinux/man/man3/selinux_restorecon_xattr.3 new file mode 100644 index 0000000..2f03f8f --- /dev/null +++ b/libselinux/man/man3/selinux_restorecon_xattr.3 @@ -0,0 +1,170 @@ +.TH "selinux_restorecon_xattr" "3" "30 July 2016" "" "SELinux API documentation" + +.SH "NAME" +selinux_restorecon_xattr \- manage default +.I security.restorecon_last +extended attribute entries added by +.BR selinux_restorecon (3), +.BR setfiles (8) +or +.BR restorecon (8). + +.SH "SYNOPSIS" +.B #include +.sp +.BI "int selinux_restorecon_xattr(const char *" pathname , +.in +\w'int selinux_restorecon('u +.br +.BI "unsigned int " xattr_flags , +.br +.BI "struct dir_xattr ***" xattr_list ");" +.in +. +.SH "DESCRIPTION" +.BR selinux_restorecon_xattr () +returns a linked list of +.B dir_xattr +structures containing information described below based on: +.sp +.RS +.IR pathname +containing a directory tree to be searched for +.I security.restorecon_last +extended attribute entries. +.sp +.IR xattr_flags +contains options as follows: +.sp +.RS +.sp +.B SELINUX_RESTORECON_XATTR_RECURSE +recursively descend directories. +.sp +.B SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS +delete non-matching digests from each directory in +.IR pathname . +.sp +.B SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS +delete all digests from each directory in +.IR pathname . +.sp +.B SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS +do not read +.B /proc/mounts +to obtain a list of non-seclabel mounts to be excluded from the search. +.br +Setting +.B SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS +is useful where there is a non-seclabel fs mounted with a seclabel fs mounted +on a directory below this. +.RE +.sp +.I xattr_list +is the returned pointer to a linked list of +.B dir_xattr +structures, each containing the following information: +.sp +.RS +.ta 4n 16n 24n +.nf +struct dir_xattr { + char *directory; + char *digest; /* Printable hex encoded string */ + enum digest_result result; + struct dir_xattr *next; +}; +.fi +.ta +.RE +.sp +The +.B result +entry is enumerated as follows: +.RS +.ta 4n 16n 24n +.nf +enum digest_result { + MATCH = 0, + NOMATCH, + DELETED_MATCH, + DELETED_NOMATCH, + ERROR +}; +.fi +.ta +.RE +.sp +.I xattr_list +must be set to +.B NULL +before calling +.BR selinux_restorecon_xattr (3). +The caller is responsible for freeing the returned +.I xattr_list +entries in the linked list. +.RE +.sp +See the +.B NOTES +section for more information. + +.SH "RETURN VALUE" +On success, zero is returned. On error, \-1 is returned and +.I errno +is set appropriately. + +.SH "NOTES" +.IP "1." 4 +By default +.BR selinux_restorecon_xattr (3) +will use the default set of specfiles described in +.BR files_contexts (5) +to calculate the initial SHA1 digest to be used for comparision. +To change this default behavior +.BR selabel_open (3) +must be called specifying the required +.B SELABEL_OPT_PATH +and setting the +.B SELABEL_OPT_DIGEST +option to a non-NULL value. +.BR selinux_restorecon_set_sehandle (3) +is then called to set the handle to be used by +.BR selinux_restorecon_xattr (3). +.IP "2." 4 +By default +.BR selinux_restorecon_xattr (3) +reads +.B /proc/mounts +to obtain a list of non-seclabel mounts to be excluded from searches unless the +.B SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS +flag has been set. +.IP "3." 4 +.B RAMFS +and +.B TMPFS +filesystems do not support the +.IR security.restorecon_last +extended attribute and are automatically excluded from searches. +.IP "4." 4 +By default +.B stderr +is used to log output messages and errors. This may be changed by calling +.BR selinux_set_callback (3) +with the +.B SELINUX_CB_LOG +.I type +option. + +.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), +.br +.BR selinux_restorecon_set_alt_rootpath (3), +.br +.BR selinux_set_callback (3) + diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c index 0d3e4d5..0945138 100644 --- a/libselinux/src/selinux_restorecon.c +++ b/libselinux/src/selinux_restorecon.c @@ -64,6 +64,10 @@ static struct edir *exclude_lst = NULL; static uint64_t fc_count = 0; /* Number of files processed so far */ static uint64_t efile_count; /* Estimated total number of files */ +/* Store information on directories with xattr's. */ +struct dir_xattr *dir_xattr_list; +static struct dir_xattr *dir_xattr_last; + /* * If SELINUX_RESTORECON_PROGRESS is set and mass_relabel = true, then * output approx % complete, else output * for every STAR_COUNT files @@ -292,6 +296,90 @@ static int exclude_non_seclabel_mounts(void) return nfile * 1.05; } +/* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */ +static int add_xattr_entry(const char *directory, bool delete_nonmatch, + bool delete_all) +{ + char *sha1_buf = NULL; + unsigned char *xattr_value = NULL; + ssize_t xattr_size; + size_t i; + int rc, digest_result; + struct dir_xattr *new_entry; + + if (!directory) { + errno = EINVAL; + return -1; + } + + xattr_value = malloc(fc_digest_len); + if (!xattr_value) + goto oom; + + xattr_size = getxattr(directory, RESTORECON_LAST, xattr_value, + fc_digest_len); + if (xattr_size < 0) { + free(xattr_value); + return 1; + } + + /* Convert entry to a hex encoded string. */ + sha1_buf = malloc(xattr_size * 2 + 1); + if (!sha1_buf) { + free(xattr_value); + goto oom; + } + + for (i = 0; i < (size_t)xattr_size; i++) + sprintf((&sha1_buf[i * 2]), "%02x", xattr_value[i]); + + rc = memcmp(fc_digest, xattr_value, fc_digest_len); + digest_result = rc ? NOMATCH : MATCH; + + if ((delete_nonmatch && rc != 0) || delete_all) { + digest_result = rc ? DELETED_NOMATCH : DELETED_MATCH; + rc = removexattr(directory, RESTORECON_LAST); + if (rc) { + selinux_log(SELINUX_ERROR, + "Error: %s removing xattr \"%s\" from: %s\n", + strerror(errno), RESTORECON_LAST, directory); + digest_result = ERROR; + } + } + free(xattr_value); + + /* Now add entries to link list. */ + new_entry = malloc(sizeof(struct dir_xattr)); + if (!new_entry) + goto oom; + new_entry->next = NULL; + + new_entry->directory = strdup(directory); + if (!new_entry->directory) + goto oom; + + new_entry->digest = strdup(sha1_buf); + if (!new_entry->digest) + goto oom; + + new_entry->result = digest_result; + + if (!dir_xattr_list) { + dir_xattr_list = new_entry; + dir_xattr_last = new_entry; + } else { + dir_xattr_last->next = new_entry; + dir_xattr_last = new_entry; + } + + free(sha1_buf); + return 0; + +oom: + selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); + return -1; +} + /* * Support filespec services filespec_add(), filespec_eval() and * filespec_destroy(). @@ -1028,3 +1116,122 @@ int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath) return 0; } + +/* selinux_restorecon_xattr(3) - Find RESTORECON_LAST entries. */ +int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags, + struct dir_xattr ***xattr_list) +{ + bool recurse = (xattr_flags & + SELINUX_RESTORECON_XATTR_RECURSE) ? true : false; + bool delete_nonmatch = (xattr_flags & + SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false; + bool delete_all = (xattr_flags & + SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false; + ignore_mounts = (xattr_flags & + SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false; + + int rc, fts_flags; + struct stat sb; + struct statfs sfsb; + struct dir_xattr *current, *next; + FTS *fts; + FTSENT *ftsent; + char *paths[2] = { NULL, NULL }; + + __selinux_once(fc_once, restorecon_init); + + if (!fc_sehandle || !fc_digest_len) + return -1; + + if (lstat(pathname, &sb) < 0) { + if (errno == ENOENT) + return 0; + + selinux_log(SELINUX_ERROR, + "lstat(%s) failed: %s\n", + pathname, strerror(errno)); + return -1; + } + + if (!recurse) { + if (statfs(pathname, &sfsb) == 0) { + if (sfsb.f_type == RAMFS_MAGIC || + sfsb.f_type == TMPFS_MAGIC) + return 0; + } + + if (check_excluded(pathname)) + return 0; + + rc = add_xattr_entry(pathname, delete_nonmatch, delete_all); + + if (!rc && dir_xattr_list) + *xattr_list = &dir_xattr_list; + else if (rc == -1) + return rc; + + return 0; + } + + paths[0] = (char *)pathname; + fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; + + fts = fts_open(paths, fts_flags, NULL); + if (!fts) { + selinux_log(SELINUX_ERROR, + "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: + if (statfs(ftsent->fts_path, &sfsb) == 0) { + if (sfsb.f_type == RAMFS_MAGIC || + sfsb.f_type == TMPFS_MAGIC) + continue; + } + if (check_excluded(ftsent->fts_path)) { + fts_set(fts, ftsent, FTS_SKIP); + continue; + } + + rc = add_xattr_entry(ftsent->fts_path, + delete_nonmatch, delete_all); + if (rc == 1) + continue; + else if (rc == -1) + goto cleanup; + break; + default: + break; + } + } + + if (dir_xattr_list) + *xattr_list = &dir_xattr_list; + + (void) fts_close(fts); + return 0; + +cleanup: + rc = errno; + (void) fts_close(fts); + errno = rc; + + if (dir_xattr_list) { + /* Free any used memory */ + current = dir_xattr_list; + while (current) { + next = current->next; + free(current->directory); + free(current->digest); + free(current); + current = next; + } + } + return -1; +}