From: djm Date: Fri, 23 Jul 2021 04:00:59 +0000 (+0000) Subject: Add a StdinNull directive to ssh_config(5) that allows the config X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=2e79209a48203692574aac41508a463f1e67e24d;p=openbsd 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 --- diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c index b84c986bd35..1e1fb8e3273 100644 --- a/usr.bin/ssh/clientloop.c +++ b/usr.bin/ssh/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 * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -108,9 +108,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/usr.bin/ssh/mux.c b/usr.bin/ssh/mux.c index 78dd4fe8f15..e39a134ff12 100644 --- a/usr.bin/ssh/mux.c +++ b/usr.bin/ssh/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 * @@ -58,7 +58,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; @@ -1860,7 +1859,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, @@ -2082,7 +2081,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/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index 761ca67f9d6..316ed5ab3a9 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/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 * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -153,7 +153,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, @@ -285,6 +285,7 @@ static struct { { "ipqos", oIPQoS }, { "requesttty", oRequestTTY }, { "sessiontype", oSessionType }, + { "stdinnull", oStdinNull }, { "proxyusefdpass", oProxyUseFdpass }, { "canonicaldomains", oCanonicalDomains }, { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, @@ -1940,6 +1941,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; @@ -2363,6 +2368,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; @@ -2549,6 +2555,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) @@ -3222,6 +3230,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/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h index e4ebc6fb80e..08ca9e7a7d6 100644 --- a/usr.bin/ssh/readconf.h +++ b/usr.bin/ssh/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 @@ -147,6 +147,7 @@ typedef struct { int request_tty; int session_type; + int stdin_null; int proxy_use_fdpass; diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1 index 6d0839761d4..b31175ffeb3 100644 --- a/usr.bin/ssh/ssh.1 +++ b/usr.bin/ssh/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/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index 66ce7b1dad7..0b86ab881f4 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/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 * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -110,12 +110,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. @@ -697,11 +691,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; @@ -1336,7 +1330,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 " @@ -1713,7 +1707,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; @@ -2054,7 +2048,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); @@ -2123,11 +2117,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/usr.bin/ssh/ssh_config.5 b/usr.bin/ssh/ssh_config.5 index 793035b35b7..3464bb0c6f2 100644 --- a/usr.bin/ssh/ssh_config.5 +++ b/usr.bin/ssh/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 @@ -1676,6 +1676,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/usr.bin/ssh/sshsig.c b/usr.bin/ssh/sshsig.c index 58ad4c2643f..9301f2e9f17 100644 --- a/usr.bin/ssh/sshsig.c +++ b/usr.bin/ssh/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 * @@ -614,6 +614,7 @@ sshsig_verify_fd(struct sshbuf *signature, int fd, struct sshsigopt { int ca; char *namespaces; + uint64_t valid_after, valid_before; }; struct sshsigopt * @@ -622,6 +623,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) @@ -641,6 +643,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 @@ -659,6 +689,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: @@ -777,12 +813,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, @@ -791,44 +828,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; @@ -848,7 +904,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; @@ -869,7 +925,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; @@ -892,7 +948,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; @@ -923,7 +979,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; @@ -949,7 +1005,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); @@ -975,7 +1031,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; @@ -994,7 +1050,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;