diff options
Diffstat (limited to 'sshsig.c')
-rw-r--r-- | sshsig.c | 284 |
1 files changed, 164 insertions, 120 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: sshsig.c,v 1.21 2021/07/23 04:00:59 djm Exp $ */ +/* $OpenBSD: sshsig.c,v 1.28 2022/02/01 23:34:47 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -813,19 +813,79 @@ parse_principals_key_and_options(const char *path, u_long linenum, char *line, } static int +cert_filter_principals(const char *path, u_long linenum, + char **principalsp, const struct sshkey *cert, uint64_t verify_time) +{ + char *cp, *oprincipals, *principals; + const char *reason; + struct sshbuf *nprincipals; + int r = SSH_ERR_INTERNAL_ERROR, success = 0; + u_int i; + + oprincipals = principals = *principalsp; + *principalsp = NULL; + + if ((nprincipals = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { + /* Check certificate validity */ + if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, + verify_time, NULL, &reason)) != 0) { + debug("%s:%lu: principal \"%s\" not authorized: %s", + path, linenum, cp, reason); + continue; + } + /* Return all matching principal names from the cert */ + for (i = 0; i < cert->cert->nprincipals; i++) { + if (match_pattern(cert->cert->principals[i], cp)) { + if ((r = sshbuf_putf(nprincipals, "%s%s", + sshbuf_len(nprincipals) != 0 ? "," : "", + cert->cert->principals[i])) != 0) { + error_f("buffer error"); + goto out; + } + } + } + } + if (sshbuf_len(nprincipals) == 0) { + error("%s:%lu: no valid principals found", path, linenum); + r = SSH_ERR_KEY_CERT_INVALID; + goto out; + } + if ((principals = sshbuf_dup_string(nprincipals)) == NULL) { + error_f("buffer error"); + goto out; + } + /* success */ + success = 1; + *principalsp = principals; + out: + sshbuf_free(nprincipals); + free(oprincipals); + return success ? 0 : r; +} + +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, uint64_t verify_time) + const char *sig_namespace, uint64_t verify_time, char **principalsp) { struct sshkey *found_key = NULL; + char *principals = NULL; int r, success = 0; const char *reason = NULL; struct sshsigopt *sigopts = NULL; char tvalid[64], tverify[64]; + if (principalsp != NULL) + *principalsp = NULL; + /* Parse the line */ if ((r = parse_principals_key_and_options(path, linenum, line, - principal, NULL, &found_key, &sigopts)) != 0) { + principal, &principals, &found_key, &sigopts)) != 0) { /* error already logged */ goto done; } @@ -835,21 +895,35 @@ check_allowed_keys_line(const char *path, u_long linenum, char *line, 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, - verify_time, principal, &reason)) != 0) { - error("%s:%lu: certificate not authorized: %s", - path, linenum, reason); - goto done; + if (principal) { + /* Match certificate CA key with specified principal */ + if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 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); + } else { + /* No principal specified - find all matching ones */ + if ((r = cert_filter_principals(path, linenum, + &principals, sign_key, verify_time)) != 0) { + /* error already displayed */ + debug_r(r, "%s:%lu: cert_filter_principals", + path, linenum); + goto done; + } + debug("%s:%lu: matched certificate CA key", + path, linenum); } - debug("%s:%lu: matched certificate CA key", path, linenum); } else { /* Didn't match key */ goto done; } /* Check whether options preclude the use of this key */ - if (sigopts->namespaces != NULL && + if (sigopts->namespaces != NULL && sig_namespace != 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); @@ -879,6 +953,11 @@ check_allowed_keys_line(const char *path, u_long linenum, char *line, success = 1; done: + if (success && principalsp != NULL) { + *principalsp = principals; + principals = NULL; /* transferred */ + } + free(principals); sshkey_free(found_key); sshsigopt_free(sigopts); return success ? 0 : SSH_ERR_KEY_NOT_FOUND; @@ -906,7 +985,7 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, while (getline(&line, &linesize, f) != -1) { linenum++; r = check_allowed_keys_line(path, linenum, line, sign_key, - principal, sig_namespace, verify_time); + principal, sig_namespace, verify_time, NULL); free(line); line = NULL; linesize = 0; @@ -925,112 +1004,6 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; } -static int -cert_filter_principals(const char *path, u_long linenum, - char **principalsp, const struct sshkey *cert, uint64_t verify_time) -{ - char *cp, *oprincipals, *principals; - const char *reason; - struct sshbuf *nprincipals; - int r = SSH_ERR_INTERNAL_ERROR, success = 0; - - oprincipals = principals = *principalsp; - *principalsp = NULL; - - if ((nprincipals = sshbuf_new()) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } - - while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { - if (strcspn(cp, "!?*") != strlen(cp)) { - debug("%s:%lu: principal \"%s\" not authorized: " - "contains wildcards", path, linenum, cp); - continue; - } - /* Check against principals list in certificate */ - if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, - verify_time, cp, &reason)) != 0) { - debug("%s:%lu: principal \"%s\" not authorized: %s", - path, linenum, cp, reason); - continue; - } - if ((r = sshbuf_putf(nprincipals, "%s%s", - sshbuf_len(nprincipals) != 0 ? "," : "", cp)) != 0) { - error_f("buffer error"); - goto out; - } - } - if (sshbuf_len(nprincipals) == 0) { - error("%s:%lu: no valid principals found", path, linenum); - r = SSH_ERR_KEY_CERT_INVALID; - goto out; - } - if ((principals = sshbuf_dup_string(nprincipals)) == NULL) { - error_f("buffer error"); - goto out; - } - /* success */ - success = 1; - *principalsp = principals; - out: - sshbuf_free(nprincipals); - free(oprincipals); - return success ? 0 : r; -} - -static int -get_matching_principals_from_line(const char *path, u_long linenum, char *line, - const struct sshkey *sign_key, uint64_t verify_time, char **principalsp) -{ - struct sshkey *found_key = NULL; - char *principals = NULL; - int r, found = 0; - struct sshsigopt *sigopts = NULL; - - if (principalsp != NULL) - *principalsp = NULL; - - /* Parse the line */ - if ((r = parse_principals_key_and_options(path, linenum, line, - NULL, &principals, &found_key, &sigopts)) != 0) { - /* error already logged */ - goto done; - } - - if (!sigopts->ca && sshkey_equal(found_key, sign_key)) { - /* Exact match of key */ - debug("%s:%lu: matched key", path, linenum); - /* success */ - found = 1; - } else if (sigopts->ca && sshkey_is_cert(sign_key) && - 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, verify_time)) != 0) { - /* error already displayed */ - debug_r(r, "%s:%lu: cert_filter_principals", - path, linenum); - goto done; - } - debug("%s:%lu: matched certificate CA key", path, linenum); - /* success */ - found = 1; - } else { - /* Key didn't match */ - goto done; - } - done: - if (found && principalsp != NULL) { - *principalsp = principals; - principals = NULL; /* transferred */ - } - free(principals); - sshkey_free(found_key); - sshsigopt_free(sigopts); - return found ? 0 : SSH_ERR_KEY_NOT_FOUND; -} - int sshsig_find_principals(const char *path, const struct sshkey *sign_key, uint64_t verify_time, char **principals) @@ -1049,10 +1022,11 @@ sshsig_find_principals(const char *path, const struct sshkey *sign_key, return SSH_ERR_SYSTEM_ERROR; } + r = SSH_ERR_KEY_NOT_FOUND; while (getline(&line, &linesize, f) != -1) { linenum++; - r = get_matching_principals_from_line(path, linenum, line, - sign_key, verify_time, principals); + r = check_allowed_keys_line(path, linenum, line, + sign_key, NULL, NULL, verify_time, principals); free(line); line = NULL; linesize = 0; @@ -1080,6 +1054,76 @@ sshsig_find_principals(const char *path, const struct sshkey *sign_key, } int +sshsig_match_principals(const char *path, const char *principal, + char ***principalsp, size_t *nprincipalsp) +{ + FILE *f = NULL; + char *found, *line = NULL, **principals = NULL, **tmp; + size_t i, nprincipals = 0, linesize = 0; + u_long linenum = 0; + int oerrno = 0, r, ret = 0; + + if (principalsp != NULL) + *principalsp = NULL; + if (nprincipalsp != NULL) + *nprincipalsp = 0; + + /* Check key and principal against file */ + if ((f = fopen(path, "r")) == NULL) { + oerrno = errno; + error("Unable to open allowed keys file \"%s\": %s", + path, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + + while (getline(&line, &linesize, f) != -1) { + linenum++; + /* Parse the line */ + if ((r = parse_principals_key_and_options(path, linenum, line, + principal, &found, NULL, NULL)) != 0) { + if (r == SSH_ERR_KEY_NOT_FOUND) + continue; + ret = r; + oerrno = errno; + break; /* unexpected error */ + } + if ((tmp = recallocarray(principals, nprincipals, + nprincipals + 1, sizeof(*principals))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + free(found); + break; + } + principals = tmp; + principals[nprincipals++] = found; /* transferred */ + free(line); + line = NULL; + linesize = 0; + } + fclose(f); + + if (ret == 0) { + if (nprincipals == 0) + ret = SSH_ERR_KEY_NOT_FOUND; + if (principalsp != NULL) { + *principalsp = principals; + principals = NULL; /* transferred */ + } + if (nprincipalsp != 0) { + *nprincipalsp = nprincipals; + nprincipals = 0; + } + } + + for (i = 0; i < nprincipals; i++) + free(principals[i]); + free(principals); + + errno = oerrno; + return ret; +} + +int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) { struct sshkey *pk = NULL; |