From 03d4d7e60b16f913c75382e32e136ddfa8d6485f Mon Sep 17 00:00:00 2001
From: Damien Miller <djm@mindrot.org>
Date: Tue, 23 Apr 2013 15:21:06 +1000
Subject: [PATCH]    - dtucker@cvs.openbsd.org 2013/04/07 02:10:33      [log.c
 log.h ssh.1 ssh.c sshd.8 sshd.c]      Add -E option to ssh and sshd to append
 debugging logs to a specified file      instead of stderr or syslog.  ok
 markus@, man page help jmc@

---
 ChangeLog |  4 ++++
 log.c     | 20 ++++++++++++++++++--
 log.h     |  3 ++-
 ssh.1     |  9 +++++++--
 ssh.c     | 28 ++++++++++++++++++++--------
 sshd.8    |  9 +++++++--
 sshd.c    | 19 ++++++++++++++-----
 7 files changed, 72 insertions(+), 20 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 47c7ce01c..51077256d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -39,6 +39,10 @@
    - markus@cvs.openbsd.org 2013/04/06 16:07:00
      [channels.c sshd.c]
      handle ECONNABORTED for accept(); ok deraadt some time ago...
+   - dtucker@cvs.openbsd.org 2013/04/07 02:10:33
+     [log.c log.h ssh.1 ssh.c sshd.8 sshd.c]
+     Add -E option to ssh and sshd to append debugging logs to a specified file
+     instead of stderr or syslog.  ok markus@, man page help jmc@
 
 20130418
  - (djm) [config.guess config.sub] Update to last versions before they switch
diff --git a/log.c b/log.c
index d69154a67..81497a442 100644
--- a/log.c
+++ b/log.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.c,v 1.43 2012/09/06 04:37:39 dtucker Exp $ */
+/* $OpenBSD: log.c,v 1.44 2013/04/07 02:10:33 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -38,6 +38,7 @@
 
 #include <sys/types.h>
 
+#include <fcntl.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -54,6 +55,7 @@
 
 static LogLevel log_level = SYSLOG_LEVEL_INFO;
 static int log_on_stderr = 1;
+static int log_stderr_fd = STDERR_FILENO;
 static int log_facility = LOG_AUTH;
 static char *argv0;
 static log_handler_fn *log_handler;
@@ -344,6 +346,20 @@ log_is_on_stderr(void)
 	return log_on_stderr;
 }
 
+/* redirect what would usually get written to stderr to specified file */
+void
+log_redirect_stderr_to(const char *logfile)
+{
+	int fd;
+
+	if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
+		fprintf(stderr, "Couldn't open logfile %s: %s\n", logfile,
+		     strerror(errno));
+		exit(1);
+	}
+	log_stderr_fd = fd;
+}
+
 #define MSGBUFSIZ 1024
 
 void
@@ -429,7 +445,7 @@ do_log(LogLevel level, const char *fmt, va_list args)
 		log_handler = tmp_handler;
 	} else if (log_on_stderr) {
 		snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf);
-		write(STDERR_FILENO, msgbuf, strlen(msgbuf));
+		write(log_stderr_fd, msgbuf, strlen(msgbuf));
 	} else {
 #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
 		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);
diff --git a/log.h b/log.h
index e3e328b06..ae7df25d3 100644
--- a/log.h
+++ b/log.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.h,v 1.19 2012/09/06 04:37:39 dtucker Exp $ */
+/* $OpenBSD: log.h,v 1.20 2013/04/07 02:10:33 dtucker Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -51,6 +51,7 @@ typedef void (log_handler_fn)(LogLevel, const char *, void *);
 void     log_init(char *, LogLevel, SyslogFacility, int);
 void     log_change_level(LogLevel);
 int      log_is_on_stderr(void);
+void     log_redirect_stderr_to(const char *);
 
 SyslogFacility	log_facility_number(char *);
 const char * 	log_facility_name(SyslogFacility);
diff --git a/ssh.1 b/ssh.1
index a5576edb6..d77494b83 100644
--- a/ssh.1
+++ b/ssh.1
@@ -33,8 +33,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh.1,v 1.330 2012/10/04 13:21:50 markus Exp $
-.Dd $Mdocdate: October 4 2012 $
+.\" $OpenBSD: ssh.1,v 1.331 2013/04/07 02:10:33 dtucker Exp $
+.Dd $Mdocdate: April 7 2013 $
 .Dt SSH 1
 .Os
 .Sh NAME
@@ -47,6 +47,7 @@
 .Op Fl b Ar bind_address
 .Op Fl c Ar cipher_spec
 .Op Fl D Oo Ar bind_address : Oc Ns Ar port
+.Op Fl E Ar log_file
 .Op Fl e Ar escape_char
 .Op Fl F Ar configfile
 .Op Fl I Ar pkcs11
@@ -217,6 +218,10 @@ indicates that the listening port be bound for local use only, while an
 empty address or
 .Sq *
 indicates that the port should be available from all interfaces.
+.It Fl E Ar log_file
+Append debug logs to
+.Ar log_file
+instead of standard error.
 .It Fl e Ar escape_char
 Sets the escape character for sessions with a pty (default:
 .Ql ~ ) .
diff --git a/ssh.c b/ssh.c
index b50fca38f..cd56f8a74 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.374 2013/03/08 06:32:58 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.375 2013/04/07 02:10:33 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -197,8 +197,8 @@ usage(void)
 {
 	fprintf(stderr,
 "usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n"
-"           [-D [bind_address:]port] [-e escape_char] [-F configfile]\n"
-"           [-I pkcs11] [-i identity_file]\n"
+"           [-D [bind_address:]port] [-E log_file] [-e escape_char]\n"
+"           [-F configfile] [-I pkcs11] [-i identity_file]\n"
 "           [-L [bind_address:]port:host:hostport]\n"
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
 "           [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
@@ -238,7 +238,7 @@ int
 main(int ac, char **av)
 {
 	int i, r, opt, exit_status, use_syslog;
-	char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg;
+	char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile;
 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
 	struct stat st;
 	struct passwd *pw;
@@ -322,11 +322,12 @@ main(int ac, char **av)
 	/* Parse command-line arguments. */
 	host = NULL;
 	use_syslog = 0;
