[openssh-commits] [openssh] 03/04: upstream: Add a StdinNull directive to ssh_config(5) that allows

git+noreply at mindrot.org git+noreply at mindrot.org
Fri Jul 23 14:07:31 AEST 2021


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit e0c5088f1c96a145eb6ea1dee438010da78f9ef5
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Fri Jul 23 04:00:59 2021 +0000

    upstream: Add a StdinNull directive to ssh_config(5) that allows
    
    the config file to do the same thing as -n does on the ssh(1) commandline.
    Patch from Volker Diels-Grabsch via GHPR231; ok dtucker
    
    OpenBSD-Commit-ID: 66ddf3f15c76796d4dcd22ff464aed1edd62468e
---
 clientloop.c |   5 +--
 mux.c        |   7 ++--
 readconf.c   |  13 ++++++-
 readconf.h   |   3 +-
 ssh.1        |  10 ++++-
 ssh.c        |  22 ++++-------
 ssh_config.5 |  20 +++++++++-
 sshsig.c     | 118 +++++++++++++++++++++++++++++++++++++++++++----------------
 8 files changed, 138 insertions(+), 60 deletions(-)

diff --git a/clientloop.c b/clientloop.c
index 0b8a3fd3..7eb6b63b 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.367 2021/07/16 09:00:23 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.368 2021/07/23 04:00:59 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -115,9 +115,6 @@
 /* import options */
 extern Options options;
 
-/* Flag indicating that stdin should be redirected from /dev/null. */
-extern int stdin_null_flag;
-
 /* Flag indicating that ssh should daemonise after authentication is complete */
 extern int fork_after_authentication_flag;
 
diff --git a/mux.c b/mux.c
index 26741202..4c0eb424 100644
--- a/mux.c
+++ b/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.90 2021/07/13 23:48:36 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.91 2021/07/23 04:00:59 djm Exp $ */
 /*
  * Copyright (c) 2002-2008 Damien Miller <djm at openbsd.org>
  *
@@ -71,7 +71,6 @@
 /* from ssh.c */
 extern int tty_flag;
 extern Options options;
-extern int stdin_null_flag;
 extern char *host;
 extern struct sshbuf *command;
 extern volatile sig_atomic_t quit_pending;
@@ -1879,7 +1878,7 @@ mux_client_request_session(int fd)
 
 	ssh_signal(SIGPIPE, SIG_IGN);
 
