upstream: Let allowed signers files used by ssh-keygen(1)

signatures support key 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@

OpenBSD-Commit-ID: 3e2c67b7dcd94f0610194d1e8e4907829a40cf31
This commit is contained in:
djm@openbsd.org 2021-07-23 03:37:52 +00:00 committed by Damien Miller
parent 44142068dc
commit d0bb1ce731
7 changed files with 104 additions and 31 deletions

View 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. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -213,7 +213,7 @@ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
debug2_f("access allowed by auth_rhosts2"); debug2_f("access allowed by auth_rhosts2");
if (sshkey_is_cert(key) && 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); error("%s", reason);
auth_debug_add("%s", reason); auth_debug_add("%s", reason);
return 0; return 0;

View 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. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -674,7 +674,7 @@ check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
reason = "Certificate does not contain an authorized principal"; reason = "Certificate does not contain an authorized principal";
goto fail_reason; 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, keyopts->cert_principals == NULL ? pw->pw_name : NULL,
&reason) != 0) &reason) != 0)
goto fail_reason; goto fail_reason;
@ -794,7 +794,7 @@ user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
} }
if (use_authorized_principals && principals_opts == NULL) if (use_authorized_principals && principals_opts == NULL)
fatal_f("internal error: missing principals_opts"); 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) use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
goto fail_reason; goto fail_reason;

View 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> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" 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 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -147,10 +147,12 @@
.Ar .Ar
.Nm ssh-keygen .Nm ssh-keygen
.Fl Y Cm find-principals .Fl Y Cm find-principals
.Op Fl O Ar option
.Fl s Ar signature_file .Fl s Ar signature_file
.Fl f Ar allowed_signers_file .Fl f Ar allowed_signers_file
.Nm ssh-keygen .Nm ssh-keygen
.Fl Y Cm check-novalidate .Fl Y Cm check-novalidate
.Op Fl O Ar option
.Fl n Ar namespace .Fl n Ar namespace
.Fl s Ar signature_file .Fl s Ar signature_file
.Nm ssh-keygen .Nm ssh-keygen
@ -160,6 +162,7 @@
.Ar .Ar
.Nm ssh-keygen .Nm ssh-keygen
.Fl Y Cm verify .Fl Y Cm verify
.Op Fl O Ar option
.Fl f Ar allowed_signers_file .Fl f Ar allowed_signers_file
.Fl I Ar signer_identity .Fl I Ar signer_identity
.Fl n Ar namespace .Fl n Ar namespace
@ -530,6 +533,17 @@ Please note that this information is potentially sensitive.
By default, this information is discarded. By default, this information is discarded.
.El .El
.Pp .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 The
.Fl O .Fl O
option may be specified multiple times. option may be specified multiple times.
@ -1134,11 +1148,16 @@ are case-insensitive):
.It Cm cert-authority .It Cm cert-authority
Indicates that this key is accepted as a certificate authority (CA) and Indicates that this key is accepted as a certificate authority (CA) and
that certificates signed by this CA may be accepted for verification. 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. Specifies a pattern-list of namespaces that are accepted for this key.
If this option is present, the signature namespace embedded in the If this option is present, the signature namespace embedded in the
signature object and presented on the verification command-line must signature object and presented on the verification command-line must
match the specified list before the key will be considered acceptable. 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 .El
.Pp .Pp
When verifying signatures made by certificates, the expected principal When verifying signatures made by certificates, the expected principal

