upstream: allow certificate validity intervals, sshsig verification

times and authorized_keys expiry-time options to accept dates in the UTC time
zone in addition to the default of interpreting them in the system time zone.
YYYYMMDD and YYMMDDHHMM[SS] dates/times will be interpreted as UTC if
suffixed with a 'Z' character.

Also allow certificate validity intervals to be specified in raw
seconds-since-epoch as hex value, e.g. -V 0x1234:0x4567890. This
is intended for use by regress tests and other tools that call
ssh-keygen as part of a CA workflow.

bz3468 ok dtucker

OpenBSD-Commit-ID: 454db1cdffa9fa346aea5211223a2ce0588dfe13
This commit is contained in:
djm@openbsd.org 2022-08-11 01:56:51 +00:00 committed by Damien Miller
parent 4df246ec75
commit ec1ddb72a1
4 changed files with 113 additions and 35 deletions

26
misc.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.176 2022/06/03 04:30:47 djm Exp $ */ /* $OpenBSD: misc.c,v 1.177 2022/08/11 01:56:51 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved. * Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@ -2399,15 +2399,26 @@ parse_absolute_time(const char *s, uint64_t *tp)
struct tm tm; struct tm tm;
time_t tt; time_t tt;
char buf[32], *fmt; char buf[32], *fmt;
const char *cp;
size_t l;
int is_utc = 0;
*tp = 0; *tp = 0;
l = strlen(s);
if (l > 1 && strcasecmp(s + l - 1, "Z") == 0) {
is_utc = 1;
l--;
} else if (l > 3 && strcasecmp(s + l - 3, "UTC") == 0) {
is_utc = 1;
l -= 3;
}
/* /*
* POSIX strptime says "The application shall ensure that there * POSIX strptime says "The application shall ensure that there
* is white-space or other non-alphanumeric characters between * is white-space or other non-alphanumeric characters between
* any two conversion specifications" so arrange things this way. * any two conversion specifications" so arrange things this way.
*/ */
switch (strlen(s)) { switch (l) {
case 8: /* YYYYMMDD */ case 8: /* YYYYMMDD */
fmt = "%Y-%m-%d"; fmt = "%Y-%m-%d";
snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
@ -2427,10 +2438,15 @@ parse_absolute_time(const char *s, uint64_t *tp)
} }
memset(&tm, 0, sizeof(tm)); memset(&tm, 0, sizeof(tm));
if (strptime(buf, fmt, &tm) == NULL) if ((cp = strptime(buf, fmt, &tm)) == NULL || *cp != '\0')
return SSH_ERR_INVALID_FORMAT;
if ((tt = mktime(&tm)) < 0)
return SSH_ERR_INVALID_FORMAT; return SSH_ERR_INVALID_FORMAT;
if (is_utc) {
if ((tt = timegm(&tm)) < 0)
return SSH_ERR_INVALID_FORMAT;
} else {
if ((tt = mktime(&tm)) < 0)
return SSH_ERR_INVALID_FORMAT;
}
/* success */ /* success */
*tp = (uint64_t)tt; *tp = (uint64_t)tt;
return 0; return 0;

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.223 2022/06/03 03:17:42 dtucker Exp $ .\" $OpenBSD: ssh-keygen.1,v 1.224 2022/08/11 01:56:51 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: June 3 2022 $ .Dd $Mdocdate: August 11 2022 $
.Dt SSH-KEYGEN 1 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -511,8 +511,11 @@ Print the full public key to standard output after signature verification.
.It Cm verify-time Ns = Ns Ar timestamp .It Cm verify-time Ns = Ns Ar timestamp
Specifies a time to use when validating signatures instead of the current Specifies a time to use when validating signatures instead of the current
time. time.
The time may be specified as a date in YYYYMMDD format or a time The time may be specified as a date or time in the YYYYMMDD[Z] or
in YYYYMMDDHHMM[SS] format. in YYYYMMDDHHMM[SS][Z] formats.
Dates and times will be interpreted in the current system time zone unless
suffixed with a Z character, which causes them to be interpreted in the
UTC time zone.
.El .El
.Pp .Pp
The The
@ -603,31 +606,67 @@ A validity interval may consist of a single time, indicating that the
certificate is valid beginning now and expiring at that time, or may consist certificate is valid beginning now and expiring at that time, or may consist
of two times separated by a colon to indicate an explicit time interval. of two times separated by a colon to indicate an explicit time interval.
.Pp .Pp
The start time may be specified as the string The start time may be specified as:
.Bl -bullet -compact
.It
The string
.Dq always .Dq always
to indicate the certificate has no specified start time, to indicate the certificate has no specified start time.
a date in YYYYMMDD format, a time in YYYYMMDDHHMM[SS] format, .It
a relative time (to the current time) consisting of a minus sign followed by A date or time in the system time zone formatted as YYYYMMDD or
an interval in the format described in the YYYYMMDDHHMM[SS].
.It
A date or time in the UTC time zone as YYYYMMDDZ or YYYYMMDDHHMM[SS]Z.
.It
A relative time before the current system time consisting of a minus sign
followed by an interval in the format described in the
TIME FORMATS section of TIME FORMATS section of
.Xr sshd_config 5 . .Xr sshd_config 5 .
.It
A raw seconds since epoch (Jan 1 1970 00:00:00 UTC) as a hexadecimal
number beginning with
.Dq 0x .
.El
.Pp .Pp
The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMM[SS] time, The end time may be specified similarly to the start time:
a relative time starting with a plus character or the string .Bl -bullet -compact
.It
The string
.Dq forever .Dq forever
to indicate that the certificate has no expiry date. to indicate the certificate has no specified end time.
.It
A date or time in the system time zone formatted as YYYYMMDD or
YYYYMMDDHHMM[SS].
.It
A date or time in the UTC time zone as YYYYMMDDZ or YYYYMMDDHHMM[SS]Z.
.It
A relative time after the current system time consisting of a plus sign
followed by an interval in the format described in the
TIME FORMATS section of
.Xr sshd_config 5 .
.It
A raw seconds since epoch (Jan 1 1970 00:00:00 UTC) as a hexadecimal
number beginning with
.Dq 0x .
.El
.Pp .Pp
For example: For example:
.Dq +52w1d .Bl -tag -width Ds
(valid from now to 52 weeks and one day from now), .It +52w1d
.Dq -4w:+4w Valid from now to 52 weeks and one day from now.
(valid from four weeks ago to four weeks from now), .It -4w:+4w
.Dq 20100101123000:20110101123000 Valid from four weeks ago to four weeks from now.
(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011), .It 20100101123000:20110101123000
.Dq -1d:20110101 Valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011.
(valid from yesterday to midnight, January 1st, 2011), .It 20100101123000Z:20110101123000Z
.Dq -1m:forever Similar, but interpreted in the UTC time zone rather than the system time zone.
(valid from one minute ago and never expiring). .It -1d:20110101
Valid from yesterday to midnight, January 1st, 2011.
.It 0x1:0x2000000000
Valid from roughly early 1970 to May 2033.
.It -1m:forever
Valid from one minute ago and never expiring.
.El
.It Fl v .It Fl v
Verbose mode. Verbose mode.
Causes Causes
@ -1206,7 +1245,10 @@ 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" .It Cm valid-after Ns = Ns "timestamp"
Indicates that the key is valid for use at or after the specified 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. which may be a date or time in the YYYYMMDD[Z] or YYYYMMDDHHMM[SS][Z] formats.
Dates and times will be interpreted in the current system time zone unless
suffixed with a Z character, which causes them to be interpreted in the UTC
time zone.
.It Cm valid-before Ns = Ns "timestamp" .It Cm valid-before Ns = Ns "timestamp"
Indicates that the key is valid for use at or before the specified timestamp. Indicates that the key is valid for use at or before the specified timestamp.
.El .El

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.458 2022/08/05 05:01:40 djm Exp $ */ /* $OpenBSD: ssh-keygen.c,v 1.459 2022/08/11 01:56:51 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
@ -1916,6 +1916,21 @@ parse_relative_time(const char *s, time_t now)
return now + (u_int64_t)(secs * mul); return now + (u_int64_t)(secs * mul);
} }
static void
parse_hex_u64(const char *s, uint64_t *up)
{
char *ep;
unsigned long long ull;
errno = 0;
ull = strtoull(s, &ep, 16);
if (*s == '\0' || *ep != '\0')
fatal("Invalid certificate time: not a number");
if (errno == ERANGE && ull == ULONG_MAX)
fatal_fr(SSH_ERR_SYSTEM_ERROR, "Invalid certificate time");
*up = (uint64_t)ull;
}
static void static void
parse_cert_times(char *timespec) parse_cert_times(char *timespec)
{ {
@ -1938,8 +1953,8 @@ parse_cert_times(char *timespec)
/* /*
* from:to, where * from:to, where
* from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "always" * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "always"
* to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "forever" * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "forever"
*/ */
from = xstrdup(timespec); from = xstrdup(timespec);
to = strchr(from, ':'); to = strchr(from, ':');
@ -1951,6 +1966,8 @@ parse_cert_times(char *timespec)
cert_valid_from = parse_relative_time(from, now); cert_valid_from = parse_relative_time(from, now);
else if (strcmp(from, "always") == 0) else if (strcmp(from, "always") == 0)
cert_valid_from = 0; cert_valid_from = 0;
else if (strncmp(from, "0x", 2) == 0)
parse_hex_u64(from, &cert_valid_from);
else if (parse_absolute_time(from, &cert_valid_from) != 0) else if (parse_absolute_time(from, &cert_valid_from) != 0)
fatal("Invalid from time \"%s\"", from); fatal("Invalid from time \"%s\"", from);
@ -1958,6 +1975,8 @@ parse_cert_times(char *timespec)
cert_valid_to = parse_relative_time(to, now); cert_valid_to = parse_relative_time(to, now);
else if (strcmp(to, "forever") == 0) else if (strcmp(to, "forever") == 0)
cert_valid_to = ~(u_int64_t)0; cert_valid_to = ~(u_int64_t)0;
else if (strncmp(from, "0x", 2) == 0)
parse_hex_u64(to, &cert_valid_to);
else if (parse_absolute_time(to, &cert_valid_to) != 0) else if (parse_absolute_time(to, &cert_valid_to) != 0)
fatal("Invalid to time \"%s\"", to); fatal("Invalid to time \"%s\"", to);

