preauth privsep logging via monitor

Damien Miller djm at mindrot.org
Thu Jun 2 15:04:06 EST 2011


Hi,

This diff (for portable) makes the chrooted preauth privsep process
log via the monitor using a shared socketpair. It removes the need
for /dev/log inside /var/empty and makes mandatory sandboxing of the
privsep child easier down the road (no more socket() syscall required).

Please test.

-d

Index: log.c
===================================================================
RCS file: /var/cvs/openssh/log.c,v
retrieving revision 1.54
diff -u -p -r1.54 log.c
--- log.c	10 Jun 2008 13:01:51 -0000	1.54
+++ log.c	2 Jun 2011 03:59:56 -0000
@@ -56,6 +56,8 @@ static LogLevel log_level = SYSLOG_LEVEL
 static int log_on_stderr = 1;
 static int log_facility = LOG_AUTH;
 static char *argv0;
+static log_handler_fn *log_handler;
+static void *log_handler_ctx;
 
 extern char *__progname;
 
@@ -260,6 +262,9 @@ log_init(char *av0, LogLevel level, Sysl
 		exit(1);
 	}
 
+	log_handler = NULL;
+	log_handler_ctx = NULL;
+
 	log_on_stderr = on_stderr;
 	if (on_stderr)
 		return;
