Bug 220487

Summary: LSPP: ssh not usable when labeled networking active
Product: Red Hat Enterprise Linux 5 Reporter: Klaus Weidner <kweidner>
Component: opensshAssignee: Tomas Mraz <tmraz>
Status: CLOSED CURRENTRELEASE QA Contact: Brian Brock <bbrock>
Severity: medium Docs Contact:
Priority: medium    
Version: 5.0CC: dwalsh, iboverma, james.antill, krisw, linda.knippers, mattdm, sgrubb
Target Milestone: ---   
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: RC Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2007-02-08 01:58:13 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
proposed openssh patch to support MLS networking
none
The patch tidied up
none
Another try on tidying up the patch
none
updated patch
none
pam_namespace patch
none
pam_selinux patch to handle new "use_current_range" option
none
openssh patch: if execcon already set, use that instead of recalculating it
none
Adjusted pam_selinux patch for use_current_range option
none
openssh patch: if run via xinetd and execcon already set, use that instead of calculating a new context
none
Another use_current_range patch adjustment
none
update to Tomas' latest use_current_range patch
none
Applied patch to openssh none

Description Klaus Weidner 2006-12-21 18:04:14 UTC
Description of problem:

When labeled networking is active, ssh is unusable at any level other than
SystemLow, for example when a "s2" process tries to do "ssh localhost". In part
this is due to missing policy rules that are easy to fix, but there are problems
that seem to require code changes to pam_selinux, sshd, or both.

When using standalone sshd, the initial TCP connection gets refused due to the
MLS constraints, and it would be inappropriate to give MLS network overrides to
sshd (that would be exploitable by clients using port forwarding). When using
xinetd to launch sshd, the sshd process is launched at the correct MLS level,
but then both sshd and "pam_selinux.so open" try (and fail) to set the MLS level
back to the user's default MLS range.

Tested using CIPSO, expect same behavior with labeled IPSEC.

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

RHEL5 snapshot3
selinux-policy-mls-2.4.6-15.el5
openssh-4.3p2-14.el5
pam-0.99.6.2-3.8.el5
kernel-2.6.18-1.2840.2.1.el5.lspp.57

How reproducible: always

Steps to Reproduce:
1. activate MLS policy in enforcing mode

2. activate CIPSO labeled networking:

 netlabelctl cipsov4 add pass doi:1 tags:1
 netlabelctl map del default
 netlabelctl map add default protocol:cipsov4,1

3. as appropriate, configure sshd via xinetd, and add policy to work around
denials (details below)

4. Create a user with clearance from SystemLow-SystemHigh

5. Log in on console and choose MLS level "s2"

6. While logged in at "s2" level, run: ssh localhost
  
Actual results: connection refused (standalone), or connection closed
prematurely (xinetd)


Expected results: successful login at "s2" level


Additional info:

I'm working on debugging and/or fixing this further, will update this bug when I
have more information.

Comment 2 Klaus Weidner 2006-12-22 00:32:53 UTC
Created attachment 144235 [details]
proposed openssh patch to support MLS networking

