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