@@ -327,6 +332,23 @@ log_init(char *av0, LogLevel level, Sysl
 #define MSGBUFSIZ 1024
 
 void
+set_log_handler(log_handler_fn *handler, void *ctx)
+{
+	log_handler = handler;
+	log_handler_ctx = ctx;
+}
+
+void
+do_log2(LogLevel level, const char *fmt,...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	do_log(level, fmt, args);
+	va_end(args);
+}
+
+void
 do_log(LogLevel level, const char *fmt, va_list args)
 {
 #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
@@ -337,6 +359,7 @@ do_log(LogLevel level, const char *fmt, 
 	char *txt = NULL;
 	int pri = LOG_INFO;
 	int saved_errno = errno;
+	log_handler_fn *tmp_handler;
 
 	if (level > log_level)
 		return;
@@ -375,7 +398,7 @@ do_log(LogLevel level, const char *fmt, 
 		pri = LOG_ERR;
 		break;
 	}
-	if (txt != NULL) {
+	if (txt != NULL && log_handler == NULL) {
 		snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt);
 		vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args);
 	} else {
@@ -383,7 +406,13 @@ do_log(LogLevel level, const char *fmt, 
 	}
 	strnvis(fmtbuf, msgbuf, sizeof(fmtbuf),
 	    log_on_stderr ? LOG_STDERR_VIS : LOG_SYSLOG_VIS);
-	if (log_on_stderr) {
+	if (log_handler != NULL) {
+		/* Avoid recursion */
+		tmp_handler = log_handler;
+		log_handler = NULL;
+		tmp_handler(level, fmtbuf, log_handler_ctx);
+		log_handler = tmp_handler;
+	} else if (log_on_stderr) {
 		snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf);
 		write(STDERR_FILENO, msgbuf, strlen(msgbuf));
 	} else {
Index: log.h
===================================================================
RCS file: /var/cvs/openssh/log.h,v
retrieving revision 1.21
diff -u -p -r1.21 log.h
--- log.h	13 Jun 2008 00:22:54 -0000	1.21
+++ log.h	2 Jun 2011 03:58:14 -0000
@@ -46,6 +46,8 @@ typedef enum {
 	SYSLOG_LEVEL_NOT_SET = -1
 }       LogLevel;
 
+typedef void (log_handler_fn)(LogLevel, const char *, void *);
+
 void     log_init(char *, LogLevel, SyslogFacility, int);
 
 SyslogFacility	log_facility_number(char *);
@@ -64,6 +66,9 @@ void     debug(const char *, ...) __attr
 void     debug2(const char *, ...) __attribute__((format(printf, 1, 2)));
 void     debug3(const char *, ...) __attribute__((format(printf, 1, 2)));
 
+
+void	 set_log_handler(log_handler_fn *, void *);
+void	 do_log2(LogLevel, const char *, ...);
 void	 do_log(LogLevel, const char *, va_list);
 void	 cleanup_exit(int) __attribute__((noreturn));
 #endif
Index: monitor.c
===================================================================
RCS file: /var/cvs/openssh/monitor.c,v
retrieving revision 1.147
diff -u -p -r1.147 monitor.c
--- monitor.c	29 May 2011 11:39:38 -0000	1.147
+++ monitor.c	2 Jun 2011 04:02:42 -0000
@@ -45,6 +45,14 @@
 #include <string.h>
 #include <unistd.h>
 
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+# ifdef HAVE_SYS_POLL_H
+#  include <sys/poll.h>
+# endif
+#endif
+
 #ifdef SKEY
 #include <skey.h>
 #endif
@@ -52,6 +60,7 @@
 #include <openssl/dh.h>
 
 #include "openbsd-compat/sys-queue.h"
+#include "atomicio.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "key.h"
@@ -179,6 +188,8 @@ int mm_answer_audit_event(int, Buffer *)
 int mm_answer_audit_command(int, Buffer *);
 #endif
 
+static int monitor_read_log(struct monitor *);
+
 static Authctxt *authctxt;
 static BIGNUM *ssh1_challenge = NULL;	/* used for ssh1 rsa auth */
 
@@ -346,6 +357,10 @@ monitor_child_preauth(Authctxt *_authctx
 
 	debug3("preauth child monitor started");
 
+	close(pmonitor->m_recvfd);
+	close(pmonitor->m_log_sendfd);
+	pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1;
+
 	authctxt = _authctxt;
 	memset(authctxt, 0, sizeof(*authctxt));
 
@@ -405,6 +420,10 @@ monitor_child_preauth(Authctxt *_authctx
 #endif
 	}
 
+	/* Drain any buffered messages from the child */
+	while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0)
+		;
+
 	if (!authctxt->valid)
 		fatal("%s: authenticated invalid user", __func__);
 	if (strcmp(auth_method, "unknown") == 0)
@@ -414,6 +433,10 @@ monitor_child_preauth(Authctxt *_authctx
 	    __func__, authctxt->user);
 
 	mm_get_keystate(pmonitor);
+
+	close(pmonitor->m_sendfd);
+	close(pmonitor->m_log_recvfd);
+	pmonitor->m_sendfd = pmonitor->m_log_recvfd = -1;
 }
 
 static void
@@ -431,6 +454,9 @@ monitor_child_handler(int sig)
 void
 monitor_child_postauth(struct monitor *pmonitor)
 {
+	close(pmonitor->m_recvfd);
+	pmonitor->m_recvfd = -1;
+
 	monitor_set_child_handler(pmonitor->m_pid);
 	signal(SIGHUP, &monitor_child_handler);
 	signal(SIGTERM, &monitor_child_handler);
@@ -454,6 +480,9 @@ monitor_child_postauth(struct monitor *p
 
 	for (;;)
 		monitor_read(pmonitor, mon_dispatch, NULL);
+
+	close(pmonitor->m_sendfd);
+	pmonitor->m_sendfd = -1;
 }
 
 void
@@ -465,6 +494,45 @@ monitor_sync(struct monitor *pmonitor)
 	}
 }
 
+static int
+monitor_read_log(struct monitor *pmonitor)
+{
+	Buffer logmsg;
+	u_int len, level;
+	char *msg;
+
+	buffer_init(&logmsg);
+	buffer_append_space(&logmsg, 4);
+	if (atomicio(read, pmonitor->m_log_recvfd,
+	    buffer_ptr(&logmsg), buffer_len(&logmsg)) != buffer_len(&logmsg)) {
+		if (errno == EPIPE) {
+			debug("%s: child log fd closed", __func__);
+			close(pmonitor->m_log_recvfd);
+			pmonitor->m_log_recvfd = -1;
+			return -1;
+		}
+		fatal("%s: log fd read: %s", __func__, strerror(errno));
+	}
+	len = buffer_get_int(&logmsg);
+	if (len <= 4 || len > 8192)
+		fatal("%s: invalid log message length %u", __func__, len);
+	buffer_clear(&logmsg);
+	buffer_append_space(&logmsg, len);
+	if (atomicio(read, pmonitor->m_log_recvfd,
+	    buffer_ptr(&logmsg), buffer_len(&logmsg)) != buffer_len(&logmsg))
+		fatal("%s: log fd read: %s", __func__, strerror(errno));
+
+	level = buffer_get_int(&logmsg);
+	msg = buffer_get_string(&logmsg, NULL);
+	if (log_level_name(level) == NULL)
+		fatal("%s: invalid log level %u (corrupted message?)",
+		    __func__, level);
+	do_log2(level, "[preauth] %s", msg);
+	buffer_free(&logmsg);
+	xfree(msg);
+	return 0;
+}
+
 int
 monitor_read(struct monitor *pmonitor, struct mon_table *ent,
     struct mon_table **pent)
@@ -472,6 +540,27 @@ monitor_read(struct monitor *pmonitor, s
 	Buffer m;
 	int ret;
 	u_char type;
+	struct pollfd pfd[2];
+
+	for (;;) {
+		bzero(&pfd, sizeof(pfd));
+		pfd[0].fd = pmonitor->m_sendfd;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = pmonitor->m_log_recvfd;
+		pfd[1].events = pfd[1].fd == -1 ? 0 : POLLIN;
+		if (poll(pfd, pfd[1].fd == -1 ? 1 : 2, -1) == -1)
+			fatal("%s: poll: %s", __func__, strerror(errno));
+		if (pfd[1].revents) {
+			/*
+			 * Drain all log messages before processing next
+			 * monitor request.
+			 */
+			monitor_read_log(pmonitor);
+			continue;
+		}
+		if (pfd[0].revents)
+			break;  /* Continues below */
+	}
 
 	buffer_init(&m);
 
@@ -1851,17 +1940,30 @@ mm_init_compression(struct mm_master *mm
 } while (0)
 
 static void
-monitor_socketpair(int *pair)
+monitor_openfds(struct monitor *mon, int do_logfds)
 {
 #ifdef HAVE_SOCKETPAIR
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
-		fatal("%s: socketpair", __func__);
+	int pair[2];
+
+ 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
+		fatal("%s: socketpair: %s", __func__, strerror(errno));
+ 	FD_CLOSEONEXEC(pair[0]);
+ 	FD_CLOSEONEXEC(pair[1]);
+	mon->m_recvfd = pair[0];
+	mon->m_sendfd = pair[1];
+
+	if (do_logfds) {
+		if (pipe(pair) == -1)
+			fatal("%s: pipe: %s", __func__, strerror(errno));
+		FD_CLOSEONEXEC(pair[0]);
+		FD_CLOSEONEXEC(pair[1]);
+		mon->m_log_recvfd = pair[0];
+		mon->m_log_sendfd = pair[1];
+	} else
+		mon->m_log_recvfd = mon->m_log_sendfd = -1;
 #else
-	fatal("%s: UsePrivilegeSeparation=yes not supported",
-	    __func__);
-#endif
-	FD_CLOSEONEXEC(pair[0]);
-	FD_CLOSEONEXEC(pair[1]);
+	fatal("%s: UsePrivilegeSeparation=yes not supported", __func__);
+#endif /* HAVE_SOCKETPAIR */
 }
 
 #define MM_MEMSIZE	65536
@@ -1870,14 +1972,10 @@ struct monitor *
 monitor_init(void)
 {
 	struct monitor *mon;
-	int pair[2];
 
 	mon = xcalloc(1, sizeof(*mon));
 
-	monitor_socketpair(pair);
-
-	mon->m_recvfd = pair[0];
-	mon->m_sendfd = pair[1];
+	monitor_openfds(mon, 1);
 
 	/* Used to share zlib space across processes */
 	if (options.compression) {
@@ -1894,12 +1992,7 @@ monitor_init(void)
 void
 monitor_reinit(struct monitor *mon)
 {
-	int pair[2];
-
-	monitor_socketpair(pair);
-
-	mon->m_recvfd = pair[0];
-	mon->m_sendfd = pair[1];
+	monitor_openfds(mon, 0);
 }
 
 #ifdef GSSAPI
Index: monitor.h
===================================================================
RCS file: /var/cvs/openssh/monitor.h,v
retrieving revision 1.22
diff -u -p -r1.22 monitor.h
--- monitor.h	5 Nov 2008 05:20:46 -0000	1.22
+++ monitor.h	2 Jun 2011 03:58:14 -0000
@@ -72,6 +72,8 @@ struct mm_master;
 struct monitor {
 	int			 m_recvfd;
 	int			 m_sendfd;
+	int			 m_log_recvfd;
+	int			 m_log_sendfd;
 	struct mm_master	*m_zback;
 	struct mm_master	*m_zlib;
 	struct Kex		**m_pkex;
Index: monitor_wrap.c
===================================================================
RCS file: /var/cvs/openssh/monitor_wrap.c,v
retrieving revision 1.88
diff -u -p -r1.88 monitor_wrap.c
--- monitor_wrap.c	29 May 2011 11:39:38 -0000	1.88
+++ monitor_wrap.c	2 Jun 2011 03:58:14 -0000
@@ -88,6 +88,26 @@ extern struct monitor *pmonitor;
 extern Buffer loginmsg;
 extern ServerOptions options;
 
+void
+mm_log_handler(LogLevel level, const char *msg, void *ctx)
+{
+	Buffer log_msg;
+	struct monitor *mon = (struct monitor *)ctx;
+
+	if (mon->m_log_sendfd == -1)
+		fatal("%s: no log channel", __func__);
+
+	buffer_init(&log_msg);
+	buffer_put_int(&log_msg, 0);  /* filled in with length below */
+	buffer_put_int(&log_msg, level);
+	buffer_put_cstring(&log_msg, msg);
+	put_u32(buffer_ptr(&log_msg), buffer_len(&log_msg) - 4);
+	if (atomicio(vwrite, mon->m_log_sendfd, buffer_ptr(&log_msg),
+	    buffer_len(&log_msg)) != buffer_len(&log_msg))
+		fatal("%s: write: %s", __func__, strerror(errno));
+	buffer_free(&log_msg);
+}
+
 int
 mm_is_monitor(void)
 {
Index: monitor_wrap.h
===================================================================
RCS file: /var/cvs/openssh/monitor_wrap.h,v
retrieving revision 1.29
diff -u -p -r1.29 monitor_wrap.h
--- monitor_wrap.h	5 Mar 2009 13:58:22 -0000	1.29
+++ monitor_wrap.h	2 Jun 2011 03:58:14 -0000
@@ -37,6 +37,7 @@ struct monitor;
 struct mm_master;
 struct Authctxt;
 
+void mm_log_handler(LogLevel, const char *, void *);
 int mm_is_monitor(void);
 DH *mm_choose_dh(int, int, int);
 int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int);
Index: sshd.c
===================================================================
RCS file: /var/cvs/openssh/sshd.c,v
retrieving revision 1.404
diff -u -p -r1.404 sshd.c
--- sshd.c	5 May 2011 04:15:09 -0000	1.404
+++ sshd.c	2 Jun 2011 03:58:14 -0000
@@ -636,10 +636,8 @@ privsep_preauth(Authctxt *authctxt)
 	} else if (pid != 0) {
 		debug2("Network child is on pid %ld", (long)pid);
 
-		close(pmonitor->m_recvfd);
 		pmonitor->m_pid = pid;
 		monitor_child_preauth(authctxt, pmonitor);
-		close(pmonitor->m_sendfd);
 
 		/* Sync memory */
 		monitor_sync(pmonitor);
@@ -651,8 +649,11 @@ privsep_preauth(Authctxt *authctxt)
 		return (1);
 	} else {
 		/* child */
-
 		close(pmonitor->m_sendfd);
+		close(pmonitor->m_log_recvfd);
+
+		/* Arrange for logging to be sent to the monitor */
+		set_log_handler(mm_log_handler, pmonitor);
 
 		/* Demote the child */
 		if (getuid() == 0 || geteuid() == 0)
@@ -685,7 +686,6 @@ privsep_postauth(Authctxt *authctxt)
 		fatal("fork of unprivileged child failed");
 	else if (pmonitor->m_pid != 0) {
 		verbose("User child is on pid %ld", (long)pmonitor->m_pid);
-		close(pmonitor->m_recvfd);
 		buffer_clear(&loginmsg);
 		monitor_child_postauth(pmonitor);
 
@@ -693,7 +693,10 @@ privsep_postauth(Authctxt *authctxt)
 		exit(0);
 	}
 
+	/* child */
+
 	close(pmonitor->m_sendfd);
+	pmonitor->m_sendfd = -1;
 
 	/* Demote the private keys to public keys. */
 	demote_sensitive_data();


More information about the openssh-unix-dev mailing list