xref: /dflybsd-src/crypto/openssh/sshsig.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
1*ba1276acSMatthew Dillon /* $OpenBSD: sshsig.c,v 1.35 2024/03/08 22:16:32 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)
41*ba1276acSMatthew Dillon #define BEGIN_SIGNATURE		"-----BEGIN SSH SIGNATURE-----"
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
sshsig_armor(const struct sshbuf * blob,struct sshbuf ** out)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 
62*ba1276acSMatthew Dillon 	if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) {
6350a69bb5SSascha Wildner 		error_fr(r, "sshbuf_putf");
640cbfa66cSDaniel Fojt 		goto out;
650cbfa66cSDaniel Fojt 	}
660cbfa66cSDaniel Fojt 
670cbfa66cSDaniel Fojt 	if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
6850a69bb5SSascha Wildner 		error_fr(r, "base64 encode signature");
690cbfa66cSDaniel Fojt 		goto out;
700cbfa66cSDaniel Fojt 	}
710cbfa66cSDaniel Fojt 
720cbfa66cSDaniel Fojt 	if ((r = sshbuf_put(buf, END_SIGNATURE,
730cbfa66cSDaniel Fojt 	    sizeof(END_SIGNATURE)-1)) != 0 ||
740cbfa66cSDaniel Fojt 	    (r = sshbuf_put_u8(buf, '\n')) != 0) {
7550a69bb5SSascha Wildner 		error_fr(r, "sshbuf_put");
760cbfa66cSDaniel Fojt 		goto out;
770cbfa66cSDaniel Fojt 	}
780cbfa66cSDaniel Fojt 	/* success */
790cbfa66cSDaniel Fojt 	*out = buf;
800cbfa66cSDaniel Fojt 	buf = NULL; /* transferred */
810cbfa66cSDaniel Fojt 	r = 0;
820cbfa66cSDaniel Fojt  out:
830cbfa66cSDaniel Fojt 	sshbuf_free(buf);
840cbfa66cSDaniel Fojt 	return r;
850cbfa66cSDaniel Fojt }
860cbfa66cSDaniel Fojt 
870cbfa66cSDaniel Fojt int
sshsig_dearmor(struct sshbuf * sig,struct sshbuf ** out)880cbfa66cSDaniel Fojt sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
890cbfa66cSDaniel Fojt {
900cbfa66cSDaniel Fojt 	int r;
910cbfa66cSDaniel Fojt 	size_t eoffset = 0;
920cbfa66cSDaniel Fojt 	struct sshbuf *buf = NULL;
930cbfa66cSDaniel Fojt 	struct sshbuf *sbuf = NULL;
940cbfa66cSDaniel Fojt 	char *b64 = NULL;
950cbfa66cSDaniel Fojt 
960cbfa66cSDaniel Fojt 	if ((sbuf = sshbuf_fromb(sig)) == NULL) {
9750a69bb5SSascha Wildner 		error_f("sshbuf_fromb failed");
980cbfa66cSDaniel Fojt 		return SSH_ERR_ALLOC_FAIL;
990cbfa66cSDaniel Fojt 	}
1000cbfa66cSDaniel Fojt 
101*ba1276acSMatthew Dillon 	/* Expect and consume preamble + lf/crlf */
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 	if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
10850a69bb5SSascha Wildner 		error_fr(r, "consume");
1090cbfa66cSDaniel Fojt 		goto done;
1100cbfa66cSDaniel Fojt 	}
111*ba1276acSMatthew Dillon 	if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0)
112*ba1276acSMatthew Dillon 		eoffset = 2;
113*ba1276acSMatthew Dillon 	else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0)
114*ba1276acSMatthew Dillon 		eoffset = 1;
115*ba1276acSMatthew Dillon 	else {
116*ba1276acSMatthew Dillon 		r = SSH_ERR_INVALID_FORMAT;
117*ba1276acSMatthew Dillon 		error_f("no header eol");
118*ba1276acSMatthew Dillon 		goto done;
119*ba1276acSMatthew Dillon 	}
120*ba1276acSMatthew Dillon 	if ((r = sshbuf_consume(sbuf, eoffset)) != 0) {
121*ba1276acSMatthew Dillon 		error_fr(r, "consume eol");
122*ba1276acSMatthew Dillon 		goto done;
123*ba1276acSMatthew Dillon 	}
124*ba1276acSMatthew Dillon 	/* Find and consume lf + suffix (any prior cr would be ignored) */
1250cbfa66cSDaniel Fojt 	if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
126*ba1276acSMatthew Dillon 	    sizeof(END_SIGNATURE), &eoffset)) != 0) {
1270cbfa66cSDaniel Fojt 		error("Couldn't parse signature: missing footer");
1280cbfa66cSDaniel Fojt 		goto done;
1290cbfa66cSDaniel Fojt 	}
1300cbfa66cSDaniel Fojt 	if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
13150a69bb5SSascha Wildner 		error_fr(r, "consume");
1320cbfa66cSDaniel Fojt 		goto done;
1330cbfa66cSDaniel Fojt 	}
1340cbfa66cSDaniel Fojt 
1350cbfa66cSDaniel Fojt 	if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
13650a69bb5SSascha Wildner 		error_f("sshbuf_dup_string failed");
1370cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
1380cbfa66cSDaniel Fojt 		goto done;
1390cbfa66cSDaniel Fojt 	}
1400cbfa66cSDaniel Fojt 
1410cbfa66cSDaniel Fojt 	if ((buf = sshbuf_new()) == NULL) {
14250a69bb5SSascha Wildner 		error_f("sshbuf_new() failed");
1430cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
1440cbfa66cSDaniel Fojt 		goto done;
1450cbfa66cSDaniel Fojt 	}
1460cbfa66cSDaniel Fojt 
1470cbfa66cSDaniel Fojt 	if ((r = sshbuf_b64tod(buf, b64)) != 0) {
14850a69bb5SSascha Wildner 		error_fr(r, "decode base64");
1490cbfa66cSDaniel Fojt 		goto done;
1500cbfa66cSDaniel Fojt 	}
1510cbfa66cSDaniel Fojt 
1520cbfa66cSDaniel Fojt 	/* success */
1530cbfa66cSDaniel Fojt 	*out = buf;
1540cbfa66cSDaniel Fojt 	r = 0;
1550cbfa66cSDaniel Fojt 	buf = NULL; /* transferred */
1560cbfa66cSDaniel Fojt done:
1570cbfa66cSDaniel Fojt 	sshbuf_free(buf);
1580cbfa66cSDaniel Fojt 	sshbuf_free(sbuf);
1590cbfa66cSDaniel Fojt 	free(b64);
1600cbfa66cSDaniel Fojt 	return r;
1610cbfa66cSDaniel Fojt }
1620cbfa66cSDaniel Fojt 
1630cbfa66cSDaniel Fojt static int
sshsig_wrap_sign(struct sshkey * key,const char * hashalg,const char * sk_provider,const char * sk_pin,const struct sshbuf * h_message,const char * sig_namespace,struct sshbuf ** out,sshsig_signer * signer,void * signer_ctx)1640cbfa66cSDaniel Fojt sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
16550a69bb5SSascha Wildner     const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
1660cbfa66cSDaniel Fojt     const char *sig_namespace, struct sshbuf **out,
1670cbfa66cSDaniel Fojt     sshsig_signer *signer, void *signer_ctx)
1680cbfa66cSDaniel Fojt {
1690cbfa66cSDaniel Fojt 	int r;
1700cbfa66cSDaniel Fojt 	size_t slen = 0;
1710cbfa66cSDaniel Fojt 	u_char *sig = NULL;
1720cbfa66cSDaniel Fojt 	struct sshbuf *blob = NULL;
1730cbfa66cSDaniel Fojt 	struct sshbuf *tosign = NULL;
1740cbfa66cSDaniel Fojt 	const char *sign_alg = NULL;
1750cbfa66cSDaniel Fojt 
1760cbfa66cSDaniel Fojt 	if ((tosign = sshbuf_new()) == NULL ||
1770cbfa66cSDaniel Fojt 	    (blob = sshbuf_new()) == NULL) {
17850a69bb5SSascha Wildner 		error_f("sshbuf_new failed");
1790cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
1800cbfa66cSDaniel Fojt 		goto done;
1810cbfa66cSDaniel Fojt 	}
1820cbfa66cSDaniel Fojt 
1830cbfa66cSDaniel Fojt 	if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
1840cbfa66cSDaniel Fojt 	    (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
1850cbfa66cSDaniel Fojt 	    (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
1860cbfa66cSDaniel Fojt 	    (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
1870cbfa66cSDaniel Fojt 	    (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
18850a69bb5SSascha Wildner 		error_fr(r, "assemble message to sign");
1890cbfa66cSDaniel Fojt 		goto done;
1900cbfa66cSDaniel Fojt 	}
1910cbfa66cSDaniel Fojt 
1920cbfa66cSDaniel Fojt 	/* If using RSA keys then default to a good signature algorithm */
1930cbfa66cSDaniel Fojt 	if (sshkey_type_plain(key->type) == KEY_RSA)
1940cbfa66cSDaniel Fojt 		sign_alg = RSA_SIGN_ALG;
1950cbfa66cSDaniel Fojt 
1960cbfa66cSDaniel Fojt 	if (signer != NULL) {
1970cbfa66cSDaniel Fojt 		if ((r = signer(key, &sig, &slen,
1980cbfa66cSDaniel Fojt 		    sshbuf_ptr(tosign), sshbuf_len(tosign),
19950a69bb5SSascha Wildner 		    sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
20050a69bb5SSascha Wildner 			error_r(r, "Couldn't sign message (signer)");
2010cbfa66cSDaniel Fojt 			goto done;
2020cbfa66cSDaniel Fojt 		}
2030cbfa66cSDaniel Fojt 	} else {
2040cbfa66cSDaniel Fojt 		if ((r = sshkey_sign(key, &sig, &slen,
2050cbfa66cSDaniel Fojt 		    sshbuf_ptr(tosign), sshbuf_len(tosign),
20650a69bb5SSascha Wildner 		    sign_alg, sk_provider, sk_pin, 0)) != 0) {
20750a69bb5SSascha Wildner 			error_r(r, "Couldn't sign message");
2080cbfa66cSDaniel Fojt 			goto done;
2090cbfa66cSDaniel Fojt 		}
2100cbfa66cSDaniel Fojt 	}
2110cbfa66cSDaniel Fojt 
2120cbfa66cSDaniel Fojt 	if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
2130cbfa66cSDaniel Fojt 	    (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
2140cbfa66cSDaniel Fojt 	    (r = sshkey_puts(key, blob)) != 0 ||
2150cbfa66cSDaniel Fojt 	    (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
2160cbfa66cSDaniel Fojt 	    (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
2170cbfa66cSDaniel Fojt 	    (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
2180cbfa66cSDaniel Fojt 	    (r = sshbuf_put_string(blob, sig, slen)) != 0) {
21950a69bb5SSascha Wildner 		error_fr(r, "assemble signature object");
2200cbfa66cSDaniel Fojt 		goto done;
2210cbfa66cSDaniel Fojt 	}
2220cbfa66cSDaniel Fojt 
2230cbfa66cSDaniel Fojt 	if (out != NULL) {
2240cbfa66cSDaniel Fojt 		*out = blob;
2250cbfa66cSDaniel Fojt 		blob = NULL;
2260cbfa66cSDaniel Fojt 	}
2270cbfa66cSDaniel Fojt 	r = 0;
2280cbfa66cSDaniel Fojt done:
2290cbfa66cSDaniel Fojt 	free(sig);
2300cbfa66cSDaniel Fojt 	sshbuf_free(blob);
2310cbfa66cSDaniel Fojt 	sshbuf_free(tosign);
2320cbfa66cSDaniel Fojt 	return r;
2330cbfa66cSDaniel Fojt }
2340cbfa66cSDaniel Fojt 
2350cbfa66cSDaniel Fojt /* Check preamble and version. */
2360cbfa66cSDaniel Fojt static int
sshsig_parse_preamble(struct sshbuf * buf)2370cbfa66cSDaniel Fojt sshsig_parse_preamble(struct sshbuf *buf)
2380cbfa66cSDaniel Fojt {
2390cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
2400cbfa66cSDaniel Fojt 	uint32_t sversion;
2410cbfa66cSDaniel Fojt 
2420cbfa66cSDaniel Fojt 	if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
2430cbfa66cSDaniel Fojt 	    (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
2440cbfa66cSDaniel Fojt 	    (r = sshbuf_get_u32(buf, &sversion)) != 0) {
2450cbfa66cSDaniel Fojt 		error("Couldn't verify signature: invalid format");
2460cbfa66cSDaniel Fojt 		return r;
2470cbfa66cSDaniel Fojt 	}
2480cbfa66cSDaniel Fojt 
2490cbfa66cSDaniel Fojt 	if (sversion > SIG_VERSION) {
2500cbfa66cSDaniel Fojt 		error("Signature version %lu is larger than supported "
2510cbfa66cSDaniel Fojt 		    "version %u", (unsigned long)sversion, SIG_VERSION);
2520cbfa66cSDaniel Fojt 		return SSH_ERR_INVALID_FORMAT;
2530cbfa66cSDaniel Fojt 	}
2540cbfa66cSDaniel Fojt 	return 0;
2550cbfa66cSDaniel Fojt }
2560cbfa66cSDaniel Fojt 
2570cbfa66cSDaniel Fojt static int
sshsig_check_hashalg(const char * hashalg)2580cbfa66cSDaniel Fojt sshsig_check_hashalg(const char *hashalg)
2590cbfa66cSDaniel Fojt {
2600cbfa66cSDaniel Fojt 	if (hashalg == NULL ||
2610cbfa66cSDaniel Fojt 	    match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
2620cbfa66cSDaniel Fojt 		return 0;
26350a69bb5SSascha Wildner 	error_f("unsupported hash algorithm \"%.100s\"", hashalg);
2640cbfa66cSDaniel Fojt 	return SSH_ERR_SIGN_ALG_UNSUPPORTED;
2650cbfa66cSDaniel Fojt }
2660cbfa66cSDaniel Fojt 
2670cbfa66cSDaniel Fojt static int
sshsig_peek_hashalg(struct sshbuf * signature,char ** hashalgp)2680cbfa66cSDaniel Fojt sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
2690cbfa66cSDaniel Fojt {
2700cbfa66cSDaniel Fojt 	struct sshbuf *buf = NULL;
2710cbfa66cSDaniel Fojt 	char *hashalg = NULL;
2720cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
2730cbfa66cSDaniel Fojt 
2740cbfa66cSDaniel Fojt 	if (hashalgp != NULL)
2750cbfa66cSDaniel Fojt 		*hashalgp = NULL;
2760cbfa66cSDaniel Fojt 	if ((buf = sshbuf_fromb(signature)) == NULL)
2770cbfa66cSDaniel Fojt 		return SSH_ERR_ALLOC_FAIL;
2780cbfa66cSDaniel Fojt 	if ((r = sshsig_parse_preamble(buf)) != 0)
2790cbfa66cSDaniel Fojt 		goto done;
2800cbfa66cSDaniel Fojt 	if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
2810cbfa66cSDaniel Fojt 	    (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
2820cbfa66cSDaniel Fojt 	    (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
2830cbfa66cSDaniel Fojt 	    (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
2840cbfa66cSDaniel Fojt 	    (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
28550a69bb5SSascha Wildner 		error_fr(r, "parse signature object");
2860cbfa66cSDaniel Fojt 		goto done;
2870cbfa66cSDaniel Fojt 	}
2880cbfa66cSDaniel Fojt 
2890cbfa66cSDaniel Fojt 	/* success */
2900cbfa66cSDaniel Fojt 	r = 0;
2910cbfa66cSDaniel Fojt 	*hashalgp = hashalg;
2920cbfa66cSDaniel Fojt 	hashalg = NULL;
2930cbfa66cSDaniel Fojt  done:
2940cbfa66cSDaniel Fojt 	free(hashalg);
2950cbfa66cSDaniel Fojt 	sshbuf_free(buf);
2960cbfa66cSDaniel Fojt 	return r;
2970cbfa66cSDaniel Fojt }
2980cbfa66cSDaniel Fojt 
2990cbfa66cSDaniel Fojt static int
sshsig_wrap_verify(struct sshbuf * signature,const char * hashalg,const struct sshbuf * h_message,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)3000cbfa66cSDaniel Fojt sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
3010cbfa66cSDaniel Fojt     const struct sshbuf *h_message, const char *expect_namespace,
3020cbfa66cSDaniel Fojt     struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
3030cbfa66cSDaniel Fojt {
3040cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
3050cbfa66cSDaniel Fojt 	struct sshbuf *buf = NULL, *toverify = NULL;
3060cbfa66cSDaniel Fojt 	struct sshkey *key = NULL;
3070cbfa66cSDaniel Fojt 	const u_char *sig;
3080cbfa66cSDaniel Fojt 	char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
3090cbfa66cSDaniel Fojt 	size_t siglen;
3100cbfa66cSDaniel Fojt 
31150a69bb5SSascha Wildner 	debug_f("verify message length %zu", sshbuf_len(h_message));
3120cbfa66cSDaniel Fojt 	if (sig_details != NULL)
3130cbfa66cSDaniel Fojt 		*sig_details = NULL;
3140cbfa66cSDaniel Fojt 	if (sign_keyp != NULL)
3150cbfa66cSDaniel Fojt 		*sign_keyp = NULL;
3160cbfa66cSDaniel Fojt 
3170cbfa66cSDaniel Fojt 	if ((toverify = sshbuf_new()) == NULL) {
31850a69bb5SSascha Wildner 		error_f("sshbuf_new failed");
3190cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
3200cbfa66cSDaniel Fojt 		goto done;
3210cbfa66cSDaniel Fojt 	}
3220cbfa66cSDaniel Fojt 	if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
3230cbfa66cSDaniel Fojt 	    MAGIC_PREAMBLE_LEN)) != 0 ||
3240cbfa66cSDaniel Fojt 	    (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
3250cbfa66cSDaniel Fojt 	    (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
3260cbfa66cSDaniel Fojt 	    (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
3270cbfa66cSDaniel Fojt 	    (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
32850a69bb5SSascha Wildner 		error_fr(r, "assemble message to verify");
3290cbfa66cSDaniel Fojt 		goto done;
3300cbfa66cSDaniel Fojt 	}
3310cbfa66cSDaniel Fojt 
3320cbfa66cSDaniel Fojt 	if ((r = sshsig_parse_preamble(signature)) != 0)
3330cbfa66cSDaniel Fojt 		goto done;
3340cbfa66cSDaniel Fojt 
3350cbfa66cSDaniel Fojt 	if ((r = sshkey_froms(signature, &key)) != 0 ||
3360cbfa66cSDaniel Fojt 	    (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
3370cbfa66cSDaniel Fojt 	    (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
3380cbfa66cSDaniel Fojt 	    (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
3390cbfa66cSDaniel Fojt 	    (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
34050a69bb5SSascha Wildner 		error_fr(r, "parse signature object");
3410cbfa66cSDaniel Fojt 		goto done;
3420cbfa66cSDaniel Fojt 	}
3430cbfa66cSDaniel Fojt 
3440cbfa66cSDaniel Fojt 	if (sshbuf_len(signature) != 0) {
3450cbfa66cSDaniel Fojt 		error("Signature contains trailing data");
3460cbfa66cSDaniel Fojt 		r = SSH_ERR_INVALID_FORMAT;
3470cbfa66cSDaniel Fojt 		goto done;
3480cbfa66cSDaniel Fojt 	}
3490cbfa66cSDaniel Fojt 
3500cbfa66cSDaniel Fojt 	if (strcmp(expect_namespace, got_namespace) != 0) {
3510cbfa66cSDaniel Fojt 		error("Couldn't verify signature: namespace does not match");
35250a69bb5SSascha Wildner 		debug_f("expected namespace \"%s\" received \"%s\"",
35350a69bb5SSascha Wildner 		    expect_namespace, got_namespace);
3540cbfa66cSDaniel Fojt 		r = SSH_ERR_SIGNATURE_INVALID;
3550cbfa66cSDaniel Fojt 		goto done;
3560cbfa66cSDaniel Fojt 	}
3570cbfa66cSDaniel Fojt 	if (strcmp(hashalg, sig_hashalg) != 0) {
3580cbfa66cSDaniel Fojt 		error("Couldn't verify signature: hash algorithm mismatch");
35950a69bb5SSascha Wildner 		debug_f("expected algorithm \"%s\" received \"%s\"",
36050a69bb5SSascha Wildner 		    hashalg, sig_hashalg);
3610cbfa66cSDaniel Fojt 		r = SSH_ERR_SIGNATURE_INVALID;
3620cbfa66cSDaniel Fojt 		goto done;
3630cbfa66cSDaniel Fojt 	}
3640cbfa66cSDaniel Fojt 	/* Ensure that RSA keys use an acceptable signature algorithm */
3650cbfa66cSDaniel Fojt 	if (sshkey_type_plain(key->type) == KEY_RSA) {
3660cbfa66cSDaniel Fojt 		if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
36750a69bb5SSascha Wildner 			error_r(r, "Couldn't verify signature: unable to get "
36850a69bb5SSascha Wildner 			    "signature type");
3690cbfa66cSDaniel Fojt 			goto done;
3700cbfa66cSDaniel Fojt 		}
3710cbfa66cSDaniel Fojt 		if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
3720cbfa66cSDaniel Fojt 			error("Couldn't verify signature: unsupported RSA "
3730cbfa66cSDaniel Fojt 			    "signature algorithm %s", sigtype);
3740cbfa66cSDaniel Fojt 			r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
3750cbfa66cSDaniel Fojt 			goto done;
3760cbfa66cSDaniel Fojt 		}
3770cbfa66cSDaniel Fojt 	}
3780cbfa66cSDaniel Fojt 	if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
3790cbfa66cSDaniel Fojt 	    sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
38050a69bb5SSascha Wildner 		error_r(r, "Signature verification failed");
3810cbfa66cSDaniel Fojt 		goto done;
3820cbfa66cSDaniel Fojt 	}
3830cbfa66cSDaniel Fojt 
3840cbfa66cSDaniel Fojt 	/* success */
3850cbfa66cSDaniel Fojt 	r = 0;
3860cbfa66cSDaniel Fojt 	if (sign_keyp != NULL) {
3870cbfa66cSDaniel Fojt 		*sign_keyp = key;
3880cbfa66cSDaniel Fojt 		key = NULL; /* transferred */
3890cbfa66cSDaniel Fojt 	}
3900cbfa66cSDaniel Fojt done:
3910cbfa66cSDaniel Fojt 	free(got_namespace);
3920cbfa66cSDaniel Fojt 	free(sigtype);
3930cbfa66cSDaniel Fojt 	free(sig_hashalg);
3940cbfa66cSDaniel Fojt 	sshbuf_free(buf);
3950cbfa66cSDaniel Fojt 	sshbuf_free(toverify);
3960cbfa66cSDaniel Fojt 	sshkey_free(key);
3970cbfa66cSDaniel Fojt 	return r;
3980cbfa66cSDaniel Fojt }
3990cbfa66cSDaniel Fojt 
4000cbfa66cSDaniel Fojt static int
hash_buffer(const struct sshbuf * m,const char * hashalg,struct sshbuf ** bp)4010cbfa66cSDaniel Fojt hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
4020cbfa66cSDaniel Fojt {
4030cbfa66cSDaniel Fojt 	char *hex, hash[SSH_DIGEST_MAX_LENGTH];
4040cbfa66cSDaniel Fojt 	int alg, r = SSH_ERR_INTERNAL_ERROR;
4050cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
4060cbfa66cSDaniel Fojt 
4070cbfa66cSDaniel Fojt 	*bp = NULL;
4080cbfa66cSDaniel Fojt 	memset(hash, 0, sizeof(hash));
4090cbfa66cSDaniel Fojt 
4100cbfa66cSDaniel Fojt 	if ((r = sshsig_check_hashalg(hashalg)) != 0)
4110cbfa66cSDaniel Fojt 		return r;
4120cbfa66cSDaniel Fojt 	if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
41350a69bb5SSascha Wildner 		error_f("can't look up hash algorithm %s", hashalg);
4140cbfa66cSDaniel Fojt 		return SSH_ERR_INTERNAL_ERROR;
4150cbfa66cSDaniel Fojt 	}
4160cbfa66cSDaniel Fojt 	if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
41750a69bb5SSascha Wildner 		error_fr(r, "ssh_digest_buffer");
4180cbfa66cSDaniel Fojt 		return r;
4190cbfa66cSDaniel Fojt 	}
4200cbfa66cSDaniel Fojt 	if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
42150a69bb5SSascha Wildner 		debug3_f("final hash: %s", hex);
4220cbfa66cSDaniel Fojt 		freezero(hex, strlen(hex));
4230cbfa66cSDaniel Fojt 	}
4240cbfa66cSDaniel Fojt 	if ((b = sshbuf_new()) == NULL) {
4250cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
4260cbfa66cSDaniel Fojt 		goto out;
4270cbfa66cSDaniel Fojt 	}
4280cbfa66cSDaniel Fojt 	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
42950a69bb5SSascha Wildner 		error_fr(r, "sshbuf_put");
4300cbfa66cSDaniel Fojt 		goto out;
4310cbfa66cSDaniel Fojt 	}
4320cbfa66cSDaniel Fojt 	*bp = b;
4330cbfa66cSDaniel Fojt 	b = NULL; /* transferred */
4340cbfa66cSDaniel Fojt 	/* success */
4350cbfa66cSDaniel Fojt 	r = 0;
4360cbfa66cSDaniel Fojt  out:
4370cbfa66cSDaniel Fojt 	sshbuf_free(b);
4380cbfa66cSDaniel Fojt 	explicit_bzero(hash, sizeof(hash));
4390cbfa66cSDaniel Fojt 	return r;
4400cbfa66cSDaniel Fojt }
4410cbfa66cSDaniel Fojt 
4420cbfa66cSDaniel Fojt int
sshsig_signb(struct sshkey * key,const char * hashalg,const char * sk_provider,const char * sk_pin,const struct sshbuf * message,const char * sig_namespace,struct sshbuf ** out,sshsig_signer * signer,void * signer_ctx)44350a69bb5SSascha Wildner sshsig_signb(struct sshkey *key, const char *hashalg,
44450a69bb5SSascha Wildner     const char *sk_provider, const char *sk_pin,
4450cbfa66cSDaniel Fojt     const struct sshbuf *message, const char *sig_namespace,
4460cbfa66cSDaniel Fojt     struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
4470cbfa66cSDaniel Fojt {
4480cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
4490cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
4500cbfa66cSDaniel Fojt 
4510cbfa66cSDaniel Fojt 	if (hashalg == NULL)
4520cbfa66cSDaniel Fojt 		hashalg = HASHALG_DEFAULT;
4530cbfa66cSDaniel Fojt 	if (out != NULL)
4540cbfa66cSDaniel Fojt 		*out = NULL;
4550cbfa66cSDaniel Fojt 	if ((r = hash_buffer(message, hashalg, &b)) != 0) {
45650a69bb5SSascha Wildner 		error_fr(r, "hash buffer");
4570cbfa66cSDaniel Fojt 		goto out;
4580cbfa66cSDaniel Fojt 	}
45950a69bb5SSascha Wildner 	if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
4600cbfa66cSDaniel Fojt 	    sig_namespace, out, signer, signer_ctx)) != 0)
4610cbfa66cSDaniel Fojt 		goto out;
4620cbfa66cSDaniel Fojt 	/* success */
4630cbfa66cSDaniel Fojt 	r = 0;
4640cbfa66cSDaniel Fojt  out:
4650cbfa66cSDaniel Fojt 	sshbuf_free(b);
4660cbfa66cSDaniel Fojt 	return r;
4670cbfa66cSDaniel Fojt }
4680cbfa66cSDaniel Fojt 
4690cbfa66cSDaniel Fojt int
sshsig_verifyb(struct sshbuf * signature,const struct sshbuf * message,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)4700cbfa66cSDaniel Fojt sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
4710cbfa66cSDaniel Fojt     const char *expect_namespace, struct sshkey **sign_keyp,
4720cbfa66cSDaniel Fojt     struct sshkey_sig_details **sig_details)
4730cbfa66cSDaniel Fojt {
4740cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
4750cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
4760cbfa66cSDaniel Fojt 	char *hashalg = NULL;
4770cbfa66cSDaniel Fojt 
4780cbfa66cSDaniel Fojt 	if (sig_details != NULL)
4790cbfa66cSDaniel Fojt 		*sig_details = NULL;
4800cbfa66cSDaniel Fojt 	if (sign_keyp != NULL)
4810cbfa66cSDaniel Fojt 		*sign_keyp = NULL;
4820cbfa66cSDaniel Fojt 	if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
4830cbfa66cSDaniel Fojt 		return r;
48450a69bb5SSascha Wildner 	debug_f("signature made with hash \"%s\"", hashalg);
4850cbfa66cSDaniel Fojt 	if ((r = hash_buffer(message, hashalg, &b)) != 0) {
48650a69bb5SSascha Wildner 		error_fr(r, "hash buffer");
4870cbfa66cSDaniel Fojt 		goto out;
4880cbfa66cSDaniel Fojt 	}
4890cbfa66cSDaniel Fojt 	if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
4900cbfa66cSDaniel Fojt 	    sign_keyp, sig_details)) != 0)
4910cbfa66cSDaniel Fojt 		goto out;
4920cbfa66cSDaniel Fojt 	/* success */
4930cbfa66cSDaniel Fojt 	r = 0;
4940cbfa66cSDaniel Fojt  out:
4950cbfa66cSDaniel Fojt 	sshbuf_free(b);
4960cbfa66cSDaniel Fojt 	free(hashalg);
4970cbfa66cSDaniel Fojt 	return r;
4980cbfa66cSDaniel Fojt }
4990cbfa66cSDaniel Fojt 
5000cbfa66cSDaniel Fojt static int
hash_file(int fd,const char * hashalg,struct sshbuf ** bp)5010cbfa66cSDaniel Fojt hash_file(int fd, const char *hashalg, struct sshbuf **bp)
5020cbfa66cSDaniel Fojt {
5030cbfa66cSDaniel Fojt 	char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
5040cbfa66cSDaniel Fojt 	ssize_t n, total = 0;
505ee116499SAntonio Huete Jimenez 	struct ssh_digest_ctx *ctx = NULL;
5060cbfa66cSDaniel Fojt 	int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
5070cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
5080cbfa66cSDaniel Fojt 
5090cbfa66cSDaniel Fojt 	*bp = NULL;
5100cbfa66cSDaniel Fojt 	memset(hash, 0, sizeof(hash));
5110cbfa66cSDaniel Fojt 
5120cbfa66cSDaniel Fojt 	if ((r = sshsig_check_hashalg(hashalg)) != 0)
5130cbfa66cSDaniel Fojt 		return r;
5140cbfa66cSDaniel Fojt 	if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
51550a69bb5SSascha Wildner 		error_f("can't look up hash algorithm %s", hashalg);
5160cbfa66cSDaniel Fojt 		return SSH_ERR_INTERNAL_ERROR;
5170cbfa66cSDaniel Fojt 	}
5180cbfa66cSDaniel Fojt 	if ((ctx = ssh_digest_start(alg)) == NULL) {
51950a69bb5SSascha Wildner 		error_f("ssh_digest_start failed");
5200cbfa66cSDaniel Fojt 		return SSH_ERR_INTERNAL_ERROR;
5210cbfa66cSDaniel Fojt 	}
5220cbfa66cSDaniel Fojt 	for (;;) {
5230cbfa66cSDaniel Fojt 		if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
5240cbfa66cSDaniel Fojt 			if (errno == EINTR || errno == EAGAIN)
5250cbfa66cSDaniel Fojt 				continue;
5260cbfa66cSDaniel Fojt 			oerrno = errno;
52750a69bb5SSascha Wildner 			error_f("read: %s", strerror(errno));
5280cbfa66cSDaniel Fojt 			errno = oerrno;
5290cbfa66cSDaniel Fojt 			r = SSH_ERR_SYSTEM_ERROR;
5300cbfa66cSDaniel Fojt 			goto out;
5310cbfa66cSDaniel Fojt 		} else if (n == 0) {
53250a69bb5SSascha Wildner 			debug2_f("hashed %zu bytes", total);
5330cbfa66cSDaniel Fojt 			break; /* EOF */
5340cbfa66cSDaniel Fojt 		}
5350cbfa66cSDaniel Fojt 		total += (size_t)n;
5360cbfa66cSDaniel Fojt 		if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
53750a69bb5SSascha Wildner 			error_fr(r, "ssh_digest_update");
5380cbfa66cSDaniel Fojt 			goto out;
5390cbfa66cSDaniel Fojt 		}
5400cbfa66cSDaniel Fojt 	}
5410cbfa66cSDaniel Fojt 	if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
54250a69bb5SSascha Wildner 		error_fr(r, "ssh_digest_final");
5430cbfa66cSDaniel Fojt 		goto out;
5440cbfa66cSDaniel Fojt 	}
5450cbfa66cSDaniel Fojt 	if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
54650a69bb5SSascha Wildner 		debug3_f("final hash: %s", hex);
5470cbfa66cSDaniel Fojt 		freezero(hex, strlen(hex));
5480cbfa66cSDaniel Fojt 	}
5490cbfa66cSDaniel Fojt 	if ((b = sshbuf_new()) == NULL) {
5500cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
5510cbfa66cSDaniel Fojt 		goto out;
5520cbfa66cSDaniel Fojt 	}
5530cbfa66cSDaniel Fojt 	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
55450a69bb5SSascha Wildner 		error_fr(r, "sshbuf_put");
5550cbfa66cSDaniel Fojt 		goto out;
5560cbfa66cSDaniel Fojt 	}
5570cbfa66cSDaniel Fojt 	*bp = b;
5580cbfa66cSDaniel Fojt 	b = NULL; /* transferred */
5590cbfa66cSDaniel Fojt 	/* success */
5600cbfa66cSDaniel Fojt 	r = 0;
5610cbfa66cSDaniel Fojt  out:
562ee116499SAntonio Huete Jimenez 	oerrno = errno;
5630cbfa66cSDaniel Fojt 	sshbuf_free(b);
5640cbfa66cSDaniel Fojt 	ssh_digest_free(ctx);
5650cbfa66cSDaniel Fojt 	explicit_bzero(hash, sizeof(hash));
566ee116499SAntonio Huete Jimenez 	errno = oerrno;
5670cbfa66cSDaniel Fojt 	return r;
5680cbfa66cSDaniel Fojt }
5690cbfa66cSDaniel Fojt 
5700cbfa66cSDaniel Fojt int
sshsig_sign_fd(struct sshkey * key,const char * hashalg,const char * sk_provider,const char * sk_pin,int fd,const char * sig_namespace,struct sshbuf ** out,sshsig_signer * signer,void * signer_ctx)57150a69bb5SSascha Wildner sshsig_sign_fd(struct sshkey *key, const char *hashalg,
57250a69bb5SSascha Wildner     const char *sk_provider, const char *sk_pin,
5730cbfa66cSDaniel Fojt     int fd, const char *sig_namespace, struct sshbuf **out,
5740cbfa66cSDaniel Fojt     sshsig_signer *signer, void *signer_ctx)
5750cbfa66cSDaniel Fojt {
5760cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
5770cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
5780cbfa66cSDaniel Fojt 
5790cbfa66cSDaniel Fojt 	if (hashalg == NULL)
5800cbfa66cSDaniel Fojt 		hashalg = HASHALG_DEFAULT;
5810cbfa66cSDaniel Fojt 	if (out != NULL)
5820cbfa66cSDaniel Fojt 		*out = NULL;
5830cbfa66cSDaniel Fojt 	if ((r = hash_file(fd, hashalg, &b)) != 0) {
58450a69bb5SSascha Wildner 		error_fr(r, "hash_file");
5850cbfa66cSDaniel Fojt 		return r;
5860cbfa66cSDaniel Fojt 	}
58750a69bb5SSascha Wildner 	if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
5880cbfa66cSDaniel Fojt 	    sig_namespace, out, signer, signer_ctx)) != 0)
5890cbfa66cSDaniel Fojt 		goto out;
5900cbfa66cSDaniel Fojt 	/* success */
5910cbfa66cSDaniel Fojt 	r = 0;
5920cbfa66cSDaniel Fojt  out:
5930cbfa66cSDaniel Fojt 	sshbuf_free(b);
5940cbfa66cSDaniel Fojt 	return r;
5950cbfa66cSDaniel Fojt }
5960cbfa66cSDaniel Fojt 
5970cbfa66cSDaniel Fojt int
sshsig_verify_fd(struct sshbuf * signature,int fd,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)5980cbfa66cSDaniel Fojt sshsig_verify_fd(struct sshbuf *signature, int fd,
5990cbfa66cSDaniel Fojt     const char *expect_namespace, struct sshkey **sign_keyp,
6000cbfa66cSDaniel Fojt     struct sshkey_sig_details **sig_details)
6010cbfa66cSDaniel Fojt {
6020cbfa66cSDaniel Fojt 	struct sshbuf *b = NULL;
6030cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
6040cbfa66cSDaniel Fojt 	char *hashalg = NULL;
6050cbfa66cSDaniel Fojt 
6060cbfa66cSDaniel Fojt 	if (sig_details != NULL)
6070cbfa66cSDaniel Fojt 		*sig_details = NULL;
6080cbfa66cSDaniel Fojt 	if (sign_keyp != NULL)
6090cbfa66cSDaniel Fojt 		*sign_keyp = NULL;
6100cbfa66cSDaniel Fojt 	if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
6110cbfa66cSDaniel Fojt 		return r;
61250a69bb5SSascha Wildner 	debug_f("signature made with hash \"%s\"", hashalg);
6130cbfa66cSDaniel Fojt 	if ((r = hash_file(fd, hashalg, &b)) != 0) {
61450a69bb5SSascha Wildner 		error_fr(r, "hash_file");
6150cbfa66cSDaniel Fojt 		goto out;
6160cbfa66cSDaniel Fojt 	}
6170cbfa66cSDaniel Fojt 	if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
6180cbfa66cSDaniel Fojt 	    sign_keyp, sig_details)) != 0)
6190cbfa66cSDaniel Fojt 		goto out;
6200cbfa66cSDaniel Fojt 	/* success */
6210cbfa66cSDaniel Fojt 	r = 0;
6220cbfa66cSDaniel Fojt  out:
6230cbfa66cSDaniel Fojt 	sshbuf_free(b);
6240cbfa66cSDaniel Fojt 	free(hashalg);
6250cbfa66cSDaniel Fojt 	return r;
6260cbfa66cSDaniel Fojt }
6270cbfa66cSDaniel Fojt 
6280cbfa66cSDaniel Fojt struct sshsigopt {
6290cbfa66cSDaniel Fojt 	int ca;
6300cbfa66cSDaniel Fojt 	char *namespaces;
63150a69bb5SSascha Wildner 	uint64_t valid_after, valid_before;
6320cbfa66cSDaniel Fojt };
6330cbfa66cSDaniel Fojt 
6340cbfa66cSDaniel Fojt struct sshsigopt *
sshsigopt_parse(const char * opts,const char * path,u_long linenum,const char ** errstrp)6350cbfa66cSDaniel Fojt sshsigopt_parse(const char *opts, const char *path, u_long linenum,
6360cbfa66cSDaniel Fojt     const char **errstrp)
6370cbfa66cSDaniel Fojt {
6380cbfa66cSDaniel Fojt 	struct sshsigopt *ret;
6390cbfa66cSDaniel Fojt 	int r;
64050a69bb5SSascha Wildner 	char *opt;
6410cbfa66cSDaniel Fojt 	const char *errstr = NULL;
6420cbfa66cSDaniel Fojt 
6430cbfa66cSDaniel Fojt 	if ((ret = calloc(1, sizeof(*ret))) == NULL)
6440cbfa66cSDaniel Fojt 		return NULL;
6450cbfa66cSDaniel Fojt 	if (opts == NULL || *opts == '\0')
6460cbfa66cSDaniel Fojt 		return ret; /* Empty options yields empty options :) */
6470cbfa66cSDaniel Fojt 
6480cbfa66cSDaniel Fojt 	while (*opts && *opts != ' ' && *opts != '\t') {
6490cbfa66cSDaniel Fojt 		/* flag options */
6500cbfa66cSDaniel Fojt 		if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
6510cbfa66cSDaniel Fojt 			ret->ca = 1;
6520cbfa66cSDaniel Fojt 		} else if (opt_match(&opts, "namespaces")) {
6530cbfa66cSDaniel Fojt 			if (ret->namespaces != NULL) {
6540cbfa66cSDaniel Fojt 				errstr = "multiple \"namespaces\" clauses";
6550cbfa66cSDaniel Fojt 				goto fail;
6560cbfa66cSDaniel Fojt 			}
6570cbfa66cSDaniel Fojt 			ret->namespaces = opt_dequote(&opts, &errstr);
6580cbfa66cSDaniel Fojt 			if (ret->namespaces == NULL)
6590cbfa66cSDaniel Fojt 				goto fail;
66050a69bb5SSascha Wildner 		} else if (opt_match(&opts, "valid-after")) {
66150a69bb5SSascha Wildner 			if (ret->valid_after != 0) {
66250a69bb5SSascha Wildner 				errstr = "multiple \"valid-after\" clauses";
66350a69bb5SSascha Wildner 				goto fail;
66450a69bb5SSascha Wildner 			}
66550a69bb5SSascha Wildner 			if ((opt = opt_dequote(&opts, &errstr)) == NULL)
66650a69bb5SSascha Wildner 				goto fail;
66750a69bb5SSascha Wildner 			if (parse_absolute_time(opt, &ret->valid_after) != 0 ||
66850a69bb5SSascha Wildner 			    ret->valid_after == 0) {
66950a69bb5SSascha Wildner 				free(opt);
67050a69bb5SSascha Wildner 				errstr = "invalid \"valid-after\" time";
67150a69bb5SSascha Wildner 				goto fail;
67250a69bb5SSascha Wildner 			}
67350a69bb5SSascha Wildner 			free(opt);
67450a69bb5SSascha Wildner 		} else if (opt_match(&opts, "valid-before")) {
67550a69bb5SSascha Wildner 			if (ret->valid_before != 0) {
67650a69bb5SSascha Wildner 				errstr = "multiple \"valid-before\" clauses";
67750a69bb5SSascha Wildner 				goto fail;
67850a69bb5SSascha Wildner 			}
67950a69bb5SSascha Wildner 			if ((opt = opt_dequote(&opts, &errstr)) == NULL)
68050a69bb5SSascha Wildner 				goto fail;
68150a69bb5SSascha Wildner 			if (parse_absolute_time(opt, &ret->valid_before) != 0 ||
68250a69bb5SSascha Wildner 			    ret->valid_before == 0) {
68350a69bb5SSascha Wildner 				free(opt);
68450a69bb5SSascha Wildner 				errstr = "invalid \"valid-before\" time";
68550a69bb5SSascha Wildner 				goto fail;
68650a69bb5SSascha Wildner 			}
68750a69bb5SSascha Wildner 			free(opt);
6880cbfa66cSDaniel Fojt 		}
6890cbfa66cSDaniel Fojt 		/*
6900cbfa66cSDaniel Fojt 		 * Skip the comma, and move to the next option
6910cbfa66cSDaniel Fojt 		 * (or break out if there are no more).
6920cbfa66cSDaniel Fojt 		 */
6930cbfa66cSDaniel Fojt 		if (*opts == '\0' || *opts == ' ' || *opts == '\t')
6940cbfa66cSDaniel Fojt 			break;		/* End of options. */
6950cbfa66cSDaniel Fojt 		/* Anything other than a comma is an unknown option */
6960cbfa66cSDaniel Fojt 		if (*opts != ',') {
6970cbfa66cSDaniel Fojt 			errstr = "unknown key option";
6980cbfa66cSDaniel Fojt 			goto fail;
6990cbfa66cSDaniel Fojt 		}
7000cbfa66cSDaniel Fojt 		opts++;
7010cbfa66cSDaniel Fojt 		if (*opts == '\0') {
7020cbfa66cSDaniel Fojt 			errstr = "unexpected end-of-options";
7030cbfa66cSDaniel Fojt 			goto fail;
7040cbfa66cSDaniel Fojt 		}
7050cbfa66cSDaniel Fojt 	}
70650a69bb5SSascha Wildner 	/* final consistency check */
70750a69bb5SSascha Wildner 	if (ret->valid_after != 0 && ret->valid_before != 0 &&
70850a69bb5SSascha Wildner 	    ret->valid_before <= ret->valid_after) {
70950a69bb5SSascha Wildner 		errstr = "\"valid-before\" time is before \"valid-after\"";
71050a69bb5SSascha Wildner 		goto fail;
71150a69bb5SSascha Wildner 	}
7120cbfa66cSDaniel Fojt 	/* success */
7130cbfa66cSDaniel Fojt 	return ret;
7140cbfa66cSDaniel Fojt  fail:
7150cbfa66cSDaniel Fojt 	if (errstrp != NULL)
7160cbfa66cSDaniel Fojt 		*errstrp = errstr;
7170cbfa66cSDaniel Fojt 	sshsigopt_free(ret);
7180cbfa66cSDaniel Fojt 	return NULL;
7190cbfa66cSDaniel Fojt }
7200cbfa66cSDaniel Fojt 
7210cbfa66cSDaniel Fojt void
sshsigopt_free(struct sshsigopt * opts)7220cbfa66cSDaniel Fojt sshsigopt_free(struct sshsigopt *opts)
7230cbfa66cSDaniel Fojt {
7240cbfa66cSDaniel Fojt 	if (opts == NULL)
7250cbfa66cSDaniel Fojt 		return;
7260cbfa66cSDaniel Fojt 	free(opts->namespaces);
7270cbfa66cSDaniel Fojt 	free(opts);
7280cbfa66cSDaniel Fojt }
7290cbfa66cSDaniel Fojt 
7300cbfa66cSDaniel Fojt static int
parse_principals_key_and_options(const char * path,u_long linenum,char * line,const char * required_principal,char ** principalsp,struct sshkey ** keyp,struct sshsigopt ** sigoptsp)7310cbfa66cSDaniel Fojt parse_principals_key_and_options(const char *path, u_long linenum, char *line,
7320cbfa66cSDaniel Fojt     const char *required_principal, char **principalsp, struct sshkey **keyp,
7330cbfa66cSDaniel Fojt     struct sshsigopt **sigoptsp)
7340cbfa66cSDaniel Fojt {
7350cbfa66cSDaniel Fojt 	char *opts = NULL, *tmp, *cp, *principals = NULL;
7360cbfa66cSDaniel Fojt 	const char *reason = NULL;
7370cbfa66cSDaniel Fojt 	struct sshsigopt *sigopts = NULL;
7380cbfa66cSDaniel Fojt 	struct sshkey *key = NULL;
7390cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
7400cbfa66cSDaniel Fojt 
7410cbfa66cSDaniel Fojt 	if (principalsp != NULL)
7420cbfa66cSDaniel Fojt 		*principalsp = NULL;
7430cbfa66cSDaniel Fojt 	if (sigoptsp != NULL)
7440cbfa66cSDaniel Fojt 		*sigoptsp = NULL;
7450cbfa66cSDaniel Fojt 	if (keyp != NULL)
7460cbfa66cSDaniel Fojt 		*keyp = NULL;
7470cbfa66cSDaniel Fojt 
7480cbfa66cSDaniel Fojt 	cp = line;
749*ba1276acSMatthew Dillon 	cp = cp + strspn(cp, " \t\n\r"); /* skip leading whitespace */
7500cbfa66cSDaniel Fojt 	if (*cp == '#' || *cp == '\0')
7510cbfa66cSDaniel Fojt 		return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
7520cbfa66cSDaniel Fojt 
7530cbfa66cSDaniel Fojt 	/* format: identity[,identity...] [option[,option...]] key */
754ee116499SAntonio Huete Jimenez 	if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) {
7550cbfa66cSDaniel Fojt 		error("%s:%lu: invalid line", path, linenum);
7560cbfa66cSDaniel Fojt 		r = SSH_ERR_INVALID_FORMAT;
7570cbfa66cSDaniel Fojt 		goto out;
7580cbfa66cSDaniel Fojt 	}
7590cbfa66cSDaniel Fojt 	if ((principals = strdup(tmp)) == NULL) {
76050a69bb5SSascha Wildner 		error_f("strdup failed");
7610cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
7620cbfa66cSDaniel Fojt 		goto out;
7630cbfa66cSDaniel Fojt 	}
7640cbfa66cSDaniel Fojt 	/*
7650cbfa66cSDaniel Fojt 	 * Bail out early if we're looking for a particular principal and this
7660cbfa66cSDaniel Fojt 	 * line does not list it.
7670cbfa66cSDaniel Fojt 	 */
7680cbfa66cSDaniel Fojt 	if (required_principal != NULL) {
7690cbfa66cSDaniel Fojt 		if (match_pattern_list(required_principal,
7700cbfa66cSDaniel Fojt 		    principals, 0) != 1) {
7710cbfa66cSDaniel Fojt 			/* principal didn't match */
7720cbfa66cSDaniel Fojt 			r = SSH_ERR_KEY_NOT_FOUND;
7730cbfa66cSDaniel Fojt 			goto out;
7740cbfa66cSDaniel Fojt 		}
77550a69bb5SSascha Wildner 		debug_f("%s:%lu: matched principal \"%s\"",
77650a69bb5SSascha Wildner 		    path, linenum, required_principal);
7770cbfa66cSDaniel Fojt 	}
7780cbfa66cSDaniel Fojt 
7790cbfa66cSDaniel Fojt 	if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
78050a69bb5SSascha Wildner 		error_f("sshkey_new failed");
7810cbfa66cSDaniel Fojt 		r = SSH_ERR_ALLOC_FAIL;
7820cbfa66cSDaniel Fojt 		goto out;
7830cbfa66cSDaniel Fojt 	}
7840cbfa66cSDaniel Fojt 	if (sshkey_read(key, &cp) != 0) {
7850cbfa66cSDaniel Fojt 		/* no key? Check for options */
7860cbfa66cSDaniel Fojt 		opts = cp;
7870cbfa66cSDaniel Fojt 		if (sshkey_advance_past_options(&cp) != 0) {
7880cbfa66cSDaniel Fojt 			error("%s:%lu: invalid options", path, linenum);
7890cbfa66cSDaniel Fojt 			r = SSH_ERR_INVALID_FORMAT;
7900cbfa66cSDaniel Fojt 			goto out;
7910cbfa66cSDaniel Fojt 		}
792ee116499SAntonio Huete Jimenez 		if (cp == NULL || *cp == '\0') {
793ee116499SAntonio Huete Jimenez 			error("%s:%lu: missing key", path, linenum);
794ee116499SAntonio Huete Jimenez 			r = SSH_ERR_INVALID_FORMAT;
795ee116499SAntonio Huete Jimenez 			goto out;
796ee116499SAntonio Huete Jimenez 		}
7970cbfa66cSDaniel Fojt 		*cp++ = '\0';
7980cbfa66cSDaniel Fojt 		skip_space(&cp);
7990cbfa66cSDaniel Fojt 		if (sshkey_read(key, &cp) != 0) {
8000cbfa66cSDaniel Fojt 			error("%s:%lu: invalid key", path, linenum);
8010cbfa66cSDaniel Fojt 			r = SSH_ERR_INVALID_FORMAT;
8020cbfa66cSDaniel Fojt 			goto out;
8030cbfa66cSDaniel Fojt 		}
8040cbfa66cSDaniel Fojt 	}
8050cbfa66cSDaniel Fojt 	debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
8060cbfa66cSDaniel Fojt 	if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
8070cbfa66cSDaniel Fojt 		error("%s:%lu: bad options: %s", path, linenum, reason);
8080cbfa66cSDaniel Fojt 		r = SSH_ERR_INVALID_FORMAT;
8090cbfa66cSDaniel Fojt 		goto out;
8100cbfa66cSDaniel Fojt 	}
8110cbfa66cSDaniel Fojt 	/* success */
8120cbfa66cSDaniel Fojt 	if (principalsp != NULL) {
8130cbfa66cSDaniel Fojt 		*principalsp = principals;
8140cbfa66cSDaniel Fojt 		principals = NULL; /* transferred */
8150cbfa66cSDaniel Fojt 	}
8160cbfa66cSDaniel Fojt 	if (sigoptsp != NULL) {
8170cbfa66cSDaniel Fojt 		*sigoptsp = sigopts;
8180cbfa66cSDaniel Fojt 		sigopts = NULL; /* transferred */
8190cbfa66cSDaniel Fojt 	}
8200cbfa66cSDaniel Fojt 	if (keyp != NULL) {
8210cbfa66cSDaniel Fojt 		*keyp = key;
8220cbfa66cSDaniel Fojt 		key = NULL; /* transferred */
8230cbfa66cSDaniel Fojt 	}
8240cbfa66cSDaniel Fojt 	r = 0;
8250cbfa66cSDaniel Fojt  out:
8260cbfa66cSDaniel Fojt 	free(principals);
8270cbfa66cSDaniel Fojt 	sshsigopt_free(sigopts);
8280cbfa66cSDaniel Fojt 	sshkey_free(key);
8290cbfa66cSDaniel Fojt 	return r;
8300cbfa66cSDaniel Fojt }
8310cbfa66cSDaniel Fojt 
8320cbfa66cSDaniel Fojt static int
cert_filter_principals(const char * path,u_long linenum,char ** principalsp,const struct sshkey * cert,uint64_t verify_time)833ee116499SAntonio Huete Jimenez cert_filter_principals(const char *path, u_long linenum,
834ee116499SAntonio Huete Jimenez     char **principalsp, const struct sshkey *cert, uint64_t verify_time)
835ee116499SAntonio Huete Jimenez {
836ee116499SAntonio Huete Jimenez 	char *cp, *oprincipals, *principals;
837ee116499SAntonio Huete Jimenez 	const char *reason;
838ee116499SAntonio Huete Jimenez 	struct sshbuf *nprincipals;
839ee116499SAntonio Huete Jimenez 	int r = SSH_ERR_INTERNAL_ERROR, success = 0;
840ee116499SAntonio Huete Jimenez 	u_int i;
841ee116499SAntonio Huete Jimenez 
842ee116499SAntonio Huete Jimenez 	oprincipals = principals = *principalsp;
843ee116499SAntonio Huete Jimenez 	*principalsp = NULL;
844ee116499SAntonio Huete Jimenez 
845ee116499SAntonio Huete Jimenez 	if ((nprincipals = sshbuf_new()) == NULL) {
846ee116499SAntonio Huete Jimenez 		r = SSH_ERR_ALLOC_FAIL;
847ee116499SAntonio Huete Jimenez 		goto out;
848ee116499SAntonio Huete Jimenez 	}
849ee116499SAntonio Huete Jimenez 
850ee116499SAntonio Huete Jimenez 	while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
851ee116499SAntonio Huete Jimenez 		/* Check certificate validity */
852ee116499SAntonio Huete Jimenez 		if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
853ee116499SAntonio Huete Jimenez 		    verify_time, NULL, &reason)) != 0) {
854ee116499SAntonio Huete Jimenez 			debug("%s:%lu: principal \"%s\" not authorized: %s",
855ee116499SAntonio Huete Jimenez 			    path, linenum, cp, reason);
856ee116499SAntonio Huete Jimenez 			continue;
857ee116499SAntonio Huete Jimenez 		}
858ee116499SAntonio Huete Jimenez 		/* Return all matching principal names from the cert */
859ee116499SAntonio Huete Jimenez 		for (i = 0; i < cert->cert->nprincipals; i++) {
860ee116499SAntonio Huete Jimenez 			if (match_pattern(cert->cert->principals[i], cp)) {
861ee116499SAntonio Huete Jimenez 				if ((r = sshbuf_putf(nprincipals, "%s%s",
862ee116499SAntonio Huete Jimenez 					sshbuf_len(nprincipals) != 0 ? "," : "",
863ee116499SAntonio Huete Jimenez 						cert->cert->principals[i])) != 0) {
864ee116499SAntonio Huete Jimenez 					error_f("buffer error");
865ee116499SAntonio Huete Jimenez 					goto out;
866ee116499SAntonio Huete Jimenez 				}
867ee116499SAntonio Huete Jimenez 			}
868ee116499SAntonio Huete Jimenez 		}
869ee116499SAntonio Huete Jimenez 	}
870ee116499SAntonio Huete Jimenez 	if (sshbuf_len(nprincipals) == 0) {
871ee116499SAntonio Huete Jimenez 		error("%s:%lu: no valid principals found", path, linenum);
872ee116499SAntonio Huete Jimenez 		r = SSH_ERR_KEY_CERT_INVALID;
873ee116499SAntonio Huete Jimenez 		goto out;
874ee116499SAntonio Huete Jimenez 	}
875ee116499SAntonio Huete Jimenez 	if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
876ee116499SAntonio Huete Jimenez 		error_f("buffer error");
877ee116499SAntonio Huete Jimenez 		goto out;
878ee116499SAntonio Huete Jimenez 	}
879ee116499SAntonio Huete Jimenez 	/* success */
880ee116499SAntonio Huete Jimenez 	success = 1;
881ee116499SAntonio Huete Jimenez 	*principalsp = principals;
882ee116499SAntonio Huete Jimenez  out:
883ee116499SAntonio Huete Jimenez 	sshbuf_free(nprincipals);
884ee116499SAntonio Huete Jimenez 	free(oprincipals);
885ee116499SAntonio Huete Jimenez 	return success ? 0 : r;
886ee116499SAntonio Huete Jimenez }
887ee116499SAntonio Huete Jimenez 
888ee116499SAntonio Huete Jimenez static int
check_allowed_keys_line(const char * path,u_long linenum,char * line,const struct sshkey * sign_key,const char * principal,const char * sig_namespace,uint64_t verify_time,char ** principalsp)8890cbfa66cSDaniel Fojt check_allowed_keys_line(const char *path, u_long linenum, char *line,
8900cbfa66cSDaniel Fojt     const struct sshkey *sign_key, const char *principal,
891ee116499SAntonio Huete Jimenez     const char *sig_namespace, uint64_t verify_time, char **principalsp)
8920cbfa66cSDaniel Fojt {
8930cbfa66cSDaniel Fojt 	struct sshkey *found_key = NULL;
894ee116499SAntonio Huete Jimenez 	char *principals = NULL;
89550a69bb5SSascha Wildner 	int r, success = 0;
8960cbfa66cSDaniel Fojt 	const char *reason = NULL;
8970cbfa66cSDaniel Fojt 	struct sshsigopt *sigopts = NULL;
89850a69bb5SSascha Wildner 	char tvalid[64], tverify[64];
8990cbfa66cSDaniel Fojt 
900ee116499SAntonio Huete Jimenez 	if (principalsp != NULL)
901ee116499SAntonio Huete Jimenez 		*principalsp = NULL;
902ee116499SAntonio Huete Jimenez 
9030cbfa66cSDaniel Fojt 	/* Parse the line */
9040cbfa66cSDaniel Fojt 	if ((r = parse_principals_key_and_options(path, linenum, line,
905ee116499SAntonio Huete Jimenez 	    principal, &principals, &found_key, &sigopts)) != 0) {
9060cbfa66cSDaniel Fojt 		/* error already logged */
9070cbfa66cSDaniel Fojt 		goto done;
9080cbfa66cSDaniel Fojt 	}
9090cbfa66cSDaniel Fojt 
91050a69bb5SSascha Wildner 	if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
91150a69bb5SSascha Wildner 		/* Exact match of key */
91250a69bb5SSascha Wildner 		debug("%s:%lu: matched key", path, linenum);
91350a69bb5SSascha Wildner 	} else if (sigopts->ca && sshkey_is_cert(sign_key) &&
91450a69bb5SSascha Wildner 	    sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
915ee116499SAntonio Huete Jimenez 		if (principal) {
916ee116499SAntonio Huete Jimenez 			/* Match certificate CA key with specified principal */
91750a69bb5SSascha Wildner 			if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
91850a69bb5SSascha Wildner 			    verify_time, principal, &reason)) != 0) {
91950a69bb5SSascha Wildner 				error("%s:%lu: certificate not authorized: %s",
92050a69bb5SSascha Wildner 				    path, linenum, reason);
92150a69bb5SSascha Wildner 				goto done;
92250a69bb5SSascha Wildner 			}
923ee116499SAntonio Huete Jimenez 			debug("%s:%lu: matched certificate CA key",
924ee116499SAntonio Huete Jimenez 			    path, linenum);
925ee116499SAntonio Huete Jimenez 		} else {
926ee116499SAntonio Huete Jimenez 			/* No principal specified - find all matching ones */
927ee116499SAntonio Huete Jimenez 			if ((r = cert_filter_principals(path, linenum,
928ee116499SAntonio Huete Jimenez 			    &principals, sign_key, verify_time)) != 0) {
929ee116499SAntonio Huete Jimenez 				/* error already displayed */
930ee116499SAntonio Huete Jimenez 				debug_r(r, "%s:%lu: cert_filter_principals",
931ee116499SAntonio Huete Jimenez 				    path, linenum);
932ee116499SAntonio Huete Jimenez 				goto done;
933ee116499SAntonio Huete Jimenez 			}
934ee116499SAntonio Huete Jimenez 			debug("%s:%lu: matched certificate CA key",
935ee116499SAntonio Huete Jimenez 			    path, linenum);
936ee116499SAntonio Huete Jimenez 		}
93750a69bb5SSascha Wildner 	} else {
93850a69bb5SSascha Wildner 		/* Didn't match key */
93950a69bb5SSascha Wildner 		goto done;
94050a69bb5SSascha Wildner 	}
94150a69bb5SSascha Wildner 
9420cbfa66cSDaniel Fojt 	/* Check whether options preclude the use of this key */
943ee116499SAntonio Huete Jimenez 	if (sigopts->namespaces != NULL && sig_namespace != NULL &&
9440cbfa66cSDaniel Fojt 	    match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
9450cbfa66cSDaniel Fojt 		error("%s:%lu: key is not permitted for use in signature "
9460cbfa66cSDaniel Fojt 		    "namespace \"%s\"", path, linenum, sig_namespace);
9470cbfa66cSDaniel Fojt 		goto done;
9480cbfa66cSDaniel Fojt 	}
9490cbfa66cSDaniel Fojt 
95050a69bb5SSascha Wildner 	/* check key time validity */
95150a69bb5SSascha Wildner 	format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
95250a69bb5SSascha Wildner 	if (sigopts->valid_after != 0 &&
95350a69bb5SSascha Wildner 	    (uint64_t)verify_time < sigopts->valid_after) {
95450a69bb5SSascha Wildner 		format_absolute_time(sigopts->valid_after,
95550a69bb5SSascha Wildner 		    tvalid, sizeof(tvalid));
95650a69bb5SSascha Wildner 		error("%s:%lu: key is not yet valid: "
95750a69bb5SSascha Wildner 		    "verify time %s < valid-after %s", path, linenum,
95850a69bb5SSascha Wildner 		    tverify, tvalid);
9590cbfa66cSDaniel Fojt 		goto done;
9600cbfa66cSDaniel Fojt 	}
96150a69bb5SSascha Wildner 	if (sigopts->valid_before != 0 &&
96250a69bb5SSascha Wildner 	    (uint64_t)verify_time > sigopts->valid_before) {
96350a69bb5SSascha Wildner 		format_absolute_time(sigopts->valid_before,
96450a69bb5SSascha Wildner 		    tvalid, sizeof(tvalid));
96550a69bb5SSascha Wildner 		error("%s:%lu: key has expired: "
96650a69bb5SSascha Wildner 		    "verify time %s > valid-before %s", path, linenum,
96750a69bb5SSascha Wildner 		    tverify, tvalid);
9680cbfa66cSDaniel Fojt 		goto done;
9690cbfa66cSDaniel Fojt 	}
97050a69bb5SSascha Wildner 	success = 1;
97150a69bb5SSascha Wildner 
9720cbfa66cSDaniel Fojt  done:
973ee116499SAntonio Huete Jimenez 	if (success && principalsp != NULL) {
974ee116499SAntonio Huete Jimenez 		*principalsp = principals;
975ee116499SAntonio Huete Jimenez 		principals = NULL; /* transferred */
976ee116499SAntonio Huete Jimenez 	}
977ee116499SAntonio Huete Jimenez 	free(principals);
9780cbfa66cSDaniel Fojt 	sshkey_free(found_key);
9790cbfa66cSDaniel Fojt 	sshsigopt_free(sigopts);
98050a69bb5SSascha Wildner 	return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
9810cbfa66cSDaniel Fojt }
9820cbfa66cSDaniel Fojt 
9830cbfa66cSDaniel Fojt int
sshsig_check_allowed_keys(const char * path,const struct sshkey * sign_key,const char * principal,const char * sig_namespace,uint64_t verify_time)9840cbfa66cSDaniel Fojt sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
98550a69bb5SSascha Wildner     const char *principal, const char *sig_namespace, uint64_t verify_time)
9860cbfa66cSDaniel Fojt {
9870cbfa66cSDaniel Fojt 	FILE *f = NULL;
9880cbfa66cSDaniel Fojt 	char *line = NULL;
9890cbfa66cSDaniel Fojt 	size_t linesize = 0;
9900cbfa66cSDaniel Fojt 	u_long linenum = 0;
991*ba1276acSMatthew Dillon 	int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
9920cbfa66cSDaniel Fojt 
9930cbfa66cSDaniel Fojt 	/* Check key and principal against file */
9940cbfa66cSDaniel Fojt 	if ((f = fopen(path, "r")) == NULL) {
9950cbfa66cSDaniel Fojt 		oerrno = errno;
9960cbfa66cSDaniel Fojt 		error("Unable to open allowed keys file \"%s\": %s",
9970cbfa66cSDaniel Fojt 		    path, strerror(errno));
9980cbfa66cSDaniel Fojt 		errno = oerrno;
9990cbfa66cSDaniel Fojt 		return SSH_ERR_SYSTEM_ERROR;
10000cbfa66cSDaniel Fojt 	}
10010cbfa66cSDaniel Fojt 
10020cbfa66cSDaniel Fojt 	while (getline(&line, &linesize, f) != -1) {
10030cbfa66cSDaniel Fojt 		linenum++;
10040cbfa66cSDaniel Fojt 		r = check_allowed_keys_line(path, linenum, line, sign_key,
1005ee116499SAntonio Huete Jimenez 		    principal, sig_namespace, verify_time, NULL);
10060cbfa66cSDaniel Fojt 		free(line);
10070cbfa66cSDaniel Fojt 		line = NULL;
100850a69bb5SSascha Wildner 		linesize = 0;
10090cbfa66cSDaniel Fojt 		if (r == SSH_ERR_KEY_NOT_FOUND)
10100cbfa66cSDaniel Fojt 			continue;
10110cbfa66cSDaniel Fojt 		else if (r == 0) {
10120cbfa66cSDaniel Fojt 			/* success */
10130cbfa66cSDaniel Fojt 			fclose(f);
10140cbfa66cSDaniel Fojt 			return 0;
10150cbfa66cSDaniel Fojt 		} else
10160cbfa66cSDaniel Fojt 			break;
10170cbfa66cSDaniel Fojt 	}
10180cbfa66cSDaniel Fojt 	/* Either we hit an error parsing or we simply didn't find the key */
10190cbfa66cSDaniel Fojt 	fclose(f);
10200cbfa66cSDaniel Fojt 	free(line);
1021*ba1276acSMatthew Dillon 	return r;
10220cbfa66cSDaniel Fojt }
10230cbfa66cSDaniel Fojt 
10240cbfa66cSDaniel Fojt int
sshsig_find_principals(const char * path,const struct sshkey * sign_key,uint64_t verify_time,char ** principals)10250cbfa66cSDaniel Fojt sshsig_find_principals(const char *path, const struct sshkey *sign_key,
102650a69bb5SSascha Wildner     uint64_t verify_time, char **principals)
10270cbfa66cSDaniel Fojt {
10280cbfa66cSDaniel Fojt 	FILE *f = NULL;
10290cbfa66cSDaniel Fojt 	char *line = NULL;
10300cbfa66cSDaniel Fojt 	size_t linesize = 0;
10310cbfa66cSDaniel Fojt 	u_long linenum = 0;
1032*ba1276acSMatthew Dillon 	int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
10330cbfa66cSDaniel Fojt 
10340cbfa66cSDaniel Fojt 	if ((f = fopen(path, "r")) == NULL) {
10350cbfa66cSDaniel Fojt 		oerrno = errno;
10360cbfa66cSDaniel Fojt 		error("Unable to open allowed keys file \"%s\": %s",
10370cbfa66cSDaniel Fojt 		    path, strerror(errno));
10380cbfa66cSDaniel Fojt 		errno = oerrno;
10390cbfa66cSDaniel Fojt 		return SSH_ERR_SYSTEM_ERROR;
10400cbfa66cSDaniel Fojt 	}
10410cbfa66cSDaniel Fojt 
10420cbfa66cSDaniel Fojt 	while (getline(&line, &linesize, f) != -1) {
10430cbfa66cSDaniel Fojt 		linenum++;
1044ee116499SAntonio Huete Jimenez 		r = check_allowed_keys_line(path, linenum, line,
1045ee116499SAntonio Huete Jimenez 		    sign_key, NULL, NULL, verify_time, principals);
10460cbfa66cSDaniel Fojt 		free(line);
10470cbfa66cSDaniel Fojt 		line = NULL;
104850a69bb5SSascha Wildner 		linesize = 0;
10490cbfa66cSDaniel Fojt 		if (r == SSH_ERR_KEY_NOT_FOUND)
10500cbfa66cSDaniel Fojt 			continue;
10510cbfa66cSDaniel Fojt 		else if (r == 0) {
10520cbfa66cSDaniel Fojt 			/* success */
10530cbfa66cSDaniel Fojt 			fclose(f);
10540cbfa66cSDaniel Fojt 			return 0;
10550cbfa66cSDaniel Fojt 		} else
10560cbfa66cSDaniel Fojt 			break;
10570cbfa66cSDaniel Fojt 	}
10580cbfa66cSDaniel Fojt 	free(line);
10590cbfa66cSDaniel Fojt 	/* Either we hit an error parsing or we simply didn't find the key */
10600cbfa66cSDaniel Fojt 	if (ferror(f) != 0) {
10610cbfa66cSDaniel Fojt 		oerrno = errno;
10620cbfa66cSDaniel Fojt 		fclose(f);
10630cbfa66cSDaniel Fojt 		error("Unable to read allowed keys file \"%s\": %s",
10640cbfa66cSDaniel Fojt 		    path, strerror(errno));
10650cbfa66cSDaniel Fojt 		errno = oerrno;
10660cbfa66cSDaniel Fojt 		return SSH_ERR_SYSTEM_ERROR;
10670cbfa66cSDaniel Fojt 	}
10680cbfa66cSDaniel Fojt 	fclose(f);
1069*ba1276acSMatthew Dillon 	return r;
10700cbfa66cSDaniel Fojt }
10710cbfa66cSDaniel Fojt 
10720cbfa66cSDaniel Fojt int
sshsig_match_principals(const char * path,const char * principal,char *** principalsp,size_t * nprincipalsp)1073ee116499SAntonio Huete Jimenez sshsig_match_principals(const char *path, const char *principal,
1074ee116499SAntonio Huete Jimenez     char ***principalsp, size_t *nprincipalsp)
1075ee116499SAntonio Huete Jimenez {
1076ee116499SAntonio Huete Jimenez 	FILE *f = NULL;
1077ee116499SAntonio Huete Jimenez 	char *found, *line = NULL, **principals = NULL, **tmp;
1078ee116499SAntonio Huete Jimenez 	size_t i, nprincipals = 0, linesize = 0;
1079ee116499SAntonio Huete Jimenez 	u_long linenum = 0;
1080ee116499SAntonio Huete Jimenez 	int oerrno = 0, r, ret = 0;
1081ee116499SAntonio Huete Jimenez 
1082ee116499SAntonio Huete Jimenez 	if (principalsp != NULL)
1083ee116499SAntonio Huete Jimenez 		*principalsp = NULL;
1084ee116499SAntonio Huete Jimenez 	if (nprincipalsp != NULL)
1085ee116499SAntonio Huete Jimenez 		*nprincipalsp = 0;
1086ee116499SAntonio Huete Jimenez 
1087ee116499SAntonio Huete Jimenez 	/* Check key and principal against file */
1088ee116499SAntonio Huete Jimenez 	if ((f = fopen(path, "r")) == NULL) {
1089ee116499SAntonio Huete Jimenez 		oerrno = errno;
1090ee116499SAntonio Huete Jimenez 		error("Unable to open allowed keys file \"%s\": %s",
1091ee116499SAntonio Huete Jimenez 		    path, strerror(errno));
1092ee116499SAntonio Huete Jimenez 		errno = oerrno;
1093ee116499SAntonio Huete Jimenez 		return SSH_ERR_SYSTEM_ERROR;
1094ee116499SAntonio Huete Jimenez 	}
1095ee116499SAntonio Huete Jimenez 
1096ee116499SAntonio Huete Jimenez 	while (getline(&line, &linesize, f) != -1) {
1097ee116499SAntonio Huete Jimenez 		linenum++;
1098ee116499SAntonio Huete Jimenez 		/* Parse the line */
1099ee116499SAntonio Huete Jimenez 		if ((r = parse_principals_key_and_options(path, linenum, line,
1100ee116499SAntonio Huete Jimenez 		    principal, &found, NULL, NULL)) != 0) {
1101ee116499SAntonio Huete Jimenez 			if (r == SSH_ERR_KEY_NOT_FOUND)
1102ee116499SAntonio Huete Jimenez 				continue;
1103ee116499SAntonio Huete Jimenez 			ret = r;
1104ee116499SAntonio Huete Jimenez 			oerrno = errno;
1105ee116499SAntonio Huete Jimenez 			break; /* unexpected error */
1106ee116499SAntonio Huete Jimenez 		}
1107ee116499SAntonio Huete Jimenez 		if ((tmp = recallocarray(principals, nprincipals,
1108ee116499SAntonio Huete Jimenez 		    nprincipals + 1, sizeof(*principals))) == NULL) {
1109ee116499SAntonio Huete Jimenez 			ret = SSH_ERR_ALLOC_FAIL;
1110ee116499SAntonio Huete Jimenez 			free(found);
1111ee116499SAntonio Huete Jimenez 			break;
1112ee116499SAntonio Huete Jimenez 		}
1113ee116499SAntonio Huete Jimenez 		principals = tmp;
1114ee116499SAntonio Huete Jimenez 		principals[nprincipals++] = found; /* transferred */
1115ee116499SAntonio Huete Jimenez 		free(line);
1116ee116499SAntonio Huete Jimenez 		line = NULL;
1117ee116499SAntonio Huete Jimenez 		linesize = 0;
1118ee116499SAntonio Huete Jimenez 	}
1119ee116499SAntonio Huete Jimenez 	fclose(f);
1120ee116499SAntonio Huete Jimenez 
1121ee116499SAntonio Huete Jimenez 	if (ret == 0) {
1122ee116499SAntonio Huete Jimenez 		if (nprincipals == 0)
1123ee116499SAntonio Huete Jimenez 			ret = SSH_ERR_KEY_NOT_FOUND;
1124*ba1276acSMatthew Dillon 		if (nprincipalsp != 0)
1125*ba1276acSMatthew Dillon 			*nprincipalsp = nprincipals;
1126ee116499SAntonio Huete Jimenez 		if (principalsp != NULL) {
1127ee116499SAntonio Huete Jimenez 			*principalsp = principals;
1128ee116499SAntonio Huete Jimenez 			principals = NULL; /* transferred */
1129ee116499SAntonio Huete Jimenez 			nprincipals = 0;
1130ee116499SAntonio Huete Jimenez 		}
1131ee116499SAntonio Huete Jimenez 	}
1132ee116499SAntonio Huete Jimenez 
1133ee116499SAntonio Huete Jimenez 	for (i = 0; i < nprincipals; i++)
1134ee116499SAntonio Huete Jimenez 		free(principals[i]);
1135ee116499SAntonio Huete Jimenez 	free(principals);
1136ee116499SAntonio Huete Jimenez 
1137ee116499SAntonio Huete Jimenez 	errno = oerrno;
1138ee116499SAntonio Huete Jimenez 	return ret;
1139ee116499SAntonio Huete Jimenez }
1140ee116499SAntonio Huete Jimenez 
1141ee116499SAntonio Huete Jimenez int
sshsig_get_pubkey(struct sshbuf * signature,struct sshkey ** pubkey)11420cbfa66cSDaniel Fojt sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
11430cbfa66cSDaniel Fojt {
11440cbfa66cSDaniel Fojt 	struct sshkey *pk = NULL;
11450cbfa66cSDaniel Fojt 	int r = SSH_ERR_SIGNATURE_INVALID;
11460cbfa66cSDaniel Fojt 
11470cbfa66cSDaniel Fojt 	if (pubkey == NULL)
11480cbfa66cSDaniel Fojt 		return SSH_ERR_INTERNAL_ERROR;
11490cbfa66cSDaniel Fojt 	if ((r = sshsig_parse_preamble(signature)) != 0)
11500cbfa66cSDaniel Fojt 		return r;
11510cbfa66cSDaniel Fojt 	if ((r = sshkey_froms(signature, &pk)) != 0)
11520cbfa66cSDaniel Fojt 		return r;
11530cbfa66cSDaniel Fojt 
11540cbfa66cSDaniel Fojt 	*pubkey = pk;
11550cbfa66cSDaniel Fojt 	pk = NULL;
11560cbfa66cSDaniel Fojt 	return 0;
11570cbfa66cSDaniel Fojt }
1158