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