file to do the same thing as -n does on the ssh(1) commandline.
Patch from Volker Diels-Grabsch via GHPR231; ok dtucker
-/* $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
/* 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;
-/* $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>
*
/* 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;
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,
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)
-/* $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
oTunnel, oTunnelDevice,
oLocalCommand, oPermitLocalCommand, oRemoteCommand,
oVisualHostKey,
- oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType,
+ oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
oIgnoreUnknown, oProxyUseFdpass,
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
{ "ipqos", oIPQoS },
{ "requesttty", oRequestTTY },
{ "sessiontype", oSessionType },
+ { "stdinnull", oStdinNull },
{ "proxyusefdpass", oProxyUseFdpass },
{ "canonicaldomains", oCanonicalDomains },
{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
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;
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;
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)
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);
-/* $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>
int request_tty;
int session_type;
+ int stdin_null;
int proxy_use_fdpass;
.\" (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
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.
.It ServerAliveCountMax
.It SessionType
.It SetEnv
+.It StdinNull
.It StreamLocalBindMask
.It StreamLocalBindUnlink
.It StrictHostKeyChecking
-/* $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
/* 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.
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;
(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 "
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;
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);
* 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 &&
.\" (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
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
-/* $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
*
struct sshsigopt {
int ca;
char *namespaces;
+ uint64_t valid_after, valid_before;
};
struct sshsigopt *
{
struct sshsigopt *ret;
int r;
+ char *opt;
const char *errstr = NULL;
if ((ret = calloc(1, sizeof(*ret))) == NULL)
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
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:
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,
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;
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;
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;
}
/* 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;
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;
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);
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;
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;