summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2021-07-23 04:00:59 +0000
committerDamien Miller <djm@mindrot.org>2021-07-23 14:07:19 +1000
commite0c5088f1c96a145eb6ea1dee438010da78f9ef5 (patch)
treebf1fe4b7bca8439d4523466cb6d879581115a5ee
parente3957e21ffdc119d6d04c0b1686f8e2fe052f5ea (diff)
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
-rw-r--r--clientloop.c5
-rw-r--r--mux.c7
-rw-r--r--readconf.c13
-rw-r--r--readconf.h3
-rw-r--r--ssh.110
-rw-r--r--ssh.c22
-rw-r--r--ssh_config.520
-rw-r--r--sshsig.c110
8 files changed, 134 insertions, 56 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@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@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@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@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@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@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@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -127,12 +127,6 @@ int debug_flag = 0;
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,44 +830,63 @@ check_allowed_keys_line(const char *path, u_long linenum, char *line,
goto done;
}
- /* Check whether options preclude the use of this key */
- if (sigopts->namespaces != NULL &&
- match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
- error("%s:%lu: key is not permitted for use in signature "
- "namespace \"%s\"", path, linenum, sig_namespace);
- 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;
+ 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,
- principal, &reason)) != 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);
- /* success */
- found = 1;
} else {
- /* Principal matched but key didn't */
+ /* 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) {
+ error("%s:%lu: key is not permitted for use in signature "
+ "namespace \"%s\"", path, linenum, sig_namespace);
+ goto done;
+ }
+
+ /* 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;