-/* $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.
*
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;
-/* $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.
*
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;
}
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;
-.\" $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
.\" (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
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.
.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
-/* $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
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) {
}
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;
}
}
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");
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;
"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') {
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') {
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();
-/* $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.
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;
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;
}
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,
{
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) {
-/* $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.
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 **);
-/* $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
*
* 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,
* 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 */