allow certificate validity intervals, sshsig verification times and
authordjm <djm@openbsd.org>
Thu, 11 Aug 2022 01:56:51 +0000 (01:56 +0000)
committerdjm <djm@openbsd.org>
Thu, 11 Aug 2022 01:56:51 +0000 (01:56 +0000)
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

usr.bin/ssh/misc.c
usr.bin/ssh/ssh-keygen.1
usr.bin/ssh/ssh-keygen.c
usr.bin/ssh/sshd.8

index a864b87..f3534e4 100644 (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) 2005-2020 Damien Miller.  All rights reserved.
@@ -2299,15 +2299,26 @@ parse_absolute_time(const char *s, uint64_t *tp)
        struct tm tm;
        time_t tt;
        char buf[32], *fmt;
+       const char *cp;
+       size_t l;
+       int is_utc = 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
         * is white-space or other non-alphanumeric characters between
         * any two conversion specifications" so arrange things this way.
         */
-       switch (strlen(s)) {
+       switch (l) {
        case 8: /* YYYYMMDD */
                fmt = "%Y-%m-%d";
                snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
@@ -2327,10 +2338,15 @@ parse_absolute_time(const char *s, uint64_t *tp)
        }
 
        memset(&tm, 0, sizeof(tm));
-       if (strptime(buf, fmt, &tm) == NULL)
-               return SSH_ERR_INVALID_FORMAT;
-       if ((tt = mktime(&tm)) < 0)
+       if ((cp = strptime(buf, fmt, &tm)) == NULL || *cp != '\0')
                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 */
        *tp = (uint64_t)tt;
        return 0;
index 5f42981..6aeab1c 100644 (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>
 .\" 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: June 3 2022 $
+.Dd $Mdocdate: August 11 2022 $
 .Dt SSH-KEYGEN 1
 .Os
 .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
 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.
+The time may be specified as a date or time in the YYYYMMDD[Z] or
+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
 .Pp
 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
 of two times separated by a colon to indicate an explicit time interval.
 .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
-to indicate the certificate has no specified start time,
-a date in YYYYMMDD format, a time in YYYYMMDDHHMM[SS] format,
-a relative time (to the current time) consisting of a minus sign followed by
-an interval in the format described in the
+to indicate the certificate has no specified start 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 before the current system time consisting of a minus 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
-The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMM[SS] time,
-a relative time starting with a plus character or the string
+The end time may be specified similarly to the start time:
+.Bl -bullet -compact
+.It
+The string
 .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
 For example:
-.Dq +52w1d
-(valid from now to 52 weeks and one day from now),
-.Dq -4w:+4w
-(valid from four weeks ago to four weeks from now),
-.Dq 20100101123000:20110101123000
-(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011),
-.Dq -1d:20110101
-(valid from yesterday to midnight, January 1st, 2011),
-.Dq -1m:forever
-(valid from one minute ago and never expiring).
+.Bl -tag -width Ds
+.It +52w1d
+Valid from now to 52 weeks and one day from now.
+.It -4w:+4w
+Valid from four weeks ago to four weeks from now.
+.It 20100101123000:20110101123000
+Valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011.
+.It 20100101123000Z:20110101123000Z
+Similar, but interpreted in the UTC time zone rather than the system time zone.
+.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
 Verbose mode.
 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.
 .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.
+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"
 Indicates that the key is valid for use at or before the specified timestamp.
 .El
index 556679d..e327158 100644 (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>
  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1893,6 +1893,21 @@ parse_relative_time(const char *s, time_t now)
        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
 parse_cert_times(char *timespec)
 {
@@ -1915,8 +1930,8 @@ parse_cert_times(char *timespec)
 
        /*
         * from:to, where
-        * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "always"
-        *   to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "forever"
+        * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "always"
+        *   to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "forever"
         */
        from = xstrdup(timespec);
        to = strchr(from, ':');
@@ -1928,6 +1943,8 @@ parse_cert_times(char *timespec)
                cert_valid_from = parse_relative_time(from, now);
        else if (strcmp(from, "always") == 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)
                fatal("Invalid from time \"%s\"", from);
 
@@ -1935,6 +1952,8 @@ parse_cert_times(char *timespec)
                cert_valid_to = parse_relative_time(to, now);
        else if (strcmp(to, "forever") == 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)
                fatal("Invalid to time \"%s\"", to);
 
index b3684ca..d356071 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: sshd.8,v 1.319 2022/05/02 05:40:37 jmc Exp $
-.Dd $Mdocdate: May 2 2022 $
+.\" $OpenBSD: sshd.8,v 1.320 2022/08/11 01:56:51 djm Exp $
+.Dd $Mdocdate: August 11 2022 $
 .Dt SSHD 8
 .Os
 .Sh NAME
@@ -506,8 +506,9 @@ controlled via the
 option.
 .It Cm expiry-time="timespec"
 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
-in the system time-zone.
+The time may be specified as a YYYYMMDD[Z] date or a YYYYMMDDHHMM[SS][Z] time.
+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"
 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