=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshsig.c,v retrieving revision 1.20 retrieving revision 1.21 diff -u -r1.20 -r1.21 --- src/usr.bin/ssh/sshsig.c 2021/01/31 10:50:10 1.20 +++ src/usr.bin/ssh/sshsig.c 2021/07/23 04:00:59 1.21 @@ -1,4 +1,4 @@ -/* $OpenBSD: sshsig.c,v 1.20 2021/01/31 10:50:10 dtucker Exp $ */ +/* $OpenBSD: sshsig.c,v 1.21 2021/07/23 04:00:59 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -614,6 +614,7 @@ struct sshsigopt { int ca; char *namespaces; + uint64_t valid_after, valid_before; }; struct sshsigopt * @@ -622,6 +623,7 @@ { struct sshsigopt *ret; int r; + char *opt; const char *errstr = NULL; if ((ret = calloc(1, sizeof(*ret))) == NULL) @@ -641,6 +643,34 @@ ret->namespaces = opt_dequote(&opts, &errstr); if (ret->namespaces == NULL) goto fail; + } else if (opt_match(&opts, "valid-after")) { + if (ret->valid_after != 0) { + errstr = "multiple \"valid-after\" clauses"; + goto fail; + } + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + if (parse_absolute_time(opt, &ret->valid_after) != 0 || + ret->valid_after == 0) { + free(opt); + errstr = "invalid \"valid-after\" time"; + goto fail; + } + free(opt); + } else if (opt_match(&opts, "valid-before")) { + if (ret->valid_before != 0) { + errstr = "multiple \"valid-before\" clauses"; + goto fail; + } + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + if (parse_absolute_time(opt, &ret->valid_before) != 0 || + ret->valid_before == 0) { + free(opt); + errstr = "invalid \"valid-before\" time"; + goto fail; + } + free(opt); } /* * Skip the comma, and move to the next option @@ -659,6 +689,12 @@ goto fail; } } + /* final consistency check */ + if (ret->valid_after != 0 && ret->valid_before != 0 && + ret->valid_before <= ret->valid_after) { + errstr = "\"valid-before\" time is before \"valid-after\""; + goto fail; + } /* success */ return ret; fail: @@ -777,12 +813,13 @@ static int check_allowed_keys_line(const char *path, u_long linenum, char *line, const struct sshkey *sign_key, const char *principal, - const char *sig_namespace) + const char *sig_namespace, uint64_t verify_time) { struct sshkey *found_key = NULL; - int r, found = 0; + int r, success = 0; const char *reason = NULL; struct sshsigopt *sigopts = NULL; + char tvalid[64], tverify[64]; /* Parse the line */ if ((r = parse_principals_key_and_options(path, linenum, line, @@ -791,44 +828,63 @@ goto done; } - /* Check whether options preclude the use of this key */ - if (sigopts->namespaces != NULL && - match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) { - error("%s:%lu: key is not permitted for use in signature " - "namespace \"%s\"", path, linenum, sig_namespace); - goto done; - } - if (!sigopts->ca && sshkey_equal(found_key, sign_key)) { /* Exact match of key */ - debug("%s:%lu: matched key and principal", path, linenum); - /* success */ - found = 1; + debug("%s:%lu: matched key", path, linenum); } else if (sigopts->ca && sshkey_is_cert(sign_key) && sshkey_equal_public(sign_key->cert->signature_key, found_key)) { /* Match of certificate's CA key */ if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0, - principal, &reason)) != 0) { + verify_time, principal, &reason)) != 0) { error("%s:%lu: certificate not authorized: %s", path, linenum, reason); goto done; } debug("%s:%lu: matched certificate CA key", path, linenum); - /* success */ - found = 1; } else { - /* Principal matched but key didn't */ + /* Didn't match key */ goto done; } + + /* Check whether options preclude the use of this key */ + if (sigopts->namespaces != NULL && + match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) { + error("%s:%lu: key is not permitted for use in signature " + "namespace \"%s\"", path, linenum, sig_namespace); + goto done; + } + + /* check key time validity */ + format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify)); + if (sigopts->valid_after != 0 && + (uint64_t)verify_time < sigopts->valid_after) { + format_absolute_time(sigopts->valid_after, + tvalid, sizeof(tvalid)); + error("%s:%lu: key is not yet valid: " + "verify time %s < valid-after %s", path, linenum, + tverify, tvalid); + goto done; + } + if (sigopts->valid_before != 0 && + (uint64_t)verify_time > sigopts->valid_before) { + format_absolute_time(sigopts->valid_before, + tvalid, sizeof(tvalid)); + error("%s:%lu: key has expired: " + "verify time %s > valid-before %s", path, linenum, + tverify, tvalid); + goto done; + } + success = 1; + done: sshkey_free(found_key); sshsigopt_free(sigopts); - return found ? 0 : SSH_ERR_KEY_NOT_FOUND; + return success ? 0 : SSH_ERR_KEY_NOT_FOUND; } int sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, - const char *principal, const char *sig_namespace) + const char *principal, const char *sig_namespace, uint64_t verify_time) { FILE *f = NULL; char *line = NULL; @@ -848,7 +904,7 @@ while (getline(&line, &linesize, f) != -1) { linenum++; r = check_allowed_keys_line(path, linenum, line, sign_key, - principal, sig_namespace); + principal, sig_namespace, verify_time); free(line); line = NULL; linesize = 0; @@ -869,7 +925,7 @@ static int cert_filter_principals(const char *path, u_long linenum, - char **principalsp, const struct sshkey *cert) + char **principalsp, const struct sshkey *cert, uint64_t verify_time) { char *cp, *oprincipals, *principals; const char *reason; @@ -892,7 +948,7 @@ } /* Check against principals list in certificate */ if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, - cp, &reason)) != 0) { + verify_time, cp, &reason)) != 0) { debug("%s:%lu: principal \"%s\" not authorized: %s", path, linenum, cp, reason); continue; @@ -923,7 +979,7 @@ static int get_matching_principals_from_line(const char *path, u_long linenum, char *line, - const struct sshkey *sign_key, char **principalsp) + const struct sshkey *sign_key, uint64_t verify_time, char **principalsp) { struct sshkey *found_key = NULL; char *principals = NULL; @@ -949,7 +1005,7 @@ sshkey_equal_public(sign_key->cert->signature_key, found_key)) { /* Remove principals listed in file but not allowed by cert */ if ((r = cert_filter_principals(path, linenum, - &principals, sign_key)) != 0) { + &principals, sign_key, verify_time)) != 0) { /* error already displayed */ debug_r(r, "%s:%lu: cert_filter_principals", path, linenum); @@ -975,7 +1031,7 @@ int sshsig_find_principals(const char *path, const struct sshkey *sign_key, - char **principals) + uint64_t verify_time, char **principals) { FILE *f = NULL; char *line = NULL; @@ -994,7 +1050,7 @@ while (getline(&line, &linesize, f) != -1) { linenum++; r = get_matching_principals_from_line(path, linenum, line, - sign_key, principals); + sign_key, verify_time, principals); free(line); line = NULL; linesize = 0;