Bug 225360
| Summary: | Sudo can hang with SELinux because sesh lacks signal handling | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Red Hat Enterprise Linux 4 | Reporter: | Richard Basch <redhat> | ||||
| Component: | sudo | Assignee: | Daniel Kopeček <dkopecek> | ||||
| Status: | CLOSED WONTFIX | QA Contact: | |||||
| Severity: | medium | Docs Contact: | |||||
| Priority: | medium | ||||||
| Version: | 4.0 | CC: | jiml, sgrubb | ||||
| Target Milestone: | --- | ||||||
| Target Release: | --- | ||||||
| Hardware: | i386 | ||||||
| OS: | Linux | ||||||
| Whiteboard: | |||||||
| Fixed In Version: | Doc Type: | Bug Fix | |||||
| Doc Text: | Story Points: | --- | |||||
| Clone Of: | Environment: | ||||||
| Last Closed: | 2012-06-20 13:23:07 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: |
|
||||||
Created attachment 149466 [details]
another patch proposed by sgrubb
The only issue I see with the proposed patch is the need to pass on signals to the child... I haven't actually tried to apply the patch, but I am not sure the child will resume. Scenario: $ sudo bash # suspend $ fg While I agree the new patch should handle the suspend properly, will it handle the re-foregrounding properly? Not only does sesh have to catch the SIGTSTP or SIGSTOP that was fielded by the child, it also has to resume the child with a SIGCONT after it is resumed. Therefore, the logic for sesh should be: 1. check child status (stopped or exited) -- following steps if child is stopped 2. raise stop signal to myself (sesh) 3. send CONT signal to child (will only be reached if I am continued) The scenario above was tested and it worked as expected. This request was evaluated by Red Hat Product Management for inclusion, but this component is not scheduled to be updated in the current Red Hat Enterprise Linux release. If you would like this request to be reviewed for the next minor release, ask your support representative to set the next rhel-x.y flag to "?". This is slightly annoying and we'd like it to be fixed. It hits us on redhat es4.7 servers with SeLinux either enforcing or permissive. Sending both the sesh and the child bash process SIGTSTP to suspend and SIGCONT to resume is a kludgy workaround. Thank you for submitting this issue for consideration in Red Hat Enterprise Linux. The release for which you requested us to review is now End of Life. Please See https://access.redhat.com/support/policy/updates/errata/ If you would like Red Hat to re-consider your feature request for an active release, please re-open the request via appropriate support channels and provide additional supporting details about the importance of this issue. |
From Bugzilla Helper: User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) Description of problem: When SELinux is enabled, sudo invokes "sesh". However, because "sesh" lacks some signal handling, it can hang. If SELinux is enabled (even in permissive mode), the following will cause sudo to apparently hang: $ sudo bash # suspend This hang occurs because bash will send a SIGTSTP to itself, but sesh (which is invoked by sudo when SELinux is enabled) only looks for termination signals, not all signals of its children. The following patch seems to remediate the problem: # cat /usr/src/redhat/SOURCES/sudo-1.6.7p5-sesh-stopsig.patch --- sudo-1.6.7p5/sesh.c.sigstop 2005-09-19 17:38:15.000000000 +0200 +++ sudo-1.6.7p5/sesh.c 2005-09-19 17:41:48.000000000 +0200 @@ -1,4 +1,5 @@ #include <stdio.h> +#include <stdlib.h> #include <unistd.h> #include <limits.h> #include <sys/types.h> @@ -22,19 +23,33 @@ int status; int ret; - do { - if ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) + while (1) { + if ((ret = waitpid(pid, &status, WUNTRACED)) < 0 && errno == EINTR) continue; else if (ret < 0) { perror("waitpid failed"); exit(1); } - } while (0); + + if (!WIFSTOPPED(status)) + break; + + /* Reset the handler in case it was inherited ignored, + but the child reset it and stopped anyway. */ + signal(WSTOPSIG(status), SIG_DFL); + raise(WSTOPSIG(status)); + + /* Now we stop until continued ourselves. */ + kill(getpgid(pid) == pid ? -pid : pid, SIGCONT); + } if (WIFEXITED(status)) exit(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + /* XXX print here like the shell would? */ + exit(128 + WTERMSIG(status)); else - exit(1); + exit(127); /* Should never happen. */ } else { /* Child */ execv(argv[1], &argv[1]); Version-Release number of selected component (if applicable): sudo-1.6.7p5-30.1.3 How reproducible: Always Steps to Reproduce: 1. Ensure SELinux is enabled (set SELINUX=permissive in /etc/sysconfig/selinux and reboot). 2. sudo bash 3. suspend Actual Results: The bash shell is suspended, but the sesh process that sudo invoked is still running, causing an apparent hang. No tty characters (ctrl-\, ctrl-c, etc) will restore functionality. If you have another root shell on the system, you can kill the appropriate processes. Expected Results: sesh should have caught the SIGTSTP the child bash shell sent to itself and suspended itself. When resumed, sesh should send a SIGCONT to the child. If SELinux is not enabled, there is no issue because "sesh" is not invoked, and sudo merely executes the command line explicitly. However, sudo's design to use a common process that fields subcommands when used in a SELinux environment means that "middle" process should watch for signals of its children and act upon them accordingly rather than ignoring them. I presume the reason for "sesh" is to provide a common executable that can be authorized for context authorization changes within SELinux, rather than having to register every sudo command that might be executed as being authorized to allow its uid context to be switched from its parent. Additional info: See patch... # cat /usr/src/redhat/SOURCES/sudo-1.6.7p5-sesh-stopsig.patch --- sudo-1.6.7p5/sesh.c.sigstop 2005-09-19 17:38:15.000000000 +0200 +++ sudo-1.6.7p5/sesh.c 2005-09-19 17:41:48.000000000 +0200 @@ -1,4 +1,5 @@ #include <stdio.h> +#include <stdlib.h> #include <unistd.h> #include <limits.h> #include <sys/types.h> @@ -22,19 +23,33 @@ int status; int ret; - do { - if ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) + while (1) { + if ((ret = waitpid(pid, &status, WUNTRACED)) < 0 && errno == EINTR) continue; else if (ret < 0) { perror("waitpid failed"); exit(1); } - } while (0); + + if (!WIFSTOPPED(status)) + break; + + /* Reset the handler in case it was inherited ignored, + but the child reset it and stopped anyway. */ + signal(WSTOPSIG(status), SIG_DFL); + raise(WSTOPSIG(status)); + + /* Now we stop until continued ourselves. */ + kill(getpgid(pid) == pid ? -pid : pid, SIGCONT); + } if (WIFEXITED(status)) exit(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + /* XXX print here like the shell would? */ + exit(128 + WTERMSIG(status)); else - exit(1); + exit(127); /* Should never happen. */ } else { /* Child */ execv(argv[1], &argv[1]);