xref: /dragonfly/crypto/openssh/sshsig.c (revision ee116499)
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
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 
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
sshsig_dearmor(struct sshbuf * sig,struct sshbuf ** out)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
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)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
sshsig_parse_preamble(struct sshbuf * buf)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
sshsig_check_hashalg(const char * hashalg)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
sshsig_peek_hashalg(struct sshbuf * signature,char ** hashalgp)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
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)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
hash_buffer(const struct sshbuf * m,const char * hashalg,struct sshbuf ** bp)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
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)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
sshsig_verifyb(struct sshbuf * signature,const struct sshbuf * message,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)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
hash_file(int fd,const char * hashalg,struct sshbuf ** bp)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
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)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
sshsig_verify_fd(struct sshbuf * signature,int fd,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)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 *
sshsigopt_parse(const char * opts,const char * path,u_long linenum,const char ** errstrp)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
sshsigopt_free(struct sshsigopt * opts)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
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)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
cert_filter_principals(const char * path,u_long linenum,char ** principalsp,const struct sshkey * cert,uint64_t verify_time)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
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)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
sshsig_check_allowed_keys(const char * path,const struct sshkey * sign_key,const char * principal,const char * sig_namespace,uint64_t verify_time)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
sshsig_find_principals(const char * path,const struct sshkey * sign_key,uint64_t verify_time,char ** principals)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
sshsig_match_principals(const char * path,const char * principal,char *** principalsp,size_t * nprincipalsp)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
sshsig_get_pubkey(struct sshbuf * signature,struct sshkey ** pubkey)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