This patch adds a special case to the openssh code for selecting the SELinux
session context. If sshd is in xinetd mode (-i flag), get the MLS level of the
current process (as set by xinetd based on the incoming connection's level),
and combine that with the user's default context. Deny access if the level is
outside the user's permitted range.

Note that both sshd and a "pam_selinux.so open" session use setexeccon() to set
the executable context, with sshd overwriting the pam setting since it runs
last. (I first tried making this change in PAM's pam_selinux.c, but that didn't
work due to the double setting.)

In addition, sshd needs to set the context of the pty. If I understand it
correctly, the tty isn't available yet when the pam session is opened, but I
hadn't investigated this further. Ideally, the level selection code would be in
one place only...

Comment 3 Klaus Weidner 2006-12-22 00:43:06 UTC
*** /etc/xinetd.d/ssh-mls

# runs on port 2222, use "ssh -p 2222 user@localhost" to test
service ssh-mls
{
        flags           = LABELED
	type		= UNLISTED
	port		= 2222
        socket_type     = stream
        wait            = no
        user            = root
        group           = root
        server          = /usr/sbin/sshd
	server_args	= -i
        log_on_failure  += USERID
        disable         = no
}

*** Policy additions needed to allow sshd via xinetd to work

### sshd ##################################################

gen_require(`
        type sshd_t, unlabeled_t, staff_ssh_t, user_ssh_t, port_t;
')
kernel_tcp_recvfrom_unlabeled(sshd_t)
kernel_tcp_recvfrom_unlabeled(staff_ssh_t)
kernel_tcp_recvfrom_unlabeled(user_ssh_t)
allow staff_ssh_t port_t:tcp_socket name_connect;
allow user_ssh_t port_t:tcp_socket name_connect;

### xinetd ################################################

gen_require(`
        type inetd_t, bin_t, proc_t;
        type sshd_exec_t, sshd_t;
')

# xinetd needs MLS override privileges to work
mls_fd_use_all_levels(inetd_t)
mls_fd_share_all_levels(inetd_t)
mls_socket_read_to_clearance(inetd_t)
mls_process_set_level(inetd_t)

# miscellaneous xinetd fixes
allow inetd_t self:fd use;
allow inetd_t proc_t:file read;
kernel_read_system_state(inetd_t)
selinux_validate_context(inetd_t)
selinux_compute_create_context(inetd_t)
kernel_tcp_recvfrom_unlabeled(inetd_t)
allow inetd_t self:process { noatsecure rlimitinh setexec siginh transition };

### xinetd running sshd ###################################

# allow xinetd to transition to sshd_t via sshd_exec_t
allow inetd_t bin_t:file { entrypoint execute getattr read };
allow inetd_t sshd_exec_t:file { entrypoint execute getattr read };
type_transition inetd_t sshd_exec_t : process sshd_t;
domain_trans(inetd_t, sshd_exec_t, sshd_t)

# various interactions
allow sshd_t inetd_t:fd use;
allow sshd_t inetd_t:process sigchld;
allow sshd_t inetd_t:tcp_socket { getattr getopt ioctl read setopt write };


Comment 4 Klaus Weidner 2006-12-22 02:05:06 UTC
The latest lspp kickstart package contains the necessary policy and configuration:

  http://www.redhat.com/archives/redhat-lspp/2006-December/msg00112.html

I've tested with snapshot5 and that configuration, with the only additional
change being a rebuilt sshd using the proposed patch. I've tested standalone and
xinetd-launched modes, everything works for me as expected in enforcing mode
with no regressions.

An unsuccessful and succesful login, from /var/log/message:

Dec 20 23:49:01 rhel5lspp sshd[13464]: fatal: deny MLS level s3 (user range s0-s1)
Dec 20 23:49:14 rhel5lspp sshd[13480]: permit MLS level s3 (user range
s0-s15:c0.c1023)


Comment 5 Tomas Mraz 2006-12-22 08:41:33 UTC
Yes, pty is obtained in a child process after the session was already opened.


Comment 6 Tomas Mraz 2006-12-22 09:01:41 UTC
Created attachment 144260 [details]
The patch tidied up

I have tidied up the patch a little - it is not necessary to call the
get_default_context_with_level twice.

Comment 7 Tomas Mraz 2006-12-22 09:06:02 UTC
Comment on attachment 144260 [details]
The patch tidied up

Oops sorry I didn't notice that we need the default context.

Comment 8 Tomas Mraz 2006-12-22 09:28:59 UTC
Created attachment 144261 [details]
Another try on tidying up the patch

This time I've just added freecon for the user_context and I'm skipping the
inetd part when the get_default_context... already failed.

What about permissive mode? Shouldn't the fatal() calls be called only in
enforcing mode and skipped in permissive?

Comment 11 Klaus Weidner 2006-12-22 21:58:18 UTC
Created attachment 144314 [details]
updated patch

Based on Tomas' edited patch. As requested, replace the fatal() with a warning
in nonenforcing mode. Moved the context selection into a static utility
function to reduce duplication.

Comment 12 Klaus Weidner 2006-12-22 22:23:08 UTC
Unfortunately it appears to be insufficient to change only sshd...
Polyinstantiation sets the wrong level:

Dec 21 07:15:18 rhel5lspp sshd[2696]: pam_namespace(sshd:session): poly_name
system_u:object_r:tmp_t:SystemLow-SystemHigh_kw

This was supposed to be "s2" or "Secret"... The problem again seems to be that
the PAM session modules (including pam_namespace) get run before the selinux
code from sshd is executed.

pam_namespace chooses the polyinstantiation level based on a call to
getexeccon(), which retrieves the wrong value set by "pam_selinux.so open". So
even though both the starting and ending execcon are correct, the wrong
intermediate value messes it up. 

I tried disabling the pam_selinux session module, but then pam_namespace
polyinstantiates based on username instead of the current level:

Dec 21 07:15:47 rhel5lspp sshd[2737]: pam_namespace(sshd:session): poly_name kw

The choices seem to be:

- add an option to pam_namespace to use the current process level instead of the
execcon level for "level" polyinstantiation (it's not appropriate for full
context since that's likely to be different)

- update pam_selinux to set the right execcon, for example by adding
"copy_level" option. This involves some code duplication, probably the cleaner
solution would be to move the common parts into libselinux.

Any preferences? I think I'll try the first one since that seems easiest.


Comment 13 Tomas Mraz 2006-12-22 22:44:22 UTC
It seems to me, that fixing it in pam_selinux would be more appropriate so the
execcon is not switched back and forth between levels.

Also cannot the pam_selinux somehow detect (even without option) that it must
keep the level of the process because it of course may not switch to a lower
level. And return failure if the user is not allowed for this level.

Wouldn't fixing the context selection in pam_selinux mean that openssh could be
just changed to skip the setexeccon in inetd mode completely -> thus avoiding
code duplication?


Comment 14 Klaus Weidner 2006-12-22 22:45:51 UTC
Created attachment 144321 [details]
pam_namespace patch

here's the patch mentioned in the previous comment, it implements the
"use_current_level" parameter for pam_namespace.

Comment 15 Klaus Weidner 2006-12-23 03:38:52 UTC
Created attachment 144325 [details]
pam_selinux patch to handle new "use_current_range" option

Ok, here's the new alternative. I moved the level selection code to
pam_selinux, adding a new option "use_current_range" which tells it to combine
the user context with the process' current level.

Comment 16 Klaus Weidner 2006-12-23 03:45:58 UTC
Created attachment 144326 [details]
openssh patch: if execcon already set, use that instead of recalculating it

Simplify the selinux context selection in openssh - if there's already an
execcon set, just use that, and don't attempt to recalculate it. That context
will also be used to label the pty.

This doesn't yet support using a combination of pam_selinux to set the level,
and the builtin user/role@host syntax to set the role. I think the cleaner
approach for that would be to pass the desired role to pam_selinux and have it
take care of that, instead of duplicating the code in sshd.

Comment 17 Tomas Mraz 2006-12-23 09:27:54 UTC
(In reply to comment #16)
> This doesn't yet support using a combination of pam_selinux to set the level,
> and the builtin user/role@host syntax to set the role. I think the cleaner
> approach for that would be to pass the desired role to pam_selinux and have it
> take care of that, instead of duplicating the code in sshd.

I'm afraid that wouldn't be easy because currently openssh doesn't support PAM
conversation in pam_selinux and so there is currently no way how to pass the
desired role to pam_selinux from sshd.
We could modify the pam conversation function which is set for pam session
functions and send the role using some heuristics (detecting the prompt to
answer). By the way we could send a desired level this way too so pam_selinux
wouldn't have to be modified at all.

The question is whether this solution isn't even more "hackish" than having code
duplication in pam_selinux and sshd.


Comment 19 Tomas Mraz 2007-01-04 23:09:40 UTC
Created attachment 144865 [details]
Adjusted pam_selinux patch for use_current_range option

I've added audit call when the user is not allowed to this range. And made the
use_current_range disable select_context if both options are used.

Comment 20 Klaus Weidner 2007-01-04 23:56:58 UTC
Created attachment 144869 [details]
openssh patch: if run via xinetd and execcon already set, use that instead of calculating a new context

Updated version of the patch as discussed on IRC, to ensure that the behavior
of sshd stays unchanged when it's run standalone, and use the new context
selection mechanism only when run via xinetd.

Note that role selection via the "ssh user/role@host" mechanism does not appear
to work even without this patch, but that's a separate issue. Please file a
separate bug for that if necessary.

Comment 21 Tomas Mraz 2007-01-05 00:03:57 UTC
Created attachment 144870 [details]
Another use_current_range patch adjustment

The mls_range_allowed now takes two more arguments.

Comment 22 Klaus Weidner 2007-01-05 00:37:00 UTC
Created attachment 144876 [details]
update to Tomas' latest use_current_range patch

Based on attachment #144870 [details], and the latest select_context patch from bug
#220652 is again a prerequisite. 

The only change from Tomas' version of the patch is to use
"default_user_context" since "user_context" is NULL in this code path.

Comment 23 Klaus Weidner 2007-01-05 00:51:33 UTC
Here's a summary of testing the latest PAM patch (attachment #144876 [details]) and
openssh patch (attachment #144869 [details]).

standalone sshd, pam_selinux.so NOT active in /etc/pam.d/sshd (this is the
default in RHEL5): works for normal login.

standalone sshd, role selection using "ssh user/role@host" does not work, but it
also didn't work without the patch, so this appears unrelated.

standalone sshd, pam_selinux.so and pam_namespace.so active in /etc/pam.d/sshd
(this is the proposed evaluated config, see below for pam.d files): works, both
for labeled networking on and off, when logging in at SystemLow. Connection
rejected when labeled networking active and connection != SystemLow.

sshd via xinetd (see top of this bugzilla for config), labeled networking
active: works, both success case and deny case (when connection level outside
permitted range)

sshd via xinetd, labeled networking off: does not work, but this is expected
since xinetd can't assign a label and the LABELED flag is set.

In summary I think this now works as expected. 

#### /etc/pam.d/sshd used for these tests:
auth       include      system-auth
auth       required     pam_tally2.so deny=5 onerr=fail
account    required     pam_nologin.so
account    include      system-auth
account    required     pam_tally2.so
password   include      system-auth
session    required     pam_selinux.so close
session    include      system-auth
session    required     pam_loginuid.so require_auditd
session    required     pam_selinux.so open use_current_range verbose debug
session    required     pam_namespace.so

#### /etc/pam.d/system-auth for reference:
auth        required      pam_env.so
auth        required      pam_unix.so nullok try_first_pass
account     required      pam_unix.so
password    required      pam_passwdqc.so min=disabled,disabled,16,12,8 \
                          random=42
password    required      pam_unix.so nullok use_authtok md5 \
                          shadow remember=7
session     required      pam_limits.so
session     required      pam_unix.so


Comment 24 Tomas Mraz 2007-01-05 14:16:18 UTC
Patches applied to pam-0.99.6.2-3.12.el5 and openssh-4.3p2-15.el5


Comment 25 Tomas Mraz 2007-01-11 16:45:38 UTC
Back to ASSIGNED as we need a level selection in case of unlabeled networking.
So the SELinux support in openssh still has to be reworked.


Comment 26 Tomas Mraz 2007-01-12 07:55:44 UTC
New fix applied in openssh-4.3p2-16.el5


Comment 27 Tomas Mraz 2007-01-12 08:02:56 UTC
Created attachment 145433 [details]
Applied patch to openssh

This is the applied patch. With this patch to openssh it should work fine
either with labeled or unlabeled networking. Role selection is also supported
in both situations. Level selection (using user/role/level@host) is supported
on unlabeled network, otherwise the level is overriden by the current level the
sshd is running in.

pam_selinux MUST NOT be used in the pam configuration for sshd.

Comment 28 RHEL Program Management 2007-02-08 01:58:13 UTC
A package has been built which should help the problem described in 
this bug report. This report is therefore being closed with a resolution 
of CURRENTRELEASE. You may reopen this bug report if the solution does 
not work for you.


Comment 29 Klaus Weidner 2007-02-19 17:38:31 UTC
Retested with the latest package openssh-4.3p2-17.el5.i386.rpm from
http://people.redhat.com/dwalsh/SELinux/RHEL5/ - in summary, I can confirm that
everything works as expected. 

As required, not using pam_selinux in the PAM configuration for sshd for any of
these tests.

standalone sshd, no labeled networking: works for normal login, including role
and level selection using "ssh user/role/level@host".

standalone sshd, labeled networking on: works same as without labeled
networking, including selecting levels. Since this is NOT desirable for MLS/LSPP
operations, the evaluated config guidance will tell admins to disable standalone
sshd when using labeled networking.

sshd via xinetd (see top of this bugzilla for config), labeled networking
active: works, both success case and deny case (when connection level outside
permitted range). user/role/level@host may be supplied, role is supported (not
fully tested), the level is ignored and overridden by the connection level.

sshd via xinetd, labeled networking off: does not work
(ssh_exchange_identification: Connection closed by remote host), but this is
expected since xinetd can't assign a label and the LABELED flag is set.

/etc/pam.d/ssh used for the test:

auth       include      system-auth
auth       required     pam_tally2.so deny=5 onerr=fail

account    required     pam_nologin.so
account    include      system-auth
account    required     pam_tally2.so

password   include      system-auth

session    required     pam_selinux.so close
session    include      system-auth
session    required     pam_loginuid.so require_auditd
session    required     pam_namespace.so debug # FIXME, remove debug

Comment 30 Matthew Miller 2007-08-06 03:04:11 UTC
Note that as written, the current specfile will not build with the SELINUX flag
set to 0 -- this patch fails. Should probably be if'd out in that case.