1*ee116499SAntonio Huete Jimenez /* $OpenBSD: sshsig.c,v 1.30 2022/08/19 03:06:30 djm Exp $ */ 20cbfa66cSDaniel Fojt /* 30cbfa66cSDaniel Fojt * Copyright (c) 2019 Google LLC 40cbfa66cSDaniel Fojt * 50cbfa66cSDaniel Fojt * Permission to use, copy, modify, and distribute this software for any 60cbfa66cSDaniel Fojt * purpose with or without fee is hereby granted, provided that the above 70cbfa66cSDaniel Fojt * copyright notice and this permission notice appear in all copies. 80cbfa66cSDaniel Fojt * 90cbfa66cSDaniel Fojt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 100cbfa66cSDaniel Fojt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 110cbfa66cSDaniel Fojt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 120cbfa66cSDaniel Fojt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 130cbfa66cSDaniel Fojt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 140cbfa66cSDaniel Fojt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 150cbfa66cSDaniel Fojt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 160cbfa66cSDaniel Fojt */ 170cbfa66cSDaniel Fojt 180cbfa66cSDaniel Fojt #include "includes.h" 190cbfa66cSDaniel Fojt 200cbfa66cSDaniel Fojt #include <stdio.h> 210cbfa66cSDaniel Fojt #include <stdlib.h> 220cbfa66cSDaniel Fojt #include <stdarg.h> 230cbfa66cSDaniel Fojt #include <errno.h> 240cbfa66cSDaniel Fojt #include <string.h> 250cbfa66cSDaniel Fojt #include <unistd.h> 260cbfa66cSDaniel Fojt 270cbfa66cSDaniel Fojt #include "authfd.h" 280cbfa66cSDaniel Fojt #include "authfile.h" 290cbfa66cSDaniel Fojt #include "log.h" 300cbfa66cSDaniel Fojt #include "misc.h" 310cbfa66cSDaniel Fojt #include "sshbuf.h" 320cbfa66cSDaniel Fojt #include "sshsig.h" 330cbfa66cSDaniel Fojt #include "ssherr.h" 340cbfa66cSDaniel Fojt #include "sshkey.h" 350cbfa66cSDaniel Fojt #include "match.h" 360cbfa66cSDaniel Fojt #include "digest.h" 370cbfa66cSDaniel Fojt 380cbfa66cSDaniel Fojt #define SIG_VERSION 0x01 390cbfa66cSDaniel Fojt #define MAGIC_PREAMBLE "SSHSIG" 400cbfa66cSDaniel Fojt #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1) 410cbfa66cSDaniel Fojt #define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n" 420cbfa66cSDaniel Fojt #define END_SIGNATURE "-----END SSH SIGNATURE-----" 430cbfa66cSDaniel Fojt #define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */ 440cbfa66cSDaniel Fojt #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256" 450cbfa66cSDaniel Fojt #define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */ 460cbfa66cSDaniel Fojt #define HASHALG_ALLOWED "sha256,sha512" 470cbfa66cSDaniel Fojt 480cbfa66cSDaniel Fojt int 490cbfa66cSDaniel Fojt sshsig_armor(const struct sshbuf *blob, struct sshbuf **out) 500cbfa66cSDaniel Fojt { 510cbfa66cSDaniel Fojt struct sshbuf *buf = NULL; 520cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 530cbfa66cSDaniel Fojt 540cbfa66cSDaniel Fojt *out = NULL; 550cbfa66cSDaniel Fojt 560cbfa66cSDaniel Fojt if ((buf = sshbuf_new()) == NULL) { 5750a69bb5SSascha Wildner error_f("sshbuf_new failed"); 580cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 590cbfa66cSDaniel Fojt goto out; 600cbfa66cSDaniel Fojt } 610cbfa66cSDaniel Fojt 620cbfa66cSDaniel Fojt if ((r = sshbuf_put(buf, BEGIN_SIGNATURE, 630cbfa66cSDaniel Fojt sizeof(BEGIN_SIGNATURE)-1)) != 0) { 6450a69bb5SSascha Wildner error_fr(r, "sshbuf_putf"); 650cbfa66cSDaniel Fojt goto out; 660cbfa66cSDaniel Fojt } 670cbfa66cSDaniel Fojt 680cbfa66cSDaniel Fojt if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) { 6950a69bb5SSascha Wildner error_fr(r, "base64 encode signature"); 700cbfa66cSDaniel Fojt goto out; 710cbfa66cSDaniel Fojt } 720cbfa66cSDaniel Fojt 730cbfa66cSDaniel Fojt if ((r = sshbuf_put(buf, END_SIGNATURE, 740cbfa66cSDaniel Fojt sizeof(END_SIGNATURE)-1)) != 0 || 750cbfa66cSDaniel Fojt (r = sshbuf_put_u8(buf, '\n')) != 0) { 7650a69bb5SSascha Wildner error_fr(r, "sshbuf_put"); 770cbfa66cSDaniel Fojt goto out; 780cbfa66cSDaniel Fojt } 790cbfa66cSDaniel Fojt /* success */ 800cbfa66cSDaniel Fojt *out = buf; 810cbfa66cSDaniel Fojt buf = NULL; /* transferred */ 820cbfa66cSDaniel Fojt r = 0; 830cbfa66cSDaniel Fojt out: 840cbfa66cSDaniel Fojt sshbuf_free(buf); 850cbfa66cSDaniel Fojt return r; 860cbfa66cSDaniel Fojt } 870cbfa66cSDaniel Fojt 880cbfa66cSDaniel Fojt int 890cbfa66cSDaniel Fojt sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out) 900cbfa66cSDaniel Fojt { 910cbfa66cSDaniel Fojt int r; 920cbfa66cSDaniel Fojt size_t eoffset = 0; 930cbfa66cSDaniel Fojt struct sshbuf *buf = NULL; 940cbfa66cSDaniel Fojt struct sshbuf *sbuf = NULL; 950cbfa66cSDaniel Fojt char *b64 = NULL; 960cbfa66cSDaniel Fojt 970cbfa66cSDaniel Fojt if ((sbuf = sshbuf_fromb(sig)) == NULL) { 9850a69bb5SSascha Wildner error_f("sshbuf_fromb failed"); 990cbfa66cSDaniel Fojt return SSH_ERR_ALLOC_FAIL; 1000cbfa66cSDaniel Fojt } 1010cbfa66cSDaniel Fojt 1020cbfa66cSDaniel Fojt if ((r = sshbuf_cmp(sbuf, 0, 1030cbfa66cSDaniel Fojt BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) { 1040cbfa66cSDaniel Fojt error("Couldn't parse signature: missing header"); 1050cbfa66cSDaniel Fojt goto done; 1060cbfa66cSDaniel Fojt } 1070cbfa66cSDaniel Fojt 1080cbfa66cSDaniel Fojt if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) { 10950a69bb5SSascha Wildner error_fr(r, "consume"); 1100cbfa66cSDaniel Fojt goto done; 1110cbfa66cSDaniel Fojt } 1120cbfa66cSDaniel Fojt 1130cbfa66cSDaniel Fojt if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE, 1140cbfa66cSDaniel Fojt sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) { 1150cbfa66cSDaniel Fojt error("Couldn't parse signature: missing footer"); 1160cbfa66cSDaniel Fojt goto done; 1170cbfa66cSDaniel Fojt } 1180cbfa66cSDaniel Fojt 1190cbfa66cSDaniel Fojt if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) { 12050a69bb5SSascha Wildner error_fr(r, "consume"); 1210cbfa66cSDaniel Fojt goto done; 1220cbfa66cSDaniel Fojt } 1230cbfa66cSDaniel Fojt 1240cbfa66cSDaniel Fojt if ((b64 = sshbuf_dup_string(sbuf)) == NULL) { 12550a69bb5SSascha Wildner error_f("sshbuf_dup_string failed"); 1260cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 1270cbfa66cSDaniel Fojt goto done; 1280cbfa66cSDaniel Fojt } 1290cbfa66cSDaniel Fojt 1300cbfa66cSDaniel Fojt if ((buf = sshbuf_new()) == NULL) { 13150a69bb5SSascha Wildner error_f("sshbuf_new() failed"); 1320cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 1330cbfa66cSDaniel Fojt goto done; 1340cbfa66cSDaniel Fojt } 1350cbfa66cSDaniel Fojt 1360cbfa66cSDaniel Fojt if ((r = sshbuf_b64tod(buf, b64)) != 0) { 13750a69bb5SSascha Wildner error_fr(r, "decode base64"); 1380cbfa66cSDaniel Fojt goto done; 1390cbfa66cSDaniel Fojt } 1400cbfa66cSDaniel Fojt 1410cbfa66cSDaniel Fojt /* success */ 1420cbfa66cSDaniel Fojt *out = buf; 1430cbfa66cSDaniel Fojt r = 0; 1440cbfa66cSDaniel Fojt buf = NULL; /* transferred */ 1450cbfa66cSDaniel Fojt done: 1460cbfa66cSDaniel Fojt sshbuf_free(buf); 1470cbfa66cSDaniel Fojt sshbuf_free(sbuf); 1480cbfa66cSDaniel Fojt free(b64); 1490cbfa66cSDaniel Fojt return r; 1500cbfa66cSDaniel Fojt } 1510cbfa66cSDaniel Fojt 1520cbfa66cSDaniel Fojt static int 1530cbfa66cSDaniel Fojt sshsig_wrap_sign(struct sshkey *key, const char *hashalg, 15450a69bb5SSascha Wildner const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message, 1550cbfa66cSDaniel Fojt const char *sig_namespace, struct sshbuf **out, 1560cbfa66cSDaniel Fojt sshsig_signer *signer, void *signer_ctx) 1570cbfa66cSDaniel Fojt { 1580cbfa66cSDaniel Fojt int r; 1590cbfa66cSDaniel Fojt size_t slen = 0; 1600cbfa66cSDaniel Fojt u_char *sig = NULL; 1610cbfa66cSDaniel Fojt struct sshbuf *blob = NULL; 1620cbfa66cSDaniel Fojt struct sshbuf *tosign = NULL; 1630cbfa66cSDaniel Fojt const char *sign_alg = NULL; 1640cbfa66cSDaniel Fojt 1650cbfa66cSDaniel Fojt if ((tosign = sshbuf_new()) == NULL || 1660cbfa66cSDaniel Fojt (blob = sshbuf_new()) == NULL) { 16750a69bb5SSascha Wildner error_f("sshbuf_new failed"); 1680cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 1690cbfa66cSDaniel Fojt goto done; 1700cbfa66cSDaniel Fojt } 1710cbfa66cSDaniel Fojt 1720cbfa66cSDaniel Fojt if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || 1730cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 || 1740cbfa66cSDaniel Fojt (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */ 1750cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(tosign, hashalg)) != 0 || 1760cbfa66cSDaniel Fojt (r = sshbuf_put_stringb(tosign, h_message)) != 0) { 17750a69bb5SSascha Wildner error_fr(r, "assemble message to sign"); 1780cbfa66cSDaniel Fojt goto done; 1790cbfa66cSDaniel Fojt } 1800cbfa66cSDaniel Fojt 1810cbfa66cSDaniel Fojt /* If using RSA keys then default to a good signature algorithm */ 1820cbfa66cSDaniel Fojt if (sshkey_type_plain(key->type) == KEY_RSA) 1830cbfa66cSDaniel Fojt sign_alg = RSA_SIGN_ALG; 1840cbfa66cSDaniel Fojt 1850cbfa66cSDaniel Fojt if (signer != NULL) { 1860cbfa66cSDaniel Fojt if ((r = signer(key, &sig, &slen, 1870cbfa66cSDaniel Fojt sshbuf_ptr(tosign), sshbuf_len(tosign), 18850a69bb5SSascha Wildner sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) { 18950a69bb5SSascha Wildner error_r(r, "Couldn't sign message (signer)"); 1900cbfa66cSDaniel Fojt goto done; 1910cbfa66cSDaniel Fojt } 1920cbfa66cSDaniel Fojt } else { 1930cbfa66cSDaniel Fojt if ((r = sshkey_sign(key, &sig, &slen, 1940cbfa66cSDaniel Fojt sshbuf_ptr(tosign), sshbuf_len(tosign), 19550a69bb5SSascha Wildner sign_alg, sk_provider, sk_pin, 0)) != 0) { 19650a69bb5SSascha Wildner error_r(r, "Couldn't sign message"); 1970cbfa66cSDaniel Fojt goto done; 1980cbfa66cSDaniel Fojt } 1990cbfa66cSDaniel Fojt } 2000cbfa66cSDaniel Fojt 2010cbfa66cSDaniel Fojt if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || 2020cbfa66cSDaniel Fojt (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 || 2030cbfa66cSDaniel Fojt (r = sshkey_puts(key, blob)) != 0 || 2040cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 || 2050cbfa66cSDaniel Fojt (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */ 2060cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(blob, hashalg)) != 0 || 2070cbfa66cSDaniel Fojt (r = sshbuf_put_string(blob, sig, slen)) != 0) { 20850a69bb5SSascha Wildner error_fr(r, "assemble signature object"); 2090cbfa66cSDaniel Fojt goto done; 2100cbfa66cSDaniel Fojt } 2110cbfa66cSDaniel Fojt 2120cbfa66cSDaniel Fojt if (out != NULL) { 2130cbfa66cSDaniel Fojt *out = blob; 2140cbfa66cSDaniel Fojt blob = NULL; 2150cbfa66cSDaniel Fojt } 2160cbfa66cSDaniel Fojt r = 0; 2170cbfa66cSDaniel Fojt done: 2180cbfa66cSDaniel Fojt free(sig); 2190cbfa66cSDaniel Fojt sshbuf_free(blob); 2200cbfa66cSDaniel Fojt sshbuf_free(tosign); 2210cbfa66cSDaniel Fojt return r; 2220cbfa66cSDaniel Fojt } 2230cbfa66cSDaniel Fojt 2240cbfa66cSDaniel Fojt /* Check preamble and version. */ 2250cbfa66cSDaniel Fojt static int 2260cbfa66cSDaniel Fojt sshsig_parse_preamble(struct sshbuf *buf) 2270cbfa66cSDaniel Fojt { 2280cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 2290cbfa66cSDaniel Fojt uint32_t sversion; 2300cbfa66cSDaniel Fojt 2310cbfa66cSDaniel Fojt if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || 2320cbfa66cSDaniel Fojt (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 || 2330cbfa66cSDaniel Fojt (r = sshbuf_get_u32(buf, &sversion)) != 0) { 2340cbfa66cSDaniel Fojt error("Couldn't verify signature: invalid format"); 2350cbfa66cSDaniel Fojt return r; 2360cbfa66cSDaniel Fojt } 2370cbfa66cSDaniel Fojt 2380cbfa66cSDaniel Fojt if (sversion > SIG_VERSION) { 2390cbfa66cSDaniel Fojt error("Signature version %lu is larger than supported " 2400cbfa66cSDaniel Fojt "version %u", (unsigned long)sversion, SIG_VERSION); 2410cbfa66cSDaniel Fojt return SSH_ERR_INVALID_FORMAT; 2420cbfa66cSDaniel Fojt } 2430cbfa66cSDaniel Fojt return 0; 2440cbfa66cSDaniel Fojt } 2450cbfa66cSDaniel Fojt 2460cbfa66cSDaniel Fojt static int 2470cbfa66cSDaniel Fojt sshsig_check_hashalg(const char *hashalg) 2480cbfa66cSDaniel Fojt { 2490cbfa66cSDaniel Fojt if (hashalg == NULL || 2500cbfa66cSDaniel Fojt match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1) 2510cbfa66cSDaniel Fojt return 0; 25250a69bb5SSascha Wildner error_f("unsupported hash algorithm \"%.100s\"", hashalg); 2530cbfa66cSDaniel Fojt return SSH_ERR_SIGN_ALG_UNSUPPORTED; 2540cbfa66cSDaniel Fojt } 2550cbfa66cSDaniel Fojt 2560cbfa66cSDaniel Fojt static int 2570cbfa66cSDaniel Fojt sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp) 2580cbfa66cSDaniel Fojt { 2590cbfa66cSDaniel Fojt struct sshbuf *buf = NULL; 2600cbfa66cSDaniel Fojt char *hashalg = NULL; 2610cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 2620cbfa66cSDaniel Fojt 2630cbfa66cSDaniel Fojt if (hashalgp != NULL) 2640cbfa66cSDaniel Fojt *hashalgp = NULL; 2650cbfa66cSDaniel Fojt if ((buf = sshbuf_fromb(signature)) == NULL) 2660cbfa66cSDaniel Fojt return SSH_ERR_ALLOC_FAIL; 2670cbfa66cSDaniel Fojt if ((r = sshsig_parse_preamble(buf)) != 0) 2680cbfa66cSDaniel Fojt goto done; 2690cbfa66cSDaniel Fojt if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || 2700cbfa66cSDaniel Fojt (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || 2710cbfa66cSDaniel Fojt (r = sshbuf_get_string(buf, NULL, NULL)) != 0 || 2720cbfa66cSDaniel Fojt (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 || 2730cbfa66cSDaniel Fojt (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) { 27450a69bb5SSascha Wildner error_fr(r, "parse signature object"); 2750cbfa66cSDaniel Fojt goto done; 2760cbfa66cSDaniel Fojt } 2770cbfa66cSDaniel Fojt 2780cbfa66cSDaniel Fojt /* success */ 2790cbfa66cSDaniel Fojt r = 0; 2800cbfa66cSDaniel Fojt *hashalgp = hashalg; 2810cbfa66cSDaniel Fojt hashalg = NULL; 2820cbfa66cSDaniel Fojt done: 2830cbfa66cSDaniel Fojt free(hashalg); 2840cbfa66cSDaniel Fojt sshbuf_free(buf); 2850cbfa66cSDaniel Fojt return r; 2860cbfa66cSDaniel Fojt } 2870cbfa66cSDaniel Fojt 2880cbfa66cSDaniel Fojt static int 2890cbfa66cSDaniel Fojt sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg, 2900cbfa66cSDaniel Fojt const struct sshbuf *h_message, const char *expect_namespace, 2910cbfa66cSDaniel Fojt struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details) 2920cbfa66cSDaniel Fojt { 2930cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 2940cbfa66cSDaniel Fojt struct sshbuf *buf = NULL, *toverify = NULL; 2950cbfa66cSDaniel Fojt struct sshkey *key = NULL; 2960cbfa66cSDaniel Fojt const u_char *sig; 2970cbfa66cSDaniel Fojt char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL; 2980cbfa66cSDaniel Fojt size_t siglen; 2990cbfa66cSDaniel Fojt 30050a69bb5SSascha Wildner debug_f("verify message length %zu", sshbuf_len(h_message)); 3010cbfa66cSDaniel Fojt if (sig_details != NULL) 3020cbfa66cSDaniel Fojt *sig_details = NULL; 3030cbfa66cSDaniel Fojt if (sign_keyp != NULL) 3040cbfa66cSDaniel Fojt *sign_keyp = NULL; 3050cbfa66cSDaniel Fojt 3060cbfa66cSDaniel Fojt if ((toverify = sshbuf_new()) == NULL) { 30750a69bb5SSascha Wildner error_f("sshbuf_new failed"); 3080cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 3090cbfa66cSDaniel Fojt goto done; 3100cbfa66cSDaniel Fojt } 3110cbfa66cSDaniel Fojt if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE, 3120cbfa66cSDaniel Fojt MAGIC_PREAMBLE_LEN)) != 0 || 3130cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 || 3140cbfa66cSDaniel Fojt (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */ 3150cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(toverify, hashalg)) != 0 || 3160cbfa66cSDaniel Fojt (r = sshbuf_put_stringb(toverify, h_message)) != 0) { 31750a69bb5SSascha Wildner error_fr(r, "assemble message to verify"); 3180cbfa66cSDaniel Fojt goto done; 3190cbfa66cSDaniel Fojt } 3200cbfa66cSDaniel Fojt 3210cbfa66cSDaniel Fojt if ((r = sshsig_parse_preamble(signature)) != 0) 3220cbfa66cSDaniel Fojt goto done; 3230cbfa66cSDaniel Fojt 3240cbfa66cSDaniel Fojt if ((r = sshkey_froms(signature, &key)) != 0 || 3250cbfa66cSDaniel Fojt (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 || 3260cbfa66cSDaniel Fojt (r = sshbuf_get_string(signature, NULL, NULL)) != 0 || 3270cbfa66cSDaniel Fojt (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 || 3280cbfa66cSDaniel Fojt (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) { 32950a69bb5SSascha Wildner error_fr(r, "parse signature object"); 3300cbfa66cSDaniel Fojt goto done; 3310cbfa66cSDaniel Fojt } 3320cbfa66cSDaniel Fojt 3330cbfa66cSDaniel Fojt if (sshbuf_len(signature) != 0) { 3340cbfa66cSDaniel Fojt error("Signature contains trailing data"); 3350cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT; 3360cbfa66cSDaniel Fojt goto done; 3370cbfa66cSDaniel Fojt } 3380cbfa66cSDaniel Fojt 3390cbfa66cSDaniel Fojt if (strcmp(expect_namespace, got_namespace) != 0) { 3400cbfa66cSDaniel Fojt error("Couldn't verify signature: namespace does not match"); 34150a69bb5SSascha Wildner debug_f("expected namespace \"%s\" received \"%s\"", 34250a69bb5SSascha Wildner expect_namespace, got_namespace); 3430cbfa66cSDaniel Fojt r = SSH_ERR_SIGNATURE_INVALID; 3440cbfa66cSDaniel Fojt goto done; 3450cbfa66cSDaniel Fojt } 3460cbfa66cSDaniel Fojt if (strcmp(hashalg, sig_hashalg) != 0) { 3470cbfa66cSDaniel Fojt error("Couldn't verify signature: hash algorithm mismatch"); 34850a69bb5SSascha Wildner debug_f("expected algorithm \"%s\" received \"%s\"", 34950a69bb5SSascha Wildner hashalg, sig_hashalg); 3500cbfa66cSDaniel Fojt r = SSH_ERR_SIGNATURE_INVALID; 3510cbfa66cSDaniel Fojt goto done; 3520cbfa66cSDaniel Fojt } 3530cbfa66cSDaniel Fojt /* Ensure that RSA keys use an acceptable signature algorithm */ 3540cbfa66cSDaniel Fojt if (sshkey_type_plain(key->type) == KEY_RSA) { 3550cbfa66cSDaniel Fojt if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) { 35650a69bb5SSascha Wildner error_r(r, "Couldn't verify signature: unable to get " 35750a69bb5SSascha Wildner "signature type"); 3580cbfa66cSDaniel Fojt goto done; 3590cbfa66cSDaniel Fojt } 3600cbfa66cSDaniel Fojt if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) { 3610cbfa66cSDaniel Fojt error("Couldn't verify signature: unsupported RSA " 3620cbfa66cSDaniel Fojt "signature algorithm %s", sigtype); 3630cbfa66cSDaniel Fojt r = SSH_ERR_SIGN_ALG_UNSUPPORTED; 3640cbfa66cSDaniel Fojt goto done; 3650cbfa66cSDaniel Fojt } 3660cbfa66cSDaniel Fojt } 3670cbfa66cSDaniel Fojt if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify), 3680cbfa66cSDaniel Fojt sshbuf_len(toverify), NULL, 0, sig_details)) != 0) { 36950a69bb5SSascha Wildner error_r(r, "Signature verification failed"); 3700cbfa66cSDaniel Fojt goto done; 3710cbfa66cSDaniel Fojt } 3720cbfa66cSDaniel Fojt 3730cbfa66cSDaniel Fojt /* success */ 3740cbfa66cSDaniel Fojt r = 0; 3750cbfa66cSDaniel Fojt if (sign_keyp != NULL) { 3760cbfa66cSDaniel Fojt *sign_keyp = key; 3770cbfa66cSDaniel Fojt key = NULL; /* transferred */ 3780cbfa66cSDaniel Fojt } 3790cbfa66cSDaniel Fojt done: 3800cbfa66cSDaniel Fojt free(got_namespace); 3810cbfa66cSDaniel Fojt free(sigtype); 3820cbfa66cSDaniel Fojt free(sig_hashalg); 3830cbfa66cSDaniel Fojt sshbuf_free(buf); 3840cbfa66cSDaniel Fojt sshbuf_free(toverify); 3850cbfa66cSDaniel Fojt sshkey_free(key); 3860cbfa66cSDaniel Fojt return r; 3870cbfa66cSDaniel Fojt } 3880cbfa66cSDaniel Fojt 3890cbfa66cSDaniel Fojt static int 3900cbfa66cSDaniel Fojt hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp) 3910cbfa66cSDaniel Fojt { 3920cbfa66cSDaniel Fojt char *hex, hash[SSH_DIGEST_MAX_LENGTH]; 3930cbfa66cSDaniel Fojt int alg, r = SSH_ERR_INTERNAL_ERROR; 3940cbfa66cSDaniel Fojt struct sshbuf *b = NULL; 3950cbfa66cSDaniel Fojt 3960cbfa66cSDaniel Fojt *bp = NULL; 3970cbfa66cSDaniel Fojt memset(hash, 0, sizeof(hash)); 3980cbfa66cSDaniel Fojt 3990cbfa66cSDaniel Fojt if ((r = sshsig_check_hashalg(hashalg)) != 0) 4000cbfa66cSDaniel Fojt return r; 4010cbfa66cSDaniel Fojt if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { 40250a69bb5SSascha Wildner error_f("can't look up hash algorithm %s", hashalg); 4030cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 4040cbfa66cSDaniel Fojt } 4050cbfa66cSDaniel Fojt if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) { 40650a69bb5SSascha Wildner error_fr(r, "ssh_digest_buffer"); 4070cbfa66cSDaniel Fojt return r; 4080cbfa66cSDaniel Fojt } 4090cbfa66cSDaniel Fojt if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { 41050a69bb5SSascha Wildner debug3_f("final hash: %s", hex); 4110cbfa66cSDaniel Fojt freezero(hex, strlen(hex)); 4120cbfa66cSDaniel Fojt } 4130cbfa66cSDaniel Fojt if ((b = sshbuf_new()) == NULL) { 4140cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 4150cbfa66cSDaniel Fojt goto out; 4160cbfa66cSDaniel Fojt } 4170cbfa66cSDaniel Fojt if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { 41850a69bb5SSascha Wildner error_fr(r, "sshbuf_put"); 4190cbfa66cSDaniel Fojt goto out; 4200cbfa66cSDaniel Fojt } 4210cbfa66cSDaniel Fojt *bp = b; 4220cbfa66cSDaniel Fojt b = NULL; /* transferred */ 4230cbfa66cSDaniel Fojt /* success */ 4240cbfa66cSDaniel Fojt r = 0; 4250cbfa66cSDaniel Fojt out: 4260cbfa66cSDaniel Fojt sshbuf_free(b); 4270cbfa66cSDaniel Fojt explicit_bzero(hash, sizeof(hash)); 4280cbfa66cSDaniel Fojt return r; 4290cbfa66cSDaniel Fojt } 4300cbfa66cSDaniel Fojt 4310cbfa66cSDaniel Fojt int 43250a69bb5SSascha Wildner sshsig_signb(struct sshkey *key, const char *hashalg, 43350a69bb5SSascha Wildner const char *sk_provider, const char *sk_pin, 4340cbfa66cSDaniel Fojt const struct sshbuf *message, const char *sig_namespace, 4350cbfa66cSDaniel Fojt struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) 4360cbfa66cSDaniel Fojt { 4370cbfa66cSDaniel Fojt struct sshbuf *b = NULL; 4380cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 4390cbfa66cSDaniel Fojt 4400cbfa66cSDaniel Fojt if (hashalg == NULL) 4410cbfa66cSDaniel Fojt hashalg = HASHALG_DEFAULT; 4420cbfa66cSDaniel Fojt if (out != NULL) 4430cbfa66cSDaniel Fojt *out = NULL; 4440cbfa66cSDaniel Fojt if ((r = hash_buffer(message, hashalg, &b)) != 0) { 44550a69bb5SSascha Wildner error_fr(r, "hash buffer"); 4460cbfa66cSDaniel Fojt goto out; 4470cbfa66cSDaniel Fojt } 44850a69bb5SSascha Wildner if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, 4490cbfa66cSDaniel Fojt sig_namespace, out, signer, signer_ctx)) != 0) 4500cbfa66cSDaniel Fojt goto out; 4510cbfa66cSDaniel Fojt /* success */ 4520cbfa66cSDaniel Fojt r = 0; 4530cbfa66cSDaniel Fojt out: 4540cbfa66cSDaniel Fojt sshbuf_free(b); 4550cbfa66cSDaniel Fojt return r; 4560cbfa66cSDaniel Fojt } 4570cbfa66cSDaniel Fojt 4580cbfa66cSDaniel Fojt int 4590cbfa66cSDaniel Fojt sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message, 4600cbfa66cSDaniel Fojt const char *expect_namespace, struct sshkey **sign_keyp, 4610cbfa66cSDaniel Fojt struct sshkey_sig_details **sig_details) 4620cbfa66cSDaniel Fojt { 4630cbfa66cSDaniel Fojt struct sshbuf *b = NULL; 4640cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 4650cbfa66cSDaniel Fojt char *hashalg = NULL; 4660cbfa66cSDaniel Fojt 4670cbfa66cSDaniel Fojt if (sig_details != NULL) 4680cbfa66cSDaniel Fojt *sig_details = NULL; 4690cbfa66cSDaniel Fojt if (sign_keyp != NULL) 4700cbfa66cSDaniel Fojt *sign_keyp = NULL; 4710cbfa66cSDaniel Fojt if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) 4720cbfa66cSDaniel Fojt return r; 47350a69bb5SSascha Wildner debug_f("signature made with hash \"%s\"", hashalg); 4740cbfa66cSDaniel Fojt if ((r = hash_buffer(message, hashalg, &b)) != 0) { 47550a69bb5SSascha Wildner error_fr(r, "hash buffer"); 4760cbfa66cSDaniel Fojt goto out; 4770cbfa66cSDaniel Fojt } 4780cbfa66cSDaniel Fojt if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, 4790cbfa66cSDaniel Fojt sign_keyp, sig_details)) != 0) 4800cbfa66cSDaniel Fojt goto out; 4810cbfa66cSDaniel Fojt /* success */ 4820cbfa66cSDaniel Fojt r = 0; 4830cbfa66cSDaniel Fojt out: 4840cbfa66cSDaniel Fojt sshbuf_free(b); 4850cbfa66cSDaniel Fojt free(hashalg); 4860cbfa66cSDaniel Fojt return r; 4870cbfa66cSDaniel Fojt } 4880cbfa66cSDaniel Fojt 4890cbfa66cSDaniel Fojt static int 4900cbfa66cSDaniel Fojt hash_file(int fd, const char *hashalg, struct sshbuf **bp) 4910cbfa66cSDaniel Fojt { 4920cbfa66cSDaniel Fojt char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH]; 4930cbfa66cSDaniel Fojt ssize_t n, total = 0; 494*ee116499SAntonio Huete Jimenez struct ssh_digest_ctx *ctx = NULL; 4950cbfa66cSDaniel Fojt int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR; 4960cbfa66cSDaniel Fojt struct sshbuf *b = NULL; 4970cbfa66cSDaniel Fojt 4980cbfa66cSDaniel Fojt *bp = NULL; 4990cbfa66cSDaniel Fojt memset(hash, 0, sizeof(hash)); 5000cbfa66cSDaniel Fojt 5010cbfa66cSDaniel Fojt if ((r = sshsig_check_hashalg(hashalg)) != 0) 5020cbfa66cSDaniel Fojt return r; 5030cbfa66cSDaniel Fojt if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { 50450a69bb5SSascha Wildner error_f("can't look up hash algorithm %s", hashalg); 5050cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 5060cbfa66cSDaniel Fojt } 5070cbfa66cSDaniel Fojt if ((ctx = ssh_digest_start(alg)) == NULL) { 50850a69bb5SSascha Wildner error_f("ssh_digest_start failed"); 5090cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 5100cbfa66cSDaniel Fojt } 5110cbfa66cSDaniel Fojt for (;;) { 5120cbfa66cSDaniel Fojt if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) { 5130cbfa66cSDaniel Fojt if (errno == EINTR || errno == EAGAIN) 5140cbfa66cSDaniel Fojt continue; 5150cbfa66cSDaniel Fojt oerrno = errno; 51650a69bb5SSascha Wildner error_f("read: %s", strerror(errno)); 5170cbfa66cSDaniel Fojt errno = oerrno; 5180cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR; 5190cbfa66cSDaniel Fojt goto out; 5200cbfa66cSDaniel Fojt } else if (n == 0) { 52150a69bb5SSascha Wildner debug2_f("hashed %zu bytes", total); 5220cbfa66cSDaniel Fojt break; /* EOF */ 5230cbfa66cSDaniel Fojt } 5240cbfa66cSDaniel Fojt total += (size_t)n; 5250cbfa66cSDaniel Fojt if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) { 52650a69bb5SSascha Wildner error_fr(r, "ssh_digest_update"); 5270cbfa66cSDaniel Fojt goto out; 5280cbfa66cSDaniel Fojt } 5290cbfa66cSDaniel Fojt } 5300cbfa66cSDaniel Fojt if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) { 53150a69bb5SSascha Wildner error_fr(r, "ssh_digest_final"); 5320cbfa66cSDaniel Fojt goto out; 5330cbfa66cSDaniel Fojt } 5340cbfa66cSDaniel Fojt if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { 53550a69bb5SSascha Wildner debug3_f("final hash: %s", hex); 5360cbfa66cSDaniel Fojt freezero(hex, strlen(hex)); 5370cbfa66cSDaniel Fojt } 5380cbfa66cSDaniel Fojt if ((b = sshbuf_new()) == NULL) { 5390cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 5400cbfa66cSDaniel Fojt goto out; 5410cbfa66cSDaniel Fojt } 5420cbfa66cSDaniel Fojt if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { 54350a69bb5SSascha Wildner error_fr(r, "sshbuf_put"); 5440cbfa66cSDaniel Fojt goto out; 5450cbfa66cSDaniel Fojt } 5460cbfa66cSDaniel Fojt *bp = b; 5470cbfa66cSDaniel Fojt b = NULL; /* transferred */ 5480cbfa66cSDaniel Fojt /* success */ 5490cbfa66cSDaniel Fojt r = 0; 5500cbfa66cSDaniel Fojt out: 551*ee116499SAntonio Huete Jimenez oerrno = errno; 5520cbfa66cSDaniel Fojt sshbuf_free(b); 5530cbfa66cSDaniel Fojt ssh_digest_free(ctx); 5540cbfa66cSDaniel Fojt explicit_bzero(hash, sizeof(hash)); 555*ee116499SAntonio Huete Jimenez errno = oerrno; 5560cbfa66cSDaniel Fojt return r; 5570cbfa66cSDaniel Fojt } 5580cbfa66cSDaniel Fojt 5590cbfa66cSDaniel Fojt int 56050a69bb5SSascha Wildner sshsig_sign_fd(struct sshkey *key, const char *hashalg, 56150a69bb5SSascha Wildner const char *sk_provider, const char *sk_pin, 5620cbfa66cSDaniel Fojt int fd, const char *sig_namespace, struct sshbuf **out, 5630cbfa66cSDaniel Fojt sshsig_signer *signer, void *signer_ctx) 5640cbfa66cSDaniel Fojt { 5650cbfa66cSDaniel Fojt struct sshbuf *b = NULL; 5660cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 5670cbfa66cSDaniel Fojt 5680cbfa66cSDaniel Fojt if (hashalg == NULL) 5690cbfa66cSDaniel Fojt hashalg = HASHALG_DEFAULT; 5700cbfa66cSDaniel Fojt if (out != NULL) 5710cbfa66cSDaniel Fojt *out = NULL; 5720cbfa66cSDaniel Fojt if ((r = hash_file(fd, hashalg, &b)) != 0) { 57350a69bb5SSascha Wildner error_fr(r, "hash_file"); 5740cbfa66cSDaniel Fojt return r; 5750cbfa66cSDaniel Fojt } 57650a69bb5SSascha Wildner if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, 5770cbfa66cSDaniel Fojt sig_namespace, out, signer, signer_ctx)) != 0) 5780cbfa66cSDaniel Fojt goto out; 5790cbfa66cSDaniel Fojt /* success */ 5800cbfa66cSDaniel Fojt r = 0; 5810cbfa66cSDaniel Fojt out: 5820cbfa66cSDaniel Fojt sshbuf_free(b); 5830cbfa66cSDaniel Fojt return r; 5840cbfa66cSDaniel Fojt } 5850cbfa66cSDaniel Fojt 5860cbfa66cSDaniel Fojt int 5870cbfa66cSDaniel Fojt sshsig_verify_fd(struct sshbuf *signature, int fd, 5880cbfa66cSDaniel Fojt const char *expect_namespace, struct sshkey **sign_keyp, 5890cbfa66cSDaniel Fojt struct sshkey_sig_details **sig_details) 5900cbfa66cSDaniel Fojt { 5910cbfa66cSDaniel Fojt struct sshbuf *b = NULL; 5920cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 5930cbfa66cSDaniel Fojt char *hashalg = NULL; 5940cbfa66cSDaniel Fojt 5950cbfa66cSDaniel Fojt if (sig_details != NULL) 5960cbfa66cSDaniel Fojt *sig_details = NULL; 5970cbfa66cSDaniel Fojt if (sign_keyp != NULL) 5980cbfa66cSDaniel Fojt *sign_keyp = NULL; 5990cbfa66cSDaniel Fojt if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) 6000cbfa66cSDaniel Fojt return r; 60150a69bb5SSascha Wildner debug_f("signature made with hash \"%s\"", hashalg); 6020cbfa66cSDaniel Fojt if ((r = hash_file(fd, hashalg, &b)) != 0) { 60350a69bb5SSascha Wildner error_fr(r, "hash_file"); 6040cbfa66cSDaniel Fojt goto out; 6050cbfa66cSDaniel Fojt } 6060cbfa66cSDaniel Fojt if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, 6070cbfa66cSDaniel Fojt sign_keyp, sig_details)) != 0) 6080cbfa66cSDaniel Fojt goto out; 6090cbfa66cSDaniel Fojt /* success */ 6100cbfa66cSDaniel Fojt r = 0; 6110cbfa66cSDaniel Fojt out: 6120cbfa66cSDaniel Fojt sshbuf_free(b); 6130cbfa66cSDaniel Fojt free(hashalg); 6140cbfa66cSDaniel Fojt return r; 6150cbfa66cSDaniel Fojt } 6160cbfa66cSDaniel Fojt 6170cbfa66cSDaniel Fojt struct sshsigopt { 6180cbfa66cSDaniel Fojt int ca; 6190cbfa66cSDaniel Fojt char *namespaces; 62050a69bb5SSascha Wildner uint64_t valid_after, valid_before; 6210cbfa66cSDaniel Fojt }; 6220cbfa66cSDaniel Fojt 6230cbfa66cSDaniel Fojt struct sshsigopt * 6240cbfa66cSDaniel Fojt sshsigopt_parse(const char *opts, const char *path, u_long linenum, 6250cbfa66cSDaniel Fojt const char **errstrp) 6260cbfa66cSDaniel Fojt { 6270cbfa66cSDaniel Fojt struct sshsigopt *ret; 6280cbfa66cSDaniel Fojt int r; 62950a69bb5SSascha Wildner char *opt; 6300cbfa66cSDaniel Fojt const char *errstr = NULL; 6310cbfa66cSDaniel Fojt 6320cbfa66cSDaniel Fojt if ((ret = calloc(1, sizeof(*ret))) == NULL) 6330cbfa66cSDaniel Fojt return NULL; 6340cbfa66cSDaniel Fojt if (opts == NULL || *opts == '\0') 6350cbfa66cSDaniel Fojt return ret; /* Empty options yields empty options :) */ 6360cbfa66cSDaniel Fojt 6370cbfa66cSDaniel Fojt while (*opts && *opts != ' ' && *opts != '\t') { 6380cbfa66cSDaniel Fojt /* flag options */ 6390cbfa66cSDaniel Fojt if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { 6400cbfa66cSDaniel Fojt ret->ca = 1; 6410cbfa66cSDaniel Fojt } else if (opt_match(&opts, "namespaces")) { 6420cbfa66cSDaniel Fojt if (ret->namespaces != NULL) { 6430cbfa66cSDaniel Fojt errstr = "multiple \"namespaces\" clauses"; 6440cbfa66cSDaniel Fojt goto fail; 6450cbfa66cSDaniel Fojt } 6460cbfa66cSDaniel Fojt ret->namespaces = opt_dequote(&opts, &errstr); 6470cbfa66cSDaniel Fojt if (ret->namespaces == NULL) 6480cbfa66cSDaniel Fojt goto fail; 64950a69bb5SSascha Wildner } else if (opt_match(&opts, "valid-after")) { 65050a69bb5SSascha Wildner if (ret->valid_after != 0) { 65150a69bb5SSascha Wildner errstr = "multiple \"valid-after\" clauses"; 65250a69bb5SSascha Wildner goto fail; 65350a69bb5SSascha Wildner } 65450a69bb5SSascha Wildner if ((opt = opt_dequote(&opts, &errstr)) == NULL) 65550a69bb5SSascha Wildner goto fail; 65650a69bb5SSascha Wildner if (parse_absolute_time(opt, &ret->valid_after) != 0 || 65750a69bb5SSascha Wildner ret->valid_after == 0) { 65850a69bb5SSascha Wildner free(opt); 65950a69bb5SSascha Wildner errstr = "invalid \"valid-after\" time"; 66050a69bb5SSascha Wildner goto fail; 66150a69bb5SSascha Wildner } 66250a69bb5SSascha Wildner free(opt); 66350a69bb5SSascha Wildner } else if (opt_match(&opts, "valid-before")) { 66450a69bb5SSascha Wildner if (ret->valid_before != 0) { 66550a69bb5SSascha Wildner errstr = "multiple \"valid-before\" clauses"; 66650a69bb5SSascha Wildner goto fail; 66750a69bb5SSascha Wildner } 66850a69bb5SSascha Wildner if ((opt = opt_dequote(&opts, &errstr)) == NULL) 66950a69bb5SSascha Wildner goto fail; 67050a69bb5SSascha Wildner if (parse_absolute_time(opt, &ret->valid_before) != 0 || 67150a69bb5SSascha Wildner ret->valid_before == 0) { 67250a69bb5SSascha Wildner free(opt); 67350a69bb5SSascha Wildner errstr = "invalid \"valid-before\" time"; 67450a69bb5SSascha Wildner goto fail; 67550a69bb5SSascha Wildner } 67650a69bb5SSascha Wildner free(opt); 6770cbfa66cSDaniel Fojt } 6780cbfa66cSDaniel Fojt /* 6790cbfa66cSDaniel Fojt * Skip the comma, and move to the next option 6800cbfa66cSDaniel Fojt * (or break out if there are no more). 6810cbfa66cSDaniel Fojt */ 6820cbfa66cSDaniel Fojt if (*opts == '\0' || *opts == ' ' || *opts == '\t') 6830cbfa66cSDaniel Fojt break; /* End of options. */ 6840cbfa66cSDaniel Fojt /* Anything other than a comma is an unknown option */ 6850cbfa66cSDaniel Fojt if (*opts != ',') { 6860cbfa66cSDaniel Fojt errstr = "unknown key option"; 6870cbfa66cSDaniel Fojt goto fail; 6880cbfa66cSDaniel Fojt } 6890cbfa66cSDaniel Fojt opts++; 6900cbfa66cSDaniel Fojt if (*opts == '\0') { 6910cbfa66cSDaniel Fojt errstr = "unexpected end-of-options"; 6920cbfa66cSDaniel Fojt goto fail; 6930cbfa66cSDaniel Fojt } 6940cbfa66cSDaniel Fojt } 69550a69bb5SSascha Wildner /* final consistency check */ 69650a69bb5SSascha Wildner if (ret->valid_after != 0 && ret->valid_before != 0 && 69750a69bb5SSascha Wildner ret->valid_before <= ret->valid_after) { 69850a69bb5SSascha Wildner errstr = "\"valid-before\" time is before \"valid-after\""; 69950a69bb5SSascha Wildner goto fail; 70050a69bb5SSascha Wildner } 7010cbfa66cSDaniel Fojt /* success */ 7020cbfa66cSDaniel Fojt return ret; 7030cbfa66cSDaniel Fojt fail: 7040cbfa66cSDaniel Fojt if (errstrp != NULL) 7050cbfa66cSDaniel Fojt *errstrp = errstr; 7060cbfa66cSDaniel Fojt sshsigopt_free(ret); 7070cbfa66cSDaniel Fojt return NULL; 7080cbfa66cSDaniel Fojt } 7090cbfa66cSDaniel Fojt 7100cbfa66cSDaniel Fojt void 7110cbfa66cSDaniel Fojt sshsigopt_free(struct sshsigopt *opts) 7120cbfa66cSDaniel Fojt { 7130cbfa66cSDaniel Fojt if (opts == NULL) 7140cbfa66cSDaniel Fojt return; 7150cbfa66cSDaniel Fojt free(opts->namespaces); 7160cbfa66cSDaniel Fojt free(opts); 7170cbfa66cSDaniel Fojt } 7180cbfa66cSDaniel Fojt 7190cbfa66cSDaniel Fojt static int 7200cbfa66cSDaniel Fojt parse_principals_key_and_options(const char *path, u_long linenum, char *line, 7210cbfa66cSDaniel Fojt const char *required_principal, char **principalsp, struct sshkey **keyp, 7220cbfa66cSDaniel Fojt struct sshsigopt **sigoptsp) 7230cbfa66cSDaniel Fojt { 7240cbfa66cSDaniel Fojt char *opts = NULL, *tmp, *cp, *principals = NULL; 7250cbfa66cSDaniel Fojt const char *reason = NULL; 7260cbfa66cSDaniel Fojt struct sshsigopt *sigopts = NULL; 7270cbfa66cSDaniel Fojt struct sshkey *key = NULL; 7280cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR; 7290cbfa66cSDaniel Fojt 7300cbfa66cSDaniel Fojt if (principalsp != NULL) 7310cbfa66cSDaniel Fojt *principalsp = NULL; 7320cbfa66cSDaniel Fojt if (sigoptsp != NULL) 7330cbfa66cSDaniel Fojt *sigoptsp = NULL; 7340cbfa66cSDaniel Fojt if (keyp != NULL) 7350cbfa66cSDaniel Fojt *keyp = NULL; 7360cbfa66cSDaniel Fojt 7370cbfa66cSDaniel Fojt cp = line; 7380cbfa66cSDaniel Fojt cp = cp + strspn(cp, " \t"); /* skip leading whitespace */ 7390cbfa66cSDaniel Fojt if (*cp == '#' || *cp == '\0') 7400cbfa66cSDaniel Fojt return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */ 7410cbfa66cSDaniel Fojt 7420cbfa66cSDaniel Fojt /* format: identity[,identity...] [option[,option...]] key */ 743*ee116499SAntonio Huete Jimenez if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) { 7440cbfa66cSDaniel Fojt error("%s:%lu: invalid line", path, linenum); 7450cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT; 7460cbfa66cSDaniel Fojt goto out; 7470cbfa66cSDaniel Fojt } 7480cbfa66cSDaniel Fojt if ((principals = strdup(tmp)) == NULL) { 74950a69bb5SSascha Wildner error_f("strdup failed"); 7500cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 7510cbfa66cSDaniel Fojt goto out; 7520cbfa66cSDaniel Fojt } 7530cbfa66cSDaniel Fojt /* 7540cbfa66cSDaniel Fojt * Bail out early if we're looking for a particular principal and this 7550cbfa66cSDaniel Fojt * line does not list it. 7560cbfa66cSDaniel Fojt */ 7570cbfa66cSDaniel Fojt if (required_principal != NULL) { 7580cbfa66cSDaniel Fojt if (match_pattern_list(required_principal, 7590cbfa66cSDaniel Fojt principals, 0) != 1) { 7600cbfa66cSDaniel Fojt /* principal didn't match */ 7610cbfa66cSDaniel Fojt r = SSH_ERR_KEY_NOT_FOUND; 7620cbfa66cSDaniel Fojt goto out; 7630cbfa66cSDaniel Fojt } 76450a69bb5SSascha Wildner debug_f("%s:%lu: matched principal \"%s\"", 76550a69bb5SSascha Wildner path, linenum, required_principal); 7660cbfa66cSDaniel Fojt } 7670cbfa66cSDaniel Fojt 7680cbfa66cSDaniel Fojt if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { 76950a69bb5SSascha Wildner error_f("sshkey_new failed"); 7700cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 7710cbfa66cSDaniel Fojt goto out; 7720cbfa66cSDaniel Fojt } 7730cbfa66cSDaniel Fojt if (sshkey_read(key, &cp) != 0) { 7740cbfa66cSDaniel Fojt /* no key? Check for options */ 7750cbfa66cSDaniel Fojt opts = cp; 7760cbfa66cSDaniel Fojt if (sshkey_advance_past_options(&cp) != 0) { 7770cbfa66cSDaniel Fojt error("%s:%lu: invalid options", path, linenum); 7780cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT; 7790cbfa66cSDaniel Fojt goto out; 7800cbfa66cSDaniel Fojt } 781*ee116499SAntonio Huete Jimenez if (cp == NULL || *cp == '\0') { 782*ee116499SAntonio Huete Jimenez error("%s:%lu: missing key", path, linenum); 783*ee116499SAntonio Huete Jimenez r = SSH_ERR_INVALID_FORMAT; 784*ee116499SAntonio Huete Jimenez goto out; 785*ee116499SAntonio Huete Jimenez } 7860cbfa66cSDaniel Fojt *cp++ = '\0'; 7870cbfa66cSDaniel Fojt skip_space(&cp); 7880cbfa66cSDaniel Fojt if (sshkey_read(key, &cp) != 0) { 7890cbfa66cSDaniel Fojt error("%s:%lu: invalid key", path, linenum); 7900cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT; 7910cbfa66cSDaniel Fojt goto out; 7920cbfa66cSDaniel Fojt } 7930cbfa66cSDaniel Fojt } 7940cbfa66cSDaniel Fojt debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts); 7950cbfa66cSDaniel Fojt if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) { 7960cbfa66cSDaniel Fojt error("%s:%lu: bad options: %s", path, linenum, reason); 7970cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT; 7980cbfa66cSDaniel Fojt goto out; 7990cbfa66cSDaniel Fojt } 8000cbfa66cSDaniel Fojt /* success */ 8010cbfa66cSDaniel Fojt if (principalsp != NULL) { 8020cbfa66cSDaniel Fojt *principalsp = principals; 8030cbfa66cSDaniel Fojt principals = NULL; /* transferred */ 8040cbfa66cSDaniel Fojt } 8050cbfa66cSDaniel Fojt if (sigoptsp != NULL) { 8060cbfa66cSDaniel Fojt *sigoptsp = sigopts; 8070cbfa66cSDaniel Fojt sigopts = NULL; /* transferred */ 8080cbfa66cSDaniel Fojt } 8090cbfa66cSDaniel Fojt if (keyp != NULL) { 8100cbfa66cSDaniel Fojt *keyp = key; 8110cbfa66cSDaniel Fojt key = NULL; /* transferred */ 8120cbfa66cSDaniel Fojt } 8130cbfa66cSDaniel Fojt r = 0; 8140cbfa66cSDaniel Fojt out: 8150cbfa66cSDaniel Fojt free(principals); 8160cbfa66cSDaniel Fojt sshsigopt_free(sigopts); 8170cbfa66cSDaniel Fojt sshkey_free(key); 8180cbfa66cSDaniel Fojt return r; 8190cbfa66cSDaniel Fojt } 8200cbfa66cSDaniel Fojt 8210cbfa66cSDaniel Fojt static int 822*ee116499SAntonio Huete Jimenez cert_filter_principals(const char *path, u_long linenum, 823*ee116499SAntonio Huete Jimenez char **principalsp, const struct sshkey *cert, uint64_t verify_time) 824*ee116499SAntonio Huete Jimenez { 825*ee116499SAntonio Huete Jimenez char *cp, *oprincipals, *principals; 826*ee116499SAntonio Huete Jimenez const char *reason; 827*ee116499SAntonio Huete Jimenez struct sshbuf *nprincipals; 828*ee116499SAntonio Huete Jimenez int r = SSH_ERR_INTERNAL_ERROR, success = 0; 829*ee116499SAntonio Huete Jimenez u_int i; 830*ee116499SAntonio Huete Jimenez 831*ee116499SAntonio Huete Jimenez oprincipals = principals = *principalsp; 832*ee116499SAntonio Huete Jimenez *principalsp = NULL; 833*ee116499SAntonio Huete Jimenez 834*ee116499SAntonio Huete Jimenez if ((nprincipals = sshbuf_new()) == NULL) { 835*ee116499SAntonio Huete Jimenez r = SSH_ERR_ALLOC_FAIL; 836*ee116499SAntonio Huete Jimenez goto out; 837*ee116499SAntonio Huete Jimenez } 838*ee116499SAntonio Huete Jimenez 839*ee116499SAntonio Huete Jimenez while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { 840*ee116499SAntonio Huete Jimenez /* Check certificate validity */ 841*ee116499SAntonio Huete Jimenez if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, 842*ee116499SAntonio Huete Jimenez verify_time, NULL, &reason)) != 0) { 843*ee116499SAntonio Huete Jimenez debug("%s:%lu: principal \"%s\" not authorized: %s", 844*ee116499SAntonio Huete Jimenez path, linenum, cp, reason); 845*ee116499SAntonio Huete Jimenez continue; 846*ee116499SAntonio Huete Jimenez } 847*ee116499SAntonio Huete Jimenez /* Return all matching principal names from the cert */ 848*ee116499SAntonio Huete Jimenez for (i = 0; i < cert->cert->nprincipals; i++) { 849*ee116499SAntonio Huete Jimenez if (match_pattern(cert->cert->principals[i], cp)) { 850*ee116499SAntonio Huete Jimenez if ((r = sshbuf_putf(nprincipals, "%s%s", 851*ee116499SAntonio Huete Jimenez sshbuf_len(nprincipals) != 0 ? "," : "", 852*ee116499SAntonio Huete Jimenez cert->cert->principals[i])) != 0) { 853*ee116499SAntonio Huete Jimenez error_f("buffer error"); 854*ee116499SAntonio Huete Jimenez goto out; 855*ee116499SAntonio Huete Jimenez } 856*ee116499SAntonio Huete Jimenez } 857*ee116499SAntonio Huete Jimenez } 858*ee116499SAntonio Huete Jimenez } 859*ee116499SAntonio Huete Jimenez if (sshbuf_len(nprincipals) == 0) { 860*ee116499SAntonio Huete Jimenez error("%s:%lu: no valid principals found", path, linenum); 861*ee116499SAntonio Huete Jimenez r = SSH_ERR_KEY_CERT_INVALID; 862*ee116499SAntonio Huete Jimenez goto out; 863*ee116499SAntonio Huete Jimenez } 864*ee116499SAntonio Huete Jimenez if ((principals = sshbuf_dup_string(nprincipals)) == NULL) { 865*ee116499SAntonio Huete Jimenez error_f("buffer error"); 866*ee116499SAntonio Huete Jimenez goto out; 867*ee116499SAntonio Huete Jimenez } 868*ee116499SAntonio Huete Jimenez /* success */ 869*ee116499SAntonio Huete Jimenez success = 1; 870*ee116499SAntonio Huete Jimenez *principalsp = principals; 871*ee116499SAntonio Huete Jimenez out: 872*ee116499SAntonio Huete Jimenez sshbuf_free(nprincipals); 873*ee116499SAntonio Huete Jimenez free(oprincipals); 874*ee116499SAntonio Huete Jimenez return success ? 0 : r; 875*ee116499SAntonio Huete Jimenez } 876*ee116499SAntonio Huete Jimenez 877*ee116499SAntonio Huete Jimenez static int 8780cbfa66cSDaniel Fojt check_allowed_keys_line(const char *path, u_long linenum, char *line, 8790cbfa66cSDaniel Fojt const struct sshkey *sign_key, const char *principal, 880*ee116499SAntonio Huete Jimenez const char *sig_namespace, uint64_t verify_time, char **principalsp) 8810cbfa66cSDaniel Fojt { 8820cbfa66cSDaniel Fojt struct sshkey *found_key = NULL; 883*ee116499SAntonio Huete Jimenez char *principals = NULL; 88450a69bb5SSascha Wildner int r, success = 0; 8850cbfa66cSDaniel Fojt const char *reason = NULL; 8860cbfa66cSDaniel Fojt struct sshsigopt *sigopts = NULL; 88750a69bb5SSascha Wildner char tvalid[64], tverify[64]; 8880cbfa66cSDaniel Fojt 889*ee116499SAntonio Huete Jimenez if (principalsp != NULL) 890*ee116499SAntonio Huete Jimenez *principalsp = NULL; 891*ee116499SAntonio Huete Jimenez 8920cbfa66cSDaniel Fojt /* Parse the line */ 8930cbfa66cSDaniel Fojt if ((r = parse_principals_key_and_options(path, linenum, line, 894*ee116499SAntonio Huete Jimenez principal, &principals, &found_key, &sigopts)) != 0) { 8950cbfa66cSDaniel Fojt /* error already logged */ 8960cbfa66cSDaniel Fojt goto done; 8970cbfa66cSDaniel Fojt } 8980cbfa66cSDaniel Fojt 89950a69bb5SSascha Wildner if (!sigopts->ca && sshkey_equal(found_key, sign_key)) { 90050a69bb5SSascha Wildner /* Exact match of key */ 90150a69bb5SSascha Wildner debug("%s:%lu: matched key", path, linenum); 90250a69bb5SSascha Wildner } else if (sigopts->ca && sshkey_is_cert(sign_key) && 90350a69bb5SSascha Wildner sshkey_equal_public(sign_key->cert->signature_key, found_key)) { 904*ee116499SAntonio Huete Jimenez if (principal) { 905*ee116499SAntonio Huete Jimenez /* Match certificate CA key with specified principal */ 90650a69bb5SSascha Wildner if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0, 90750a69bb5SSascha Wildner verify_time, principal, &reason)) != 0) { 90850a69bb5SSascha Wildner error("%s:%lu: certificate not authorized: %s", 90950a69bb5SSascha Wildner path, linenum, reason); 91050a69bb5SSascha Wildner goto done; 91150a69bb5SSascha Wildner } 912*ee116499SAntonio Huete Jimenez debug("%s:%lu: matched certificate CA key", 913*ee116499SAntonio Huete Jimenez path, linenum); 914*ee116499SAntonio Huete Jimenez } else { 915*ee116499SAntonio Huete Jimenez /* No principal specified - find all matching ones */ 916*ee116499SAntonio Huete Jimenez if ((r = cert_filter_principals(path, linenum, 917*ee116499SAntonio Huete Jimenez &principals, sign_key, verify_time)) != 0) { 918*ee116499SAntonio Huete Jimenez /* error already displayed */ 919*ee116499SAntonio Huete Jimenez debug_r(r, "%s:%lu: cert_filter_principals", 920*ee116499SAntonio Huete Jimenez path, linenum); 921*ee116499SAntonio Huete Jimenez goto done; 922*ee116499SAntonio Huete Jimenez } 923*ee116499SAntonio Huete Jimenez debug("%s:%lu: matched certificate CA key", 924*ee116499SAntonio Huete Jimenez path, linenum); 925*ee116499SAntonio Huete Jimenez } 92650a69bb5SSascha Wildner } else { 92750a69bb5SSascha Wildner /* Didn't match key */ 92850a69bb5SSascha Wildner goto done; 92950a69bb5SSascha Wildner } 93050a69bb5SSascha Wildner 9310cbfa66cSDaniel Fojt /* Check whether options preclude the use of this key */ 932*ee116499SAntonio Huete Jimenez if (sigopts->namespaces != NULL && sig_namespace != NULL && 9330cbfa66cSDaniel Fojt match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) { 9340cbfa66cSDaniel Fojt error("%s:%lu: key is not permitted for use in signature " 9350cbfa66cSDaniel Fojt "namespace \"%s\"", path, linenum, sig_namespace); 9360cbfa66cSDaniel Fojt goto done; 9370cbfa66cSDaniel Fojt } 9380cbfa66cSDaniel Fojt 93950a69bb5SSascha Wildner /* check key time validity */ 94050a69bb5SSascha Wildner format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify)); 94150a69bb5SSascha Wildner if (sigopts->valid_after != 0 && 94250a69bb5SSascha Wildner (uint64_t)verify_time < sigopts->valid_after) { 94350a69bb5SSascha Wildner format_absolute_time(sigopts->valid_after, 94450a69bb5SSascha Wildner tvalid, sizeof(tvalid)); 94550a69bb5SSascha Wildner error("%s:%lu: key is not yet valid: " 94650a69bb5SSascha Wildner "verify time %s < valid-after %s", path, linenum, 94750a69bb5SSascha Wildner tverify, tvalid); 9480cbfa66cSDaniel Fojt goto done; 9490cbfa66cSDaniel Fojt } 95050a69bb5SSascha Wildner if (sigopts->valid_before != 0 && 95150a69bb5SSascha Wildner (uint64_t)verify_time > sigopts->valid_before) { 95250a69bb5SSascha Wildner format_absolute_time(sigopts->valid_before, 95350a69bb5SSascha Wildner tvalid, sizeof(tvalid)); 95450a69bb5SSascha Wildner error("%s:%lu: key has expired: " 95550a69bb5SSascha Wildner "verify time %s > valid-before %s", path, linenum, 95650a69bb5SSascha Wildner tverify, tvalid); 9570cbfa66cSDaniel Fojt goto done; 9580cbfa66cSDaniel Fojt } 95950a69bb5SSascha Wildner success = 1; 96050a69bb5SSascha Wildner 9610cbfa66cSDaniel Fojt done: 962*ee116499SAntonio Huete Jimenez if (success && principalsp != NULL) { 963*ee116499SAntonio Huete Jimenez *principalsp = principals; 964*ee116499SAntonio Huete Jimenez principals = NULL; /* transferred */ 965*ee116499SAntonio Huete Jimenez } 966*ee116499SAntonio Huete Jimenez free(principals); 9670cbfa66cSDaniel Fojt sshkey_free(found_key); 9680cbfa66cSDaniel Fojt sshsigopt_free(sigopts); 96950a69bb5SSascha Wildner return success ? 0 : SSH_ERR_KEY_NOT_FOUND; 9700cbfa66cSDaniel Fojt } 9710cbfa66cSDaniel Fojt 9720cbfa66cSDaniel Fojt int 9730cbfa66cSDaniel Fojt sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, 97450a69bb5SSascha Wildner const char *principal, const char *sig_namespace, uint64_t verify_time) 9750cbfa66cSDaniel Fojt { 9760cbfa66cSDaniel Fojt FILE *f = NULL; 9770cbfa66cSDaniel Fojt char *line = NULL; 9780cbfa66cSDaniel Fojt size_t linesize = 0; 9790cbfa66cSDaniel Fojt u_long linenum = 0; 9800cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR, oerrno; 9810cbfa66cSDaniel Fojt 9820cbfa66cSDaniel Fojt /* Check key and principal against file */ 9830cbfa66cSDaniel Fojt if ((f = fopen(path, "r")) == NULL) { 9840cbfa66cSDaniel Fojt oerrno = errno; 9850cbfa66cSDaniel Fojt error("Unable to open allowed keys file \"%s\": %s", 9860cbfa66cSDaniel Fojt path, strerror(errno)); 9870cbfa66cSDaniel Fojt errno = oerrno; 9880cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR; 9890cbfa66cSDaniel Fojt } 9900cbfa66cSDaniel Fojt 9910cbfa66cSDaniel Fojt while (getline(&line, &linesize, f) != -1) { 9920cbfa66cSDaniel Fojt linenum++; 9930cbfa66cSDaniel Fojt r = check_allowed_keys_line(path, linenum, line, sign_key, 994*ee116499SAntonio Huete Jimenez principal, sig_namespace, verify_time, NULL); 9950cbfa66cSDaniel Fojt free(line); 9960cbfa66cSDaniel Fojt line = NULL; 99750a69bb5SSascha Wildner linesize = 0; 9980cbfa66cSDaniel Fojt if (r == SSH_ERR_KEY_NOT_FOUND) 9990cbfa66cSDaniel Fojt continue; 10000cbfa66cSDaniel Fojt else if (r == 0) { 10010cbfa66cSDaniel Fojt /* success */ 10020cbfa66cSDaniel Fojt fclose(f); 10030cbfa66cSDaniel Fojt return 0; 10040cbfa66cSDaniel Fojt } else 10050cbfa66cSDaniel Fojt break; 10060cbfa66cSDaniel Fojt } 10070cbfa66cSDaniel Fojt /* Either we hit an error parsing or we simply didn't find the key */ 10080cbfa66cSDaniel Fojt fclose(f); 10090cbfa66cSDaniel Fojt free(line); 10100cbfa66cSDaniel Fojt return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; 10110cbfa66cSDaniel Fojt } 10120cbfa66cSDaniel Fojt 10130cbfa66cSDaniel Fojt int 10140cbfa66cSDaniel Fojt sshsig_find_principals(const char *path, const struct sshkey *sign_key, 101550a69bb5SSascha Wildner uint64_t verify_time, char **principals) 10160cbfa66cSDaniel Fojt { 10170cbfa66cSDaniel Fojt FILE *f = NULL; 10180cbfa66cSDaniel Fojt char *line = NULL; 10190cbfa66cSDaniel Fojt size_t linesize = 0; 10200cbfa66cSDaniel Fojt u_long linenum = 0; 10210cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR, oerrno; 10220cbfa66cSDaniel Fojt 10230cbfa66cSDaniel Fojt if ((f = fopen(path, "r")) == NULL) { 10240cbfa66cSDaniel Fojt oerrno = errno; 10250cbfa66cSDaniel Fojt error("Unable to open allowed keys file \"%s\": %s", 10260cbfa66cSDaniel Fojt path, strerror(errno)); 10270cbfa66cSDaniel Fojt errno = oerrno; 10280cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR; 10290cbfa66cSDaniel Fojt } 10300cbfa66cSDaniel Fojt 1031*ee116499SAntonio Huete Jimenez r = SSH_ERR_KEY_NOT_FOUND; 10320cbfa66cSDaniel Fojt while (getline(&line, &linesize, f) != -1) { 10330cbfa66cSDaniel Fojt linenum++; 1034*ee116499SAntonio Huete Jimenez r = check_allowed_keys_line(path, linenum, line, 1035*ee116499SAntonio Huete Jimenez sign_key, NULL, NULL, verify_time, principals); 10360cbfa66cSDaniel Fojt free(line); 10370cbfa66cSDaniel Fojt line = NULL; 103850a69bb5SSascha Wildner linesize = 0; 10390cbfa66cSDaniel Fojt if (r == SSH_ERR_KEY_NOT_FOUND) 10400cbfa66cSDaniel Fojt continue; 10410cbfa66cSDaniel Fojt else if (r == 0) { 10420cbfa66cSDaniel Fojt /* success */ 10430cbfa66cSDaniel Fojt fclose(f); 10440cbfa66cSDaniel Fojt return 0; 10450cbfa66cSDaniel Fojt } else 10460cbfa66cSDaniel Fojt break; 10470cbfa66cSDaniel Fojt } 10480cbfa66cSDaniel Fojt free(line); 10490cbfa66cSDaniel Fojt /* Either we hit an error parsing or we simply didn't find the key */ 10500cbfa66cSDaniel Fojt if (ferror(f) != 0) { 10510cbfa66cSDaniel Fojt oerrno = errno; 10520cbfa66cSDaniel Fojt fclose(f); 10530cbfa66cSDaniel Fojt error("Unable to read allowed keys file \"%s\": %s", 10540cbfa66cSDaniel Fojt path, strerror(errno)); 10550cbfa66cSDaniel Fojt errno = oerrno; 10560cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR; 10570cbfa66cSDaniel Fojt } 10580cbfa66cSDaniel Fojt fclose(f); 10590cbfa66cSDaniel Fojt return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; 10600cbfa66cSDaniel Fojt } 10610cbfa66cSDaniel Fojt 10620cbfa66cSDaniel Fojt int 1063*ee116499SAntonio Huete Jimenez sshsig_match_principals(const char *path, const char *principal, 1064*ee116499SAntonio Huete Jimenez char ***principalsp, size_t *nprincipalsp) 1065*ee116499SAntonio Huete Jimenez { 1066*ee116499SAntonio Huete Jimenez FILE *f = NULL; 1067*ee116499SAntonio Huete Jimenez char *found, *line = NULL, **principals = NULL, **tmp; 1068*ee116499SAntonio Huete Jimenez size_t i, nprincipals = 0, linesize = 0; 1069*ee116499SAntonio Huete Jimenez u_long linenum = 0; 1070*ee116499SAntonio Huete Jimenez int oerrno = 0, r, ret = 0; 1071*ee116499SAntonio Huete Jimenez 1072*ee116499SAntonio Huete Jimenez if (principalsp != NULL) 1073*ee116499SAntonio Huete Jimenez *principalsp = NULL; 1074*ee116499SAntonio Huete Jimenez if (nprincipalsp != NULL) 1075*ee116499SAntonio Huete Jimenez *nprincipalsp = 0; 1076*ee116499SAntonio Huete Jimenez 1077*ee116499SAntonio Huete Jimenez /* Check key and principal against file */ 1078*ee116499SAntonio Huete Jimenez if ((f = fopen(path, "r")) == NULL) { 1079*ee116499SAntonio Huete Jimenez oerrno = errno; 1080*ee116499SAntonio Huete Jimenez error("Unable to open allowed keys file \"%s\": %s", 1081*ee116499SAntonio Huete Jimenez path, strerror(errno)); 1082*ee116499SAntonio Huete Jimenez errno = oerrno; 1083*ee116499SAntonio Huete Jimenez return SSH_ERR_SYSTEM_ERROR; 1084*ee116499SAntonio Huete Jimenez } 1085*ee116499SAntonio Huete Jimenez 1086*ee116499SAntonio Huete Jimenez while (getline(&line, &linesize, f) != -1) { 1087*ee116499SAntonio Huete Jimenez linenum++; 1088*ee116499SAntonio Huete Jimenez /* Parse the line */ 1089*ee116499SAntonio Huete Jimenez if ((r = parse_principals_key_and_options(path, linenum, line, 1090*ee116499SAntonio Huete Jimenez principal, &found, NULL, NULL)) != 0) { 1091*ee116499SAntonio Huete Jimenez if (r == SSH_ERR_KEY_NOT_FOUND) 1092*ee116499SAntonio Huete Jimenez continue; 1093*ee116499SAntonio Huete Jimenez ret = r; 1094*ee116499SAntonio Huete Jimenez oerrno = errno; 1095*ee116499SAntonio Huete Jimenez break; /* unexpected error */ 1096*ee116499SAntonio Huete Jimenez } 1097*ee116499SAntonio Huete Jimenez if ((tmp = recallocarray(principals, nprincipals, 1098*ee116499SAntonio Huete Jimenez nprincipals + 1, sizeof(*principals))) == NULL) { 1099*ee116499SAntonio Huete Jimenez ret = SSH_ERR_ALLOC_FAIL; 1100*ee116499SAntonio Huete Jimenez free(found); 1101*ee116499SAntonio Huete Jimenez break; 1102*ee116499SAntonio Huete Jimenez } 1103*ee116499SAntonio Huete Jimenez principals = tmp; 1104*ee116499SAntonio Huete Jimenez principals[nprincipals++] = found; /* transferred */ 1105*ee116499SAntonio Huete Jimenez free(line); 1106*ee116499SAntonio Huete Jimenez line = NULL; 1107*ee116499SAntonio Huete Jimenez linesize = 0; 1108*ee116499SAntonio Huete Jimenez } 1109*ee116499SAntonio Huete Jimenez fclose(f); 1110*ee116499SAntonio Huete Jimenez 1111*ee116499SAntonio Huete Jimenez if (ret == 0) { 1112*ee116499SAntonio Huete Jimenez if (nprincipals == 0) 1113*ee116499SAntonio Huete Jimenez ret = SSH_ERR_KEY_NOT_FOUND; 1114*ee116499SAntonio Huete Jimenez if (principalsp != NULL) { 1115*ee116499SAntonio Huete Jimenez *principalsp = principals; 1116*ee116499SAntonio Huete Jimenez principals = NULL; /* transferred */ 1117*ee116499SAntonio Huete Jimenez } 1118*ee116499SAntonio Huete Jimenez if (nprincipalsp != 0) { 1119*ee116499SAntonio Huete Jimenez *nprincipalsp = nprincipals; 1120*ee116499SAntonio Huete Jimenez nprincipals = 0; 1121*ee116499SAntonio Huete Jimenez } 1122*ee116499SAntonio Huete Jimenez } 1123*ee116499SAntonio Huete Jimenez 1124*ee116499SAntonio Huete Jimenez for (i = 0; i < nprincipals; i++) 1125*ee116499SAntonio Huete Jimenez free(principals[i]); 1126*ee116499SAntonio Huete Jimenez free(principals); 1127*ee116499SAntonio Huete Jimenez 1128*ee116499SAntonio Huete Jimenez errno = oerrno; 1129*ee116499SAntonio Huete Jimenez return ret; 1130*ee116499SAntonio Huete Jimenez } 1131*ee116499SAntonio Huete Jimenez 1132*ee116499SAntonio Huete Jimenez int 11330cbfa66cSDaniel Fojt sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) 11340cbfa66cSDaniel Fojt { 11350cbfa66cSDaniel Fojt struct sshkey *pk = NULL; 11360cbfa66cSDaniel Fojt int r = SSH_ERR_SIGNATURE_INVALID; 11370cbfa66cSDaniel Fojt 11380cbfa66cSDaniel Fojt if (pubkey == NULL) 11390cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 11400cbfa66cSDaniel Fojt if ((r = sshsig_parse_preamble(signature)) != 0) 11410cbfa66cSDaniel Fojt return r; 11420cbfa66cSDaniel Fojt if ((r = sshkey_froms(signature, &pk)) != 0) 11430cbfa66cSDaniel Fojt return r; 11440cbfa66cSDaniel Fojt 11450cbfa66cSDaniel Fojt *pubkey = pk; 11460cbfa66cSDaniel Fojt pk = NULL; 11470cbfa66cSDaniel Fojt return 0; 11480cbfa66cSDaniel Fojt } 1149