View 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> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2672,15 +2672,49 @@ done:
return ret; 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 static int
sig_verify(const char *signature, const char *sig_namespace, 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; int r, ret = -1;
struct sshbuf *sigbuf = NULL, *abuf = NULL; struct sshbuf *sigbuf = NULL, *abuf = NULL;
struct sshkey *sign_key = NULL; struct sshkey *sign_key = NULL;
char *fp = NULL; char *fp = NULL;
struct sshkey_sig_details *sig_details = 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)); memset(&sig_details, 0, sizeof(sig_details));
if ((r = sshbuf_load_file(signature, &abuf)) != 0) { if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
@ -2715,7 +2749,7 @@ sig_verify(const char *signature, const char *sig_namespace,
} }
if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys, 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"); debug3_fr(r, "sshsig_check_allowed_keys");
goto done; goto done;
} }
@ -2749,11 +2783,17 @@ done:
} }
static int 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; int r, ret = -1;
struct sshbuf *sigbuf = NULL, *abuf = NULL; struct sshbuf *sigbuf = NULL, *abuf = NULL;
struct sshkey *sign_key = NULL; struct sshkey *sign_key = NULL;
char *principals = NULL, *cp, *tmp; 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) { if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
error_r(r, "Couldn't read signature file"); error_r(r, "Couldn't read signature file");
@ -2768,7 +2808,7 @@ sig_find_principals(const char *signature, const char *allowed_keys) {
goto done; goto done;
} }
if ((r = sshsig_find_principals(allowed_keys, sign_key, if ((r = sshsig_find_principals(allowed_keys, sign_key,
&principals)) != 0) { verify_time, &principals)) != 0) {
if (r != SSH_ERR_KEY_NOT_FOUND) if (r != SSH_ERR_KEY_NOT_FOUND)
error_fr(r, "sshsig_find_principal"); error_fr(r, "sshsig_find_principal");
goto done; goto done;
@ -3380,7 +3420,8 @@ main(int argc, char **argv)
"missing allowed keys file"); "missing allowed keys file");
exit(1); 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) { } else if (strncmp(sign_op, "sign", 4) == 0) {
if (cert_principals == NULL || if (cert_principals == NULL ||
*cert_principals == '\0') { *cert_principals == '\0') {
@ -3402,7 +3443,7 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
return sig_verify(ca_key_path, cert_principals, return sig_verify(ca_key_path, cert_principals,
NULL, NULL, NULL); NULL, NULL, NULL, opts, nopts);
} else if (strncmp(sign_op, "verify", 6) == 0) { } else if (strncmp(sign_op, "verify", 6) == 0) {
if (cert_principals == NULL || if (cert_principals == NULL ||
*cert_principals == '\0') { *cert_principals == '\0') {
@ -3426,7 +3467,8 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
return sig_verify(ca_key_path, cert_principals, 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); error("Unsupported operation for -Y: \"%s\"", sign_op);
usage(); usage();

View 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) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -3077,10 +3077,9 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg,
int int
sshkey_cert_check_authority(const struct sshkey *k, sshkey_cert_check_authority(const struct sshkey *k,
int want_host, int require_principal, int wildcard_pattern, 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; u_int i, principal_matches;
time_t now = time(NULL);
if (reason == NULL) if (reason == NULL)
return SSH_ERR_INVALID_ARGUMENT; return SSH_ERR_INVALID_ARGUMENT;
@ -3099,16 +3098,11 @@ sshkey_cert_check_authority(const struct sshkey *k,
return SSH_ERR_KEY_CERT_INVALID; return SSH_ERR_KEY_CERT_INVALID;
} }
} }
if (now < 0) { if (verify_time < k->cert->valid_after) {
/* yikes - system clock before epoch! */
*reason = "Certificate invalid: not yet valid"; *reason = "Certificate invalid: not yet valid";
return SSH_ERR_KEY_CERT_INVALID; return SSH_ERR_KEY_CERT_INVALID;
} }
if ((u_int64_t)now < k->cert->valid_after) { if (verify_time >= k->cert->valid_before) {
*reason = "Certificate invalid: not yet valid";
return SSH_ERR_KEY_CERT_INVALID;
}
if ((u_int64_t)now >= k->cert->valid_before) {
*reason = "Certificate invalid: expired"; *reason = "Certificate invalid: expired";
return SSH_ERR_KEY_CERT_INVALID; return SSH_ERR_KEY_CERT_INVALID;
} }
@ -3140,6 +3134,22 @@ sshkey_cert_check_authority(const struct sshkey *k,
return 0; 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 int
sshkey_cert_check_host(const struct sshkey *key, const char *host, sshkey_cert_check_host(const struct sshkey *key, const char *host,
int wildcard_principals, const char *ca_sign_algorithms, int wildcard_principals, const char *ca_sign_algorithms,
@ -3147,7 +3157,7 @@ sshkey_cert_check_host(const struct sshkey *key, const char *host,
{ {
int r; 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) host, reason)) != 0)
return r; return r;
if (sshbuf_len(key->cert->critical) != 0) { if (sshbuf_len(key->cert->critical) != 0) {

View 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. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -196,6 +196,8 @@ int sshkey_to_certified(struct sshkey *);
int sshkey_drop_cert(struct sshkey *); int sshkey_drop_cert(struct sshkey *);
int sshkey_cert_copy(const struct sshkey *, struct sshkey *); int sshkey_cert_copy(const struct sshkey *, struct sshkey *);
int sshkey_cert_check_authority(const struct sshkey *, int, int, int, 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 **); const char *, const char **);
int sshkey_cert_check_host(const struct sshkey *, const char *, int sshkey_cert_check_host(const struct sshkey *, const char *,
int , const char *, const char **); int , const char *, const char **);

View 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 * 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. * an allowed_keys file. Returns 0 on success.
*/ */
int sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, 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 */ /* Parse zero or more allowed_keys signature options */
struct sshsigopt *sshsigopt_parse(const char *opts, struct sshsigopt *sshsigopt_parse(const char *opts,
@ -102,6 +102,6 @@ int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey);
* 0 on success. * 0 on success.
*/ */
int sshsig_find_principals(const char *path, const struct sshkey *sign_key, int sshsig_find_principals(const char *path, const struct sshkey *sign_key,
char **principal); uint64_t verify_time, char **principal);
#endif /* SSHSIG_H */ #endif /* SSHSIG_H */