summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--auth-options.c32
-rw-r--r--auth-options.h5
-rw-r--r--auth.c28
-rw-r--r--misc.c55
-rw-r--r--misc.h4
-rw-r--r--ssh-keygen.18
-rw-r--r--ssh-keygen.c44
-rw-r--r--sshd.88
8 files changed, 128 insertions, 56 deletions
diff --git a/auth-options.c b/auth-options.c
index 484e44b74..38211fa2a 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-options.c,v 1.76 2018/03/03 03:15:51 djm Exp $ */ 1/* $OpenBSD: auth-options.c,v 1.77 2018/03/12 00:52:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2018 Damien Miller <djm@mindrot.org> 3 * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
4 * 4 *
@@ -311,6 +311,7 @@ sshauthopt_parse(const char *opts, const char **errstrp)
311 int r; 311 int r;
312 struct sshauthopt *ret = NULL; 312 struct sshauthopt *ret = NULL;
313 const char *errstr = "unknown error"; 313 const char *errstr = "unknown error";
314 uint64_t valid_before;
314 315
315 if (errstrp != NULL) 316 if (errstrp != NULL)
316 *errstrp = NULL; 317 *errstrp = NULL;
@@ -366,6 +367,19 @@ sshauthopt_parse(const char *opts, const char **errstrp)
366 &errstr); 367 &errstr);
367 if (ret->required_from_host_keys == NULL) 368 if (ret->required_from_host_keys == NULL)
368 goto fail; 369 goto fail;
370 } else if (opt_match(&opts, "valid-before")) {
371 if ((opt = opt_dequote(&opts, &errstr)) == NULL)
372 goto fail;
373 if (parse_absolute_time(opt, &valid_before) != 0 ||
374 valid_before == 0) {
375 free(opt);
376 errstr = "invalid expires time";
377 goto fail;
378 }
379 free(opt);
380 if (ret->valid_before == 0 ||
381 valid_before < ret->valid_before)
382 ret->valid_before = valid_before;
369 } else if (opt_match(&opts, "environment")) { 383 } else if (opt_match(&opts, "environment")) {
370 if (ret->nenv > INT_MAX) { 384 if (ret->nenv > INT_MAX) {
371 errstr = "too many environment strings"; 385 errstr = "too many environment strings";
@@ -572,6 +586,13 @@ sshauthopt_merge(const struct sshauthopt *primary,
572 OPTFLAG(permit_user_rc); 586 OPTFLAG(permit_user_rc);
573#undef OPTFLAG 587#undef OPTFLAG
574 588
589 /* Earliest expiry time should win */
590 if (primary->valid_before != 0)
591 ret->valid_before = primary->valid_before;
592 if (additional->valid_before != 0 &&
593 additional->valid_before < ret->valid_before)
594 ret->valid_before = additional->valid_before;
595
575 /* 596 /*
576 * When both multiple forced-command are specified, only 597 * When both multiple forced-command are specified, only
577 * proceed if they are identical, otherwise fail. 598 * proceed if they are identical, otherwise fail.
@@ -631,6 +652,7 @@ sshauthopt_copy(const struct sshauthopt *orig)
631 OPTSCALAR(restricted); 652 OPTSCALAR(restricted);
632 OPTSCALAR(cert_authority); 653 OPTSCALAR(cert_authority);
633 OPTSCALAR(force_tun_device); 654 OPTSCALAR(force_tun_device);
655 OPTSCALAR(valid_before);
634#undef OPTSCALAR 656#undef OPTSCALAR
635#define OPTSTRING(x) \ 657#define OPTSTRING(x) \
636 do { \ 658 do { \
@@ -751,14 +773,15 @@ sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
751{ 773{
752 int r = SSH_ERR_INTERNAL_ERROR; 774 int r = SSH_ERR_INTERNAL_ERROR;
753 775
754 /* Flag options */ 776 /* Flag and simple integer options */
755 if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 || 777 if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 ||
756 (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 || 778 (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 ||
757 (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 || 779 (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 ||
758 (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 || 780 (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 ||
759 (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 || 781 (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 ||
760 (r = sshbuf_put_u8(m, opts->restricted)) != 0 || 782 (r = sshbuf_put_u8(m, opts->restricted)) != 0 ||
761 (r = sshbuf_put_u8(m, opts->cert_authority)) != 0) 783 (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 ||
784 (r = sshbuf_put_u64(m, opts->valid_before)) != 0)
762 return r; 785 return r;
763 786
764 /* tunnel number can be negative to indicate "unset" */ 787 /* tunnel number can be negative to indicate "unset" */
@@ -815,6 +838,9 @@ sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp)
815 OPT_FLAG(cert_authority); 838 OPT_FLAG(cert_authority);
816#undef OPT_FLAG 839#undef OPT_FLAG
817 840
841 if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0)
842 goto out;
843
818 /* tunnel number can be negative to indicate "unset" */ 844 /* tunnel number can be negative to indicate "unset" */
819 if ((r = sshbuf_get_u8(m, &f)) != 0 || 845 if ((r = sshbuf_get_u8(m, &f)) != 0 ||
820 (r = sshbuf_get_u32(m, &tmp)) != 0) 846 (r = sshbuf_get_u32(m, &tmp)) != 0)
diff --git a/auth-options.h b/auth-options.h
index 16871d754..bf59b30be 100644
--- a/auth-options.h
+++ b/auth-options.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-options.h,v 1.25 2018/03/03 03:15:51 djm Exp $ */ 1/* $OpenBSD: auth-options.h,v 1.26 2018/03/12 00:52:01 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2018 Damien Miller <djm@mindrot.org> 4 * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
@@ -37,6 +37,9 @@ struct sshauthopt {
37 /* "restrict" keyword was invoked */ 37 /* "restrict" keyword was invoked */
38 int restricted; 38 int restricted;
39 39
40 /* key/principal expiry date */
41 uint64_t valid_before;
42
40 /* Certificate-related options */ 43 /* Certificate-related options */
41 int cert_authority; 44 int cert_authority;
42 char *cert_principals; 45 char *cert_principals;
diff --git a/auth.c b/auth.c
index 041a09e3f..63366768a 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.c,v 1.126 2018/03/03 03:15:51 djm Exp $ */ 1/* $OpenBSD: auth.c,v 1.127 2018/03/12 00:52:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -1004,20 +1004,21 @@ auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
1004 int do_permitopen = opts->npermitopen > 0 && 1004 int do_permitopen = opts->npermitopen > 0 &&
1005 (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0; 1005 (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0;
1006 size_t i; 1006 size_t i;
1007 char msg[1024], tbuf[32]; 1007 char msg[1024], buf[64];
1008 1008
1009 snprintf(tbuf, sizeof(tbuf), "%d", opts->force_tun_device); 1009 snprintf(buf, sizeof(buf), "%d", opts->force_tun_device);
1010 /* Try to keep this alphabetically sorted */ 1010 /* Try to keep this alphabetically sorted */
1011 snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s", 1011 snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s",
1012 opts->permit_agent_forwarding_flag ? " agent-forwarding" : "", 1012 opts->permit_agent_forwarding_flag ? " agent-forwarding" : "",
1013 opts->force_command == NULL ? "" : " command", 1013 opts->force_command == NULL ? "" : " command",
1014 do_env ? " environment" : "", 1014 do_env ? " environment" : "",
1015 opts->valid_before == 0 ? "" : "expires",
1015 do_permitopen ? " permitopen" : "", 1016 do_permitopen ? " permitopen" : "",
1016 opts->permit_port_forwarding_flag ? " port-forwarding" : "", 1017 opts->permit_port_forwarding_flag ? " port-forwarding" : "",
1017 opts->cert_principals == NULL ? "" : " principals", 1018 opts->cert_principals == NULL ? "" : " principals",
1018 opts->permit_pty_flag ? " pty" : "", 1019 opts->permit_pty_flag ? " pty" : "",
1019 opts->force_tun_device == -1 ? "" : " tun=", 1020 opts->force_tun_device == -1 ? "" : " tun=",
1020 opts->force_tun_device == -1 ? "" : tbuf, 1021 opts->force_tun_device == -1 ? "" : buf,
1021 opts->permit_user_rc ? " user-rc" : "", 1022 opts->permit_user_rc ? " user-rc" : "",
1022 opts->permit_x11_forwarding_flag ? " x11-forwarding" : ""); 1023 opts->permit_x11_forwarding_flag ? " x11-forwarding" : "");
1023 1024
@@ -1036,6 +1037,10 @@ auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
1036 } 1037 }
1037 1038
1038 /* Go into a little more details for the local logs. */ 1039 /* Go into a little more details for the local logs. */
1040 if (opts->valid_before != 0) {
1041 format_absolute_time(opts->valid_before, buf, sizeof(buf));
1042 debug("%s: expires at %s", loc, buf);
1043 }
1039 if (opts->cert_principals != NULL) { 1044 if (opts->cert_principals != NULL) {
1040 debug("%s: authorized principals: \"%s\"", 1045 debug("%s: authorized principals: \"%s\"",
1041 loc, opts->cert_principals); 1046 loc, opts->cert_principals);
@@ -1089,7 +1094,20 @@ auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw,
1089 const char *remote_ip = ssh_remote_ipaddr(ssh); 1094 const char *remote_ip = ssh_remote_ipaddr(ssh);
1090 const char *remote_host = auth_get_canonical_hostname(ssh, 1095 const char *remote_host = auth_get_canonical_hostname(ssh,
1091 options.use_dns); 1096 options.use_dns);
1097 time_t now = time(NULL);
1098 char buf[64];
1092 1099
1100 /*
1101 * Check keys/principals file expiry time.
1102 * NB. validity interval in certificate is handled elsewhere.
1103 */
1104 if (opts->valid_before && now > 0 &&
1105 opts->valid_before < (uint64_t)now) {
1106 format_absolute_time(opts->valid_before, buf, sizeof(buf));
1107 debug("%s: entry expired at %s", loc, buf);
1108 auth_debug_add("%s: entry expired at %s", loc, buf);
1109 return -1;
1110 }
1093 /* Consistency checks */ 1111 /* Consistency checks */
1094 if (opts->cert_principals != NULL && !opts->cert_authority) { 1112 if (opts->cert_principals != NULL && !opts->cert_authority) {
1095 debug("%s: principals on non-CA key", loc); 1113 debug("%s: principals on non-CA key", loc);
diff --git a/misc.c b/misc.c
index fbc363100..874dcc8a2 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.126 2018/03/07 23:53:08 djm Exp $ */ 1/* $OpenBSD: misc.c,v 1.127 2018/03/12 00:52:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -1976,3 +1976,56 @@ atoi_err(const char *nptr, int *val)
1976 *val = (int)num; 1976 *val = (int)num;
1977 return errstr; 1977 return errstr;
1978} 1978}
1979
1980int
1981parse_absolute_time(const char *s, uint64_t *tp)
1982{
1983 struct tm tm;
1984 time_t tt;
1985 char buf[32], *fmt;
1986
1987 *tp = 0;
1988
1989 /*
1990 * POSIX strptime says "The application shall ensure that there
1991 * is white-space or other non-alphanumeric characters between
1992 * any two conversion specifications" so arrange things this way.
1993 */
1994 switch (strlen(s)) {
1995 case 8: /* YYYYMMDD */
1996 fmt = "%Y-%m-%d";
1997 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
1998 break;
1999 case 12: /* YYYYMMDDHHMM */
2000 fmt = "%Y-%m-%dT%H:%M";
2001 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s",
2002 s, s + 4, s + 6, s + 8, s + 10);
2003 break;
2004 case 14: /* YYYYMMDDHHMMSS */
2005 fmt = "%Y-%m-%dT%H:%M:%S";
2006 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
2007 s, s + 4, s + 6, s + 8, s + 10, s + 12);
2008 break;
2009 default:
2010 return SSH_ERR_INVALID_FORMAT;
2011 }
2012
2013 memset(&tm, 0, sizeof(tm));
2014 if (strptime(buf, fmt, &tm) == NULL)
2015 return SSH_ERR_INVALID_FORMAT;
2016 if ((tt = mktime(&tm)) < 0)
2017 return SSH_ERR_INVALID_FORMAT;
2018 /* success */
2019 *tp = (uint64_t)tt;
2020 return 0;
2021}
2022
2023void
2024format_absolute_time(uint64_t t, char *buf, size_t len)
2025{
2026 time_t tt = t > INT_MAX ? INT_MAX : t; /* XXX revisit in 2038 :P */
2027 struct tm tm;
2028
2029 localtime_r(&tt, &tm);
2030 strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
2031}
diff --git a/misc.h b/misc.h
index 8f7780675..cdafea735 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.h,v 1.70 2018/01/08 15:21:49 markus Exp $ */ 1/* $OpenBSD: misc.h,v 1.71 2018/03/12 00:52:01 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -75,6 +75,8 @@ void lowercase(char *s);
75int unix_listener(const char *, int, int); 75int unix_listener(const char *, int, int);
76int valid_domain(char *, int, const char **); 76int valid_domain(char *, int, const char **);
77const char *atoi_err(const char *, int *); 77const char *atoi_err(const char *, int *);
78int parse_absolute_time(const char *, uint64_t *);
79void format_absolute_time(uint64_t, char *, size_t);
78 80
79void sock_set_v6only(int); 81void sock_set_v6only(int);
80 82
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index f925eb2d7..3525d7d17 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: ssh-keygen.1,v 1.146 2018/01/25 03:34:43 djm Exp $ 1.\" $OpenBSD: ssh-keygen.1,v 1.147 2018/03/12 00:52:01 djm Exp $
2.\" 2.\"
3.\" Author: Tatu Ylonen <ylo@cs.hut.fi> 3.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
4.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,7 @@
35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37.\" 37.\"
38.Dd $Mdocdate: January 25 2018 $ 38.Dd $Mdocdate: March 12 2018 $
39.Dt SSH-KEYGEN 1 39.Dt SSH-KEYGEN 1
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -588,13 +588,13 @@ of two times separated by a colon to indicate an explicit time interval.
588The start time may be specified as the string 588The start time may be specified as the string
589.Dq always 589.Dq always
590to indicate the certificate has no specified start time, 590to indicate the certificate has no specified start time,
591a date in YYYYMMDD format, a time in YYYYMMDDHHMMSS format, 591a date in YYYYMMDD format, a time in YYYYMMDDHHMM[SS] format,
592a relative time (to the current time) consisting of a minus sign followed by 592a relative time (to the current time) consisting of a minus sign followed by
593an interval in the format described in the 593an interval in the format described in the
594TIME FORMATS section of 594TIME FORMATS section of
595.Xr sshd_config 5 . 595.Xr sshd_config 5 .
596.Pp 596.Pp
597The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time, 597The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMM[SS] time,
598a relative time starting with a plus character or the string 598a relative time starting with a plus character or the string
599.Dq forever 599.Dq forever
600to indicate that the certificate has no expirty date. 600to indicate that the certificate has no expirty date.
diff --git a/ssh-keygen.c b/ssh-keygen.c
index d80930eeb..9aac64fc3 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keygen.c,v 1.313 2018/02/23 15:58:38 markus Exp $ */ 1/* $OpenBSD: ssh-keygen.c,v 1.314 2018/03/12 00:52:01 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1798,40 +1798,6 @@ parse_relative_time(const char *s, time_t now)
1798 return now + (u_int64_t)(secs * mul); 1798 return now + (u_int64_t)(secs * mul);
1799} 1799}
1800 1800
1801static u_int64_t
1802parse_absolute_time(const char *s)
1803{
1804 struct tm tm;
1805 time_t tt;
1806 char buf[32], *fmt;
1807
1808 /*
1809 * POSIX strptime says "The application shall ensure that there
1810 * is white-space or other non-alphanumeric characters between
1811 * any two conversion specifications" so arrange things this way.
1812 */
1813 switch (strlen(s)) {
1814 case 8:
1815 fmt = "%Y-%m-%d";
1816 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
1817 break;
1818 case 14:
1819 fmt = "%Y-%m-%dT%H:%M:%S";
1820 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
1821 s, s + 4, s + 6, s + 8, s + 10, s + 12);
1822 break;
1823 default:
1824 fatal("Invalid certificate time format \"%s\"", s);
1825 }
1826
1827 memset(&tm, 0, sizeof(tm));
1828 if (strptime(buf, fmt, &tm) == NULL)
1829 fatal("Invalid certificate time %s", s);
1830 if ((tt = mktime(&tm)) < 0)
1831 fatal("Certificate time %s cannot be represented", s);
1832 return (u_int64_t)tt;
1833}
1834
1835static void 1801static void
1836parse_cert_times(char *timespec) 1802parse_cert_times(char *timespec)
1837{ 1803{
@@ -1867,15 +1833,15 @@ parse_cert_times(char *timespec)
1867 cert_valid_from = parse_relative_time(from, now); 1833 cert_valid_from = parse_relative_time(from, now);
1868 else if (strcmp(from, "always") == 0) 1834 else if (strcmp(from, "always") == 0)
1869 cert_valid_from = 0; 1835 cert_valid_from = 0;
1870 else 1836 else if (parse_absolute_time(from, &cert_valid_from) != 0)
1871 cert_valid_from = parse_absolute_time(from); 1837 fatal("Invalid from time \"%s\"", from);
1872 1838
1873 if (*to == '-' || *to == '+') 1839 if (*to == '-' || *to == '+')
1874 cert_valid_to = parse_relative_time(to, now); 1840 cert_valid_to = parse_relative_time(to, now);
1875 else if (strcmp(to, "forever") == 0) 1841 else if (strcmp(to, "forever") == 0)
1876 cert_valid_to = ~(u_int64_t)0; 1842 cert_valid_to = ~(u_int64_t)0;
1877 else 1843 else if (parse_absolute_time(to, &cert_valid_to) != 0)
1878 cert_valid_to = parse_absolute_time(to); 1844 fatal("Invalid to time \"%s\"", to);
1879 1845
1880 if (cert_valid_to <= cert_valid_from) 1846 if (cert_valid_to <= cert_valid_from)
1881 fatal("Empty certificate validity interval"); 1847 fatal("Empty certificate validity interval");
diff --git a/sshd.8 b/sshd.8
index 0d52cc50a..f973cc383 100644
--- a/sshd.8
+++ b/sshd.8
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: sshd.8,v 1.296 2018/03/03 06:37:53 dtucker Exp $ 36.\" $OpenBSD: sshd.8,v 1.297 2018/03/12 00:52:01 djm Exp $
37.Dd $Mdocdate: March 3 2018 $ 37.Dd $Mdocdate: March 12 2018 $
38.Dt SSHD 8 38.Dt SSHD 8
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -602,6 +602,10 @@ Enables execution of
602previously disabled by the 602previously disabled by the
603.Cm restrict 603.Cm restrict
604option. 604option.
605.It Cm valid-before="timespec"
606Specifies a time after which the key will not be accepted.
607The time may be specified as a YYYYMMDD date or a YYYYMMDDHHMM[SS] time
608in the system time-zone.
605.It Cm X11-forwarding 609.It Cm X11-forwarding
606Permits X11 forwarding previously disabled by the 610Permits X11 forwarding previously disabled by the
607.Cm restrict 611.Cm restrict