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