Let allowed signers files used by ssh-keygen(1) signatures support key
authordjm <djm@openbsd.org>
Fri, 23 Jul 2021 03:37:52 +0000 (03:37 +0000)
committerdjm <djm@openbsd.org>
Fri, 23 Jul 2021 03:37:52 +0000 (03:37 +0000)
lifetimes, and allow the verification mode to specify a signature time
to check at. This is intended for use by git to support signing
objects using ssh keys. ok dtucker@

usr.bin/ssh/auth2-hostbased.c
usr.bin/ssh/auth2-pubkey.c
usr.bin/ssh/ssh-keygen.1
usr.bin/ssh/ssh-keygen.c
usr.bin/ssh/sshkey.c
usr.bin/ssh/sshkey.h
usr.bin/ssh/sshsig.h

index 3f201f3..cd95239 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-hostbased.c,v 1.46 2021/01/27 10:05:28 djm Exp $ */
+/* $OpenBSD: auth2-hostbased.c,v 1.47 2021/07/23 03:37:52 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -212,7 +212,7 @@ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
        debug2_f("access allowed by auth_rhosts2");
 
        if (sshkey_is_cert(key) &&
-           sshkey_cert_check_authority(key, 1, 0, 0, lookup, &reason)) {
+           sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) {
                error("%s", reason);
                auth_debug_add("%s", reason);
                return 0;
index 7c6fe33..527802b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.108 2021/06/08 06:54:40 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.109 2021/07/23 03:37:52 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -671,7 +671,7 @@ check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
                reason = "Certificate does not contain an authorized principal";
                goto fail_reason;
        }
-       if (sshkey_cert_check_authority(key, 0, 0, 0,
+       if (sshkey_cert_check_authority_now(key, 0, 0, 0,
            keyopts->cert_principals == NULL ? pw->pw_name : NULL,
            &reason) != 0)
                goto fail_reason;
@@ -791,7 +791,7 @@ user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
        }
        if (use_authorized_principals && principals_opts == NULL)
                fatal_f("internal error: missing principals_opts");
-       if (sshkey_cert_check_authority(key, 0, 1, 0,
+       if (sshkey_cert_check_authority_now(key, 0, 1, 0,
            use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
                goto fail_reason;
 
index 4e73727..9bfbcdc 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ssh-keygen.1,v 1.213 2021/05/12 11:34:30 dtucker Exp $
+.\"    $OpenBSD: ssh-keygen.1,v 1.214 2021/07/23 03:37:52 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: May 12 2021 $
+.Dd $Mdocdate: July 23 2021 $
 .Dt SSH-KEYGEN 1
 .Os
 .Sh NAME
 .Ar
 .Nm ssh-keygen
 .Fl Y Cm find-principals
+.Op Fl O Ar option
 .Fl s Ar signature_file
 .Fl f Ar allowed_signers_file
 .Nm ssh-keygen
 .Fl Y Cm check-novalidate
+.Op Fl O Ar option
 .Fl n Ar namespace
 .Fl s Ar signature_file
 .Nm ssh-keygen
 .Ar
 .Nm ssh-keygen
 .Fl Y Cm verify
+.Op Fl O Ar option
 .Fl f Ar allowed_signers_file
 .Fl I Ar signer_identity
 .Fl n Ar namespace
@@ -530,6 +533,17 @@ Please note that this information is potentially sensitive.
 By default, this information is discarded.
 .El
 .Pp
+When performing signature-related options using the
+.Fl Y
+flag, the following options are accepted:
+.Bl -tag -width Ds
+.It Cm verify-time Ns = Ns Ar timestamp
+Specifies a time to use when validating signatures instead of the current
+time.
+The time may be specified as a date in YYYYMMDD format or a time
+in YYYYMMDDHHMM[SS] format.
+.El
+.Pp
 The
 .Fl O
 option may be specified multiple times.
@@ -1134,11 +1148,16 @@ are case-insensitive):
 .It Cm cert-authority
 Indicates that this key is accepted as a certificate authority (CA) and
 that certificates signed by this CA may be accepted for verification.
-.It Cm namespaces="namespace-list"
+.It Cm namespaces Ns = Ns "namespace-list"
 Specifies a pattern-list of namespaces that are accepted for this key.
 If this option is present, the signature namespace embedded in the
 signature object and presented on the verification command-line must
 match the specified list before the key will be considered acceptable.
+.It Cm valid-after Ns = Ns "timestamp"
+Indicates that the key is valid for use at or after the specified timestamp,
+which may be a date in YYYYMMDD format or a time in YYYYMMDDHHMM[SS] format,
+.It Cm valid-before Ns = Ns "timestamp"
+Indicates that the key is valid for use at or before the specified timestamp.
 .El
 .Pp
 When verifying signatures made by certificates, the expected principal
index a5cd17b..dc70fb0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.431 2021/07/09 09:55:56 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.432 2021/07/23 03:37:52 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2649,15 +2649,49 @@ done:
        return ret;
 }
 
+static int
+sig_process_opts(char * const *opts, size_t nopts, uint64_t *verify_timep)
+{
+       size_t i;
+       time_t now;
+
+       *verify_timep = 0;
+       for (i = 0; i < nopts; i++) {
+               if (strncasecmp(opts[i], "verify-time=", 12) == 0) {
+                       if (parse_absolute_time(opts[i] + 12,
+                           verify_timep) != 0 || *verify_timep == 0) {
+                               error("Invalid \"verify-time\" option");
+                               return SSH_ERR_INVALID_ARGUMENT;
+                       }
+               } else {
+                       error("Invalid option \"%s\"", opts[i]);
+                       return SSH_ERR_INVALID_ARGUMENT;
+               }
+       }
+       if (*verify_timep == 0) {
+               if ((now = time(NULL)) < 0) {
+                       error("Time is before epoch");
+                       return SSH_ERR_INVALID_ARGUMENT;
+               }
+               *verify_timep = (uint64_t)now;
+       }
+       return 0;
+}
+
 static int
 sig_verify(const char *signature, const char *sig_namespace,
-    const char *principal, const char *allowed_keys, const char *revoked_keys)
+    const char *principal, const char *allowed_keys, const char *revoked_keys,
+    char * const *opts, size_t nopts)
 {
        int r, ret = -1;
        struct sshbuf *sigbuf = NULL, *abuf = NULL;
        struct sshkey *sign_key = NULL;
        char *fp = NULL;
        struct sshkey_sig_details *sig_details = NULL;
+       uint64_t verify_time = 0;
+
+       if (sig_process_opts(opts, nopts, &verify_time) != 0)
+               goto done; /* error already logged */
 
        memset(&sig_details, 0, sizeof(sig_details));
        if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