-	if (stdin_null_flag && stdfd_devnull(1, 0, 0) == -1)
+	if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1)
 		fatal_f("stdfd_devnull failed");
 
 	if ((term = lookup_env_in_list("TERM", options.setenv,
@@ -2102,7 +2101,7 @@ mux_client_request_stdio_fwd(int fd)
 
 	ssh_signal(SIGPIPE, SIG_IGN);
 
-	if (stdin_null_flag && stdfd_devnull(1, 0, 0) == -1)
+	if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1)
 		fatal_f("stdfd_devnull failed");
 
 	if ((m = sshbuf_new()) == NULL)
diff --git a/readconf.c b/readconf.c
index 4b1cda2e..681e78f7 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.359 2021/07/13 23:48:36 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.360 2021/07/23 04:00:59 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -167,7 +167,7 @@ typedef enum {
 	oTunnel, oTunnelDevice,
 	oLocalCommand, oPermitLocalCommand, oRemoteCommand,
 	oVisualHostKey,
-	oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType,
+	oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
 	oIgnoreUnknown, oProxyUseFdpass,
 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
@@ -299,6 +299,7 @@ static struct {
 	{ "ipqos", oIPQoS },
 	{ "requesttty", oRequestTTY },
 	{ "sessiontype", oSessionType },
+	{ "stdinnull", oStdinNull },
 	{ "proxyusefdpass", oProxyUseFdpass },
 	{ "canonicaldomains", oCanonicalDomains },
 	{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
@@ -1954,6 +1955,10 @@ parse_pubkey_algos:
 		multistate_ptr = multistate_sessiontype;
 		goto parse_multistate;
 
+	case oStdinNull:
+		intptr = &options->stdin_null;
+		goto parse_flag;
+
 	case oIgnoreUnknown:
 		charptr = &options->ignored_unknown;
 		goto parse_string;
@@ -2377,6 +2382,7 @@ initialize_options(Options * options)
 	options->ip_qos_bulk = -1;
 	options->request_tty = -1;
 	options->session_type = -1;
+	options->stdin_null = -1;
 	options->proxy_use_fdpass = -1;
 	options->ignored_unknown = NULL;
 	options->num_canonical_domains = 0;
@@ -2565,6 +2571,8 @@ fill_default_options(Options * options)
 		options->request_tty = REQUEST_TTY_AUTO;
 	if (options->session_type == -1)
 		options->session_type = SESSION_TYPE_DEFAULT;
+	if (options->stdin_null == -1)
+		options->stdin_null = 0;
 	if (options->proxy_use_fdpass == -1)
 		options->proxy_use_fdpass = 0;
 	if (options->canonicalize_max_dots == -1)
@@ -3243,6 +3251,7 @@ dump_client_config(Options *o, const char *host)
 	dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
 	dump_cfg_fmtint(oRequestTTY, o->request_tty);
 	dump_cfg_fmtint(oSessionType, o->session_type);
+	dump_cfg_fmtint(oStdinNull, o->stdin_null);
 	dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
 	dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
 	dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
diff --git a/readconf.h b/readconf.h
index e4ebc6fb..08ca9e7a 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.142 2021/07/13 23:48:36 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.143 2021/07/23 04:00:59 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -147,6 +147,7 @@ typedef struct {
 
 	int	request_tty;
 	int	session_type;
+	int	stdin_null;
 
 	int	proxy_use_fdpass;
 
diff --git a/ssh.1 b/ssh.1
index 6d083976..b31175ff 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.422 2021/07/13 23:48:36 djm Exp $
-.Dd $Mdocdate: July 13 2021 $
+.\" $OpenBSD: ssh.1,v 1.423 2021/07/23 04:00:59 djm Exp $
+.Dd $Mdocdate: July 23 2021 $
 .Dt SSH 1
 .Os
 .Sh NAME
@@ -451,6 +451,11 @@ program will be put in the background.
 needs to ask for a password or passphrase; see also the
 .Fl f
 option.)
+Refer to the description of
+.Cm StdinNull
+in
+.Xr ssh_config 5
+for details.
 .Pp
 .It Fl O Ar ctl_cmd
 Control an active connection multiplexing master process.
@@ -553,6 +558,7 @@ For full details of the options listed below, and their possible values, see
 .It ServerAliveCountMax
 .It SessionType
 .It SetEnv
+.It StdinNull
 .It StreamLocalBindMask
 .It StreamLocalBindUnlink
 .It StrictHostKeyChecking
diff --git a/ssh.c b/ssh.c
index 84672667..8a5aaa7e 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.562 2021/07/17 00:38:11 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.563 2021/07/23 04:00:59 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -126,12 +126,6 @@ int debug_flag = 0;
 /* Flag indicating whether a tty should be requested */
 int tty_flag = 0;
 
-/*
- * Flag indicating that nothing should be read from stdin.  This can be set
- * on the command line.
- */
-int stdin_null_flag = 0;
-
 /*
  * Flag indicating that the current process should be backgrounded and
  * a new mux-client launched in the foreground for ControlPersist.
@@ -723,11 +717,11 @@ main(int ac, char **av)
 			options.address_family = AF_INET6;
 			break;
 		case 'n':
-			stdin_null_flag = 1;
+			options.stdin_null = 1;
 			break;
 		case 'f':
 			fork_after_authentication_flag = 1;
-			stdin_null_flag = 1;
+			options.stdin_null = 1;
 			break;
 		case 'x':
 			options.forward_x11 = 0;
@@ -1357,7 +1351,7 @@ main(int ac, char **av)
 	    (muxclient_command && muxclient_command != SSHMUX_COMMAND_PROXY))
 		tty_flag = 0;
 	/* Do not allocate a tty if stdin is not a tty. */
-	if ((!isatty(fileno(stdin)) || stdin_null_flag) &&
+	if ((!isatty(fileno(stdin)) || options.stdin_null) &&
 	    options.request_tty != REQUEST_TTY_FORCE) {
 		if (tty_flag)
 			logit("Pseudo-terminal will not be allocated because "
@@ -1734,7 +1728,7 @@ control_persist_detach(void)
 	default:
 		/* Parent: set up mux client to connect to backgrounded master */
 		debug2_f("background process is %ld", (long)pid);
-		stdin_null_flag = ostdin_null_flag;
+		options.stdin_null = ostdin_null_flag;
 		options.request_tty = orequest_tty;
 		tty_flag = otty_flag;
 		options.session_type = osession_type;
@@ -2075,7 +2069,7 @@ ssh_session2_open(struct ssh *ssh)
 	Channel *c;
 	int window, packetmax, in, out, err;
 
-	if (stdin_null_flag) {
+	if (options.stdin_null) {
 		in = open(_PATH_DEVNULL, O_RDONLY);
 	} else {
 		in = dup(STDIN_FILENO);
@@ -2144,11 +2138,11 @@ ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo)
 	 * async rfwd replies have been received for ExitOnForwardFailure).
 	 */
 	if (options.control_persist && muxserver_sock != -1) {
-		ostdin_null_flag = stdin_null_flag;
+		ostdin_null_flag = options.stdin_null;
 		osession_type = options.session_type;
 		orequest_tty = options.request_tty;
 		otty_flag = tty_flag;
-		stdin_null_flag = 1;
+		options.stdin_null = 1;
 		options.session_type = SESSION_TYPE_NONE;
 		tty_flag = 0;
 		if (!fork_after_authentication_flag &&
diff --git a/ssh_config.5 b/ssh_config.5
index fecca39d..eb417c95 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -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_config.5,v 1.357 2021/07/14 06:46:38 jmc Exp $
-.Dd $Mdocdate: July 14 2021 $
+.\" $OpenBSD: ssh_config.5,v 1.358 2021/07/23 04:00:59 djm Exp $
+.Dd $Mdocdate: July 23 2021 $
 .Dt SSH_CONFIG 5
 .Os
 .Sh NAME
@@ -1675,6 +1675,22 @@ Similarly to
 with the exception of the
 .Ev TERM
 variable, the server must be prepared to accept the environment variable.
+.It Cm StdinNull
+Redirects stdin from
+.Pa /dev/null
+(actually, prevents reading from stdin).
+Either this or the equivalent
+.Fl n
+option must be used when
+.Nm ssh
+is run in the background.
+The argument to this keyword must be
+.Cm yes
+(same as the
+.Fl n
+option) or
+.Cm no
+(the default).
 .It Cm StreamLocalBindMask
 Sets the octal file creation mode mask
 .Pq umask
diff --git a/sshsig.c b/sshsig.c
index 4ce4674c..d0d401a3 100644
--- a/sshsig.c
+++ b/sshsig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshsig.c,v 1.20 2021/01/31 10:50:10 dtucker Exp $ */
+/* $OpenBSD: sshsig.c,v 1.21 2021/07/23 04:00:59 djm Exp $ */
 /*
  * Copyright (c) 2019 Google LLC
  *
@@ -616,6 +616,7 @@ sshsig_verify_fd(struct sshbuf *signature, int fd,
 struct sshsigopt {
 	int ca;
 	char *namespaces;
+	uint64_t valid_after, valid_before;
 };
 
 struct sshsigopt *
@@ -624,6 +625,7 @@ sshsigopt_parse(const char *opts, const char *path, u_long linenum,
 {
 	struct sshsigopt *ret;
 	int r;
+	char *opt;
 	const char *errstr = NULL;
 
 	if ((ret = calloc(1, sizeof(*ret))) == NULL)
@@ -643,6 +645,34 @@ sshsigopt_parse(const char *opts, const char *path, u_long linenum,
 			ret->namespaces = opt_dequote(&opts, &errstr);
 			if (ret->namespaces == NULL)
 				goto fail;
+		} else if (opt_match(&opts, "valid-after")) {
+			if (ret->valid_after != 0) {
+				errstr = "multiple \"valid-after\" clauses";
+				goto fail;
+			}
+			if ((opt = opt_dequote(&opts, &errstr)) == NULL)
+				goto fail;
+			if (parse_absolute_time(opt, &ret->valid_after) != 0 ||
+			    ret->valid_after == 0) {
+				free(opt);
+				errstr = "invalid \"valid-after\" time";
+				goto fail;
+			}
+			free(opt);
+		} else if (opt_match(&opts, "valid-before")) {
+			if (ret->valid_before != 0) {
+				errstr = "multiple \"valid-before\" clauses";
+				goto fail;
+			}
+			if ((opt = opt_dequote(&opts, &errstr)) == NULL)
+				goto fail;
+			if (parse_absolute_time(opt, &ret->valid_before) != 0 ||
+			    ret->valid_before == 0) {
+				free(opt);
+				errstr = "invalid \"valid-before\" time";
+				goto fail;
+			}
+			free(opt);
 		}
 		/*
 		 * Skip the comma, and move to the next option
@@ -661,6 +691,12 @@ sshsigopt_parse(const char *opts, const char *path, u_long linenum,
 			goto fail;
 		}
 	}
+	/* final consistency check */
+	if (ret->valid_after != 0 && ret->valid_before != 0 &&
+	    ret->valid_before <= ret->valid_after) {
+		errstr = "\"valid-before\" time is before \"valid-after\"";
+		goto fail;
+	}
 	/* success */
 	return ret;
  fail:
@@ -779,12 +815,13 @@ parse_principals_key_and_options(const char *path, u_long linenum, char *line,
 static int
 check_allowed_keys_line(const char *path, u_long linenum, char *line,
     const struct sshkey *sign_key, const char *principal,
-    const char *sig_namespace)
+    const char *sig_namespace, uint64_t verify_time)
 {
 	struct sshkey *found_key = NULL;
-	int r, found = 0;
+	int r, success = 0;
 	const char *reason = NULL;
 	struct sshsigopt *sigopts = NULL;
+	char tvalid[64], tverify[64];
 
 	/* Parse the line */
 	if ((r = parse_principals_key_and_options(path, linenum, line,
@@ -793,6 +830,24 @@ check_allowed_keys_line(const char *path, u_long linenum, char *line,
 		goto done;
 	}
 
+	if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
+		/* Exact match of key */
+		debug("%s:%lu: matched key", path, linenum);
+	} else if (sigopts->ca && sshkey_is_cert(sign_key) &&
+	    sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
+		/* Match of certificate's CA key */
+		if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
+		    verify_time, principal, &reason)) != 0) {
+			error("%s:%lu: certificate not authorized: %s",
+			    path, linenum, reason);
+			goto done;
+		}
+		debug("%s:%lu: matched certificate CA key", path, linenum);
+	} else {
+		/* Didn't match key */
+		goto done;
+	}
+
 	/* Check whether options preclude the use of this key */
 	if (sigopts->namespaces != NULL &&
 	    match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
@@ -801,36 +856,37 @@ check_allowed_keys_line(const char *path, u_long linenum, char *line,
 		goto done;
 	}
 
-	if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
-		/* Exact match of key */
-		debug("%s:%lu: matched key and principal", path, linenum);
-		/* success */
-		found = 1;
-	} else if (sigopts->ca && sshkey_is_cert(sign_key) &&
-	    sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
-		/* Match of certificate's CA key */
-		if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
-		    principal, &reason)) != 0) {
-			error("%s:%lu: certificate not authorized: %s",
-			    path, linenum, reason);
-			goto done;
-		}
-		debug("%s:%lu: matched certificate CA key", path, linenum);
-		/* success */
-		found = 1;
-	} else {
-		/* Principal matched but key didn't */
+	/* check key time validity */
+	format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
+	if (sigopts->valid_after != 0 &&
+	    (uint64_t)verify_time < sigopts->valid_after) {
+		format_absolute_time(sigopts->valid_after,
+		    tvalid, sizeof(tvalid));
+		error("%s:%lu: key is not yet valid: "
+		    "verify time %s < valid-after %s", path, linenum,
+		    tverify, tvalid);
 		goto done;
 	}
+	if (sigopts->valid_before != 0 &&
+	    (uint64_t)verify_time > sigopts->valid_before) {
+		format_absolute_time(sigopts->valid_before,
+		    tvalid, sizeof(tvalid));
+		error("%s:%lu: key has expired: "
+		    "verify time %s > valid-before %s", path, linenum,
+		    tverify, tvalid);
+		goto done;
+	}
+	success = 1;
+
  done:
 	sshkey_free(found_key);
 	sshsigopt_free(sigopts);
-	return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
+	return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
 }
 
 int
 sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
-    const char *principal, const char *sig_namespace)
+    const char *principal, const char *sig_namespace, uint64_t verify_time)
 {
 	FILE *f = NULL;
 	char *line = NULL;
@@ -850,7 +906,7 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
 	while (getline(&line, &linesize, f) != -1) {
 		linenum++;
 		r = check_allowed_keys_line(path, linenum, line, sign_key,
-		    principal, sig_namespace);
+		    principal, sig_namespace, verify_time);
 		free(line);
 		line = NULL;
 		linesize = 0;
@@ -871,7 +927,7 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
 
 static int
 cert_filter_principals(const char *path, u_long linenum,
-    char **principalsp, const struct sshkey *cert)
+    char **principalsp, const struct sshkey *cert, uint64_t verify_time)
 {
 	char *cp, *oprincipals, *principals;
 	const char *reason;
@@ -894,7 +950,7 @@ cert_filter_principals(const char *path, u_long linenum,
 		}
 		/* Check against principals list in certificate */
 		if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
-		    cp, &reason)) != 0) {
+		    verify_time, cp, &reason)) != 0) {
 			debug("%s:%lu: principal \"%s\" not authorized: %s",
 			    path, linenum, cp, reason);
 			continue;
@@ -925,7 +981,7 @@ cert_filter_principals(const char *path, u_long linenum,
 
 static int
 get_matching_principals_from_line(const char *path, u_long linenum, char *line,
-    const struct sshkey *sign_key, char **principalsp)
+    const struct sshkey *sign_key, uint64_t verify_time, char **principalsp)
 {
 	struct sshkey *found_key = NULL;
 	char *principals = NULL;
@@ -951,7 +1007,7 @@ get_matching_principals_from_line(const char *path, u_long linenum, char *line,
 	    sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
 		/* Remove principals listed in file but not allowed by cert */
 		if ((r = cert_filter_principals(path, linenum,
-		    &principals, sign_key)) != 0) {
+		    &principals, sign_key, verify_time)) != 0) {
 			/* error already displayed */
 			debug_r(r, "%s:%lu: cert_filter_principals",
 			    path, linenum);
@@ -977,7 +1033,7 @@ get_matching_principals_from_line(const char *path, u_long linenum, char *line,
 
 int
 sshsig_find_principals(const char *path, const struct sshkey *sign_key,
-    char **principals)
+    uint64_t verify_time, char **principals)
 {
 	FILE *f = NULL;
 	char *line = NULL;
@@ -996,7 +1052,7 @@ sshsig_find_principals(const char *path, const struct sshkey *sign_key,
 	while (getline(&line, &linesize, f) != -1) {
 		linenum++;
 		r = get_matching_principals_from_line(path, linenum, line,
-		    sign_key, principals);
+		    sign_key, verify_time, principals);
 		free(line);
 		line = NULL;
 		linesize = 0;

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list