I'm including a non-Sun-sshd-derived patch for this problem below.
To restate the problem, sshd doesn't put child processes into separate
process contracts, so every process for which sshd was ever an
ancestor (including processes that have otherwise been daemonized)
are in the same process contract. This means that if sshd was started
via the service management facility (SMF), any normal or abnormal
termination of sshd via SMF will cause all of the child sshd processes
(and any process that was ever started from within an ssh session,
regardless of whether that ssh session still exists) to be terminated.
The output below shows the wrong behavior, i.e., that the child
processes are in process contract 562 with the parent sshd process:
server>ps
PID TTY TIME CMD
16778 pts/2 0:00 tcsh
16849 pts/2 0:00 ps
server>ptree -c 16778
[process contract 1]
1 /sbin/init
[process contract 4]
7 /lib/svc/bin/svc.startd
[process contract 562]
16771 /usr/local/sbin/sshd
16774 /usr/local/sbin/sshd -R
16776 /usr/local/sbin/sshd -R
16778 -tcsh
16850 ptree -c 16778
server>
In the above case, a "svcadm disable ssh" will terminate all sshd
child processes. The output below shows the correct
behavior, i.e., that the child process is in a separate process
contract (565):
server>ps
PID TTY TIME CMD
1195 pts/2 0:00 tcsh
1256 pts/2 0:00 ps
server>ptree -c 1195
[process contract 565]
1191 /usr/local/sbin/sshd -R
1193 /usr/local/sbin/sshd -R
1195 -tcsh
1257 ptree -c 1195
server>
In this case, a "svcadm disable ssh" will not terminate existing
ssh connections.
Chad Mynhier
diff -Naur --exclude=RCS openssh-4.3p2/configure.ac
openssh-4.3p2-process-contracts/configure.ac
--- openssh-4.3p2/configure.ac Wed Feb 8 11:11:06 2006
+++ openssh-4.3p2-process-contracts/configure.ac Tue Jul 25 12:56:49 2006
@@ -1,4 +1,4 @@
-# $Id: configure.ac,v 1.322.2.6 2006/02/08 11:11:06 dtucker Exp $
+# $Id: configure.ac,v 1.8 2006/07/25 12:56:33 mynhierc Exp $
#
# Copyright (c) 1999-2004 Damien Miller
#
@@ -15,7 +15,7 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
AC_INIT(OpenSSH, Portable, openssh-unix-dev at mindrot.org)
-AC_REVISION($Revision: 1.322.2.6 $)
+AC_REVISION($Revision: 1.8 $)
AC_CONFIG_SRCDIR([ssh.c])
AC_CONFIG_HEADER(config.h)
@@ -414,6 +414,15 @@
AC_DEFINE(DISABLE_UTMP)
AC_DEFINE(DISABLE_WTMP, 1,
[Define if you don't want to use wtmp])
+ else
+ AC_MSG_RESULT(no)
+ fi
+ AC_MSG_CHECKING(for process contracts)
+ if test "$sol2ver" -ge 10; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SOLARIS_PROCESS_CONTRACTS, 1,
+ [Define if you have Solaris process contracts])
+ LIBS="$LIBS -lcontract"
else
AC_MSG_RESULT(no)
fi
diff -Naur --exclude=RCS openssh-4.3p2/sshd.c
openssh-4.3p2-process-contracts/sshd.c
--- openssh-4.3p2/sshd.c Sat Dec 24 03:59:12 2005
+++ openssh-4.3p2-process-contracts/sshd.c Tue Jul 25 20:48:02 2006
@@ -86,6 +86,12 @@
#include "monitor_wrap.h"
#include "monitor_fdpass.h"
+#ifdef WITH_SOLARIS_PROCESS_CONTRACTS
+#include <libcontract.h>
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#endif /* WITH_SOLARIS_PROCESS_CONTRACTS */
+
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
@@ -866,6 +872,144 @@
debug3("%s: done", __func__);
}
+#ifdef WITH_SOLARIS_PROCESS_CONTRACTS
+/*
+ * Returns the file descriptor for the process contract template on success,
+ * -1 on failure.
+ */
+int
+pre_fork_contract_processing()
+{
+ int tmpl_fd=-1;
+
+ if ((tmpl_fd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1)
{
+ error("Error opening " CTFS_ROOT "/process/template:
%.100s",strerror(errno));
+ goto cleanup_pre_fork_contract_processing;
+ }
+ /*
+ * We have to set certain attributes before activating the template.
+ */
+ if (ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR|CT_PR_EV_SIGNAL) != 0) {
+ error("Error setting process contract template fatal events:
%.100s",strerror(errno));
+ goto cleanup_pre_fork_contract_processing;
+ }
+ if (ct_tmpl_set_critical(tmpl_fd, CT_PR_EV_HWERR) != 0) {
+ error("Error setting process contract template critical events:
%.100s",strerror(errno));
+ goto cleanup_pre_fork_contract_processing;
+ }
+ /*
+ * Now make this the active template for this process.
+ */
+ if (ct_tmpl_activate(tmpl_fd) != 0) {
+ error("Error activating process contract template:
%.100s",strerror(errno));
+ goto cleanup_pre_fork_contract_processing;
+ }
+
+ return tmpl_fd;
+
+cleanup_pre_fork_contract_processing:
+ /*
+ * Certain failure modes could lead to file descriptor leakage
+ * if we don't clean up after ourselves.
+ */
+ if (tmpl_fd > 0) {
+ close(tmpl_fd);
+ }
+ return -1;
+}
+
+void
+post_fork_contract_processing(int tmpl_fd,int pid)
+{
+ char ctl_path[MAXPATHLEN];
+ ctid_t ctid;
+ ct_stathdl_t stathdl;
+ int ctl_fd=-1;
+ int pathlen;
+ int stat_fd=-1;
+
+ /*
+ * First clear the active template.
+ */
+ if (ct_tmpl_clear(tmpl_fd) != 0) {
+ error("Error clearing active process contract template:
%.100s",strerror(errno));
+ goto cleanup_post_fork_contract_processing;
+ }
+ close(tmpl_fd);
+
+ /*
+ * If the fork didn't succeed (pid < 0), or if we're the child
+ * (pid == 0), we have nothing more to do.
+ */
+ if (pid <= 0) {
+ return;
+ }
+
+ /*
+ * Now abandon the contract we've created. This involves the
+ * following steps:
+ * - Get the contract id (ct_status_read(), ct_status_get_id())
+ * - Get an fd for the ctl file for this contract
+ * (/proc/all/contracts/<ctid>/ctl)
+ * - Abandon the contract (ct_ctl_abandon(fd))
+ */
+ if ((stat_fd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1)
{
+ error("Error opening 'latest' process contract:
%.100s",strerror(errno));
+ goto cleanup_post_fork_contract_processing;
+ }
+ if (ct_status_read(stat_fd, CTD_COMMON, &stathdl) != 0) {
+ error("Error reading process contract status:
%.100s",strerror(errno));
+ goto cleanup_post_fork_contract_processing;
+ }
+ if ((ctid = ct_status_get_id(stathdl)) < 0) {
+ error("Error getting process contract id: %.100s",strerror(errno));
+ goto cleanup_post_fork_contract_processing;
+ }
+ ct_status_free(stathdl);
+ close(stat_fd);
+
+ pathlen = snprintf(ctl_path, MAXPATHLEN, CTFS_ROOT
"/process/%ld/ctl",ctid);
+ if (pathlen > MAXPATHLEN) {
+ /*
+ * Note: Even though this case is unlikely, this could
+ * theoretically lead to contract id leakage, as it would
+ * prevent us from abandoning the contract. If we were to
+ * run long enough with enough leakage, we would bump
+ * up against process resource limits.
+ */
+ error("Won't attempt to open process contract ctl file:
%.100s",strerror(ENAMETOOLONG));
+ goto cleanup_post_fork_contract_processing;
+ }
+ if ((ctl_fd = open64(ctl_path, O_WRONLY)) < 0) {
+ error("Error opening process contract ctl file:
%.100s",strerror(errno));
+ goto cleanup_post_fork_contract_processing;
+ }
+ if (ct_ctl_abandon(ctl_fd) < 0) {
+ error("Error abandoning process contract: %.100s",strerror(errno));
+ goto cleanup_post_fork_contract_processing;
+ }
+ close(ctl_fd);
+ return;
+
+cleanup_post_fork_contract_processing:
+ /*
+ * Certain failure modes could lead to file descriptor leakage
+ * if we don't clean up after ourselves.
+ */
+ if (tmpl_fd > 0) {
+ close(tmpl_fd);
+ }
+ if (stat_fd > 0) {
+ close(stat_fd);
+ }
+ if (ctl_fd > 0) {
+ close(ctl_fd);
+ }
+ return;
+}
+
+#endif /* WITH_SOLARIS_PROCESS_CONTRACTS */
+
/*
* Main program for the daemon.
*/
@@ -893,6 +1037,9 @@
Authctxt *authctxt;
int ret, key_used = 0;
Buffer cfg;
+#ifdef WITH_SOLARIS_PROCESS_CONTRACTS
+ int contract_fd=-1;
+#endif
#ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av);
@@ -1503,6 +1650,9 @@
* the child process the connection. The
* parent continues listening.
*/
+#ifdef WITH_SOLARIS_PROCESS_CONTRACTS
+ contract_fd = pre_fork_contract_processing();
+#endif /* WITH_SOLARIS_PROCESS_CONTRACTS */
if ((pid = fork()) == 0) {
/*
* Child. Close the listening and max_startup
@@ -1511,6 +1661,9 @@
* changed). We break out of the loop to handle
* the connection.
*/
+#ifdef WITH_SOLARIS_PROCESS_CONTRACTS
+ post_fork_contract_processing(contract_fd,pid);
+#endif /* WITH_SOLARIS_PROCESS_CONTRACTS */
startup_pipe = startup_p[1];
close_startup_pipes();
close_listen_socks();
@@ -1523,6 +1676,9 @@
}
}
+#ifdef WITH_SOLARIS_PROCESS_CONTRACTS
+ post_fork_contract_processing(contract_fd,pid);
+#endif /* WITH_SOLARIS_PROCESS_CONTRACTS */
/* Parent. Stay in the loop. */
if (pid < 0)
error("fork: %.100s", strerror(errno));