]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: Add a StdinNull directive to ssh_config(5) that allows
authordjm@openbsd.org <djm@openbsd.org>
Fri, 23 Jul 2021 04:00:59 +0000 (04:00 +0000)
committerDamien Miller <djm@mindrot.org>
Fri, 23 Jul 2021 04:07:19 +0000 (14:07 +1000)
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
mux.c
readconf.c
readconf.h
ssh.1
ssh.c
ssh_config.5
sshsig.c

index 0b8a3fd38e6282793c776b5c4297bfecc13f6fa5..7eb6b63bdd2724a78a728bd0adaf5f32c18aebaa 100644 (file)
@@ -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
 /* 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 26741202240c25bd6c9ab663342e9cb7f1416004..4c0eb4249bcdb2ba547140ba6c373591c2fe7ac0 100644 (file)
--- 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)
index 4b1cda2ee36e7463725130a8a468355fce689ad1..681e78f76651edfe9c5929260dbf8823f2481f09 100644 (file)
@@ -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);
index e4ebc6fb80e95994649c3edd3253e433f5d05d4c..08ca9e7a7d613a4ced212bde73be7d249a1f0b6f 100644 (file)
@@ -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 6d0839761d4d2442ccebb0542ff29b3f3299e52c..b31175ffeb3b4cb6f264806d749020c31144be05 100644 (file)
--- 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 84672667ae6c07c4395727d251fa479ae6a170a4..8a5aaa7ef03eaca4a52a01ddfcee0ee658120b5e 100644 (file)
--- 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
@@ -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 &&
index fecca39d2d3935bd1e49f0a7b87b8c0d17458c1a..eb417c95afb3bb75840a37f289881a8041348201 100644 (file)
@@ -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
index 4ce4674cd4238edf8b7247c211662beda8f081a1..d0d401a326ee5fa58503baf44c1927e64c2ad98f 100644 (file)
--- 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;