+	logfile = NULL;
 	argv0 = av[0];
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
-	    "ACD:F:I:KL:MNO:PR:S:TVw:W:XYy")) != -1) {
+	    "ACD:E:F:I:KL:MNO:PR:S:TVw:W:XYy")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -356,6 +357,9 @@ main(int ac, char **av)
 		case 'y':
 			use_syslog = 1;
 			break;
+		case 'E':
+			logfile = xstrdup(optarg);
+			break;
 		case 'Y':
 			options.forward_x11 = 1;
 			options.forward_x11_trusted = 1;
@@ -427,9 +431,8 @@ main(int ac, char **av)
 			} else {
 				if (options.log_level < SYSLOG_LEVEL_DEBUG3)
 					options.log_level++;
-				break;
 			}
-			/* FALLTHROUGH */
+			break;
 		case 'V':
 			fprintf(stderr, "%s, %s\n",
 			    SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
@@ -663,12 +666,21 @@ main(int ac, char **av)
 
 	/*
 	 * Initialize "log" output.  Since we are the client all output
-	 * actually goes to stderr.
+	 * goes to stderr unless otherwise specified by -y or -E.
 	 */
+	if (use_syslog && logfile != NULL)
+		fatal("Can't specify both -y and -E");
+	if (logfile != NULL) {
+		log_redirect_stderr_to(logfile);
+		xfree(logfile);
+	}
 	log_init(argv0,
 	    options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
 	    SYSLOG_FACILITY_USER, !use_syslog);
 
+	if (debug_flag)
+		logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION));
+
 	/*
 	 * Read per-user configuration file.  Ignore the system wide config
 	 * file if the user specifies a config file on the command line.
diff --git a/sshd.8 b/sshd.8
index 132397839..3ce0da6a3 100644
--- a/sshd.8
+++ b/sshd.8
@@ -33,8 +33,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd.8,v 1.267 2012/10/04 13:21:50 markus Exp $
-.Dd $Mdocdate: October 4 2012 $
+.\" $OpenBSD: sshd.8,v 1.268 2013/04/07 02:10:33 dtucker Exp $
+.Dd $Mdocdate: April 7 2013 $
 .Dt SSHD 8
 .Os
 .Sh NAME
@@ -47,6 +47,7 @@
 .Op Fl b Ar bits
 .Op Fl C Ar connection_spec
 .Op Fl c Ar host_certificate_file
+.Op Fl E Ar log_file
 .Op Fl f Ar config_file
 .Op Fl g Ar login_grace_time
 .Op Fl h Ar host_key_file
@@ -146,6 +147,10 @@ Multiple
 .Fl d
 options increase the debugging level.
 Maximum is 3.
+.It Fl E Ar log_file
+Append debug logs to
+.Ar log_file
+instead of the system log.
 .It Fl e
 When this option is specified,
 .Nm
diff --git a/sshd.c b/sshd.c
index 5fb2897fa..a0f5c0d26 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.398 2013/04/06 16:07:00 markus Exp $ */
+/* $OpenBSD: sshd.c,v 1.399 2013/04/07 02:10:33 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -900,8 +900,9 @@ usage(void)
 	    SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
 	fprintf(stderr,
 "usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
-"            [-f config_file] [-g login_grace_time] [-h host_key_file]\n"
-"            [-k key_gen_time] [-o option] [-p port] [-u len]\n"
+"            [-E log_file] [-f config_file] [-g login_grace_time]\n"
+"            [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n"
+"            [-u len]\n"
 	);
 	exit(1);
 }
@@ -1335,7 +1336,7 @@ main(int ac, char **av)
 	int sock_in = -1, sock_out = -1, newsock = -1;
 	const char *remote_ip;
 	int remote_port;
-	char *line;
+	char *line, *logfile = NULL;
 	int config_s[2] = { -1 , -1 };
 	u_int n;
 	u_int64_t ibytes, obytes;
@@ -1373,7 +1374,7 @@ main(int ac, char **av)
 	initialize_server_options(&options);
 
 	/* Parse command-line arguments. */
-	while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeiqrtQRT46")) != -1) {
+	while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeE:iqrtQRT46")) != -1) {
 		switch (opt) {
 		case '4':
 			options.address_family = AF_INET;
@@ -1402,6 +1403,9 @@ main(int ac, char **av)
 		case 'D':
 			no_daemon_flag = 1;
 			break;
+		case 'E':
+			logfile = xstrdup(optarg);
+			/* FALLTHROUGH */
 		case 'e':
 			log_stderr = 1;
 			break;
@@ -1499,6 +1503,11 @@ main(int ac, char **av)
 
 	OpenSSL_add_all_algorithms();
 
+	/* If requested, redirect the logs to the specified logfile. */
+	if (logfile != NULL) {
+		log_redirect_stderr_to(logfile);
+		xfree(logfile);
+	}
 	/*
 	 * Force logging to stderr until we have loaded the private host
 	 * key (unless started from inetd)