Bug 1777831

Summary: chcon fails with "invalid context: ‘<context>’: No such file or directory"
Product: Red Hat Enterprise Linux 8 Reporter: Renaud Métrich <rmetrich>
Component: coreutilsAssignee: Kamil Dudka <kdudka>
Status: CLOSED ERRATA QA Contact: Radka Brychtova <rskvaril>
Severity: medium Docs Contact:
Priority: urgent    
Version: 8.1CC: dperpeet, dwalsh, fkrska, kdudka, lnykryn, lvrabec, mmalik, plautrba, ssekidde, vmojzis
Target Milestone: rcKeywords: Patch, ZStream
Target Release: 8.0   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: coreutils-8.30-7.el8 Doc Type: Bug Fix
Doc Text:
Cause: chcon validated the CONTEXT argument using API that does not work when SELinux is disabled. Consequence: If SELinux was disabled, chcon unnecessarily failed. Fix: chcon has been changed to only validate the CONTEXT argument if SELinux is enabled. Result: chcon now works even if SELinux is disabled.
Story Points: ---
Clone Of:
: 1796060 1822393 (view as bug list) Environment:
Last Closed: 2020-11-04 01:37:10 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 1681018    
Bug Blocks: 1796060, 1822393    

Description Renaud Métrich 2019-11-28 12:12:16 UTC
Description of problem:

When SELinux is disabled (e.g. selinux=0 on kernel command line), changing a context of a file succeeds or fails depending on which command option is used:

- chcon unconfined_u:object_r:usr_t <file> FAILS with "chcon: invalid context: ‘unconfined_u:object_r:usr_t’: No such file or directory" error message
- chcon -u unconfined_u -r object_r -t usr_t <file> SUCCEEDS  
- on RHEL7, both methods work succeed


Version-Release number of selected component (if applicable):

coreutils-8.30-6.el8.x86_64


How reproducible:

Always


Steps to Reproduce: see description above

Additional info:

stracing the command doesn't show anything problematic.

Comment 1 Renaud Métrich 2019-11-28 12:31:15 UTC
The issue is within libselinux:

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
 12 int security_check_context_raw(const char * con)
 13 {
 :
 22         if (!selinux_mnt) {
 23                 errno = ENOENT;
 24                 return -1;
 25         }
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

we have selinux_mnt == NULL

Comment 2 Milos Malik 2019-11-28 13:38:02 UTC
Does is work if you add "s0" to the context?

# chcon unconfined_u:object_r:usr_t:s0 <file>

Comment 3 Milos Malik 2019-11-28 13:40:30 UTC
I believe that chcon needs a complete context if all options are omitted.

Comment 4 Renaud Métrich 2019-11-28 13:49:22 UTC
Nope, doesn't work either.

Comment 5 Renaud Métrich 2019-11-28 14:17:28 UTC
Honestly I don't know if it's a libselinux issue or coreutils issue.

I see a difference in chcon.c RHEL7/RHEL8:

RHEL7:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
557     {
558       context_t context;
559       specified_context = argv[optind++];
560       context = context_new (specified_context);
561       if (!context)
562         error (EXIT_FAILURE, 0, _("invalid context: %s"),
563                quotearg_colon (specified_context));
564       context_free (context);
565     }
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

Line 561 always succeeds, even with SELinux disabled.

RHEL8:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
558     {
559       specified_context = argv[optind++];
560       if (security_check_context (se_const (specified_context)) < 0)
561         die (EXIT_FAILURE, errno, _("invalid context: %s"),
562              quote (specified_context));
563     }
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

Line 560 fails with SELinux disabled.

Comment 6 Renaud Métrich 2019-11-28 14:24:08 UTC
I would tend to see back to coreutils ...

Note anyway that this is some oddity: with SELinux disabled, you can set whatever label you want, even if it doesn't exist (assuming the file already exists of course)

Enabled:

# chcon -t boulet_t /tmp/foo
chcon: failed to change context of ‘/tmp/foo’ to ‘unconfined_u:object_r:boulet_t:s0’: Invalid argument


Disabled:

# chcon -t boulet_t /tmp/foo

--> no error

Comment 7 Renaud Métrich 2019-11-28 14:36:10 UTC
The code in coreutils was changed here:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
commit e4081e44e0dcc4bfe11d0da0ae47518df087349d
Author: Namhyung Kim <namhyung>
Date:   Tue Jul 1 00:12:41 2014 +0900

    chcon: use security_check_context() for context validation
    
    context_new() and _free() are used for checking validity of a
    specified context.  libselinux provides security_check_context
    for this purpose so use it.
    
    Note that context_new() can fail for a valid context - e.g. ENOMEM.
    
    * src/chcon.c (main): Use security_check_context().

---
 src/chcon.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/chcon.c b/src/chcon.c
index 32d4b0f..8c18167 100644
--- a/src/chcon.c
+++ b/src/chcon.c
@@ -555,13 +555,10 @@ main (int argc, char **argv)
     }
   else
     {
-      context_t context;
       specified_context = argv[optind++];
-      context = context_new (specified_context);
-      if (!context)
-        error (EXIT_FAILURE, 0, _("invalid context: %s"),
+      if (security_check_context (specified_context) < 0)
+        error (EXIT_FAILURE, errno, _("invalid context: %s"),
                quotearg_colon (specified_context));
-      context_free (context);
     }
 
   if (reference_file && component_specified)
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------


I'm tempted to say that it's not possible to chcon when SELinux is disabled, since it doesn't know which contexts are available (may depend on the policy used ...), hence I would consider "context_new()" not failing when SELinux is disabled is a bug.

Comment 8 Petr Lautrbach 2019-11-28 16:49:26 UTC
context_new() and other context_* routines are used for manipulating security context so that caller doesn't need to know internal implementation. It doesn't do any check. E.g. on SELinux enabled system, you can call context_new() on invalid context it will pass:

^&^ python3
Python 3.7.5 (default, Oct 17 2019, 12:16:48) 
[GCC 9.2.1 20190827 (Red Hat 9.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import selinux
>>> c = selinux.context_new("a:b:c:d")
>>> selinux.context_type_get(c)
'c'
>>> selinux.context_user_get(c)
'a'
>>> selinux.context_role_get(c)
'b'

So it's not a bug in context_new() that it's not failing when SELinux is disabled.

But security_check_context() can't work with disabled SELinux.

In this case, I'd say that chcon should not try to valide the context if SELinux is disabled.
It should be enough add another check before context validation:

    if (is_selinux_enabled && security_check_context (specified_context) < 0)

Comment 9 Petr Lautrbach 2019-11-28 16:50:26 UTC
if (is_selinux_enabled() && security_check_context (specified_context) < 0)

Comment 10 Petr Lautrbach 2019-11-28 17:00:10 UTC
And it looks like the same check is not applied when -u/-r/-t used. It just computes a new context based on given values and applies it.

I believe that the original behavior when you can set any context in SELinux disabled mode is correct.

Comment 13 Kamil Dudka 2019-12-02 13:22:25 UTC
Thank you for finding the cause and proposing a solution!  I have submitted it upstream for review:

https://lists.gnu.org/archive/html/coreutils/2019-12/msg00002.html

Comment 14 Renaud Métrich 2019-12-03 09:18:22 UTC
Credits to Petr :-)

Comment 33 errata-xmlrpc 2020-11-04 01:37:10 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory (coreutils bug fix and enhancement update), and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2020:4454