1*bae7b9e3Sdjm /* $OpenBSD: sshsig.c,v 1.37 2024/11/26 22:05:51 djm Exp $ */ 21fa0c92bSdjm /* 31fa0c92bSdjm * Copyright (c) 2019 Google LLC 41fa0c92bSdjm * 51fa0c92bSdjm * Permission to use, copy, modify, and distribute this software for any 61fa0c92bSdjm * purpose with or without fee is hereby granted, provided that the above 71fa0c92bSdjm * copyright notice and this permission notice appear in all copies. 81fa0c92bSdjm * 91fa0c92bSdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 101fa0c92bSdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 111fa0c92bSdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 121fa0c92bSdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 131fa0c92bSdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 141fa0c92bSdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 151fa0c92bSdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 161fa0c92bSdjm */ 171fa0c92bSdjm 181fa0c92bSdjm #include <stdio.h> 191fa0c92bSdjm #include <stdlib.h> 201fa0c92bSdjm #include <stdarg.h> 211fa0c92bSdjm #include <errno.h> 221fa0c92bSdjm #include <string.h> 231fa0c92bSdjm #include <unistd.h> 241fa0c92bSdjm 251fa0c92bSdjm #include "authfd.h" 261fa0c92bSdjm #include "authfile.h" 271fa0c92bSdjm #include "log.h" 281fa0c92bSdjm #include "misc.h" 291fa0c92bSdjm #include "sshbuf.h" 301fa0c92bSdjm #include "sshsig.h" 311fa0c92bSdjm #include "ssherr.h" 321fa0c92bSdjm #include "sshkey.h" 331fa0c92bSdjm #include "match.h" 341fa0c92bSdjm #include "digest.h" 351fa0c92bSdjm 361fa0c92bSdjm #define SIG_VERSION 0x01 371fa0c92bSdjm #define MAGIC_PREAMBLE "SSHSIG" 381fa0c92bSdjm #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1) 39c39191f9Sdjm #define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----" 401fa0c92bSdjm #define END_SIGNATURE "-----END SSH SIGNATURE-----" 41*bae7b9e3Sdjm #define RSA_SIGN_ALG "rsa-sha2-512" 421fa0c92bSdjm #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256" 43*bae7b9e3Sdjm #define HASHALG_DEFAULT "sha512" 441fa0c92bSdjm #define HASHALG_ALLOWED "sha256,sha512" 451fa0c92bSdjm 461fa0c92bSdjm int 471fa0c92bSdjm sshsig_armor(const struct sshbuf *blob, struct sshbuf **out) 481fa0c92bSdjm { 491fa0c92bSdjm struct sshbuf *buf = NULL; 501fa0c92bSdjm int r = SSH_ERR_INTERNAL_ERROR; 511fa0c92bSdjm 521fa0c92bSdjm *out = NULL; 531fa0c92bSdjm 541fa0c92bSdjm if ((buf = sshbuf_new()) == NULL) { 5548e6b99dSdjm error_f("sshbuf_new failed"); 561fa0c92bSdjm r = SSH_ERR_ALLOC_FAIL; 571fa0c92bSdjm goto out; 581fa0c92bSdjm } 591fa0c92bSdjm 60c39191f9Sdjm if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) { 6148e6b99dSdjm error_fr(r, "sshbuf_putf"); 621fa0c92bSdjm goto out; 631fa0c92bSdjm } 641fa0c92bSdjm 651fa0c92bSdjm if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) { 6648e6b99dSdjm error_fr(r, "base64 encode signature"); 671fa0c92bSdjm goto out; 681fa0c92bSdjm } 691fa0c92bSdjm 701fa0c92bSdjm if ((r = sshbuf_put(buf, END_SIGNATURE, 711fa0c92bSdjm sizeof(END_SIGNATURE)-1)) != 0 || 721fa0c92bSdjm (r = sshbuf_put_u8(buf, '\n')) != 0) { 7348e6b99dSdjm error_fr(r, "sshbuf_put"); 741fa0c92bSdjm goto out; 751fa0c92bSdjm } 761fa0c92bSdjm /* success */ 771fa0c92bSdjm *out = buf; 781fa0c92bSdjm buf = NULL; /* transferred */ 791fa0c92bSdjm r = 0; 801fa0c92bSdjm out: 811fa0c92bSdjm sshbuf_free(buf); 821fa0c92bSdjm return r; 831fa0c92bSdjm } 841fa0c92bSdjm 851fa0c92bSdjm int 861fa0c92bSdjm sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out) 871fa0c92bSdjm { 881fa0c92bSdjm int r; 891fa0c92bSdjm size_t eoffset = 0; 901fa0c92bSdjm struct sshbuf *buf = NULL; 911fa0c92bSdjm struct sshbuf *sbuf = NULL; 921fa0c92bSdjm char *b64 = NULL; 931fa0c92bSdjm 941fa0c92bSdjm if ((sbuf = sshbuf_fromb(sig)) == NULL) { 9548e6b99dSdjm error_f("sshbuf_fromb failed"); 961fa0c92bSdjm return SSH_ERR_ALLOC_FAIL; 971fa0c92bSdjm } 981fa0c92bSdjm 99c39191f9Sdjm /* Expect and consume preamble + lf/crlf */ 1001fa0c92bSdjm if ((r = sshbuf_cmp(sbuf, 0, 1011fa0c92bSdjm BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) { 1021fa0c92bSdjm error("Couldn't parse signature: missing header"); 1031fa0c92bSdjm goto done; 1041fa0c92bSdjm } 1051fa0c92bSdjm if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) { 10648e6b99dSdjm error_fr(r, "consume"); 1071fa0c92bSdjm goto done; 1081fa0c92bSdjm } 109c39191f9Sdjm if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0) 110c39191f9Sdjm eoffset = 2; 111c39191f9Sdjm else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0) 112c39191f9Sdjm eoffset = 1; 113c39191f9Sdjm else { 114c39191f9Sdjm r = SSH_ERR_INVALID_FORMAT; 115c39191f9Sdjm error_f("no header eol"); 116c39191f9Sdjm goto done; 117c39191f9Sdjm } 118c39191f9Sdjm if ((r = sshbuf_consume(sbuf, eoffset)) != 0) { 119c39191f9Sdjm error_fr(r, "consume eol"); 120c39191f9Sdjm goto done; 121c39191f9Sdjm } 122c39191f9Sdjm /* Find and consume lf + suffix (any prior cr would be ignored) */ 1231fa0c92bSdjm if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE, 124c39191f9Sdjm sizeof(END_SIGNATURE), &eoffset)) != 0) { 1251fa0c92bSdjm error("Couldn't parse signature: missing footer"); 1261fa0c92bSdjm goto done; 1271fa0c92bSdjm } 1281fa0c92bSdjm if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) { 12948e6b99dSdjm error_fr(r, "consume"); 1301fa0c92bSdjm goto done; 1311fa0c92bSdjm } 1321fa0c92bSdjm 1331fa0c92bSdjm if ((b64 = sshbuf_dup_string(sbuf)) == NULL) { 13448e6b99dSdjm error_f("sshbuf_dup_string failed"); 1351fa0c92bSdjm r = SSH_ERR_ALLOC_FAIL; 1361fa0c92bSdjm goto done; 1371fa0c92bSdjm } 1381fa0c92bSdjm 1391fa0c92bSdjm if ((buf = sshbuf_new()) == NULL) { 14048e6b99dSdjm error_f("sshbuf_new() failed"); 1411fa0c92bSdjm r = SSH_ERR_ALLOC_FAIL; 1421fa0c92bSdjm goto done; 1431fa0c92bSdjm } 1441fa0c92bSdjm 1451fa0c92bSdjm if ((r = sshbuf_b64tod(buf, b64)) != 0) { 14648e6b99dSdjm error_fr(r, "decode base64"); 1471fa0c92bSdjm goto done; 1481fa0c92bSdjm } 1491fa0c92bSdjm 1501fa0c92bSdjm /* success */ 1511fa0c92bSdjm *out = buf; 1521fa0c92bSdjm r = 0; 1531fa0c92bSdjm buf = NULL; /* transferred */ 1541fa0c92bSdjm done: 1551fa0c92bSdjm sshbuf_free(buf); 1561fa0c92bSdjm sshbuf_free(sbuf); 1571fa0c92bSdjm free(b64); 1581fa0c92bSdjm return r; 1591fa0c92bSdjm } 1601fa0c92bSdjm 1611fa0c92bSdjm static int 1621fa0c92bSdjm sshsig_wrap_sign(struct sshkey *key, const char *hashalg, 1631f63d3c4Sdjm const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message, 164e3a62e69Sdjm const char *sig_namespace, struct sshbuf **out, 165e3a62e69Sdjm sshsig_signer *signer, void *signer_ctx) 1661fa0c92bSdjm { 1671fa0c92bSdjm int r; 1681fa0c92bSdjm size_t slen = 0; 1691fa0c92bSdjm u_char *sig = NULL; 1701fa0c92bSdjm struct sshbuf *blob = NULL; 1711fa0c92bSdjm struct sshbuf *tosign = NULL; 1721fa0c92bSdjm const char *sign_alg = NULL; 1731fa0c92bSdjm 1741fa0c92bSdjm if ((tosign = sshbuf_new()) == NULL || 1751fa0c92bSdjm (blob = sshbuf_new()) == NULL) { 17648e6b99dSdjm error_f("sshbuf_new failed"); 1771fa0c92bSdjm r = SSH_ERR_ALLOC_FAIL; 1781fa0c92bSdjm goto done; 1791fa0c92bSdjm } 1801fa0c92bSdjm 1811fa0c92bSdjm if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || 1821fa0c92bSdjm (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 || 1831fa0c92bSdjm (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */ 1841fa0c92bSdjm (r = sshbuf_put_cstring(tosign, hashalg)) != 0 || 185ba289540Sdjm (r = sshbuf_put_stringb(tosign, h_message)) != 0) { 18648e6b99dSdjm error_fr(r, "assemble message to sign"); 1871fa0c92bSdjm goto done; 1881fa0c92bSdjm } 1891fa0c92bSdjm 1901fa0c92bSdjm /* If using RSA keys then default to a good signature algorithm */ 19172de6406Sdjm if (sshkey_type_plain(key->type) == KEY_RSA) { 1921fa0c92bSdjm sign_alg = RSA_SIGN_ALG; 19372de6406Sdjm if (strcmp(hashalg, "sha256") == 0) 19472de6406Sdjm sign_alg = "rsa-sha2-256"; 19572de6406Sdjm else if (strcmp(hashalg, "sha512") == 0) 19672de6406Sdjm sign_alg = "rsa-sha2-512"; 19772de6406Sdjm } 1981fa0c92bSdjm 1991fa0c92bSdjm if (signer != NULL) { 2001fa0c92bSdjm if ((r = signer(key, &sig, &slen, 2011fa0c92bSdjm sshbuf_ptr(tosign), sshbuf_len(tosign), 2021f63d3c4Sdjm sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) { 20348e6b99dSdjm error_r(r, "Couldn't sign message (signer)"); 2041fa0c92bSdjm goto done; 2051fa0c92bSdjm } 2061fa0c92bSdjm } else { 2071fa0c92bSdjm if ((r = sshkey_sign(key, &sig, &slen, 2081fa0c92bSdjm sshbuf_ptr(tosign), sshbuf_len(tosign), 2091f63d3c4Sdjm sign_alg, sk_provider, sk_pin, 0)) != 0) { 21048e6b99dSdjm error_r(r, "Couldn't sign message"); 2111fa0c92bSdjm goto done; 2121fa0c92bSdjm } 2131fa0c92bSdjm } 2141fa0c92bSdjm 2151fa0c92bSdjm if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || 2161fa0c92bSdjm (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 || 2171fa0c92bSdjm (r = sshkey_puts(key, blob)) != 0 || 2181fa0c92bSdjm (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 || 2191fa0c92bSdjm (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */ 2201fa0c92bSdjm (r = sshbuf_put_cstring(blob, hashalg)) != 0 || 2211fa0c92bSdjm (r = sshbuf_put_string(blob, sig, slen)) != 0) { 22248e6b99dSdjm error_fr(r, "assemble signature object"); 2231fa0c92bSdjm goto done; 2241fa0c92bSdjm } 2251fa0c92bSdjm 226a500851bSmarkus if (out != NULL) { 2271fa0c92bSdjm *out = blob; 2281fa0c92bSdjm blob = NULL; 229a500851bSmarkus } 2301fa0c92bSdjm r = 0; 2311fa0c92bSdjm done: 2321fa0c92bSdjm free(sig); 2331fa0c92bSdjm sshbuf_free(blob); 2341fa0c92bSdjm sshbuf_free(tosign); 2351fa0c92bSdjm return r; 2361fa0c92bSdjm } 2371fa0c92bSdjm 2381fa0c92bSdjm /* Check preamble and version. */ 2391fa0c92bSdjm static int 2401fa0c92bSdjm sshsig_parse_preamble(struct sshbuf *buf) 2411fa0c92bSdjm { 2421fa0c92bSdjm int r = SSH_ERR_INTERNAL_ERROR; 2431fa0c92bSdjm uint32_t sversion; 2441fa0c92bSdjm 2451fa0c92bSdjm if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || 2461fa0c92bSdjm (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 || 2471fa0c92bSdjm (r = sshbuf_get_u32(buf, &sversion)) != 0) { 2481fa0c92bSdjm error("Couldn't verify signature: invalid format"); 2491fa0c92bSdjm return r; 2501fa0c92bSdjm } 2511fa0c92bSdjm 25264718851Sdjm if (sversion > SIG_VERSION) { 2531fa0c92bSdjm error("Signature version %lu is larger than supported " 2541fa0c92bSdjm "version %u", (unsigned long)sversion, SIG_VERSION); 2551fa0c92bSdjm return SSH_ERR_INVALID_FORMAT; 2561fa0c92bSdjm } 2571fa0c92bSdjm return 0; 2581fa0c92bSdjm } 2591fa0c92bSdjm 2601fa0c92bSdjm static int 2611fa0c92bSdjm sshsig_check_hashalg(const char *hashalg) 2621fa0c92bSdjm { 26364718851Sdjm if (hashalg == NULL || 26464718851Sdjm match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1) 2651fa0c92bSdjm return 0; 26648e6b99dSdjm error_f("unsupported hash algorithm \"%.100s\"", hashalg); 2671fa0c92bSdjm return SSH_ERR_SIGN_ALG_UNSUPPORTED; 2681fa0c92bSdjm } 2691fa0c92bSdjm 2701fa0c92bSdjm static int 2711fa0c92bSdjm sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp) 2721fa0c92bSdjm { 2731fa0c92bSdjm struct sshbuf *buf = NULL; 2741fa0c92bSdjm char *hashalg = NULL; 2751fa0c92bSdjm int r = SSH_ERR_INTERNAL_ERROR; 2761fa0c92bSdjm 2771fa0c92bSdjm if (hashalgp != NULL) 2781fa0c92bSdjm *hashalgp = NULL; 2791fa0c92bSdjm if ((buf = sshbuf_fromb(signature)) == NULL) 2801fa0c92bSdjm return SSH_ERR_ALLOC_FAIL; 2811fa0c92bSdjm if ((r = sshsig_parse_preamble(buf)) != 0) 2821fa0c92bSdjm goto done; 2831fa0c92bSdjm if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || 2841fa0c92bSdjm (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || 2851fa0c92bSdjm (r = sshbuf_get_string(buf, NULL, NULL)) != 0 || 2861fa0c92bSdjm (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 || 2871fa0c92bSdjm (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) { 28848e6b99dSdjm error_fr(r, "parse signature object"); 2891fa0c92bSdjm goto done; 2901fa0c92bSdjm } 2911fa0c92bSdjm 2921fa0c92bSdjm /* success */ 2931fa0c92bSdjm r = 0; 2941fa0c92bSdjm *hashalgp = hashalg; 2951fa0c92bSdjm hashalg = NULL; 2961fa0c92bSdjm done: 2971fa0c92bSdjm free(hashalg); 2981fa0c92bSdjm sshbuf_free(buf); 2991fa0c92bSdjm return r; 3001fa0c92bSdjm } 3011fa0c92bSdjm 3021fa0c92bSdjm static int 3031fa0c92bSdjm sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg, 3041fa0c92bSdjm const struct sshbuf *h_message, const char *expect_namespace, 305493ad5b0Sdjm struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details) 3061fa0c92bSdjm { 3071fa0c92bSdjm int r = SSH_ERR_INTERNAL_ERROR; 3081fa0c92bSdjm struct sshbuf *buf = NULL, *toverify = NULL; 3091fa0c92bSdjm struct sshkey *key = NULL; 3101fa0c92bSdjm const u_char *sig; 3111fa0c92bSdjm char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL; 3121fa0c92bSdjm size_t siglen; 3131fa0c92bSdjm 31448e6b99dSdjm debug_f("verify message length %zu", sshbuf_len(h_message)); 315493ad5b0Sdjm if (sig_details != NULL) 316493ad5b0Sdjm *sig_details = NULL; 3171fa0c92bSdjm if (sign_keyp != NULL) 3181fa0c92bSdjm *sign_keyp = NULL; 3191fa0c92bSdjm 3201fa0c92bSdjm if ((toverify = sshbuf_new()) == NULL) { 32148e6b99dSdjm error_f("sshbuf_new failed"); 3221fa0c92bSdjm r = SSH_ERR_ALLOC_FAIL; 3231fa0c92bSdjm goto done; 3241fa0c92bSdjm } 3251fa0c92bSdjm if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE, 3261fa0c92bSdjm MAGIC_PREAMBLE_LEN)) != 0 || 3271fa0c92bSdjm (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 || 3281fa0c92bSdjm (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */ 3291fa0c92bSdjm (r = sshbuf_put_cstring(toverify, hashalg)) != 0 || 330ba289540Sdjm (r = sshbuf_put_stringb(toverify, h_message)) != 0) { 33148e6b99dSdjm error_fr(r, "assemble message to verify"); 3321fa0c92bSdjm goto done; 3331fa0c92bSdjm } 3341fa0c92bSdjm 3351fa0c92bSdjm if ((r = sshsig_parse_preamble(signature)) != 0) 3361fa0c92bSdjm goto done; 3371fa0c92bSdjm 3381fa0c92bSdjm if ((r = sshkey_froms(signature, &key)) != 0 || 3391fa0c92bSdjm (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 || 3401fa0c92bSdjm (r = sshbuf_get_string(signature, NULL, NULL)) != 0 || 3411fa0c92bSdjm (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 || 3421fa0c92bSdjm (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) { 34348e6b99dSdjm error_fr(r, "parse signature object"); 3441fa0c92bSdjm goto done; 3451fa0c92bSdjm } 3461fa0c92bSdjm 3471fa0c92bSdjm if (sshbuf_len(signature) != 0) { 3481fa0c92bSdjm error("Signature contains trailing data"); 3491fa0c92bSdjm r = SSH_ERR_INVALID_FORMAT; 3501fa0c92bSdjm goto done; 3511fa0c92bSdjm } 3521fa0c92bSdjm 3531fa0c92bSdjm if (strcmp(expect_namespace, got_namespace) != 0) { 3541fa0c92bSdjm error("Couldn't verify signature: namespace does not match"); 35548e6b99dSdjm debug_f("expected namespace \"%s\" received \"%s\"", 35648e6b99dSdjm expect_namespace, got_namespace); 3571fa0c92bSdjm r = SSH_ERR_SIGNATURE_INVALID; 3581fa0c92bSdjm goto done; 3591fa0c92bSdjm } 3601fa0c92bSdjm if (strcmp(hashalg, sig_hashalg) != 0) { 3611fa0c92bSdjm error("Couldn't verify signature: hash algorithm mismatch"); 36248e6b99dSdjm debug_f("expected algorithm \"%s\" received \"%s\"", 36348e6b99dSdjm hashalg, sig_hashalg); 3641fa0c92bSdjm r = SSH_ERR_SIGNATURE_INVALID; 3651fa0c92bSdjm goto done; 3661fa0c92bSdjm } 3671fa0c92bSdjm /* Ensure that RSA keys use an acceptable signature algorithm */ 3681fa0c92bSdjm if (sshkey_type_plain(key->type) == KEY_RSA) { 3691fa0c92bSdjm if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) { 37048e6b99dSdjm error_r(r, "Couldn't verify signature: unable to get " 37148e6b99dSdjm "signature type"); 3721fa0c92bSdjm goto done; 3731fa0c92bSdjm } 3741fa0c92bSdjm if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) { 3751fa0c92bSdjm error("Couldn't verify signature: unsupported RSA " 3761fa0c92bSdjm "signature algorithm %s", sigtype); 3771fa0c92bSdjm r = SSH_ERR_SIGN_ALG_UNSUPPORTED; 3781fa0c92bSdjm goto done; 3791fa0c92bSdjm } 3801fa0c92bSdjm } 3811fa0c92bSdjm if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify), 382493ad5b0Sdjm sshbuf_len(toverify), NULL, 0, sig_details)) != 0) { 38348e6b99dSdjm error_r(r, "Signature verification failed"); 3841fa0c92bSdjm goto done; 3851fa0c92bSdjm } 3861fa0c92bSdjm 3871fa0c92bSdjm /* success */ 3881fa0c92bSdjm r = 0; 3891fa0c92bSdjm if (sign_keyp != NULL) { 3901fa0c92bSdjm *sign_keyp = key; 3911fa0c92bSdjm key = NULL; /* transferred */ 3921fa0c92bSdjm } 3931fa0c92bSdjm done: 3941fa0c92bSdjm free(got_namespace); 3951fa0c92bSdjm free(sigtype); 3961fa0c92bSdjm free(sig_hashalg); 3971fa0c92bSdjm sshbuf_free(buf); 3981fa0c92bSdjm sshbuf_free(toverify); 3991fa0c92bSdjm sshkey_free(key); 4001fa0c92bSdjm return r; 4011fa0c92bSdjm } 4021fa0c92bSdjm 40364718851Sdjm static int 40464718851Sdjm hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp) 4051fa0c92bSdjm { 40664718851Sdjm char *hex, hash[SSH_DIGEST_MAX_LENGTH]; 4071fa0c92bSdjm int alg, r = SSH_ERR_INTERNAL_ERROR; 40864718851Sdjm struct sshbuf *b = NULL; 4091fa0c92bSdjm 41064718851Sdjm *bp = NULL; 41164718851Sdjm memset(hash, 0, sizeof(hash)); 4121fa0c92bSdjm 4131fa0c92bSdjm if ((r = sshsig_check_hashalg(hashalg)) != 0) 4141fa0c92bSdjm return r; 4151fa0c92bSdjm if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { 41648e6b99dSdjm error_f("can't look up hash algorithm %s", hashalg); 4171fa0c92bSdjm return SSH_ERR_INTERNAL_ERROR; 4181fa0c92bSdjm } 41964718851Sdjm if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) { 42048e6b99dSdjm error_fr(r, "ssh_digest_buffer"); 4211fa0c92bSdjm return r; 4221fa0c92bSdjm } 42364718851Sdjm if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { 42448e6b99dSdjm debug3_f("final hash: %s", hex); 42564718851Sdjm freezero(hex, strlen(hex)); 42664718851Sdjm } 42764718851Sdjm if ((b = sshbuf_new()) == NULL) { 4281fa0c92bSdjm r = SSH_ERR_ALLOC_FAIL; 4291fa0c92bSdjm goto out; 4301fa0c92bSdjm } 43164718851Sdjm if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { 43248e6b99dSdjm error_fr(r, "sshbuf_put"); 43364718851Sdjm goto out; 43464718851Sdjm } 43564718851Sdjm *bp = b; 43664718851Sdjm b = NULL; /* transferred */ 43764718851Sdjm /* success */ 43864718851Sdjm r = 0; 43964718851Sdjm out: 44064718851Sdjm sshbuf_free(b); 44164718851Sdjm explicit_bzero(hash, sizeof(hash)); 442a500851bSmarkus return r; 44364718851Sdjm } 44464718851Sdjm 44564718851Sdjm int 4461f63d3c4Sdjm sshsig_signb(struct sshkey *key, const char *hashalg, 4471f63d3c4Sdjm const char *sk_provider, const char *sk_pin, 44864718851Sdjm const struct sshbuf *message, const char *sig_namespace, 44964718851Sdjm struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) 45064718851Sdjm { 45164718851Sdjm struct sshbuf *b = NULL; 45264718851Sdjm int r = SSH_ERR_INTERNAL_ERROR; 45364718851Sdjm 45464718851Sdjm if (hashalg == NULL) 45564718851Sdjm hashalg = HASHALG_DEFAULT; 45664718851Sdjm if (out != NULL) 45764718851Sdjm *out = NULL; 45864718851Sdjm if ((r = hash_buffer(message, hashalg, &b)) != 0) { 45948e6b99dSdjm error_fr(r, "hash buffer"); 46064718851Sdjm goto out; 46164718851Sdjm } 4621f63d3c4Sdjm if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, 463e3a62e69Sdjm sig_namespace, out, signer, signer_ctx)) != 0) 4641fa0c92bSdjm goto out; 4651fa0c92bSdjm /* success */ 4661fa0c92bSdjm r = 0; 4671fa0c92bSdjm out: 4681fa0c92bSdjm sshbuf_free(b); 4691fa0c92bSdjm return r; 4701fa0c92bSdjm } 4711fa0c92bSdjm 4721fa0c92bSdjm int 47364718851Sdjm sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message, 474493ad5b0Sdjm const char *expect_namespace, struct sshkey **sign_keyp, 475493ad5b0Sdjm struct sshkey_sig_details **sig_details) 4761fa0c92bSdjm { 4771fa0c92bSdjm struct sshbuf *b = NULL; 47864718851Sdjm int r = SSH_ERR_INTERNAL_ERROR; 4791fa0c92bSdjm char *hashalg = NULL; 4801fa0c92bSdjm 481493ad5b0Sdjm if (sig_details != NULL) 482493ad5b0Sdjm *sig_details = NULL; 4831fa0c92bSdjm if (sign_keyp != NULL) 4841fa0c92bSdjm *sign_keyp = NULL; 4851fa0c92bSdjm if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) 4861fa0c92bSdjm return r; 48748e6b99dSdjm debug_f("signature made with hash \"%s\"", hashalg); 48864718851Sdjm if ((r = hash_buffer(message, hashalg, &b)) != 0) { 48948e6b99dSdjm error_fr(r, "hash buffer"); 4901fa0c92bSdjm goto out; 4911fa0c92bSdjm } 4921fa0c92bSdjm if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, 493493ad5b0Sdjm sign_keyp, sig_details)) != 0) 4941fa0c92bSdjm goto out; 4951fa0c92bSdjm /* success */ 4961fa0c92bSdjm r = 0; 4971fa0c92bSdjm out: 4981fa0c92bSdjm sshbuf_free(b); 4991fa0c92bSdjm free(hashalg); 5001fa0c92bSdjm return r; 5011fa0c92bSdjm } 5021fa0c92bSdjm 5031fa0c92bSdjm static int 50464718851Sdjm hash_file(int fd, const char *hashalg, struct sshbuf **bp) 5051fa0c92bSdjm { 50664718851Sdjm char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH]; 5071fa0c92bSdjm ssize_t n, total = 0; 50833716391Sdjm struct ssh_digest_ctx *ctx = NULL; 50964718851Sdjm int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR; 51064718851Sdjm struct sshbuf *b = NULL; 5111fa0c92bSdjm 51264718851Sdjm *bp = NULL; 51364718851Sdjm memset(hash, 0, sizeof(hash)); 51464718851Sdjm 51564718851Sdjm if ((r = sshsig_check_hashalg(hashalg)) != 0) 51664718851Sdjm return r; 51764718851Sdjm if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { 51848e6b99dSdjm error_f("can't look up hash algorithm %s", hashalg); 51964718851Sdjm return SSH_ERR_INTERNAL_ERROR; 52064718851Sdjm } 52164718851Sdjm if ((ctx = ssh_digest_start(alg)) == NULL) { 52248e6b99dSdjm error_f("ssh_digest_start failed"); 5231fa0c92bSdjm return SSH_ERR_INTERNAL_ERROR; 5241fa0c92bSdjm } 5251fa0c92bSdjm for (;;) { 5261fa0c92bSdjm if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) { 5271fa0c92bSdjm if (errno == EINTR || errno == EAGAIN) 5281fa0c92bSdjm continue; 5291fa0c92bSdjm oerrno = errno; 53048e6b99dSdjm error_f("read: %s", strerror(errno)); 5311fa0c92bSdjm errno = oerrno; 53264718851Sdjm r = SSH_ERR_SYSTEM_ERROR; 53364718851Sdjm goto out; 5341fa0c92bSdjm } else if (n == 0) { 53548e6b99dSdjm debug2_f("hashed %zu bytes", total); 5361fa0c92bSdjm break; /* EOF */ 5371fa0c92bSdjm } 5381fa0c92bSdjm total += (size_t)n; 5391fa0c92bSdjm if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) { 54048e6b99dSdjm error_fr(r, "ssh_digest_update"); 54164718851Sdjm goto out; 5421fa0c92bSdjm } 5431fa0c92bSdjm } 54464718851Sdjm if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) { 54548e6b99dSdjm error_fr(r, "ssh_digest_final"); 54664718851Sdjm goto out; 5471fa0c92bSdjm } 54864718851Sdjm if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { 54948e6b99dSdjm debug3_f("final hash: %s", hex); 5501fa0c92bSdjm freezero(hex, strlen(hex)); 5511fa0c92bSdjm } 55264718851Sdjm if ((b = sshbuf_new()) == NULL) { 55364718851Sdjm r = SSH_ERR_ALLOC_FAIL; 55464718851Sdjm goto out; 55564718851Sdjm } 55664718851Sdjm if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { 55748e6b99dSdjm error_fr(r, "sshbuf_put"); 55864718851Sdjm goto out; 55964718851Sdjm } 56064718851Sdjm *bp = b; 56164718851Sdjm b = NULL; /* transferred */ 5621fa0c92bSdjm /* success */ 56364718851Sdjm r = 0; 56464718851Sdjm out: 56533716391Sdjm oerrno = errno; 56664718851Sdjm sshbuf_free(b); 5671fa0c92bSdjm ssh_digest_free(ctx); 56864718851Sdjm explicit_bzero(hash, sizeof(hash)); 56933716391Sdjm errno = oerrno; 570a500851bSmarkus return r; 5711fa0c92bSdjm } 5721fa0c92bSdjm 5731fa0c92bSdjm int 5741f63d3c4Sdjm sshsig_sign_fd(struct sshkey *key, const char *hashalg, 5751f63d3c4Sdjm const char *sk_provider, const char *sk_pin, 5761fa0c92bSdjm int fd, const char *sig_namespace, struct sshbuf **out, 5771fa0c92bSdjm sshsig_signer *signer, void *signer_ctx) 5781fa0c92bSdjm { 5791fa0c92bSdjm struct sshbuf *b = NULL; 58064718851Sdjm int r = SSH_ERR_INTERNAL_ERROR; 5811fa0c92bSdjm 5821fa0c92bSdjm if (hashalg == NULL) 5831fa0c92bSdjm hashalg = HASHALG_DEFAULT; 58464718851Sdjm if (out != NULL) 58564718851Sdjm *out = NULL; 58664718851Sdjm if ((r = hash_file(fd, hashalg, &b)) != 0) { 58748e6b99dSdjm error_fr(r, "hash_file"); 5881fa0c92bSdjm return r; 5891fa0c92bSdjm } 5901f63d3c4Sdjm if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, 591e3a62e69Sdjm sig_namespace, out, signer, signer_ctx)) != 0) 5921fa0c92bSdjm goto out; 5931fa0c92bSdjm /* success */ 5941fa0c92bSdjm r = 0; 5951fa0c92bSdjm out: 5961fa0c92bSdjm sshbuf_free(b); 5971fa0c92bSdjm return r; 5981fa0c92bSdjm } 5991fa0c92bSdjm 6001fa0c92bSdjm int 6011fa0c92bSdjm sshsig_verify_fd(struct sshbuf *signature, int fd, 602493ad5b0Sdjm const char *expect_namespace, struct sshkey **sign_keyp, 603493ad5b0Sdjm struct sshkey_sig_details **sig_details) 6041fa0c92bSdjm { 6051fa0c92bSdjm struct sshbuf *b = NULL; 60664718851Sdjm int r = SSH_ERR_INTERNAL_ERROR; 6071fa0c92bSdjm char *hashalg = NULL; 6081fa0c92bSdjm 609493ad5b0Sdjm if (sig_details != NULL) 610493ad5b0Sdjm *sig_details = NULL; 6111fa0c92bSdjm if (sign_keyp != NULL) 6121fa0c92bSdjm *sign_keyp = NULL; 6131fa0c92bSdjm if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) 6141fa0c92bSdjm return r; 61548e6b99dSdjm debug_f("signature made with hash \"%s\"", hashalg); 61664718851Sdjm if ((r = hash_file(fd, hashalg, &b)) != 0) { 61748e6b99dSdjm error_fr(r, "hash_file"); 6181fa0c92bSdjm goto out; 6191fa0c92bSdjm } 6201fa0c92bSdjm if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, 621493ad5b0Sdjm sign_keyp, sig_details)) != 0) 6221fa0c92bSdjm goto out; 6231fa0c92bSdjm /* success */ 6241fa0c92bSdjm r = 0; 6251fa0c92bSdjm out: 6261fa0c92bSdjm sshbuf_free(b); 6271fa0c92bSdjm free(hashalg); 6281fa0c92bSdjm return r; 6291fa0c92bSdjm } 6301fa0c92bSdjm 6315505cf32Sdjm struct sshsigopt { 6321fa0c92bSdjm int ca; 6331fa0c92bSdjm char *namespaces; 6342e79209aSdjm uint64_t valid_after, valid_before; 6351fa0c92bSdjm }; 6361fa0c92bSdjm 6375505cf32Sdjm struct sshsigopt * 6385505cf32Sdjm sshsigopt_parse(const char *opts, const char *path, u_long linenum, 6391fa0c92bSdjm const char **errstrp) 6401fa0c92bSdjm { 6415505cf32Sdjm struct sshsigopt *ret; 6421fa0c92bSdjm int r; 6432e79209aSdjm char *opt; 6441fa0c92bSdjm const char *errstr = NULL; 6451fa0c92bSdjm 6461fa0c92bSdjm if ((ret = calloc(1, sizeof(*ret))) == NULL) 6471fa0c92bSdjm return NULL; 6481fa0c92bSdjm if (opts == NULL || *opts == '\0') 6491fa0c92bSdjm return ret; /* Empty options yields empty options :) */ 6501fa0c92bSdjm 6511fa0c92bSdjm while (*opts && *opts != ' ' && *opts != '\t') { 6521fa0c92bSdjm /* flag options */ 6531fa0c92bSdjm if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { 6541fa0c92bSdjm ret->ca = 1; 6551fa0c92bSdjm } else if (opt_match(&opts, "namespaces")) { 6561fa0c92bSdjm if (ret->namespaces != NULL) { 6571fa0c92bSdjm errstr = "multiple \"namespaces\" clauses"; 6581fa0c92bSdjm goto fail; 6591fa0c92bSdjm } 6601fa0c92bSdjm ret->namespaces = opt_dequote(&opts, &errstr); 6611fa0c92bSdjm if (ret->namespaces == NULL) 6621fa0c92bSdjm goto fail; 6632e79209aSdjm } else if (opt_match(&opts, "valid-after")) { 6642e79209aSdjm if (ret->valid_after != 0) { 6652e79209aSdjm errstr = "multiple \"valid-after\" clauses"; 6662e79209aSdjm goto fail; 6672e79209aSdjm } 6682e79209aSdjm if ((opt = opt_dequote(&opts, &errstr)) == NULL) 6692e79209aSdjm goto fail; 6702e79209aSdjm if (parse_absolute_time(opt, &ret->valid_after) != 0 || 6712e79209aSdjm ret->valid_after == 0) { 6722e79209aSdjm free(opt); 6732e79209aSdjm errstr = "invalid \"valid-after\" time"; 6742e79209aSdjm goto fail; 6752e79209aSdjm } 6762e79209aSdjm free(opt); 6772e79209aSdjm } else if (opt_match(&opts, "valid-before")) { 6782e79209aSdjm if (ret->valid_before != 0) { 6792e79209aSdjm errstr = "multiple \"valid-before\" clauses"; 6802e79209aSdjm goto fail; 6812e79209aSdjm } 6822e79209aSdjm if ((opt = opt_dequote(&opts, &errstr)) == NULL) 6832e79209aSdjm goto fail; 6842e79209aSdjm if (parse_absolute_time(opt, &ret->valid_before) != 0 || 6852e79209aSdjm ret->valid_before == 0) { 6862e79209aSdjm free(opt); 6872e79209aSdjm errstr = "invalid \"valid-before\" time"; 6882e79209aSdjm goto fail; 6892e79209aSdjm } 6902e79209aSdjm free(opt); 6911fa0c92bSdjm } 6921fa0c92bSdjm /* 6931fa0c92bSdjm * Skip the comma, and move to the next option 6941fa0c92bSdjm * (or break out if there are no more). 6951fa0c92bSdjm */ 6961fa0c92bSdjm if (*opts == '\0' || *opts == ' ' || *opts == '\t') 6971fa0c92bSdjm break; /* End of options. */ 6981fa0c92bSdjm /* Anything other than a comma is an unknown option */ 6991fa0c92bSdjm if (*opts != ',') { 7001fa0c92bSdjm errstr = "unknown key option"; 7011fa0c92bSdjm goto fail; 7021fa0c92bSdjm } 7031fa0c92bSdjm opts++; 7041fa0c92bSdjm if (*opts == '\0') { 7051fa0c92bSdjm errstr = "unexpected end-of-options"; 7061fa0c92bSdjm goto fail; 7071fa0c92bSdjm } 7081fa0c92bSdjm } 7092e79209aSdjm /* final consistency check */ 7102e79209aSdjm if (ret->valid_after != 0 && ret->valid_before != 0 && 7112e79209aSdjm ret->valid_before <= ret->valid_after) { 7122e79209aSdjm errstr = "\"valid-before\" time is before \"valid-after\""; 7132e79209aSdjm goto fail; 7142e79209aSdjm } 7151fa0c92bSdjm /* success */ 7161fa0c92bSdjm return ret; 7171fa0c92bSdjm fail: 7181fa0c92bSdjm if (errstrp != NULL) 7191fa0c92bSdjm *errstrp = errstr; 7205a9136fcSdjm sshsigopt_free(ret); 7211fa0c92bSdjm return NULL; 7221fa0c92bSdjm } 7231fa0c92bSdjm 7245505cf32Sdjm void 7255505cf32Sdjm sshsigopt_free(struct sshsigopt *opts) 7261fa0c92bSdjm { 7271fa0c92bSdjm if (opts == NULL) 7281fa0c92bSdjm return; 7291fa0c92bSdjm free(opts->namespaces); 7301fa0c92bSdjm free(opts); 7311fa0c92bSdjm } 7321fa0c92bSdjm 7331fa0c92bSdjm static int 734fc1a8e71Sdjm parse_principals_key_and_options(const char *path, u_long linenum, char *line, 735fc1a8e71Sdjm const char *required_principal, char **principalsp, struct sshkey **keyp, 736fc1a8e71Sdjm struct sshsigopt **sigoptsp) 7371fa0c92bSdjm { 738fc1a8e71Sdjm char *opts = NULL, *tmp, *cp, *principals = NULL; 7391fa0c92bSdjm const char *reason = NULL; 7405505cf32Sdjm struct sshsigopt *sigopts = NULL; 741fc1a8e71Sdjm struct sshkey *key = NULL; 742fc1a8e71Sdjm int r = SSH_ERR_INTERNAL_ERROR; 7431fa0c92bSdjm 744fc1a8e71Sdjm if (principalsp != NULL) 745fc1a8e71Sdjm *principalsp = NULL; 746fc1a8e71Sdjm if (sigoptsp != NULL) 747fc1a8e71Sdjm *sigoptsp = NULL; 748fc1a8e71Sdjm if (keyp != NULL) 749fc1a8e71Sdjm *keyp = NULL; 7501fa0c92bSdjm 7511fa0c92bSdjm cp = line; 752186e249eSdjm cp = cp + strspn(cp, " \t\n\r"); /* skip leading whitespace */ 7531fa0c92bSdjm if (*cp == '#' || *cp == '\0') 754fc1a8e71Sdjm return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */ 755fc1a8e71Sdjm 756fc1a8e71Sdjm /* format: identity[,identity...] [option[,option...]] key */ 757d0f52bdbSdjm if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) { 7581fa0c92bSdjm error("%s:%lu: invalid line", path, linenum); 759fc1a8e71Sdjm r = SSH_ERR_INVALID_FORMAT; 760fc1a8e71Sdjm goto out; 7611fa0c92bSdjm } 762fc1a8e71Sdjm if ((principals = strdup(tmp)) == NULL) { 76348e6b99dSdjm error_f("strdup failed"); 764fc1a8e71Sdjm r = SSH_ERR_ALLOC_FAIL; 765fc1a8e71Sdjm goto out; 766fc1a8e71Sdjm } 767fc1a8e71Sdjm /* 768fc1a8e71Sdjm * Bail out early if we're looking for a particular principal and this 769fc1a8e71Sdjm * line does not list it. 770fc1a8e71Sdjm */ 771fc1a8e71Sdjm if (required_principal != NULL) { 772fc1a8e71Sdjm if (match_pattern_list(required_principal, 773fc1a8e71Sdjm principals, 0) != 1) { 7741fa0c92bSdjm /* principal didn't match */ 775fc1a8e71Sdjm r = SSH_ERR_KEY_NOT_FOUND; 776fc1a8e71Sdjm goto out; 7771fa0c92bSdjm } 77848e6b99dSdjm debug_f("%s:%lu: matched principal \"%s\"", 77948e6b99dSdjm path, linenum, required_principal); 780fc1a8e71Sdjm } 7811fa0c92bSdjm 782fc1a8e71Sdjm if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { 78348e6b99dSdjm error_f("sshkey_new failed"); 784fc1a8e71Sdjm r = SSH_ERR_ALLOC_FAIL; 785fc1a8e71Sdjm goto out; 786fc1a8e71Sdjm } 787fc1a8e71Sdjm if (sshkey_read(key, &cp) != 0) { 7881fa0c92bSdjm /* no key? Check for options */ 7891fa0c92bSdjm opts = cp; 7901fa0c92bSdjm if (sshkey_advance_past_options(&cp) != 0) { 791fc1a8e71Sdjm error("%s:%lu: invalid options", path, linenum); 792fc1a8e71Sdjm r = SSH_ERR_INVALID_FORMAT; 793fc1a8e71Sdjm goto out; 7941fa0c92bSdjm } 795d0f52bdbSdjm if (cp == NULL || *cp == '\0') { 796d0f52bdbSdjm error("%s:%lu: missing key", path, linenum); 797d0f52bdbSdjm r = SSH_ERR_INVALID_FORMAT; 798d0f52bdbSdjm goto out; 799d0f52bdbSdjm } 8001fa0c92bSdjm *cp++ = '\0'; 8011fa0c92bSdjm skip_space(&cp); 802fc1a8e71Sdjm if (sshkey_read(key, &cp) != 0) { 803fc1a8e71Sdjm error("%s:%lu: invalid key", path, linenum); 804fc1a8e71Sdjm r = SSH_ERR_INVALID_FORMAT; 805fc1a8e71Sdjm goto out; 8061fa0c92bSdjm } 8071fa0c92bSdjm } 8081fa0c92bSdjm debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts); 8095505cf32Sdjm if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) { 8101fa0c92bSdjm error("%s:%lu: bad options: %s", path, linenum, reason); 811fc1a8e71Sdjm r = SSH_ERR_INVALID_FORMAT; 812fc1a8e71Sdjm goto out; 813fc1a8e71Sdjm } 814fc1a8e71Sdjm /* success */ 815fc1a8e71Sdjm if (principalsp != NULL) { 816fc1a8e71Sdjm *principalsp = principals; 817fc1a8e71Sdjm principals = NULL; /* transferred */ 818fc1a8e71Sdjm } 819fc1a8e71Sdjm if (sigoptsp != NULL) { 820fc1a8e71Sdjm *sigoptsp = sigopts; 821fc1a8e71Sdjm sigopts = NULL; /* transferred */ 822fc1a8e71Sdjm } 823fc1a8e71Sdjm if (keyp != NULL) { 824fc1a8e71Sdjm *keyp = key; 825fc1a8e71Sdjm key = NULL; /* transferred */ 826fc1a8e71Sdjm } 827fc1a8e71Sdjm r = 0; 828fc1a8e71Sdjm out: 829fc1a8e71Sdjm free(principals); 830fc1a8e71Sdjm sshsigopt_free(sigopts); 831fc1a8e71Sdjm sshkey_free(key); 832fc1a8e71Sdjm return r; 833fc1a8e71Sdjm } 834fc1a8e71Sdjm 835fc1a8e71Sdjm static int 8368e703084Sdjm cert_filter_principals(const char *path, u_long linenum, 8378e703084Sdjm char **principalsp, const struct sshkey *cert, uint64_t verify_time) 8388e703084Sdjm { 8398e703084Sdjm char *cp, *oprincipals, *principals; 8408e703084Sdjm const char *reason; 8418e703084Sdjm struct sshbuf *nprincipals; 8428e703084Sdjm int r = SSH_ERR_INTERNAL_ERROR, success = 0; 843e9b9e843Sdjm u_int i; 8448e703084Sdjm 8458e703084Sdjm oprincipals = principals = *principalsp; 8468e703084Sdjm *principalsp = NULL; 8478e703084Sdjm 8488e703084Sdjm if ((nprincipals = sshbuf_new()) == NULL) { 8498e703084Sdjm r = SSH_ERR_ALLOC_FAIL; 8508e703084Sdjm goto out; 8518e703084Sdjm } 8528e703084Sdjm 8538e703084Sdjm while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { 854e9b9e843Sdjm /* Check certificate validity */ 8558e703084Sdjm if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, 856e9b9e843Sdjm verify_time, NULL, &reason)) != 0) { 8578e703084Sdjm debug("%s:%lu: principal \"%s\" not authorized: %s", 8588e703084Sdjm path, linenum, cp, reason); 8598e703084Sdjm continue; 8608e703084Sdjm } 861e9b9e843Sdjm /* Return all matching principal names from the cert */ 862e9b9e843Sdjm for (i = 0; i < cert->cert->nprincipals; i++) { 863e9b9e843Sdjm if (match_pattern(cert->cert->principals[i], cp)) { 8648e703084Sdjm if ((r = sshbuf_putf(nprincipals, "%s%s", 865e9b9e843Sdjm sshbuf_len(nprincipals) != 0 ? "," : "", 866e9b9e843Sdjm cert->cert->principals[i])) != 0) { 8678e703084Sdjm error_f("buffer error"); 8688e703084Sdjm goto out; 8698e703084Sdjm } 8708e703084Sdjm } 871e9b9e843Sdjm } 872e9b9e843Sdjm } 8738e703084Sdjm if (sshbuf_len(nprincipals) == 0) { 8748e703084Sdjm error("%s:%lu: no valid principals found", path, linenum); 8758e703084Sdjm r = SSH_ERR_KEY_CERT_INVALID; 8768e703084Sdjm goto out; 8778e703084Sdjm } 8788e703084Sdjm if ((principals = sshbuf_dup_string(nprincipals)) == NULL) { 8798e703084Sdjm error_f("buffer error"); 8808e703084Sdjm goto out; 8818e703084Sdjm } 8828e703084Sdjm /* success */ 8838e703084Sdjm success = 1; 8848e703084Sdjm *principalsp = principals; 8858e703084Sdjm out: 8868e703084Sdjm sshbuf_free(nprincipals); 8878e703084Sdjm free(oprincipals); 8888e703084Sdjm return success ? 0 : r; 8898e703084Sdjm } 8908e703084Sdjm 8918e703084Sdjm static int 892fc1a8e71Sdjm check_allowed_keys_line(const char *path, u_long linenum, char *line, 893fc1a8e71Sdjm const struct sshkey *sign_key, const char *principal, 894405a02a7Sdjm const char *sig_namespace, uint64_t verify_time, char **principalsp) 895fc1a8e71Sdjm { 896fc1a8e71Sdjm struct sshkey *found_key = NULL; 897405a02a7Sdjm char *principals = NULL; 8982e79209aSdjm int r, success = 0; 899fc1a8e71Sdjm const char *reason = NULL; 900fc1a8e71Sdjm struct sshsigopt *sigopts = NULL; 9012e79209aSdjm char tvalid[64], tverify[64]; 902fc1a8e71Sdjm 903405a02a7Sdjm if (principalsp != NULL) 904405a02a7Sdjm *principalsp = NULL; 905405a02a7Sdjm 906fc1a8e71Sdjm /* Parse the line */ 907fc1a8e71Sdjm if ((r = parse_principals_key_and_options(path, linenum, line, 908405a02a7Sdjm principal, &principals, &found_key, &sigopts)) != 0) { 909fc1a8e71Sdjm /* error already logged */ 9101fa0c92bSdjm goto done; 9111fa0c92bSdjm } 9121fa0c92bSdjm 9132e79209aSdjm if (!sigopts->ca && sshkey_equal(found_key, sign_key)) { 9142e79209aSdjm /* Exact match of key */ 9152e79209aSdjm debug("%s:%lu: matched key", path, linenum); 9162e79209aSdjm } else if (sigopts->ca && sshkey_is_cert(sign_key) && 9172e79209aSdjm sshkey_equal_public(sign_key->cert->signature_key, found_key)) { 918405a02a7Sdjm if (principal) { 919405a02a7Sdjm /* Match certificate CA key with specified principal */ 9202e79209aSdjm if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0, 9212e79209aSdjm verify_time, principal, &reason)) != 0) { 9222e79209aSdjm error("%s:%lu: certificate not authorized: %s", 9232e79209aSdjm path, linenum, reason); 9242e79209aSdjm goto done; 9252e79209aSdjm } 926405a02a7Sdjm debug("%s:%lu: matched certificate CA key", 927405a02a7Sdjm path, linenum); 928405a02a7Sdjm } else { 929405a02a7Sdjm /* No principal specified - find all matching ones */ 930405a02a7Sdjm if ((r = cert_filter_principals(path, linenum, 931405a02a7Sdjm &principals, sign_key, verify_time)) != 0) { 932405a02a7Sdjm /* error already displayed */ 933405a02a7Sdjm debug_r(r, "%s:%lu: cert_filter_principals", 934405a02a7Sdjm path, linenum); 935405a02a7Sdjm goto done; 936405a02a7Sdjm } 937405a02a7Sdjm debug("%s:%lu: matched certificate CA key", 938405a02a7Sdjm path, linenum); 939405a02a7Sdjm } 9402e79209aSdjm } else { 9412e79209aSdjm /* Didn't match key */ 9422e79209aSdjm goto done; 9432e79209aSdjm } 9442e79209aSdjm 9451fa0c92bSdjm /* Check whether options preclude the use of this key */ 946b34f6eceSdjm if (sigopts->namespaces != NULL && sig_namespace != NULL && 9471fa0c92bSdjm match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) { 9481fa0c92bSdjm error("%s:%lu: key is not permitted for use in signature " 9491fa0c92bSdjm "namespace \"%s\"", path, linenum, sig_namespace); 9501fa0c92bSdjm goto done; 9511fa0c92bSdjm } 9521fa0c92bSdjm 9532e79209aSdjm /* check key time validity */ 9542e79209aSdjm format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify)); 9552e79209aSdjm if (sigopts->valid_after != 0 && 9562e79209aSdjm (uint64_t)verify_time < sigopts->valid_after) { 9572e79209aSdjm format_absolute_time(sigopts->valid_after, 9582e79209aSdjm tvalid, sizeof(tvalid)); 9592e79209aSdjm error("%s:%lu: key is not yet valid: " 9602e79209aSdjm "verify time %s < valid-after %s", path, linenum, 9612e79209aSdjm tverify, tvalid); 9621fa0c92bSdjm goto done; 9631fa0c92bSdjm } 9642e79209aSdjm if (sigopts->valid_before != 0 && 9652e79209aSdjm (uint64_t)verify_time > sigopts->valid_before) { 9662e79209aSdjm format_absolute_time(sigopts->valid_before, 9672e79209aSdjm tvalid, sizeof(tvalid)); 9682e79209aSdjm error("%s:%lu: key has expired: " 9692e79209aSdjm "verify time %s > valid-before %s", path, linenum, 9702e79209aSdjm tverify, tvalid); 9711fa0c92bSdjm goto done; 9721fa0c92bSdjm } 9732e79209aSdjm success = 1; 9742e79209aSdjm 9751fa0c92bSdjm done: 976405a02a7Sdjm if (success && principalsp != NULL) { 977405a02a7Sdjm *principalsp = principals; 978405a02a7Sdjm principals = NULL; /* transferred */ 979405a02a7Sdjm } 980405a02a7Sdjm free(principals); 9811fa0c92bSdjm sshkey_free(found_key); 9825505cf32Sdjm sshsigopt_free(sigopts); 9832e79209aSdjm return success ? 0 : SSH_ERR_KEY_NOT_FOUND; 9841fa0c92bSdjm } 9851fa0c92bSdjm 9861fa0c92bSdjm int 9871fa0c92bSdjm sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, 9882e79209aSdjm const char *principal, const char *sig_namespace, uint64_t verify_time) 9891fa0c92bSdjm { 9901fa0c92bSdjm FILE *f = NULL; 9911fa0c92bSdjm char *line = NULL; 9921fa0c92bSdjm size_t linesize = 0; 9931fa0c92bSdjm u_long linenum = 0; 99402b7bb33Sdjm int r = SSH_ERR_KEY_NOT_FOUND, oerrno; 9951fa0c92bSdjm 9961fa0c92bSdjm /* Check key and principal against file */ 9971fa0c92bSdjm if ((f = fopen(path, "r")) == NULL) { 9981fa0c92bSdjm oerrno = errno; 9991fa0c92bSdjm error("Unable to open allowed keys file \"%s\": %s", 10001fa0c92bSdjm path, strerror(errno)); 10011fa0c92bSdjm errno = oerrno; 10021fa0c92bSdjm return SSH_ERR_SYSTEM_ERROR; 10031fa0c92bSdjm } 10041fa0c92bSdjm 10051fa0c92bSdjm while (getline(&line, &linesize, f) != -1) { 10061fa0c92bSdjm linenum++; 10071fa0c92bSdjm r = check_allowed_keys_line(path, linenum, line, sign_key, 1008405a02a7Sdjm principal, sig_namespace, verify_time, NULL); 100964718851Sdjm free(line); 101064718851Sdjm line = NULL; 1011a0f6d93fSdtucker linesize = 0; 10121fa0c92bSdjm if (r == SSH_ERR_KEY_NOT_FOUND) 10131fa0c92bSdjm continue; 10141fa0c92bSdjm else if (r == 0) { 10151fa0c92bSdjm /* success */ 10161fa0c92bSdjm fclose(f); 10171fa0c92bSdjm return 0; 10181fa0c92bSdjm } else 10191fa0c92bSdjm break; 10201fa0c92bSdjm } 10211fa0c92bSdjm /* Either we hit an error parsing or we simply didn't find the key */ 10221fa0c92bSdjm fclose(f); 10231fa0c92bSdjm free(line); 102402b7bb33Sdjm return r; 10251fa0c92bSdjm } 102680b4cf6aSdjm 102780b4cf6aSdjm int 1028cf947153Sdjm sshsig_find_principals(const char *path, const struct sshkey *sign_key, 10292e79209aSdjm uint64_t verify_time, char **principals) 103080b4cf6aSdjm { 103180b4cf6aSdjm FILE *f = NULL; 103280b4cf6aSdjm char *line = NULL; 103380b4cf6aSdjm size_t linesize = 0; 103480b4cf6aSdjm u_long linenum = 0; 10356038711aSdjm int r = SSH_ERR_KEY_NOT_FOUND, oerrno; 103680b4cf6aSdjm 103780b4cf6aSdjm if ((f = fopen(path, "r")) == NULL) { 103880b4cf6aSdjm oerrno = errno; 103980b4cf6aSdjm error("Unable to open allowed keys file \"%s\": %s", 104080b4cf6aSdjm path, strerror(errno)); 104180b4cf6aSdjm errno = oerrno; 104280b4cf6aSdjm return SSH_ERR_SYSTEM_ERROR; 104380b4cf6aSdjm } 104480b4cf6aSdjm 104580b4cf6aSdjm while (getline(&line, &linesize, f) != -1) { 104680b4cf6aSdjm linenum++; 1047405a02a7Sdjm r = check_allowed_keys_line(path, linenum, line, 1048405a02a7Sdjm sign_key, NULL, NULL, verify_time, principals); 104980b4cf6aSdjm free(line); 105080b4cf6aSdjm line = NULL; 1051a0f6d93fSdtucker linesize = 0; 105280b4cf6aSdjm if (r == SSH_ERR_KEY_NOT_FOUND) 105380b4cf6aSdjm continue; 105480b4cf6aSdjm else if (r == 0) { 105580b4cf6aSdjm /* success */ 105680b4cf6aSdjm fclose(f); 105780b4cf6aSdjm return 0; 105880b4cf6aSdjm } else 105980b4cf6aSdjm break; 106080b4cf6aSdjm } 106180b4cf6aSdjm free(line); 106280b4cf6aSdjm /* Either we hit an error parsing or we simply didn't find the key */ 106380b4cf6aSdjm if (ferror(f) != 0) { 106480b4cf6aSdjm oerrno = errno; 106580b4cf6aSdjm fclose(f); 106680b4cf6aSdjm error("Unable to read allowed keys file \"%s\": %s", 106780b4cf6aSdjm path, strerror(errno)); 106880b4cf6aSdjm errno = oerrno; 106980b4cf6aSdjm return SSH_ERR_SYSTEM_ERROR; 107080b4cf6aSdjm } 107180b4cf6aSdjm fclose(f); 10726038711aSdjm return r; 107380b4cf6aSdjm } 107480b4cf6aSdjm 107580b4cf6aSdjm int 10768c0edb7eSdjm sshsig_match_principals(const char *path, const char *principal, 10778c0edb7eSdjm char ***principalsp, size_t *nprincipalsp) 10788c0edb7eSdjm { 10798c0edb7eSdjm FILE *f = NULL; 10808c0edb7eSdjm char *found, *line = NULL, **principals = NULL, **tmp; 10818c0edb7eSdjm size_t i, nprincipals = 0, linesize = 0; 10828c0edb7eSdjm u_long linenum = 0; 108311dd54c7Sdjm int oerrno = 0, r, ret = 0; 10848c0edb7eSdjm 10858c0edb7eSdjm if (principalsp != NULL) 10868c0edb7eSdjm *principalsp = NULL; 10878c0edb7eSdjm if (nprincipalsp != NULL) 10888c0edb7eSdjm *nprincipalsp = 0; 10898c0edb7eSdjm 10908c0edb7eSdjm /* Check key and principal against file */ 10918c0edb7eSdjm if ((f = fopen(path, "r")) == NULL) { 10928c0edb7eSdjm oerrno = errno; 10938c0edb7eSdjm error("Unable to open allowed keys file \"%s\": %s", 10948c0edb7eSdjm path, strerror(errno)); 10958c0edb7eSdjm errno = oerrno; 10968c0edb7eSdjm return SSH_ERR_SYSTEM_ERROR; 10978c0edb7eSdjm } 10988c0edb7eSdjm 10998c0edb7eSdjm while (getline(&line, &linesize, f) != -1) { 11008c0edb7eSdjm linenum++; 11018c0edb7eSdjm /* Parse the line */ 11028c0edb7eSdjm if ((r = parse_principals_key_and_options(path, linenum, line, 11038c0edb7eSdjm principal, &found, NULL, NULL)) != 0) { 11048c0edb7eSdjm if (r == SSH_ERR_KEY_NOT_FOUND) 11058c0edb7eSdjm continue; 11068c0edb7eSdjm ret = r; 11078c0edb7eSdjm oerrno = errno; 11088c0edb7eSdjm break; /* unexpected error */ 11098c0edb7eSdjm } 11108c0edb7eSdjm if ((tmp = recallocarray(principals, nprincipals, 11118c0edb7eSdjm nprincipals + 1, sizeof(*principals))) == NULL) { 11128c0edb7eSdjm ret = SSH_ERR_ALLOC_FAIL; 11138c0edb7eSdjm free(found); 11148c0edb7eSdjm break; 11158c0edb7eSdjm } 11168c0edb7eSdjm principals = tmp; 11178c0edb7eSdjm principals[nprincipals++] = found; /* transferred */ 11188c0edb7eSdjm free(line); 11198c0edb7eSdjm line = NULL; 11208c0edb7eSdjm linesize = 0; 11218c0edb7eSdjm } 11228c0edb7eSdjm fclose(f); 11238c0edb7eSdjm 11248c0edb7eSdjm if (ret == 0) { 11258c0edb7eSdjm if (nprincipals == 0) 11268c0edb7eSdjm ret = SSH_ERR_KEY_NOT_FOUND; 11278d543432Smarkus if (nprincipalsp != 0) 11288d543432Smarkus *nprincipalsp = nprincipals; 11298c0edb7eSdjm if (principalsp != NULL) { 11308c0edb7eSdjm *principalsp = principals; 11318c0edb7eSdjm principals = NULL; /* transferred */ 11328c0edb7eSdjm nprincipals = 0; 11338c0edb7eSdjm } 11348c0edb7eSdjm } 11358c0edb7eSdjm 11368c0edb7eSdjm for (i = 0; i < nprincipals; i++) 11378c0edb7eSdjm free(principals[i]); 11388c0edb7eSdjm free(principals); 11398c0edb7eSdjm 11408c0edb7eSdjm errno = oerrno; 11418c0edb7eSdjm return ret; 11428c0edb7eSdjm } 11438c0edb7eSdjm 11448c0edb7eSdjm int 114580b4cf6aSdjm sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) 114680b4cf6aSdjm { 114780b4cf6aSdjm struct sshkey *pk = NULL; 114880b4cf6aSdjm int r = SSH_ERR_SIGNATURE_INVALID; 114980b4cf6aSdjm 1150ae88981dSmarkus if (pubkey == NULL) 1151ae88981dSmarkus return SSH_ERR_INTERNAL_ERROR; 115280b4cf6aSdjm if ((r = sshsig_parse_preamble(signature)) != 0) 115380b4cf6aSdjm return r; 115480b4cf6aSdjm if ((r = sshkey_froms(signature, &pk)) != 0) 115580b4cf6aSdjm return r; 115680b4cf6aSdjm 115780b4cf6aSdjm *pubkey = pk; 115880b4cf6aSdjm pk = NULL; 115980b4cf6aSdjm return 0; 116080b4cf6aSdjm } 1161