@@ -2692,7 +2726,7 @@ sig_verify(const char *signature, const char *sig_namespace,
        }
 
        if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys,
-           sign_key, principal, sig_namespace)) != 0) {
+           sign_key, principal, sig_namespace, verify_time)) != 0) {
                debug3_fr(r, "sshsig_check_allowed_keys");
                goto done;
        }
@@ -2726,11 +2760,17 @@ done:
 }
 
 static int
-sig_find_principals(const char *signature, const char *allowed_keys) {
+sig_find_principals(const char *signature, const char *allowed_keys,
+    char * const *opts, size_t nopts)
+{
        int r, ret = -1;
        struct sshbuf *sigbuf = NULL, *abuf = NULL;
        struct sshkey *sign_key = NULL;
        char *principals = NULL, *cp, *tmp;
+       uint64_t verify_time = 0;
+
+       if (sig_process_opts(opts, nopts, &verify_time) != 0)
+               goto done; /* error already logged */
 
        if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
                error_r(r, "Couldn't read signature file");
@@ -2745,7 +2785,7 @@ sig_find_principals(const char *signature, const char *allowed_keys) {
                goto done;
        }
        if ((r = sshsig_find_principals(allowed_keys, sign_key,
-           &principals)) != 0) {
+           verify_time, &principals)) != 0) {
                if (r != SSH_ERR_KEY_NOT_FOUND)
                        error_fr(r, "sshsig_find_principal");
                goto done;
@@ -3354,7 +3394,8 @@ main(int argc, char **argv)
                                    "missing allowed keys file");
                                exit(1);
                        }
-                       return sig_find_principals(ca_key_path, identity_file);
+                       return sig_find_principals(ca_key_path, identity_file,
+                           opts, nopts);
                } else if (strncmp(sign_op, "sign", 4) == 0) {
                        if (cert_principals == NULL ||
                            *cert_principals == '\0') {
@@ -3376,7 +3417,7 @@ main(int argc, char **argv)
                                exit(1);
                        }
                        return sig_verify(ca_key_path, cert_principals,
-                           NULL, NULL, NULL);
+                           NULL, NULL, NULL, opts, nopts);
                } else if (strncmp(sign_op, "verify", 6) == 0) {
                        if (cert_principals == NULL ||
                            *cert_principals == '\0') {
@@ -3400,7 +3441,8 @@ main(int argc, char **argv)
                                exit(1);
                        }
                        return sig_verify(ca_key_path, cert_principals,
-                           cert_key_id, identity_file, rr_hostname);
+                           cert_key_id, identity_file, rr_hostname,
+                           opts, nopts);
                }
                error("Unsupported operation for -Y: \"%s\"", sign_op);
                usage();
