Home | History | Annotate | Line # | Download | only in dist
sshsig.c revision 1.14
      1   1.2  christos /*	$NetBSD: sshsig.c,v 1.14 2025/02/18 17:53:24 christos Exp $	*/
      2  1.13  christos /* $OpenBSD: sshsig.c,v 1.35 2024/03/08 22:16:32 djm Exp $ */
      3  1.10  christos 
      4   1.1  christos /*
      5   1.1  christos  * Copyright (c) 2019 Google LLC
      6   1.1  christos  *
      7   1.1  christos  * Permission to use, copy, modify, and distribute this software for any
      8   1.1  christos  * purpose with or without fee is hereby granted, provided that the above
      9   1.1  christos  * copyright notice and this permission notice appear in all copies.
     10   1.1  christos  *
     11   1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12   1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13   1.1  christos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14   1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15   1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16   1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17   1.1  christos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18   1.1  christos  */
     19   1.2  christos #include "includes.h"
     20   1.2  christos __RCSID("$NetBSD: sshsig.c,v 1.14 2025/02/18 17:53:24 christos Exp $");
     21   1.1  christos 
     22   1.1  christos #include <stdio.h>
     23   1.1  christos #include <stdlib.h>
     24   1.1  christos #include <stdarg.h>
     25   1.1  christos #include <errno.h>
     26   1.1  christos #include <string.h>
     27   1.1  christos #include <unistd.h>
     28   1.1  christos 
     29   1.1  christos #include "authfd.h"
     30   1.1  christos #include "authfile.h"
     31   1.1  christos #include "log.h"
     32   1.1  christos #include "misc.h"
     33   1.1  christos #include "sshbuf.h"
     34   1.1  christos #include "sshsig.h"
     35   1.1  christos #include "ssherr.h"
     36   1.1  christos #include "sshkey.h"
     37   1.1  christos #include "match.h"
     38   1.1  christos #include "digest.h"
     39   1.1  christos 
     40   1.1  christos #define SIG_VERSION		0x01
     41   1.1  christos #define MAGIC_PREAMBLE		"SSHSIG"
     42   1.1  christos #define MAGIC_PREAMBLE_LEN	(sizeof(MAGIC_PREAMBLE) - 1)
     43  1.11  christos #define BEGIN_SIGNATURE		"-----BEGIN SSH SIGNATURE-----"
     44   1.1  christos #define END_SIGNATURE		"-----END SSH SIGNATURE-----"
     45   1.1  christos #define RSA_SIGN_ALG		"rsa-sha2-512" /* XXX maybe make configurable */
     46   1.1  christos #define RSA_SIGN_ALLOWED	"rsa-sha2-512,rsa-sha2-256"
     47   1.1  christos #define HASHALG_DEFAULT		"sha512" /* XXX maybe make configurable */
     48   1.1  christos #define HASHALG_ALLOWED		"sha256,sha512"
     49   1.1  christos 
     50   1.1  christos int
     51   1.1  christos sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
     52   1.1  christos {
     53   1.1  christos 	struct sshbuf *buf = NULL;
     54   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
     55   1.1  christos 
     56   1.1  christos 	*out = NULL;
     57   1.1  christos 
     58   1.1  christos 	if ((buf = sshbuf_new()) == NULL) {
     59   1.6  christos 		error_f("sshbuf_new failed");
     60   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
     61   1.1  christos 		goto out;
     62   1.1  christos 	}
     63   1.1  christos 
     64  1.11  christos 	if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) {
     65   1.6  christos 		error_fr(r, "sshbuf_putf");
     66   1.1  christos 		goto out;
     67   1.1  christos 	}
     68   1.1  christos 
     69   1.1  christos 	if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
     70   1.6  christos 		error_fr(r, "base64 encode signature");
     71   1.1  christos 		goto out;
     72   1.1  christos 	}
     73   1.1  christos 
     74   1.1  christos 	if ((r = sshbuf_put(buf, END_SIGNATURE,
     75   1.1  christos 	    sizeof(END_SIGNATURE)-1)) != 0 ||
     76   1.1  christos 	    (r = sshbuf_put_u8(buf, '\n')) != 0) {
     77   1.6  christos 		error_fr(r, "sshbuf_put");
     78   1.1  christos 		goto out;
     79   1.1  christos 	}
     80   1.1  christos 	/* success */
     81   1.1  christos 	*out = buf;
     82   1.1  christos 	buf = NULL; /* transferred */
     83   1.1  christos 	r = 0;
     84   1.1  christos  out:
     85   1.1  christos 	sshbuf_free(buf);
     86   1.1  christos 	return r;
     87   1.1  christos }
     88   1.1  christos 
     89   1.1  christos int
     90   1.1  christos sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
     91   1.1  christos {
     92   1.1  christos 	int r;
     93   1.1  christos 	size_t eoffset = 0;
     94   1.1  christos 	struct sshbuf *buf = NULL;
     95   1.1  christos 	struct sshbuf *sbuf = NULL;
     96   1.1  christos 	char *b64 = NULL;
     97   1.1  christos 
     98   1.1  christos 	if ((sbuf = sshbuf_fromb(sig)) == NULL) {
     99   1.6  christos 		error_f("sshbuf_fromb failed");
    100   1.1  christos 		return SSH_ERR_ALLOC_FAIL;
    101   1.1  christos 	}
    102   1.1  christos 
    103  1.11  christos 	/* Expect and consume preamble + lf/crlf */
    104   1.1  christos 	if ((r = sshbuf_cmp(sbuf, 0,
    105   1.1  christos 	    BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
    106   1.1  christos 		error("Couldn't parse signature: missing header");
    107   1.1  christos 		goto done;
    108   1.1  christos 	}
    109   1.1  christos 	if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
    110   1.6  christos 		error_fr(r, "consume");
    111   1.1  christos 		goto done;
    112   1.1  christos 	}
    113  1.11  christos 	if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0)
    114  1.11  christos 		eoffset = 2;
    115  1.11  christos 	else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0)
    116  1.11  christos 		eoffset = 1;
    117  1.11  christos 	else {
    118  1.11  christos 		r = SSH_ERR_INVALID_FORMAT;
    119  1.11  christos 		error_f("no header eol");
    120  1.11  christos 		goto done;
    121  1.11  christos 	}
    122  1.11  christos 	if ((r = sshbuf_consume(sbuf, eoffset)) != 0) {
    123  1.11  christos 		error_fr(r, "consume eol");
    124  1.11  christos 		goto done;
    125  1.11  christos 	}
    126  1.11  christos 	/* Find and consume lf + suffix (any prior cr would be ignored) */
    127   1.1  christos 	if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
    128  1.11  christos 	    sizeof(END_SIGNATURE), &eoffset)) != 0) {
    129   1.1  christos 		error("Couldn't parse signature: missing footer");
    130   1.1  christos 		goto done;
    131   1.1  christos 	}
    132   1.1  christos 	if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
    133   1.6  christos 		error_fr(r, "consume");
    134   1.1  christos 		goto done;
    135   1.1  christos 	}
    136   1.1  christos 
    137   1.1  christos 	if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
    138   1.6  christos 		error_f("sshbuf_dup_string failed");
    139   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
    140   1.1  christos 		goto done;
    141   1.1  christos 	}
    142   1.1  christos 
    143   1.1  christos 	if ((buf = sshbuf_new()) == NULL) {
    144   1.6  christos 		error_f("sshbuf_new() failed");
    145   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
    146   1.1  christos 		goto done;
    147   1.1  christos 	}
    148   1.1  christos 
    149   1.1  christos 	if ((r = sshbuf_b64tod(buf, b64)) != 0) {
    150   1.6  christos 		error_fr(r, "decode base64");
    151   1.1  christos 		goto done;
    152   1.1  christos 	}
    153   1.1  christos 
    154   1.1  christos 	/* success */
    155   1.1  christos 	*out = buf;
    156   1.1  christos 	r = 0;
    157   1.1  christos 	buf = NULL; /* transferred */
    158   1.1  christos done:
    159   1.1  christos 	sshbuf_free(buf);
    160   1.1  christos 	sshbuf_free(sbuf);
    161   1.1  christos 	free(b64);
    162   1.1  christos 	return r;
    163   1.1  christos }
    164   1.1  christos 
    165   1.1  christos static int
    166   1.1  christos sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
    167   1.5  christos     const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
    168   1.3  christos     const char *sig_namespace, struct sshbuf **out,
    169   1.3  christos     sshsig_signer *signer, void *signer_ctx)
    170   1.1  christos {
    171   1.1  christos 	int r;
    172   1.1  christos 	size_t slen = 0;
    173   1.1  christos 	u_char *sig = NULL;
    174   1.1  christos 	struct sshbuf *blob = NULL;
    175   1.1  christos 	struct sshbuf *tosign = NULL;
    176   1.1  christos 	const char *sign_alg = NULL;
    177   1.1  christos 
    178   1.1  christos 	if ((tosign = sshbuf_new()) == NULL ||
    179   1.1  christos 	    (blob = sshbuf_new()) == NULL) {
    180   1.6  christos 		error_f("sshbuf_new failed");
    181   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
    182   1.1  christos 		goto done;
    183   1.1  christos 	}
    184   1.1  christos 
    185   1.1  christos 	if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
    186   1.1  christos 	    (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
    187   1.1  christos 	    (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
    188   1.1  christos 	    (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
    189   1.1  christos 	    (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
    190   1.6  christos 		error_fr(r, "assemble message to sign");
    191   1.1  christos 		goto done;
    192   1.1  christos 	}
    193   1.1  christos 
    194   1.1  christos 	/* If using RSA keys then default to a good signature algorithm */
    195   1.1  christos 	if (sshkey_type_plain(key->type) == KEY_RSA)
    196   1.1  christos 		sign_alg = RSA_SIGN_ALG;
    197   1.1  christos 
    198   1.1  christos 	if (signer != NULL) {
    199   1.1  christos 		if ((r = signer(key, &sig, &slen,
    200   1.1  christos 		    sshbuf_ptr(tosign), sshbuf_len(tosign),
    201   1.5  christos 		    sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
    202   1.6  christos 			error_r(r, "Couldn't sign message (signer)");
    203   1.1  christos 			goto done;
    204   1.1  christos 		}
    205   1.1  christos 	} else {
    206   1.1  christos 		if ((r = sshkey_sign(key, &sig, &slen,
    207   1.1  christos 		    sshbuf_ptr(tosign), sshbuf_len(tosign),
    208   1.5  christos 		    sign_alg, sk_provider, sk_pin, 0)) != 0) {
    209   1.6  christos 			error_r(r, "Couldn't sign message");
    210   1.1  christos 			goto done;
    211   1.1  christos 		}
    212   1.1  christos 	}
    213   1.1  christos 
    214   1.1  christos 	if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
    215   1.1  christos 	    (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
    216   1.1  christos 	    (r = sshkey_puts(key, blob)) != 0 ||
    217   1.1  christos 	    (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
    218   1.1  christos 	    (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
    219   1.1  christos 	    (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
    220   1.1  christos 	    (r = sshbuf_put_string(blob, sig, slen)) != 0) {
    221   1.6  christos 		error_fr(r, "assemble signature object");
    222   1.1  christos 		goto done;
    223   1.1  christos 	}
    224   1.1  christos 
    225   1.4  christos 	if (out != NULL) {
    226   1.4  christos 		*out = blob;
    227   1.4  christos 		blob = NULL;
    228   1.4  christos 	}
    229   1.1  christos 	r = 0;
    230   1.1  christos done:
    231   1.1  christos 	free(sig);
    232   1.1  christos 	sshbuf_free(blob);
    233   1.1  christos 	sshbuf_free(tosign);
    234   1.1  christos 	return r;
    235   1.1  christos }
    236   1.1  christos 
    237   1.1  christos /* Check preamble and version. */
    238   1.1  christos static int
    239   1.1  christos sshsig_parse_preamble(struct sshbuf *buf)
    240   1.1  christos {
    241   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    242   1.1  christos 	uint32_t sversion;
    243   1.1  christos 
    244   1.1  christos 	if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
    245   1.1  christos 	    (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
    246   1.1  christos 	    (r = sshbuf_get_u32(buf, &sversion)) != 0) {
    247   1.1  christos 		error("Couldn't verify signature: invalid format");
    248   1.1  christos 		return r;
    249   1.1  christos 	}
    250   1.1  christos 
    251   1.1  christos 	if (sversion > SIG_VERSION) {
    252   1.1  christos 		error("Signature version %lu is larger than supported "
    253   1.1  christos 		    "version %u", (unsigned long)sversion, SIG_VERSION);
    254   1.1  christos 		return SSH_ERR_INVALID_FORMAT;
    255   1.1  christos 	}
    256   1.1  christos 	return 0;
    257   1.1  christos }
    258   1.1  christos 
    259   1.1  christos static int
    260   1.1  christos sshsig_check_hashalg(const char *hashalg)
    261   1.1  christos {
    262   1.1  christos 	if (hashalg == NULL ||
    263   1.1  christos 	    match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
    264   1.1  christos 		return 0;
    265   1.6  christos 	error_f("unsupported hash algorithm \"%.100s\"", hashalg);
    266   1.1  christos 	return SSH_ERR_SIGN_ALG_UNSUPPORTED;
    267   1.1  christos }
    268   1.1  christos 
    269   1.1  christos static int
    270   1.1  christos sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
    271   1.1  christos {
    272   1.1  christos 	struct sshbuf *buf = NULL;
    273   1.1  christos 	char *hashalg = NULL;
    274   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    275   1.1  christos 
    276   1.1  christos 	if (hashalgp != NULL)
    277   1.1  christos 		*hashalgp = NULL;
    278   1.1  christos 	if ((buf = sshbuf_fromb(signature)) == NULL)
    279   1.1  christos 		return SSH_ERR_ALLOC_FAIL;
    280   1.1  christos 	if ((r = sshsig_parse_preamble(buf)) != 0)
    281   1.1  christos 		goto done;
    282   1.1  christos 	if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
    283   1.1  christos 	    (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
    284   1.1  christos 	    (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
    285   1.1  christos 	    (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
    286   1.1  christos 	    (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
    287   1.6  christos 		error_fr(r, "parse signature object");
    288   1.1  christos 		goto done;
    289   1.1  christos 	}
    290   1.1  christos 
    291   1.1  christos 	/* success */
    292   1.1  christos 	r = 0;
    293   1.1  christos 	*hashalgp = hashalg;
    294   1.1  christos 	hashalg = NULL;
    295   1.1  christos  done:
    296   1.1  christos 	free(hashalg);
    297   1.1  christos 	sshbuf_free(buf);
    298   1.1  christos 	return r;
    299   1.1  christos }
    300   1.1  christos 
    301   1.1  christos static int
    302   1.1  christos sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
    303   1.1  christos     const struct sshbuf *h_message, const char *expect_namespace,
    304   1.3  christos     struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
    305   1.1  christos {
    306   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    307   1.1  christos 	struct sshbuf *buf = NULL, *toverify = NULL;
    308   1.1  christos 	struct sshkey *key = NULL;
    309   1.1  christos 	const u_char *sig;
    310   1.1  christos 	char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
    311   1.1  christos 	size_t siglen;
    312   1.1  christos 
    313   1.6  christos 	debug_f("verify message length %zu", sshbuf_len(h_message));
    314   1.3  christos 	if (sig_details != NULL)
    315   1.3  christos 		*sig_details = NULL;
    316   1.1  christos 	if (sign_keyp != NULL)
    317   1.1  christos 		*sign_keyp = NULL;
    318   1.1  christos 
    319   1.1  christos 	if ((toverify = sshbuf_new()) == NULL) {
    320   1.6  christos 		error_f("sshbuf_new failed");
    321   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
    322   1.1  christos 		goto done;
    323   1.1  christos 	}
    324   1.1  christos 	if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
    325   1.1  christos 	    MAGIC_PREAMBLE_LEN)) != 0 ||
    326   1.1  christos 	    (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
    327   1.1  christos 	    (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
    328   1.1  christos 	    (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
    329   1.1  christos 	    (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
    330   1.6  christos 		error_fr(r, "assemble message to verify");
    331   1.1  christos 		goto done;
    332   1.1  christos 	}
    333   1.1  christos 
    334   1.1  christos 	if ((r = sshsig_parse_preamble(signature)) != 0)
    335   1.1  christos 		goto done;
    336   1.1  christos 
    337   1.1  christos 	if ((r = sshkey_froms(signature, &key)) != 0 ||
    338   1.1  christos 	    (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
    339   1.1  christos 	    (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
    340   1.1  christos 	    (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
    341   1.1  christos 	    (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
    342   1.6  christos 		error_fr(r, "parse signature object");
    343   1.1  christos 		goto done;
    344   1.1  christos 	}
    345   1.1  christos 
    346   1.1  christos 	if (sshbuf_len(signature) != 0) {
    347   1.1  christos 		error("Signature contains trailing data");
    348   1.1  christos 		r = SSH_ERR_INVALID_FORMAT;
    349   1.1  christos 		goto done;
    350   1.1  christos 	}
    351   1.1  christos 
    352   1.1  christos 	if (strcmp(expect_namespace, got_namespace) != 0) {
    353   1.1  christos 		error("Couldn't verify signature: namespace does not match");
    354   1.6  christos 		debug_f("expected namespace \"%s\" received \"%s\"",
    355   1.6  christos 		    expect_namespace, got_namespace);
    356   1.1  christos 		r = SSH_ERR_SIGNATURE_INVALID;
    357   1.1  christos 		goto done;
    358   1.1  christos 	}
    359   1.1  christos 	if (strcmp(hashalg, sig_hashalg) != 0) {
    360   1.1  christos 		error("Couldn't verify signature: hash algorithm mismatch");
    361   1.6  christos 		debug_f("expected algorithm \"%s\" received \"%s\"",
    362   1.6  christos 		    hashalg, sig_hashalg);
    363   1.1  christos 		r = SSH_ERR_SIGNATURE_INVALID;
    364   1.1  christos 		goto done;
    365   1.1  christos 	}
    366   1.1  christos 	/* Ensure that RSA keys use an acceptable signature algorithm */
    367   1.1  christos 	if (sshkey_type_plain(key->type) == KEY_RSA) {
    368   1.1  christos 		if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
    369   1.6  christos 			error_r(r, "Couldn't verify signature: unable to get "
    370   1.6  christos 			    "signature type");
    371   1.1  christos 			goto done;
    372   1.1  christos 		}
    373   1.1  christos 		if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
    374   1.1  christos 			error("Couldn't verify signature: unsupported RSA "
    375   1.1  christos 			    "signature algorithm %s", sigtype);
    376   1.1  christos 			r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
    377   1.1  christos 			goto done;
    378   1.1  christos 		}
    379   1.1  christos 	}
    380   1.1  christos 	if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
    381   1.3  christos 	    sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
    382   1.6  christos 		error_r(r, "Signature verification failed");
    383   1.1  christos 		goto done;
    384   1.1  christos 	}
    385   1.1  christos 
    386   1.1  christos 	/* success */
    387   1.1  christos 	r = 0;
    388   1.1  christos 	if (sign_keyp != NULL) {
    389   1.1  christos 		*sign_keyp = key;
    390   1.1  christos 		key = NULL; /* transferred */
    391   1.1  christos 	}
    392   1.1  christos done:
    393   1.1  christos 	free(got_namespace);
    394   1.1  christos 	free(sigtype);
    395   1.1  christos 	free(sig_hashalg);
    396   1.1  christos 	sshbuf_free(buf);
    397   1.1  christos 	sshbuf_free(toverify);
    398   1.1  christos 	sshkey_free(key);
    399   1.1  christos 	return r;
    400   1.1  christos }
    401   1.1  christos 
    402   1.1  christos static int
    403   1.1  christos hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
    404   1.1  christos {
    405   1.1  christos 	char *hex, hash[SSH_DIGEST_MAX_LENGTH];
    406   1.1  christos 	int alg, r = SSH_ERR_INTERNAL_ERROR;
    407   1.1  christos 	struct sshbuf *b = NULL;
    408   1.1  christos 
    409   1.1  christos 	*bp = NULL;
    410   1.1  christos 	memset(hash, 0, sizeof(hash));
    411   1.1  christos 
    412   1.1  christos 	if ((r = sshsig_check_hashalg(hashalg)) != 0)
    413   1.1  christos 		return r;
    414   1.1  christos 	if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
    415   1.6  christos 		error_f("can't look up hash algorithm %s", hashalg);
    416   1.1  christos 		return SSH_ERR_INTERNAL_ERROR;
    417   1.1  christos 	}
    418   1.2  christos 	if ((r = ssh_digest_buffer(alg, m, (unsigned char *)hash, sizeof(hash))) != 0) {
    419   1.6  christos 		error_fr(r, "ssh_digest_buffer");
    420   1.1  christos 		return r;
    421   1.1  christos 	}
    422   1.1  christos 	if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
    423   1.6  christos 		debug3_f("final hash: %s", hex);
    424   1.1  christos 		freezero(hex, strlen(hex));
    425   1.1  christos 	}
    426   1.1  christos 	if ((b = sshbuf_new()) == NULL) {
    427   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
    428   1.1  christos 		goto out;
    429   1.1  christos 	}
    430   1.1  christos 	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
    431   1.6  christos 		error_fr(r, "sshbuf_put");
    432   1.1  christos 		goto out;
    433   1.1  christos 	}
    434   1.1  christos 	*bp = b;
    435   1.1  christos 	b = NULL; /* transferred */
    436   1.1  christos 	/* success */
    437   1.1  christos 	r = 0;
    438   1.1  christos  out:
    439   1.1  christos 	sshbuf_free(b);
    440   1.1  christos 	explicit_bzero(hash, sizeof(hash));
    441   1.4  christos 	return r;
    442   1.1  christos }
    443   1.1  christos 
    444   1.1  christos int
    445   1.5  christos sshsig_signb(struct sshkey *key, const char *hashalg,
    446   1.5  christos     const char *sk_provider, const char *sk_pin,
    447   1.1  christos     const struct sshbuf *message, const char *sig_namespace,
    448   1.1  christos     struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
    449   1.1  christos {
    450   1.1  christos 	struct sshbuf *b = NULL;
    451   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    452   1.1  christos 
    453   1.1  christos 	if (hashalg == NULL)
    454   1.1  christos 		hashalg = HASHALG_DEFAULT;
    455   1.1  christos 	if (out != NULL)
    456   1.1  christos 		*out = NULL;
    457   1.1  christos 	if ((r = hash_buffer(message, hashalg, &b)) != 0) {
    458   1.6  christos 		error_fr(r, "hash buffer");
    459   1.1  christos 		goto out;
    460   1.1  christos 	}
    461   1.5  christos 	if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
    462   1.3  christos 	    sig_namespace, out, signer, signer_ctx)) != 0)
    463   1.1  christos 		goto out;
    464   1.1  christos 	/* success */
    465   1.1  christos 	r = 0;
    466   1.1  christos  out:
    467   1.1  christos 	sshbuf_free(b);
    468   1.1  christos 	return r;
    469   1.1  christos }
    470   1.1  christos 
    471   1.1  christos int
    472   1.1  christos sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
    473   1.3  christos     const char *expect_namespace, struct sshkey **sign_keyp,
    474   1.3  christos     struct sshkey_sig_details **sig_details)
    475   1.1  christos {
    476   1.1  christos 	struct sshbuf *b = NULL;
    477   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    478   1.1  christos 	char *hashalg = NULL;
    479   1.1  christos 
    480   1.3  christos 	if (sig_details != NULL)
    481   1.3  christos 		*sig_details = NULL;
    482   1.1  christos 	if (sign_keyp != NULL)
    483   1.1  christos 		*sign_keyp = NULL;
    484   1.1  christos 	if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
    485   1.1  christos 		return r;
    486   1.6  christos 	debug_f("signature made with hash \"%s\"", hashalg);
    487   1.1  christos 	if ((r = hash_buffer(message, hashalg, &b)) != 0) {
    488   1.6  christos 		error_fr(r, "hash buffer");
    489   1.1  christos 		goto out;
    490   1.1  christos 	}
    491   1.1  christos 	if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
    492   1.3  christos 	    sign_keyp, sig_details)) != 0)
    493   1.1  christos 		goto out;
    494   1.1  christos 	/* success */
    495   1.1  christos 	r = 0;
    496   1.1  christos  out:
    497   1.1  christos 	sshbuf_free(b);
    498   1.1  christos 	free(hashalg);
    499   1.1  christos 	return r;
    500   1.1  christos }
    501   1.1  christos 
    502   1.1  christos static int
    503   1.1  christos hash_file(int fd, const char *hashalg, struct sshbuf **bp)
    504   1.1  christos {
    505   1.1  christos 	char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
    506   1.1  christos 	ssize_t n, total = 0;
    507  1.10  christos 	struct ssh_digest_ctx *ctx = NULL;
    508   1.1  christos 	int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
    509   1.1  christos 	struct sshbuf *b = NULL;
    510   1.1  christos 
    511   1.1  christos 	*bp = NULL;
    512   1.1  christos 	memset(hash, 0, sizeof(hash));
    513   1.1  christos 
    514   1.1  christos 	if ((r = sshsig_check_hashalg(hashalg)) != 0)
    515   1.1  christos 		return r;
    516   1.1  christos 	if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
    517   1.6  christos 		error_f("can't look up hash algorithm %s", hashalg);
    518   1.1  christos 		return SSH_ERR_INTERNAL_ERROR;
    519   1.1  christos 	}
    520   1.1  christos 	if ((ctx = ssh_digest_start(alg)) == NULL) {
    521   1.6  christos 		error_f("ssh_digest_start failed");
    522   1.1  christos 		return SSH_ERR_INTERNAL_ERROR;
    523   1.1  christos 	}
    524   1.1  christos 	for (;;) {
    525   1.1  christos 		if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
    526   1.1  christos 			if (errno == EINTR || errno == EAGAIN)
    527   1.1  christos 				continue;
    528   1.1  christos 			oerrno = errno;
    529   1.6  christos 			error_f("read: %s", strerror(errno));
    530   1.1  christos 			errno = oerrno;
    531   1.1  christos 			r = SSH_ERR_SYSTEM_ERROR;
    532   1.1  christos 			goto out;
    533   1.1  christos 		} else if (n == 0) {
    534   1.6  christos 			debug2_f("hashed %zu bytes", total);
    535   1.1  christos 			break; /* EOF */
    536   1.1  christos 		}
    537   1.1  christos 		total += (size_t)n;
    538   1.1  christos 		if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
    539   1.6  christos 			error_fr(r, "ssh_digest_update");
    540   1.1  christos 			goto out;
    541   1.1  christos 		}
    542   1.1  christos 	}
    543   1.2  christos 	if ((r = ssh_digest_final(ctx, (unsigned char *)hash, sizeof(hash))) != 0) {
    544   1.6  christos 		error_fr(r, "ssh_digest_final");
    545   1.1  christos 		goto out;
    546   1.1  christos 	}
    547   1.1  christos 	if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
    548   1.6  christos 		debug3_f("final hash: %s", hex);
    549   1.1  christos 		freezero(hex, strlen(hex));
    550   1.1  christos 	}
    551   1.1  christos 	if ((b = sshbuf_new()) == NULL) {
    552   1.1  christos 		r = SSH_ERR_ALLOC_FAIL;
    553   1.1  christos 		goto out;
    554   1.1  christos 	}
    555   1.1  christos 	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
    556   1.6  christos 		error_fr(r, "sshbuf_put");
    557   1.1  christos 		goto out;
    558   1.1  christos 	}
    559   1.1  christos 	*bp = b;
    560   1.1  christos 	b = NULL; /* transferred */
    561   1.1  christos 	/* success */
    562   1.1  christos 	r = 0;
    563   1.1  christos  out:
    564  1.10  christos 	oerrno = errno;
    565   1.1  christos 	sshbuf_free(b);
    566   1.1  christos 	ssh_digest_free(ctx);
    567   1.1  christos 	explicit_bzero(hash, sizeof(hash));
    568  1.10  christos 	errno = oerrno;
    569   1.4  christos 	return r;
    570   1.1  christos }
    571   1.1  christos 
    572   1.1  christos int
    573   1.5  christos sshsig_sign_fd(struct sshkey *key, const char *hashalg,
    574   1.5  christos     const char *sk_provider, const char *sk_pin,
    575   1.1  christos     int fd, const char *sig_namespace, struct sshbuf **out,
    576   1.1  christos     sshsig_signer *signer, void *signer_ctx)
    577   1.1  christos {
    578   1.1  christos 	struct sshbuf *b = NULL;
    579   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    580   1.1  christos 
    581   1.1  christos 	if (hashalg == NULL)
    582   1.1  christos 		hashalg = HASHALG_DEFAULT;
    583   1.1  christos 	if (out != NULL)
    584   1.1  christos 		*out = NULL;
    585   1.1  christos 	if ((r = hash_file(fd, hashalg, &b)) != 0) {
    586   1.6  christos 		error_fr(r, "hash_file");
    587   1.1  christos 		return r;
    588   1.1  christos 	}
    589   1.5  christos 	if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
    590   1.3  christos 	    sig_namespace, out, signer, signer_ctx)) != 0)
    591   1.1  christos 		goto out;
    592   1.1  christos 	/* success */
    593   1.1  christos 	r = 0;
    594   1.1  christos  out:
    595   1.1  christos 	sshbuf_free(b);
    596   1.1  christos 	return r;
    597   1.1  christos }
    598   1.1  christos 
    599   1.1  christos int
    600   1.1  christos sshsig_verify_fd(struct sshbuf *signature, int fd,
    601   1.3  christos     const char *expect_namespace, struct sshkey **sign_keyp,
    602   1.3  christos     struct sshkey_sig_details **sig_details)
    603   1.1  christos {
    604   1.1  christos 	struct sshbuf *b = NULL;
    605   1.1  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    606   1.1  christos 	char *hashalg = NULL;
    607   1.1  christos 
    608   1.3  christos 	if (sig_details != NULL)
    609   1.3  christos 		*sig_details = NULL;
    610   1.1  christos 	if (sign_keyp != NULL)
    611   1.1  christos 		*sign_keyp = NULL;
    612   1.1  christos 	if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
    613   1.1  christos 		return r;
    614   1.6  christos 	debug_f("signature made with hash \"%s\"", hashalg);
    615   1.1  christos 	if ((r = hash_file(fd, hashalg, &b)) != 0) {
    616   1.6  christos 		error_fr(r, "hash_file");
    617   1.1  christos 		goto out;
    618   1.1  christos 	}
    619   1.1  christos 	if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
    620   1.3  christos 	    sign_keyp, sig_details)) != 0)
    621   1.1  christos 		goto out;
    622   1.1  christos 	/* success */
    623   1.1  christos 	r = 0;
    624   1.1  christos  out:
    625   1.1  christos 	sshbuf_free(b);
    626   1.1  christos 	free(hashalg);
    627   1.1  christos 	return r;
    628   1.1  christos }
    629   1.1  christos 
    630   1.1  christos struct sshsigopt {
    631   1.1  christos 	int ca;
    632   1.1  christos 	char *namespaces;
    633   1.7  christos 	uint64_t valid_after, valid_before;
    634   1.1  christos };
    635   1.1  christos 
    636   1.1  christos struct sshsigopt *
    637   1.1  christos sshsigopt_parse(const char *opts, const char *path, u_long linenum,
    638   1.1  christos     const char **errstrp)
    639   1.1  christos {
    640   1.1  christos 	struct sshsigopt *ret;
    641   1.1  christos 	int r;
    642   1.7  christos 	char *opt;
    643   1.1  christos 	const char *errstr = NULL;
    644   1.1  christos 
    645   1.1  christos 	if ((ret = calloc(1, sizeof(*ret))) == NULL)
    646   1.1  christos 		return NULL;
    647   1.1  christos 	if (opts == NULL || *opts == '\0')
    648   1.1  christos 		return ret; /* Empty options yields empty options :) */
    649   1.1  christos 
    650   1.1  christos 	while (*opts && *opts != ' ' && *opts != '\t') {
    651   1.1  christos 		/* flag options */
    652   1.1  christos 		if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
    653   1.1  christos 			ret->ca = 1;
    654   1.1  christos 		} else if (opt_match(&opts, "namespaces")) {
    655   1.1  christos 			if (ret->namespaces != NULL) {
    656   1.1  christos 				errstr = "multiple \"namespaces\" clauses";
    657   1.1  christos 				goto fail;
    658   1.1  christos 			}
    659   1.1  christos 			ret->namespaces = opt_dequote(&opts, &errstr);
    660   1.1  christos 			if (ret->namespaces == NULL)
    661   1.1  christos 				goto fail;
    662   1.7  christos 		} else if (opt_match(&opts, "valid-after")) {
    663   1.7  christos 			if (ret->valid_after != 0) {
    664   1.7  christos 				errstr = "multiple \"valid-after\" clauses";
    665   1.7  christos 				goto fail;
    666   1.7  christos 			}
    667   1.7  christos 			if ((opt = opt_dequote(&opts, &errstr)) == NULL)
    668   1.7  christos 				goto fail;
    669   1.7  christos 			if (parse_absolute_time(opt, &ret->valid_after) != 0 ||
    670   1.7  christos 			    ret->valid_after == 0) {
    671   1.7  christos 				free(opt);
    672   1.7  christos 				errstr = "invalid \"valid-after\" time";
    673   1.7  christos 				goto fail;
    674   1.7  christos 			}
    675   1.7  christos 			free(opt);
    676   1.7  christos 		} else if (opt_match(&opts, "valid-before")) {
    677   1.7  christos 			if (ret->valid_before != 0) {
    678   1.7  christos 				errstr = "multiple \"valid-before\" clauses";
    679   1.7  christos 				goto fail;
    680   1.7  christos 			}
    681   1.7  christos 			if ((opt = opt_dequote(&opts, &errstr)) == NULL)
    682   1.7  christos 				goto fail;
    683   1.7  christos 			if (parse_absolute_time(opt, &ret->valid_before) != 0 ||
    684   1.7  christos 			    ret->valid_before == 0) {
    685   1.7  christos 				free(opt);
    686   1.7  christos 				errstr = "invalid \"valid-before\" time";
    687   1.7  christos 				goto fail;
    688   1.7  christos 			}
    689   1.7  christos 			free(opt);
    690   1.1  christos 		}
    691   1.1  christos 		/*
    692   1.1  christos 		 * Skip the comma, and move to the next option
    693   1.1  christos 		 * (or break out if there are no more).
    694   1.1  christos 		 */
    695   1.1  christos 		if (*opts == '\0' || *opts == ' ' || *opts == '\t')
    696   1.1  christos 			break;		/* End of options. */
    697   1.1  christos 		/* Anything other than a comma is an unknown option */
    698   1.1  christos 		if (*opts != ',') {
    699   1.1  christos 			errstr = "unknown key option";
    700   1.1  christos 			goto fail;
    701   1.1  christos 		}
    702   1.1  christos 		opts++;
    703   1.1  christos 		if (*opts == '\0') {
    704   1.1  christos 			errstr = "unexpected end-of-options";
    705   1.1  christos 			goto fail;
    706   1.1  christos 		}
    707   1.1  christos 	}
    708   1.7  christos 	/* final consistency check */
    709   1.7  christos 	if (ret->valid_after != 0 && ret->valid_before != 0 &&
    710   1.7  christos 	    ret->valid_before <= ret->valid_after) {
    711   1.7  christos 		errstr = "\"valid-before\" time is before \"valid-after\"";
    712   1.7  christos 		goto fail;
    713   1.7  christos 	}
    714   1.1  christos 	/* success */
    715   1.1  christos 	return ret;
    716   1.1  christos  fail:
    717   1.1  christos 	if (errstrp != NULL)
    718   1.1  christos 		*errstrp = errstr;
    719   1.1  christos 	sshsigopt_free(ret);
    720   1.1  christos 	return NULL;
    721   1.1  christos }
    722   1.1  christos 
    723   1.1  christos void
    724   1.1  christos sshsigopt_free(struct sshsigopt *opts)
    725   1.1  christos {
    726   1.1  christos 	if (opts == NULL)
    727   1.1  christos 		return;
    728   1.1  christos 	free(opts->namespaces);
    729   1.1  christos 	free(opts);
    730   1.1  christos }
    731   1.1  christos 
    732   1.1  christos static int
    733   1.3  christos parse_principals_key_and_options(const char *path, u_long linenum, char *line,
    734   1.3  christos     const char *required_principal, char **principalsp, struct sshkey **keyp,
    735   1.3  christos     struct sshsigopt **sigoptsp)
    736   1.1  christos {
    737   1.3  christos 	char *opts = NULL, *tmp, *cp, *principals = NULL;
    738   1.1  christos 	const char *reason = NULL;
    739   1.1  christos 	struct sshsigopt *sigopts = NULL;
    740   1.3  christos 	struct sshkey *key = NULL;
    741   1.3  christos 	int r = SSH_ERR_INTERNAL_ERROR;
    742   1.1  christos 
    743   1.3  christos 	if (principalsp != NULL)
    744   1.3  christos 		*principalsp = NULL;
    745   1.3  christos 	if (sigoptsp != NULL)
    746   1.3  christos 		*sigoptsp = NULL;
    747   1.3  christos 	if (keyp != NULL)
    748   1.3  christos 		*keyp = NULL;
    749   1.1  christos 
    750   1.1  christos 	cp = line;
    751  1.13  christos 	cp = cp + strspn(cp, " \t\n\r"); /* skip leading whitespace */
    752   1.1  christos 	if (*cp == '#' || *cp == '\0')
    753   1.3  christos 		return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
    754   1.3  christos 
    755   1.3  christos 	/* format: identity[,identity...] [option[,option...]] key */
    756   1.9  christos 	if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) {
    757   1.1  christos 		error("%s:%lu: invalid line", path, linenum);
    758   1.3  christos 		r = SSH_ERR_INVALID_FORMAT;
    759   1.3  christos 		goto out;
    760   1.3  christos 	}
    761   1.3  christos 	if ((principals = strdup(tmp)) == NULL) {
    762   1.6  christos 		error_f("strdup failed");
    763   1.3  christos 		r = SSH_ERR_ALLOC_FAIL;
    764   1.3  christos 		goto out;
    765   1.1  christos 	}
    766   1.3  christos 	/*
    767   1.3  christos 	 * Bail out early if we're looking for a particular principal and this
    768   1.3  christos 	 * line does not list it.
    769   1.3  christos 	 */
    770   1.3  christos 	if (required_principal != NULL) {
    771   1.3  christos 		if (match_pattern_list(required_principal,
    772   1.3  christos 		    principals, 0) != 1) {
    773   1.3  christos 			/* principal didn't match */
    774   1.3  christos 			r = SSH_ERR_KEY_NOT_FOUND;
    775   1.3  christos 			goto out;
    776   1.3  christos 		}
    777   1.6  christos 		debug_f("%s:%lu: matched principal \"%s\"",
    778   1.6  christos 		    path, linenum, required_principal);
    779   1.1  christos 	}
    780   1.1  christos 
    781   1.3  christos 	if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
    782   1.6  christos 		error_f("sshkey_new failed");
    783   1.3  christos 		r = SSH_ERR_ALLOC_FAIL;
    784   1.3  christos 		goto out;
    785   1.3  christos 	}
    786   1.3  christos 	if (sshkey_read(key, &cp) != 0) {
    787   1.1  christos 		/* no key? Check for options */
    788   1.1  christos 		opts = cp;
    789   1.1  christos 		if (sshkey_advance_past_options(&cp) != 0) {
    790   1.3  christos 			error("%s:%lu: invalid options", path, linenum);
    791   1.3  christos 			r = SSH_ERR_INVALID_FORMAT;
    792   1.3  christos 			goto out;
    793   1.1  christos 		}
    794   1.9  christos 		if (cp == NULL || *cp == '\0') {
    795   1.9  christos 			error("%s:%lu: missing key", path, linenum);
    796   1.9  christos 			r = SSH_ERR_INVALID_FORMAT;
    797   1.9  christos 			goto out;
    798   1.9  christos 		}
    799   1.1  christos 		*cp++ = '\0';
    800   1.1  christos 		skip_space(&cp);
    801   1.3  christos 		if (sshkey_read(key, &cp) != 0) {
    802   1.3  christos 			error("%s:%lu: invalid key", path, linenum);
    803   1.3  christos 			r = SSH_ERR_INVALID_FORMAT;
    804   1.3  christos 			goto out;
    805   1.1  christos 		}
    806   1.1  christos 	}
    807   1.1  christos 	debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
    808   1.1  christos 	if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
    809   1.1  christos 		error("%s:%lu: bad options: %s", path, linenum, reason);
    810   1.3  christos 		r = SSH_ERR_INVALID_FORMAT;
    811   1.3  christos 		goto out;
    812   1.3  christos 	}
    813   1.3  christos 	/* success */
    814   1.3  christos 	if (principalsp != NULL) {
    815   1.3  christos 		*principalsp = principals;
    816   1.3  christos 		principals = NULL; /* transferred */
    817   1.3  christos 	}
    818   1.3  christos 	if (sigoptsp != NULL) {
    819   1.3  christos 		*sigoptsp = sigopts;
    820   1.3  christos 		sigopts = NULL; /* transferred */
    821   1.3  christos 	}
    822   1.3  christos 	if (keyp != NULL) {
    823   1.3  christos 		*keyp = key;
    824   1.3  christos 		key = NULL; /* transferred */
    825   1.3  christos 	}
    826   1.3  christos 	r = 0;
    827   1.3  christos  out:
    828   1.3  christos 	free(principals);
    829   1.3  christos 	sshsigopt_free(sigopts);
    830   1.3  christos 	sshkey_free(key);
    831   1.3  christos 	return r;
    832   1.3  christos }
    833   1.3  christos 
    834   1.3  christos static int
    835   1.8  christos cert_filter_principals(const char *path, u_long linenum,
    836   1.8  christos     char **principalsp, const struct sshkey *cert, uint64_t verify_time)
    837   1.8  christos {
    838   1.8  christos 	char *cp, *oprincipals, *principals;
    839   1.8  christos 	const char *reason;
    840   1.8  christos 	struct sshbuf *nprincipals;
    841   1.8  christos 	int r = SSH_ERR_INTERNAL_ERROR, success = 0;
    842   1.8  christos 	u_int i;
    843   1.8  christos 
    844   1.8  christos 	oprincipals = principals = *principalsp;
    845   1.8  christos 	*principalsp = NULL;
    846   1.8  christos 
    847   1.8  christos 	if ((nprincipals = sshbuf_new()) == NULL) {
    848   1.8  christos 		r = SSH_ERR_ALLOC_FAIL;
    849   1.8  christos 		goto out;
    850   1.8  christos 	}
    851   1.8  christos 
    852   1.8  christos 	while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
    853   1.8  christos 		/* Check certificate validity */
    854   1.8  christos 		if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
    855   1.8  christos 		    verify_time, NULL, &reason)) != 0) {
    856   1.8  christos 			debug("%s:%lu: principal \"%s\" not authorized: %s",
    857   1.8  christos 			    path, linenum, cp, reason);
    858   1.8  christos 			continue;
    859   1.8  christos 		}
    860   1.8  christos 		/* Return all matching principal names from the cert */
    861   1.8  christos 		for (i = 0; i < cert->cert->nprincipals; i++) {
    862   1.8  christos 			if (match_pattern(cert->cert->principals[i], cp)) {
    863   1.8  christos 				if ((r = sshbuf_putf(nprincipals, "%s%s",
    864   1.8  christos 					sshbuf_len(nprincipals) != 0 ? "," : "",
    865   1.8  christos 						cert->cert->principals[i])) != 0) {
    866   1.8  christos 					error_f("buffer error");
    867   1.8  christos 					goto out;
    868   1.8  christos 				}
    869   1.8  christos 			}
    870   1.8  christos 		}
    871   1.8  christos 	}
    872   1.8  christos 	if (sshbuf_len(nprincipals) == 0) {
    873   1.8  christos 		error("%s:%lu: no valid principals found", path, linenum);
    874   1.8  christos 		r = SSH_ERR_KEY_CERT_INVALID;
    875   1.8  christos 		goto out;
    876   1.8  christos 	}
    877   1.8  christos 	if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
    878   1.8  christos 		error_f("buffer error");
    879  1.14  christos 		r = SSH_ERR_ALLOC_FAIL;
    880   1.8  christos 		goto out;
    881   1.8  christos 	}
    882   1.8  christos 	/* success */
    883   1.8  christos 	success = 1;
    884   1.8  christos 	*principalsp = principals;
    885   1.8  christos  out:
    886   1.8  christos 	sshbuf_free(nprincipals);
    887   1.8  christos 	free(oprincipals);
    888   1.8  christos 	return success ? 0 : r;
    889   1.8  christos }
    890   1.8  christos 
    891   1.8  christos static int
    892   1.3  christos check_allowed_keys_line(const char *path, u_long linenum, char *line,
    893   1.3  christos     const struct sshkey *sign_key, const char *principal,
    894   1.8  christos     const char *sig_namespace, uint64_t verify_time, char **principalsp)
    895   1.3  christos {
    896   1.3  christos 	struct sshkey *found_key = NULL;
    897   1.8  christos 	char *principals = NULL;
    898   1.7  christos 	int r, success = 0;
    899   1.3  christos 	const char *reason = NULL;
    900   1.3  christos 	struct sshsigopt *sigopts = NULL;
    901   1.7  christos 	char tvalid[64], tverify[64];
    902   1.3  christos 
    903   1.8  christos 	if (principalsp != NULL)
    904   1.8  christos 		*principalsp = NULL;
    905   1.8  christos 
    906   1.3  christos 	/* Parse the line */
    907   1.3  christos 	if ((r = parse_principals_key_and_options(path, linenum, line,
    908   1.8  christos 	    principal, &principals, &found_key, &sigopts)) != 0) {
    909   1.3  christos 		/* error already logged */
    910   1.1  christos 		goto done;
    911   1.1  christos 	}
    912   1.1  christos 
    913   1.1  christos 	if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
    914   1.1  christos 		/* Exact match of key */
    915   1.7  christos 		debug("%s:%lu: matched key", path, linenum);
    916   1.1  christos 	} else if (sigopts->ca && sshkey_is_cert(sign_key) &&
    917   1.1  christos 	    sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
    918   1.8  christos 		if (principal) {
    919   1.8  christos 			/* Match certificate CA key with specified principal */
    920   1.8  christos 			if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
    921   1.8  christos 			    verify_time, principal, &reason)) != 0) {
    922   1.8  christos 				error("%s:%lu: certificate not authorized: %s",
    923   1.8  christos 				    path, linenum, reason);
    924   1.8  christos 				goto done;
    925   1.8  christos 			}
    926   1.8  christos 			debug("%s:%lu: matched certificate CA key",
    927   1.8  christos 			    path, linenum);
    928   1.8  christos 		} else {
    929   1.8  christos 			/* No principal specified - find all matching ones */
    930   1.8  christos 			if ((r = cert_filter_principals(path, linenum,
    931   1.8  christos 			    &principals, sign_key, verify_time)) != 0) {
    932   1.8  christos 				/* error already displayed */
    933   1.8  christos 				debug_r(r, "%s:%lu: cert_filter_principals",
    934   1.8  christos 				    path, linenum);
    935   1.8  christos 				goto done;
    936   1.8  christos 			}
    937   1.8  christos 			debug("%s:%lu: matched certificate CA key",
    938   1.8  christos 			    path, linenum);
    939   1.1  christos 		}
    940   1.1  christos 	} else {
    941   1.7  christos 		/* Didn't match key */
    942   1.1  christos 		goto done;
    943   1.1  christos 	}
    944   1.7  christos 
    945   1.7  christos 	/* Check whether options preclude the use of this key */
    946   1.8  christos 	if (sigopts->namespaces != NULL && sig_namespace != NULL &&
    947   1.7  christos 	    match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
    948   1.7  christos 		error("%s:%lu: key is not permitted for use in signature "
    949   1.7  christos 		    "namespace \"%s\"", path, linenum, sig_namespace);
    950   1.7  christos 		goto done;
    951   1.7  christos 	}
    952   1.7  christos 
    953   1.7  christos 	/* check key time validity */
    954   1.7  christos 	format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
    955   1.7  christos 	if (sigopts->valid_after != 0 &&
    956   1.7  christos 	    (uint64_t)verify_time < sigopts->valid_after) {
    957   1.7  christos 		format_absolute_time(sigopts->valid_after,
    958   1.7  christos 		    tvalid, sizeof(tvalid));
    959   1.7  christos 		error("%s:%lu: key is not yet valid: "
    960   1.7  christos 		    "verify time %s < valid-after %s", path, linenum,
    961   1.7  christos 		    tverify, tvalid);
    962   1.7  christos 		goto done;
    963   1.7  christos 	}
    964   1.7  christos 	if (sigopts->valid_before != 0 &&
    965   1.7  christos 	    (uint64_t)verify_time > sigopts->valid_before) {
    966   1.7  christos 		format_absolute_time(sigopts->valid_before,
    967   1.7  christos 		    tvalid, sizeof(tvalid));
    968   1.7  christos 		error("%s:%lu: key has expired: "
    969   1.7  christos 		    "verify time %s > valid-before %s", path, linenum,
    970   1.7  christos 		    tverify, tvalid);
    971   1.7  christos 		goto done;
    972   1.7  christos 	}
    973   1.7  christos 	success = 1;
    974   1.7  christos 
    975   1.1  christos  done:
    976   1.8  christos 	if (success && principalsp != NULL) {
    977   1.8  christos 		*principalsp = principals;
    978   1.8  christos 		principals = NULL; /* transferred */
    979   1.8  christos 	}
    980   1.8  christos 	free(principals);
    981   1.1  christos 	sshkey_free(found_key);
    982   1.1  christos 	sshsigopt_free(sigopts);
    983   1.7  christos 	return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
    984   1.1  christos }
    985   1.1  christos 
    986   1.1  christos int
    987   1.1  christos sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
    988   1.7  christos     const char *principal, const char *sig_namespace, uint64_t verify_time)
    989   1.1  christos {
    990   1.1  christos 	FILE *f = NULL;
    991   1.1  christos 	char *line = NULL;
    992   1.1  christos 	size_t linesize = 0;
    993   1.1  christos 	u_long linenum = 0;
    994  1.11  christos 	int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
    995   1.1  christos 
    996   1.1  christos 	/* Check key and principal against file */
    997   1.1  christos 	if ((f = fopen(path, "r")) == NULL) {
    998   1.1  christos 		oerrno = errno;
    999   1.1  christos 		error("Unable to open allowed keys file \"%s\": %s",
   1000   1.1  christos 		    path, strerror(errno));
   1001   1.1  christos 		errno = oerrno;
   1002   1.1  christos 		return SSH_ERR_SYSTEM_ERROR;
   1003   1.1  christos 	}
   1004   1.1  christos 
   1005   1.1  christos 	while (getline(&line, &linesize, f) != -1) {
   1006   1.1  christos 		linenum++;
   1007   1.1  christos 		r = check_allowed_keys_line(path, linenum, line, sign_key,
   1008   1.8  christos 		    principal, sig_namespace, verify_time, NULL);
   1009   1.1  christos 		free(line);
   1010   1.1  christos 		line = NULL;
   1011   1.6  christos 		linesize = 0;
   1012   1.1  christos 		if (r == SSH_ERR_KEY_NOT_FOUND)
   1013   1.1  christos 			continue;
   1014   1.1  christos 		else if (r == 0) {
   1015   1.1  christos 			/* success */
   1016   1.1  christos 			fclose(f);
   1017   1.1  christos 			return 0;
   1018   1.1  christos 		} else
   1019   1.1  christos 			break;
   1020   1.1  christos 	}
   1021   1.1  christos 	/* Either we hit an error parsing or we simply didn't find the key */
   1022   1.1  christos 	fclose(f);
   1023   1.1  christos 	free(line);
   1024  1.11  christos 	return r;
   1025   1.1  christos }
   1026   1.3  christos 
   1027   1.3  christos int
   1028   1.3  christos sshsig_find_principals(const char *path, const struct sshkey *sign_key,
   1029   1.7  christos     uint64_t verify_time, char **principals)
   1030   1.3  christos {
   1031   1.3  christos 	FILE *f = NULL;
   1032   1.3  christos 	char *line = NULL;
   1033   1.3  christos 	size_t linesize = 0;
   1034   1.3  christos 	u_long linenum = 0;
   1035  1.11  christos 	int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
   1036   1.3  christos 
   1037   1.3  christos 	if ((f = fopen(path, "r")) == NULL) {
   1038   1.3  christos 		oerrno = errno;
   1039   1.3  christos 		error("Unable to open allowed keys file \"%s\": %s",
   1040   1.3  christos 		    path, strerror(errno));
   1041   1.3  christos 		errno = oerrno;
   1042   1.3  christos 		return SSH_ERR_SYSTEM_ERROR;
   1043   1.3  christos 	}
   1044   1.3  christos 
   1045   1.3  christos 	while (getline(&line, &linesize, f) != -1) {
   1046   1.3  christos 		linenum++;
   1047   1.8  christos 		r = check_allowed_keys_line(path, linenum, line,
   1048   1.8  christos 		    sign_key, NULL, NULL, verify_time, principals);
   1049   1.3  christos 		free(line);
   1050   1.3  christos 		line = NULL;
   1051   1.6  christos 		linesize = 0;
   1052   1.3  christos 		if (r == SSH_ERR_KEY_NOT_FOUND)
   1053   1.3  christos 			continue;
   1054   1.3  christos 		else if (r == 0) {
   1055   1.3  christos 			/* success */
   1056   1.3  christos 			fclose(f);
   1057   1.3  christos 			return 0;
   1058   1.3  christos 		} else
   1059   1.3  christos 			break;
   1060   1.3  christos 	}
   1061   1.3  christos 	free(line);
   1062   1.3  christos 	/* Either we hit an error parsing or we simply didn't find the key */
   1063   1.3  christos 	if (ferror(f) != 0) {
   1064   1.3  christos 		oerrno = errno;
   1065   1.3  christos 		fclose(f);
   1066   1.3  christos 		error("Unable to read allowed keys file \"%s\": %s",
   1067   1.3  christos 		    path, strerror(errno));
   1068   1.3  christos 		errno = oerrno;
   1069   1.3  christos 		return SSH_ERR_SYSTEM_ERROR;
   1070   1.3  christos 	}
   1071   1.3  christos 	fclose(f);
   1072  1.11  christos 	return r;
   1073   1.3  christos }
   1074   1.3  christos 
   1075   1.3  christos int
   1076   1.8  christos sshsig_match_principals(const char *path, const char *principal,
   1077   1.8  christos     char ***principalsp, size_t *nprincipalsp)
   1078   1.8  christos {
   1079   1.8  christos 	FILE *f = NULL;
   1080   1.8  christos 	char *found, *line = NULL, **principals = NULL, **tmp;
   1081   1.8  christos 	size_t i, nprincipals = 0, linesize = 0;
   1082   1.8  christos 	u_long linenum = 0;
   1083   1.8  christos 	int oerrno = 0, r, ret = 0;
   1084   1.8  christos 
   1085   1.8  christos 	if (principalsp != NULL)
   1086   1.8  christos 		*principalsp = NULL;
   1087   1.8  christos 	if (nprincipalsp != NULL)
   1088   1.8  christos 		*nprincipalsp = 0;
   1089   1.8  christos 
   1090   1.8  christos 	/* Check key and principal against file */
   1091   1.8  christos 	if ((f = fopen(path, "r")) == NULL) {
   1092   1.8  christos 		oerrno = errno;
   1093   1.8  christos 		error("Unable to open allowed keys file \"%s\": %s",
   1094   1.8  christos 		    path, strerror(errno));
   1095   1.8  christos 		errno = oerrno;
   1096   1.8  christos 		return SSH_ERR_SYSTEM_ERROR;
   1097   1.8  christos 	}
   1098   1.8  christos 
   1099   1.8  christos 	while (getline(&line, &linesize, f) != -1) {
   1100   1.8  christos 		linenum++;
   1101   1.8  christos 		/* Parse the line */
   1102   1.8  christos 		if ((r = parse_principals_key_and_options(path, linenum, line,
   1103   1.8  christos 		    principal, &found, NULL, NULL)) != 0) {
   1104   1.8  christos 			if (r == SSH_ERR_KEY_NOT_FOUND)
   1105   1.8  christos 				continue;
   1106   1.8  christos 			ret = r;
   1107   1.8  christos 			oerrno = errno;
   1108   1.8  christos 			break; /* unexpected error */
   1109   1.8  christos 		}
   1110   1.8  christos 		if ((tmp = recallocarray(principals, nprincipals,
   1111   1.8  christos 		    nprincipals + 1, sizeof(*principals))) == NULL) {
   1112   1.8  christos 			ret = SSH_ERR_ALLOC_FAIL;
   1113   1.8  christos 			free(found);
   1114   1.8  christos 			break;
   1115   1.8  christos 		}
   1116   1.8  christos 		principals = tmp;
   1117   1.8  christos 		principals[nprincipals++] = found; /* transferred */
   1118   1.8  christos 		free(line);
   1119   1.8  christos 		line = NULL;
   1120   1.8  christos 		linesize = 0;
   1121   1.8  christos 	}
   1122   1.8  christos 	fclose(f);
   1123   1.8  christos 
   1124   1.8  christos 	if (ret == 0) {
   1125   1.8  christos 		if (nprincipals == 0)
   1126   1.8  christos 			ret = SSH_ERR_KEY_NOT_FOUND;
   1127  1.12  christos 		if (nprincipalsp != 0)
   1128  1.12  christos 			*nprincipalsp = nprincipals;
   1129   1.8  christos 		if (principalsp != NULL) {
   1130   1.8  christos 			*principalsp = principals;
   1131   1.8  christos 			principals = NULL; /* transferred */
   1132   1.8  christos 			nprincipals = 0;
   1133   1.8  christos 		}
   1134   1.8  christos 	}
   1135   1.8  christos 
   1136   1.8  christos 	for (i = 0; i < nprincipals; i++)
   1137   1.8  christos 		free(principals[i]);
   1138   1.8  christos 	free(principals);
   1139   1.8  christos 
   1140   1.8  christos 	errno = oerrno;
   1141   1.8  christos 	return ret;
   1142   1.8  christos }
   1143   1.8  christos 
   1144   1.8  christos int
   1145   1.3  christos sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
   1146   1.3  christos {
   1147   1.3  christos 	struct sshkey *pk = NULL;
   1148   1.3  christos 	int r = SSH_ERR_SIGNATURE_INVALID;
   1149   1.3  christos 
   1150   1.4  christos 	if (pubkey == NULL)
   1151   1.4  christos 		return SSH_ERR_INTERNAL_ERROR;
   1152   1.3  christos 	if ((r = sshsig_parse_preamble(signature)) != 0)
   1153   1.3  christos 		return r;
   1154   1.3  christos 	if ((r = sshkey_froms(signature, &pk)) != 0)
   1155   1.3  christos 		return r;
   1156   1.3  christos 
   1157   1.3  christos 	*pubkey = pk;
   1158   1.3  christos 	pk = NULL;
   1159   1.3  christos 	return 0;
   1160   1.3  christos }
   1161