9
sshd.8
View File

@ -33,8 +33,8 @@
.\" (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.
.\" .\"
.\" $OpenBSD: sshd.8,v 1.319 2022/05/02 05:40:37 jmc Exp $ .\" $OpenBSD: sshd.8,v 1.320 2022/08/11 01:56:51 djm Exp $
.Dd $Mdocdate: May 2 2022 $ .Dd $Mdocdate: August 11 2022 $
.Dt SSHD 8 .Dt SSHD 8
.Os .Os
.Sh NAME .Sh NAME
@ -533,8 +533,9 @@ controlled via the
option. option.
.It Cm expiry-time="timespec" .It Cm expiry-time="timespec"
Specifies a time after which the key will not be accepted. Specifies a time after which the key will not be accepted.
The time may be specified as a YYYYMMDD date or a YYYYMMDDHHMM[SS] time The time may be specified as a YYYYMMDD[Z] date or a YYYYMMDDHHMM[SS][Z] time.
in the system time-zone. Dates and times will be interpreted in the system time zone unless suffixed
by a Z character, in which case they will be interpreted in the UTC time zone.
.It Cm from="pattern-list" .It Cm from="pattern-list"
Specifies that in addition to public key authentication, either the canonical Specifies that in addition to public key authentication, either the canonical
name of the remote host or its IP address must be present in the name of the remote host or its IP address must be present in the