index 3e2fdbd..193f6ec 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.c,v 1.118 2021/07/12 06:08:57 dtucker Exp $ */
+/* $OpenBSD: sshkey.c,v 1.119 2021/07/23 03:37:52 djm Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Alexander von Gernler.  All rights reserved.
@@ -3025,10 +3025,9 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg,
 int
 sshkey_cert_check_authority(const struct sshkey *k,
     int want_host, int require_principal, int wildcard_pattern,
-    const char *name, const char **reason)
+    uint64_t verify_time, const char *name, const char **reason)
 {
        u_int i, principal_matches;
-       time_t now = time(NULL);
 
        if (reason == NULL)
                return SSH_ERR_INVALID_ARGUMENT;
@@ -3047,16 +3046,11 @@ sshkey_cert_check_authority(const struct sshkey *k,
                        return SSH_ERR_KEY_CERT_INVALID;
                }
        }
-       if (now < 0) {
-               /* yikes - system clock before epoch! */
-               *reason = "Certificate invalid: not yet valid";
-               return SSH_ERR_KEY_CERT_INVALID;
-       }
-       if ((u_int64_t)now < k->cert->valid_after) {
+       if (verify_time < k->cert->valid_after) {
                *reason = "Certificate invalid: not yet valid";
                return SSH_ERR_KEY_CERT_INVALID;
        }
-       if ((u_int64_t)now >= k->cert->valid_before) {
+       if (verify_time >= k->cert->valid_before) {
                *reason = "Certificate invalid: expired";
                return SSH_ERR_KEY_CERT_INVALID;
        }
@@ -3088,6 +3082,22 @@ sshkey_cert_check_authority(const struct sshkey *k,
        return 0;
 }
 
+int
+sshkey_cert_check_authority_now(const struct sshkey *k,
+    int want_host, int require_principal, int wildcard_pattern,
+    const char *name, const char **reason)
+{
+       time_t now;
+
+       if ((now = time(NULL)) < 0) {
+               /* yikes - system clock before epoch! */
+               *reason = "Certificate invalid: not yet valid";
+               return SSH_ERR_KEY_CERT_INVALID;
+       }
+       return sshkey_cert_check_authority(k, want_host, require_principal,
+           wildcard_pattern, (uint64_t)now, name, reason);
+}
+
 int
 sshkey_cert_check_host(const struct sshkey *key, const char *host,
     int wildcard_principals, const char *ca_sign_algorithms,
@@ -3095,7 +3105,7 @@ sshkey_cert_check_host(const struct sshkey *key, const char *host,
 {
        int r;
 
-       if ((r = sshkey_cert_check_authority(key, 1, 0, wildcard_principals,
+       if ((r = sshkey_cert_check_authority_now(key, 1, 0, wildcard_principals,
            host, reason)) != 0)
                return r;
        if (sshbuf_len(key->cert->critical) != 0) {
index 18b11b5..ecae86e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.h,v 1.49 2021/01/26 00:49:30 djm Exp $ */
+/* $OpenBSD: sshkey.h,v 1.50 2021/07/23 03:37:52 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -190,6 +190,8 @@ int  sshkey_to_certified(struct sshkey *);
 int     sshkey_drop_cert(struct sshkey *);
 int     sshkey_cert_copy(const struct sshkey *, struct sshkey *);
 int     sshkey_cert_check_authority(const struct sshkey *, int, int, int,
+    uint64_t, const char *, const char **);
+int     sshkey_cert_check_authority_now(const struct sshkey *, int, int, int,
     const char *, const char **);
 int     sshkey_cert_check_host(const struct sshkey *, const char *,
     int , const char *, const char **);
index 67794a9..b725c7d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshsig.h,v 1.9 2020/08/31 00:17:41 djm Exp $ */
+/* $OpenBSD: sshsig.h,v 1.10 2021/07/23 03:37:52 djm Exp $ */
 /*
  * Copyright (c) 2019 Google LLC
  *
@@ -86,7 +86,7 @@ int sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out);
  * an allowed_keys file. Returns 0 on success.
  */
 int sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
-    const char *principal, const char *ns);
+    const char *principal, const char *ns, uint64_t verify_time);
 
 /* Parse zero or more allowed_keys signature options */
 struct sshsigopt *sshsigopt_parse(const char *opts,
@@ -102,6 +102,6 @@ int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey);
  * 0 on success.
  */
 int sshsig_find_principals(const char *path, const struct sshkey *sign_key,
-    char **principal);
+    uint64_t verify_time, char **principal);
 
 #endif /* SSHSIG_H */