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.
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...
*** /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 };
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)
Yes, pty is obtained in a child process after the session was already opened.
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 on attachment 144260 [details] The patch tidied up Oops sorry I didn't notice that we need the default context.
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?
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.
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.
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?
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.
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.
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.
(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.
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.
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.
Created attachment 144870 [details] Another use_current_range patch adjustment The mls_range_allowed now takes two more arguments.
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.
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
Patches applied to pam-0.99.6.2-3.12.el5 and openssh-4.3p2-15.el5
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.
New fix applied in openssh-4.3p2-16.el5
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.
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.
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
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.