1*a91a2465SEd Maste /* $OpenBSD: sshsig.c,v 1.35 2024/03/08 22:16:32 djm Exp $ */
219261079SEd Maste /*
319261079SEd Maste * Copyright (c) 2019 Google LLC
419261079SEd Maste *
519261079SEd Maste * Permission to use, copy, modify, and distribute this software for any
619261079SEd Maste * purpose with or without fee is hereby granted, provided that the above
719261079SEd Maste * copyright notice and this permission notice appear in all copies.
819261079SEd Maste *
919261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1019261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1119261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1219261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1319261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1419261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1519261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1619261079SEd Maste */
1719261079SEd Maste
1819261079SEd Maste #include "includes.h"
1919261079SEd Maste
2019261079SEd Maste #include <stdio.h>
2119261079SEd Maste #include <stdlib.h>
2219261079SEd Maste #include <stdarg.h>
2319261079SEd Maste #include <errno.h>
2419261079SEd Maste #include <string.h>
2519261079SEd Maste #include <unistd.h>
2619261079SEd Maste
2719261079SEd Maste #include "authfd.h"
2819261079SEd Maste #include "authfile.h"
2919261079SEd Maste #include "log.h"
3019261079SEd Maste #include "misc.h"
3119261079SEd Maste #include "sshbuf.h"
3219261079SEd Maste #include "sshsig.h"
3319261079SEd Maste #include "ssherr.h"
3419261079SEd Maste #include "sshkey.h"
3519261079SEd Maste #include "match.h"
3619261079SEd Maste #include "digest.h"
3719261079SEd Maste
3819261079SEd Maste #define SIG_VERSION 0x01
3919261079SEd Maste #define MAGIC_PREAMBLE "SSHSIG"
4019261079SEd Maste #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1)
41edf85781SEd Maste #define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----"
4219261079SEd Maste #define END_SIGNATURE "-----END SSH SIGNATURE-----"
4319261079SEd Maste #define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */
4419261079SEd Maste #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256"
4519261079SEd Maste #define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */
4619261079SEd Maste #define HASHALG_ALLOWED "sha256,sha512"
4719261079SEd Maste
4819261079SEd Maste int
sshsig_armor(const struct sshbuf * blob,struct sshbuf ** out)4919261079SEd Maste sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
5019261079SEd Maste {
5119261079SEd Maste struct sshbuf *buf = NULL;
5219261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
5319261079SEd Maste
5419261079SEd Maste *out = NULL;
5519261079SEd Maste
5619261079SEd Maste if ((buf = sshbuf_new()) == NULL) {
5719261079SEd Maste error_f("sshbuf_new failed");
5819261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
5919261079SEd Maste goto out;
6019261079SEd Maste }
6119261079SEd Maste
62edf85781SEd Maste if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) {
6319261079SEd Maste error_fr(r, "sshbuf_putf");
6419261079SEd Maste goto out;
6519261079SEd Maste }
6619261079SEd Maste
6719261079SEd Maste if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
6819261079SEd Maste error_fr(r, "base64 encode signature");
6919261079SEd Maste goto out;
7019261079SEd Maste }
7119261079SEd Maste
7219261079SEd Maste if ((r = sshbuf_put(buf, END_SIGNATURE,
7319261079SEd Maste sizeof(END_SIGNATURE)-1)) != 0 ||
7419261079SEd Maste (r = sshbuf_put_u8(buf, '\n')) != 0) {
7519261079SEd Maste error_fr(r, "sshbuf_put");
7619261079SEd Maste goto out;
7719261079SEd Maste }
7819261079SEd Maste /* success */
7919261079SEd Maste *out = buf;
8019261079SEd Maste buf = NULL; /* transferred */
8119261079SEd Maste r = 0;
8219261079SEd Maste out:
8319261079SEd Maste sshbuf_free(buf);
8419261079SEd Maste return r;
8519261079SEd Maste }
8619261079SEd Maste
8719261079SEd Maste int
sshsig_dearmor(struct sshbuf * sig,struct sshbuf ** out)8819261079SEd Maste sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
8919261079SEd Maste {
9019261079SEd Maste int r;
9119261079SEd Maste size_t eoffset = 0;
9219261079SEd Maste struct sshbuf *buf = NULL;
9319261079SEd Maste struct sshbuf *sbuf = NULL;
9419261079SEd Maste char *b64 = NULL;
9519261079SEd Maste
9619261079SEd Maste if ((sbuf = sshbuf_fromb(sig)) == NULL) {
9719261079SEd Maste error_f("sshbuf_fromb failed");
9819261079SEd Maste return SSH_ERR_ALLOC_FAIL;
9919261079SEd Maste }
10019261079SEd Maste
101edf85781SEd Maste /* Expect and consume preamble + lf/crlf */
10219261079SEd Maste if ((r = sshbuf_cmp(sbuf, 0,
10319261079SEd Maste BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
10419261079SEd Maste error("Couldn't parse signature: missing header");
10519261079SEd Maste goto done;
10619261079SEd Maste }
10719261079SEd Maste if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
10819261079SEd Maste error_fr(r, "consume");
10919261079SEd Maste goto done;
11019261079SEd Maste }
111edf85781SEd Maste if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0)
112edf85781SEd Maste eoffset = 2;
113edf85781SEd Maste else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0)
114edf85781SEd Maste eoffset = 1;
115edf85781SEd Maste else {
116edf85781SEd Maste r = SSH_ERR_INVALID_FORMAT;
117edf85781SEd Maste error_f("no header eol");
118edf85781SEd Maste goto done;
119edf85781SEd Maste }
120edf85781SEd Maste if ((r = sshbuf_consume(sbuf, eoffset)) != 0) {
121edf85781SEd Maste error_fr(r, "consume eol");
122edf85781SEd Maste goto done;
123edf85781SEd Maste }
124edf85781SEd Maste /* Find and consume lf + suffix (any prior cr would be ignored) */
12519261079SEd Maste if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
126edf85781SEd Maste sizeof(END_SIGNATURE), &eoffset)) != 0) {
12719261079SEd Maste error("Couldn't parse signature: missing footer");
12819261079SEd Maste goto done;
12919261079SEd Maste }
13019261079SEd Maste if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
13119261079SEd Maste error_fr(r, "consume");
13219261079SEd Maste goto done;
13319261079SEd Maste }
13419261079SEd Maste
13519261079SEd Maste if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
13619261079SEd Maste error_f("sshbuf_dup_string failed");
13719261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
13819261079SEd Maste goto done;
13919261079SEd Maste }
14019261079SEd Maste
14119261079SEd Maste if ((buf = sshbuf_new()) == NULL) {
14219261079SEd Maste error_f("sshbuf_new() failed");
14319261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
14419261079SEd Maste goto done;
14519261079SEd Maste }
14619261079SEd Maste
14719261079SEd Maste if ((r = sshbuf_b64tod(buf, b64)) != 0) {
14819261079SEd Maste error_fr(r, "decode base64");
14919261079SEd Maste goto done;
15019261079SEd Maste }
15119261079SEd Maste
15219261079SEd Maste /* success */
15319261079SEd Maste *out = buf;
15419261079SEd Maste r = 0;
15519261079SEd Maste buf = NULL; /* transferred */
15619261079SEd Maste done:
15719261079SEd Maste sshbuf_free(buf);
15819261079SEd Maste sshbuf_free(sbuf);
15919261079SEd Maste free(b64);
16019261079SEd Maste return r;
16119261079SEd Maste }
16219261079SEd Maste
16319261079SEd Maste 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)16419261079SEd Maste sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
16519261079SEd Maste const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
16619261079SEd Maste const char *sig_namespace, struct sshbuf **out,
16719261079SEd Maste sshsig_signer *signer, void *signer_ctx)
16819261079SEd Maste {
16919261079SEd Maste int r;
17019261079SEd Maste size_t slen = 0;
17119261079SEd Maste u_char *sig = NULL;
17219261079SEd Maste struct sshbuf *blob = NULL;
17319261079SEd Maste struct sshbuf *tosign = NULL;
17419261079SEd Maste const char *sign_alg = NULL;
17519261079SEd Maste
17619261079SEd Maste if ((tosign = sshbuf_new()) == NULL ||
17719261079SEd Maste (blob = sshbuf_new()) == NULL) {
17819261079SEd Maste error_f("sshbuf_new failed");
17919261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
18019261079SEd Maste goto done;
18119261079SEd Maste }
18219261079SEd Maste
18319261079SEd Maste if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
18419261079SEd Maste (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
18519261079SEd Maste (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
18619261079SEd Maste (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
18719261079SEd Maste (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
18819261079SEd Maste error_fr(r, "assemble message to sign");
18919261079SEd Maste goto done;
19019261079SEd Maste }
19119261079SEd Maste
19219261079SEd Maste /* If using RSA keys then default to a good signature algorithm */
19319261079SEd Maste if (sshkey_type_plain(key->type) == KEY_RSA)
19419261079SEd Maste sign_alg = RSA_SIGN_ALG;
19519261079SEd Maste
19619261079SEd Maste if (signer != NULL) {
19719261079SEd Maste if ((r = signer(key, &sig, &slen,
19819261079SEd Maste sshbuf_ptr(tosign), sshbuf_len(tosign),
19919261079SEd Maste sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
20019261079SEd Maste error_r(r, "Couldn't sign message (signer)");
20119261079SEd Maste goto done;
20219261079SEd Maste }
20319261079SEd Maste } else {
20419261079SEd Maste if ((r = sshkey_sign(key, &sig, &slen,
20519261079SEd Maste sshbuf_ptr(tosign), sshbuf_len(tosign),
20619261079SEd Maste sign_alg, sk_provider, sk_pin, 0)) != 0) {
20719261079SEd Maste error_r(r, "Couldn't sign message");
20819261079SEd Maste goto done;
20919261079SEd Maste }
21019261079SEd Maste }
21119261079SEd Maste
21219261079SEd Maste if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
21319261079SEd Maste (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
21419261079SEd Maste (r = sshkey_puts(key, blob)) != 0 ||
21519261079SEd Maste (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
21619261079SEd Maste (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
21719261079SEd Maste (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
21819261079SEd Maste (r = sshbuf_put_string(blob, sig, slen)) != 0) {
21919261079SEd Maste error_fr(r, "assemble signature object");
22019261079SEd Maste goto done;
22119261079SEd Maste }
22219261079SEd Maste
22319261079SEd Maste if (out != NULL) {
22419261079SEd Maste *out = blob;
22519261079SEd Maste blob = NULL;
22619261079SEd Maste }
22719261079SEd Maste r = 0;
22819261079SEd Maste done:
22919261079SEd Maste free(sig);
23019261079SEd Maste sshbuf_free(blob);
23119261079SEd Maste sshbuf_free(tosign);
23219261079SEd Maste return r;
23319261079SEd Maste }
23419261079SEd Maste
23519261079SEd Maste /* Check preamble and version. */
23619261079SEd Maste static int
sshsig_parse_preamble(struct sshbuf * buf)23719261079SEd Maste sshsig_parse_preamble(struct sshbuf *buf)
23819261079SEd Maste {
23919261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
24019261079SEd Maste uint32_t sversion;
24119261079SEd Maste
24219261079SEd Maste if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
24319261079SEd Maste (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
24419261079SEd Maste (r = sshbuf_get_u32(buf, &sversion)) != 0) {
24519261079SEd Maste error("Couldn't verify signature: invalid format");
24619261079SEd Maste return r;
24719261079SEd Maste }
24819261079SEd Maste
24919261079SEd Maste if (sversion > SIG_VERSION) {
25019261079SEd Maste error("Signature version %lu is larger than supported "
25119261079SEd Maste "version %u", (unsigned long)sversion, SIG_VERSION);
25219261079SEd Maste return SSH_ERR_INVALID_FORMAT;
25319261079SEd Maste }
25419261079SEd Maste return 0;
25519261079SEd Maste }
25619261079SEd Maste
25719261079SEd Maste static int
sshsig_check_hashalg(const char * hashalg)25819261079SEd Maste sshsig_check_hashalg(const char *hashalg)
25919261079SEd Maste {
26019261079SEd Maste if (hashalg == NULL ||
26119261079SEd Maste match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
26219261079SEd Maste return 0;
26319261079SEd Maste error_f("unsupported hash algorithm \"%.100s\"", hashalg);
26419261079SEd Maste return SSH_ERR_SIGN_ALG_UNSUPPORTED;
26519261079SEd Maste }
26619261079SEd Maste
26719261079SEd Maste static int
sshsig_peek_hashalg(struct sshbuf * signature,char ** hashalgp)26819261079SEd Maste sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
26919261079SEd Maste {
27019261079SEd Maste struct sshbuf *buf = NULL;
27119261079SEd Maste char *hashalg = NULL;
27219261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
27319261079SEd Maste
27419261079SEd Maste if (hashalgp != NULL)
27519261079SEd Maste *hashalgp = NULL;
27619261079SEd Maste if ((buf = sshbuf_fromb(signature)) == NULL)
27719261079SEd Maste return SSH_ERR_ALLOC_FAIL;
27819261079SEd Maste if ((r = sshsig_parse_preamble(buf)) != 0)
27919261079SEd Maste goto done;
28019261079SEd Maste if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
28119261079SEd Maste (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
28219261079SEd Maste (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
28319261079SEd Maste (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
28419261079SEd Maste (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
28519261079SEd Maste error_fr(r, "parse signature object");
28619261079SEd Maste goto done;
28719261079SEd Maste }
28819261079SEd Maste
28919261079SEd Maste /* success */
29019261079SEd Maste r = 0;
29119261079SEd Maste *hashalgp = hashalg;
29219261079SEd Maste hashalg = NULL;
29319261079SEd Maste done:
29419261079SEd Maste free(hashalg);
29519261079SEd Maste sshbuf_free(buf);
29619261079SEd Maste return r;
29719261079SEd Maste }
29819261079SEd Maste
29919261079SEd Maste 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)30019261079SEd Maste sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
30119261079SEd Maste const struct sshbuf *h_message, const char *expect_namespace,
30219261079SEd Maste struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
30319261079SEd Maste {
30419261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
30519261079SEd Maste struct sshbuf *buf = NULL, *toverify = NULL;
30619261079SEd Maste struct sshkey *key = NULL;
30719261079SEd Maste const u_char *sig;
30819261079SEd Maste char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
30919261079SEd Maste size_t siglen;
31019261079SEd Maste
31119261079SEd Maste debug_f("verify message length %zu", sshbuf_len(h_message));
31219261079SEd Maste if (sig_details != NULL)
31319261079SEd Maste *sig_details = NULL;
31419261079SEd Maste if (sign_keyp != NULL)
31519261079SEd Maste *sign_keyp = NULL;
31619261079SEd Maste
31719261079SEd Maste if ((toverify = sshbuf_new()) == NULL) {
31819261079SEd Maste error_f("sshbuf_new failed");
31919261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
32019261079SEd Maste goto done;
32119261079SEd Maste }
32219261079SEd Maste if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
32319261079SEd Maste MAGIC_PREAMBLE_LEN)) != 0 ||
32419261079SEd Maste (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
32519261079SEd Maste (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
32619261079SEd Maste (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
32719261079SEd Maste (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
32819261079SEd Maste error_fr(r, "assemble message to verify");
32919261079SEd Maste goto done;
33019261079SEd Maste }
33119261079SEd Maste
33219261079SEd Maste if ((r = sshsig_parse_preamble(signature)) != 0)
33319261079SEd Maste goto done;
33419261079SEd Maste
33519261079SEd Maste if ((r = sshkey_froms(signature, &key)) != 0 ||
33619261079SEd Maste (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
33719261079SEd Maste (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
33819261079SEd Maste (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
33919261079SEd Maste (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
34019261079SEd Maste error_fr(r, "parse signature object");
34119261079SEd Maste goto done;
34219261079SEd Maste }
34319261079SEd Maste
34419261079SEd Maste if (sshbuf_len(signature) != 0) {
34519261079SEd Maste error("Signature contains trailing data");
34619261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
34719261079SEd Maste goto done;
34819261079SEd Maste }
34919261079SEd Maste
35019261079SEd Maste if (strcmp(expect_namespace, got_namespace) != 0) {
35119261079SEd Maste error("Couldn't verify signature: namespace does not match");
35219261079SEd Maste debug_f("expected namespace \"%s\" received \"%s\"",
35319261079SEd Maste expect_namespace, got_namespace);
35419261079SEd Maste r = SSH_ERR_SIGNATURE_INVALID;
35519261079SEd Maste goto done;
35619261079SEd Maste }
35719261079SEd Maste if (strcmp(hashalg, sig_hashalg) != 0) {
35819261079SEd Maste error("Couldn't verify signature: hash algorithm mismatch");
35919261079SEd Maste debug_f("expected algorithm \"%s\" received \"%s\"",
36019261079SEd Maste hashalg, sig_hashalg);
36119261079SEd Maste r = SSH_ERR_SIGNATURE_INVALID;
36219261079SEd Maste goto done;
36319261079SEd Maste }
36419261079SEd Maste /* Ensure that RSA keys use an acceptable signature algorithm */
36519261079SEd Maste if (sshkey_type_plain(key->type) == KEY_RSA) {
36619261079SEd Maste if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
36719261079SEd Maste error_r(r, "Couldn't verify signature: unable to get "
36819261079SEd Maste "signature type");
36919261079SEd Maste goto done;
37019261079SEd Maste }
37119261079SEd Maste if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
37219261079SEd Maste error("Couldn't verify signature: unsupported RSA "
37319261079SEd Maste "signature algorithm %s", sigtype);
37419261079SEd Maste r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
37519261079SEd Maste goto done;
37619261079SEd Maste }
37719261079SEd Maste }
37819261079SEd Maste if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
37919261079SEd Maste sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
38019261079SEd Maste error_r(r, "Signature verification failed");
38119261079SEd Maste goto done;
38219261079SEd Maste }
38319261079SEd Maste
38419261079SEd Maste /* success */
38519261079SEd Maste r = 0;
38619261079SEd Maste if (sign_keyp != NULL) {
38719261079SEd Maste *sign_keyp = key;
38819261079SEd Maste key = NULL; /* transferred */
38919261079SEd Maste }
39019261079SEd Maste done:
39119261079SEd Maste free(got_namespace);
39219261079SEd Maste free(sigtype);
39319261079SEd Maste free(sig_hashalg);
39419261079SEd Maste sshbuf_free(buf);
39519261079SEd Maste sshbuf_free(toverify);
39619261079SEd Maste sshkey_free(key);
39719261079SEd Maste return r;
39819261079SEd Maste }
39919261079SEd Maste
40019261079SEd Maste static int
hash_buffer(const struct sshbuf * m,const char * hashalg,struct sshbuf ** bp)40119261079SEd Maste hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
40219261079SEd Maste {
40319261079SEd Maste char *hex, hash[SSH_DIGEST_MAX_LENGTH];
40419261079SEd Maste int alg, r = SSH_ERR_INTERNAL_ERROR;
40519261079SEd Maste struct sshbuf *b = NULL;
40619261079SEd Maste
40719261079SEd Maste *bp = NULL;
40819261079SEd Maste memset(hash, 0, sizeof(hash));
40919261079SEd Maste
41019261079SEd Maste if ((r = sshsig_check_hashalg(hashalg)) != 0)
41119261079SEd Maste return r;
41219261079SEd Maste if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
41319261079SEd Maste error_f("can't look up hash algorithm %s", hashalg);
41419261079SEd Maste return SSH_ERR_INTERNAL_ERROR;
41519261079SEd Maste }
41619261079SEd Maste if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
41719261079SEd Maste error_fr(r, "ssh_digest_buffer");
41819261079SEd Maste return r;
41919261079SEd Maste }
42019261079SEd Maste if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
42119261079SEd Maste debug3_f("final hash: %s", hex);
42219261079SEd Maste freezero(hex, strlen(hex));
42319261079SEd Maste }
42419261079SEd Maste if ((b = sshbuf_new()) == NULL) {
42519261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
42619261079SEd Maste goto out;
42719261079SEd Maste }
42819261079SEd Maste if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
42919261079SEd Maste error_fr(r, "sshbuf_put");
43019261079SEd Maste goto out;
43119261079SEd Maste }
43219261079SEd Maste *bp = b;
43319261079SEd Maste b = NULL; /* transferred */
43419261079SEd Maste /* success */
43519261079SEd Maste r = 0;
43619261079SEd Maste out:
43719261079SEd Maste sshbuf_free(b);
43819261079SEd Maste explicit_bzero(hash, sizeof(hash));
43919261079SEd Maste return r;
44019261079SEd Maste }
44119261079SEd Maste
44219261079SEd Maste 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)44319261079SEd Maste sshsig_signb(struct sshkey *key, const char *hashalg,
44419261079SEd Maste const char *sk_provider, const char *sk_pin,
44519261079SEd Maste const struct sshbuf *message, const char *sig_namespace,
44619261079SEd Maste struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
44719261079SEd Maste {
44819261079SEd Maste struct sshbuf *b = NULL;
44919261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
45019261079SEd Maste
45119261079SEd Maste if (hashalg == NULL)
45219261079SEd Maste hashalg = HASHALG_DEFAULT;
45319261079SEd Maste if (out != NULL)
45419261079SEd Maste *out = NULL;
45519261079SEd Maste if ((r = hash_buffer(message, hashalg, &b)) != 0) {
45619261079SEd Maste error_fr(r, "hash buffer");
45719261079SEd Maste goto out;
45819261079SEd Maste }
45919261079SEd Maste if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
46019261079SEd Maste sig_namespace, out, signer, signer_ctx)) != 0)
46119261079SEd Maste goto out;
46219261079SEd Maste /* success */
46319261079SEd Maste r = 0;
46419261079SEd Maste out:
46519261079SEd Maste sshbuf_free(b);
46619261079SEd Maste return r;
46719261079SEd Maste }
46819261079SEd Maste
46919261079SEd Maste int
sshsig_verifyb(struct sshbuf * signature,const struct sshbuf * message,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)47019261079SEd Maste sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
47119261079SEd Maste const char *expect_namespace, struct sshkey **sign_keyp,
47219261079SEd Maste struct sshkey_sig_details **sig_details)
47319261079SEd Maste {
47419261079SEd Maste struct sshbuf *b = NULL;
47519261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
47619261079SEd Maste char *hashalg = NULL;
47719261079SEd Maste
47819261079SEd Maste if (sig_details != NULL)
47919261079SEd Maste *sig_details = NULL;
48019261079SEd Maste if (sign_keyp != NULL)
48119261079SEd Maste *sign_keyp = NULL;
48219261079SEd Maste if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
48319261079SEd Maste return r;
48419261079SEd Maste debug_f("signature made with hash \"%s\"", hashalg);
48519261079SEd Maste if ((r = hash_buffer(message, hashalg, &b)) != 0) {
48619261079SEd Maste error_fr(r, "hash buffer");
48719261079SEd Maste goto out;
48819261079SEd Maste }
48919261079SEd Maste if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
49019261079SEd Maste sign_keyp, sig_details)) != 0)
49119261079SEd Maste goto out;
49219261079SEd Maste /* success */
49319261079SEd Maste r = 0;
49419261079SEd Maste out:
49519261079SEd Maste sshbuf_free(b);
49619261079SEd Maste free(hashalg);
49719261079SEd Maste return r;
49819261079SEd Maste }
49919261079SEd Maste
50019261079SEd Maste static int
hash_file(int fd,const char * hashalg,struct sshbuf ** bp)50119261079SEd Maste hash_file(int fd, const char *hashalg, struct sshbuf **bp)
50219261079SEd Maste {
50319261079SEd Maste char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
50419261079SEd Maste ssize_t n, total = 0;
505666605adSEd Maste struct ssh_digest_ctx *ctx = NULL;
50619261079SEd Maste int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
50719261079SEd Maste struct sshbuf *b = NULL;
50819261079SEd Maste
50919261079SEd Maste *bp = NULL;
51019261079SEd Maste memset(hash, 0, sizeof(hash));
51119261079SEd Maste
51219261079SEd Maste if ((r = sshsig_check_hashalg(hashalg)) != 0)
51319261079SEd Maste return r;
51419261079SEd Maste if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
51519261079SEd Maste error_f("can't look up hash algorithm %s", hashalg);
51619261079SEd Maste return SSH_ERR_INTERNAL_ERROR;
51719261079SEd Maste }
51819261079SEd Maste if ((ctx = ssh_digest_start(alg)) == NULL) {
51919261079SEd Maste error_f("ssh_digest_start failed");
52019261079SEd Maste return SSH_ERR_INTERNAL_ERROR;
52119261079SEd Maste }
52219261079SEd Maste for (;;) {
52319261079SEd Maste if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
52419261079SEd Maste if (errno == EINTR || errno == EAGAIN)
52519261079SEd Maste continue;
52619261079SEd Maste oerrno = errno;
52719261079SEd Maste error_f("read: %s", strerror(errno));
52819261079SEd Maste errno = oerrno;
52919261079SEd Maste r = SSH_ERR_SYSTEM_ERROR;
53019261079SEd Maste goto out;
53119261079SEd Maste } else if (n == 0) {
53219261079SEd Maste debug2_f("hashed %zu bytes", total);
53319261079SEd Maste break; /* EOF */
53419261079SEd Maste }
53519261079SEd Maste total += (size_t)n;
53619261079SEd Maste if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
53719261079SEd Maste error_fr(r, "ssh_digest_update");
53819261079SEd Maste goto out;
53919261079SEd Maste }
54019261079SEd Maste }
54119261079SEd Maste if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
54219261079SEd Maste error_fr(r, "ssh_digest_final");
54319261079SEd Maste goto out;
54419261079SEd Maste }
54519261079SEd Maste if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
54619261079SEd Maste debug3_f("final hash: %s", hex);
54719261079SEd Maste freezero(hex, strlen(hex));
54819261079SEd Maste }
54919261079SEd Maste if ((b = sshbuf_new()) == NULL) {
55019261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
55119261079SEd Maste goto out;
55219261079SEd Maste }
55319261079SEd Maste if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
55419261079SEd Maste error_fr(r, "sshbuf_put");
55519261079SEd Maste goto out;
55619261079SEd Maste }
55719261079SEd Maste *bp = b;
55819261079SEd Maste b = NULL; /* transferred */
55919261079SEd Maste /* success */
56019261079SEd Maste r = 0;
56119261079SEd Maste out:
562666605adSEd Maste oerrno = errno;
56319261079SEd Maste sshbuf_free(b);
56419261079SEd Maste ssh_digest_free(ctx);
56519261079SEd Maste explicit_bzero(hash, sizeof(hash));
566666605adSEd Maste errno = oerrno;
56719261079SEd Maste return r;
56819261079SEd Maste }
56919261079SEd Maste
57019261079SEd Maste 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)57119261079SEd Maste sshsig_sign_fd(struct sshkey *key, const char *hashalg,
57219261079SEd Maste const char *sk_provider, const char *sk_pin,
57319261079SEd Maste int fd, const char *sig_namespace, struct sshbuf **out,
57419261079SEd Maste sshsig_signer *signer, void *signer_ctx)
57519261079SEd Maste {
57619261079SEd Maste struct sshbuf *b = NULL;
57719261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
57819261079SEd Maste
57919261079SEd Maste if (hashalg == NULL)
58019261079SEd Maste hashalg = HASHALG_DEFAULT;
58119261079SEd Maste if (out != NULL)
58219261079SEd Maste *out = NULL;
58319261079SEd Maste if ((r = hash_file(fd, hashalg, &b)) != 0) {
58419261079SEd Maste error_fr(r, "hash_file");
58519261079SEd Maste return r;
58619261079SEd Maste }
58719261079SEd Maste if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
58819261079SEd Maste sig_namespace, out, signer, signer_ctx)) != 0)
58919261079SEd Maste goto out;
59019261079SEd Maste /* success */
59119261079SEd Maste r = 0;
59219261079SEd Maste out:
59319261079SEd Maste sshbuf_free(b);
59419261079SEd Maste return r;
59519261079SEd Maste }
59619261079SEd Maste
59719261079SEd Maste int
sshsig_verify_fd(struct sshbuf * signature,int fd,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)59819261079SEd Maste sshsig_verify_fd(struct sshbuf *signature, int fd,
59919261079SEd Maste const char *expect_namespace, struct sshkey **sign_keyp,
60019261079SEd Maste struct sshkey_sig_details **sig_details)
60119261079SEd Maste {
60219261079SEd Maste struct sshbuf *b = NULL;
60319261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
60419261079SEd Maste char *hashalg = NULL;
60519261079SEd Maste
60619261079SEd Maste if (sig_details != NULL)
60719261079SEd Maste *sig_details = NULL;
60819261079SEd Maste if (sign_keyp != NULL)
60919261079SEd Maste *sign_keyp = NULL;
61019261079SEd Maste if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
61119261079SEd Maste return r;
61219261079SEd Maste debug_f("signature made with hash \"%s\"", hashalg);
61319261079SEd Maste if ((r = hash_file(fd, hashalg, &b)) != 0) {
61419261079SEd Maste error_fr(r, "hash_file");
61519261079SEd Maste goto out;
61619261079SEd Maste }
61719261079SEd Maste if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
61819261079SEd Maste sign_keyp, sig_details)) != 0)
61919261079SEd Maste goto out;
62019261079SEd Maste /* success */
62119261079SEd Maste r = 0;
62219261079SEd Maste out:
62319261079SEd Maste sshbuf_free(b);
62419261079SEd Maste free(hashalg);
62519261079SEd Maste return r;
62619261079SEd Maste }
62719261079SEd Maste
62819261079SEd Maste struct sshsigopt {
62919261079SEd Maste int ca;
63019261079SEd Maste char *namespaces;
63119261079SEd Maste uint64_t valid_after, valid_before;
63219261079SEd Maste };
63319261079SEd Maste
63419261079SEd Maste struct sshsigopt *
sshsigopt_parse(const char * opts,const char * path,u_long linenum,const char ** errstrp)63519261079SEd Maste sshsigopt_parse(const char *opts, const char *path, u_long linenum,
63619261079SEd Maste const char **errstrp)
63719261079SEd Maste {
63819261079SEd Maste struct sshsigopt *ret;
63919261079SEd Maste int r;
64019261079SEd Maste char *opt;
64119261079SEd Maste const char *errstr = NULL;
64219261079SEd Maste
64319261079SEd Maste if ((ret = calloc(1, sizeof(*ret))) == NULL)
64419261079SEd Maste return NULL;
64519261079SEd Maste if (opts == NULL || *opts == '\0')
64619261079SEd Maste return ret; /* Empty options yields empty options :) */
64719261079SEd Maste
64819261079SEd Maste while (*opts && *opts != ' ' && *opts != '\t') {
64919261079SEd Maste /* flag options */
65019261079SEd Maste if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
65119261079SEd Maste ret->ca = 1;
65219261079SEd Maste } else if (opt_match(&opts, "namespaces")) {
65319261079SEd Maste if (ret->namespaces != NULL) {
65419261079SEd Maste errstr = "multiple \"namespaces\" clauses";
65519261079SEd Maste goto fail;
65619261079SEd Maste }
65719261079SEd Maste ret->namespaces = opt_dequote(&opts, &errstr);
65819261079SEd Maste if (ret->namespaces == NULL)
65919261079SEd Maste goto fail;
66019261079SEd Maste } else if (opt_match(&opts, "valid-after")) {
66119261079SEd Maste if (ret->valid_after != 0) {
66219261079SEd Maste errstr = "multiple \"valid-after\" clauses";
66319261079SEd Maste goto fail;
66419261079SEd Maste }
66519261079SEd Maste if ((opt = opt_dequote(&opts, &errstr)) == NULL)
66619261079SEd Maste goto fail;
66719261079SEd Maste if (parse_absolute_time(opt, &ret->valid_after) != 0 ||
66819261079SEd Maste ret->valid_after == 0) {
66919261079SEd Maste free(opt);
67019261079SEd Maste errstr = "invalid \"valid-after\" time";
67119261079SEd Maste goto fail;
67219261079SEd Maste }
67319261079SEd Maste free(opt);
67419261079SEd Maste } else if (opt_match(&opts, "valid-before")) {
67519261079SEd Maste if (ret->valid_before != 0) {
67619261079SEd Maste errstr = "multiple \"valid-before\" clauses";
67719261079SEd Maste goto fail;
67819261079SEd Maste }
67919261079SEd Maste if ((opt = opt_dequote(&opts, &errstr)) == NULL)
68019261079SEd Maste goto fail;
68119261079SEd Maste if (parse_absolute_time(opt, &ret->valid_before) != 0 ||
68219261079SEd Maste ret->valid_before == 0) {
68319261079SEd Maste free(opt);
68419261079SEd Maste errstr = "invalid \"valid-before\" time";
68519261079SEd Maste goto fail;
68619261079SEd Maste }
68719261079SEd Maste free(opt);
68819261079SEd Maste }
68919261079SEd Maste /*
69019261079SEd Maste * Skip the comma, and move to the next option
69119261079SEd Maste * (or break out if there are no more).
69219261079SEd Maste */
69319261079SEd Maste if (*opts == '\0' || *opts == ' ' || *opts == '\t')
69419261079SEd Maste break; /* End of options. */
69519261079SEd Maste /* Anything other than a comma is an unknown option */
69619261079SEd Maste if (*opts != ',') {
69719261079SEd Maste errstr = "unknown key option";
69819261079SEd Maste goto fail;
69919261079SEd Maste }
70019261079SEd Maste opts++;
70119261079SEd Maste if (*opts == '\0') {
70219261079SEd Maste errstr = "unexpected end-of-options";
70319261079SEd Maste goto fail;
70419261079SEd Maste }
70519261079SEd Maste }
70619261079SEd Maste /* final consistency check */
70719261079SEd Maste if (ret->valid_after != 0 && ret->valid_before != 0 &&
70819261079SEd Maste ret->valid_before <= ret->valid_after) {
70919261079SEd Maste errstr = "\"valid-before\" time is before \"valid-after\"";
71019261079SEd Maste goto fail;
71119261079SEd Maste }
71219261079SEd Maste /* success */
71319261079SEd Maste return ret;
71419261079SEd Maste fail:
71519261079SEd Maste if (errstrp != NULL)
71619261079SEd Maste *errstrp = errstr;
71719261079SEd Maste sshsigopt_free(ret);
71819261079SEd Maste return NULL;
71919261079SEd Maste }
72019261079SEd Maste
72119261079SEd Maste void
sshsigopt_free(struct sshsigopt * opts)72219261079SEd Maste sshsigopt_free(struct sshsigopt *opts)
72319261079SEd Maste {
72419261079SEd Maste if (opts == NULL)
72519261079SEd Maste return;
72619261079SEd Maste free(opts->namespaces);
72719261079SEd Maste free(opts);
72819261079SEd Maste }
72919261079SEd Maste
73019261079SEd Maste 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)73119261079SEd Maste parse_principals_key_and_options(const char *path, u_long linenum, char *line,
73219261079SEd Maste const char *required_principal, char **principalsp, struct sshkey **keyp,
73319261079SEd Maste struct sshsigopt **sigoptsp)
73419261079SEd Maste {
73519261079SEd Maste char *opts = NULL, *tmp, *cp, *principals = NULL;
73619261079SEd Maste const char *reason = NULL;
73719261079SEd Maste struct sshsigopt *sigopts = NULL;
73819261079SEd Maste struct sshkey *key = NULL;
73919261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR;
74019261079SEd Maste
74119261079SEd Maste if (principalsp != NULL)
74219261079SEd Maste *principalsp = NULL;
74319261079SEd Maste if (sigoptsp != NULL)
74419261079SEd Maste *sigoptsp = NULL;
74519261079SEd Maste if (keyp != NULL)
74619261079SEd Maste *keyp = NULL;
74719261079SEd Maste
74819261079SEd Maste cp = line;
749*a91a2465SEd Maste cp = cp + strspn(cp, " \t\n\r"); /* skip leading whitespace */
75019261079SEd Maste if (*cp == '#' || *cp == '\0')
75119261079SEd Maste return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
75219261079SEd Maste
75319261079SEd Maste /* format: identity[,identity...] [option[,option...]] key */
75487c1498dSEd Maste if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) {
75519261079SEd Maste error("%s:%lu: invalid line", path, linenum);
75619261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
75719261079SEd Maste goto out;
75819261079SEd Maste }
75919261079SEd Maste if ((principals = strdup(tmp)) == NULL) {
76019261079SEd Maste error_f("strdup failed");
76119261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
76219261079SEd Maste goto out;
76319261079SEd Maste }
76419261079SEd Maste /*
76519261079SEd Maste * Bail out early if we're looking for a particular principal and this
76619261079SEd Maste * line does not list it.
76719261079SEd Maste */
76819261079SEd Maste if (required_principal != NULL) {
76919261079SEd Maste if (match_pattern_list(required_principal,
77019261079SEd Maste principals, 0) != 1) {
77119261079SEd Maste /* principal didn't match */
77219261079SEd Maste r = SSH_ERR_KEY_NOT_FOUND;
77319261079SEd Maste goto out;
77419261079SEd Maste }
77519261079SEd Maste debug_f("%s:%lu: matched principal \"%s\"",
77619261079SEd Maste path, linenum, required_principal);
77719261079SEd Maste }
77819261079SEd Maste
77919261079SEd Maste if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
78019261079SEd Maste error_f("sshkey_new failed");
78119261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
78219261079SEd Maste goto out;
78319261079SEd Maste }
78419261079SEd Maste if (sshkey_read(key, &cp) != 0) {
78519261079SEd Maste /* no key? Check for options */
78619261079SEd Maste opts = cp;
78719261079SEd Maste if (sshkey_advance_past_options(&cp) != 0) {
78819261079SEd Maste error("%s:%lu: invalid options", path, linenum);
78919261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
79019261079SEd Maste goto out;
79119261079SEd Maste }
79287c1498dSEd Maste if (cp == NULL || *cp == '\0') {
79387c1498dSEd Maste error("%s:%lu: missing key", path, linenum);
79487c1498dSEd Maste r = SSH_ERR_INVALID_FORMAT;
79587c1498dSEd Maste goto out;
79687c1498dSEd Maste }
79719261079SEd Maste *cp++ = '\0';
79819261079SEd Maste skip_space(&cp);
79919261079SEd Maste if (sshkey_read(key, &cp) != 0) {
80019261079SEd Maste error("%s:%lu: invalid key", path, linenum);
80119261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
80219261079SEd Maste goto out;
80319261079SEd Maste }
80419261079SEd Maste }
80519261079SEd Maste debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
80619261079SEd Maste if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
80719261079SEd Maste error("%s:%lu: bad options: %s", path, linenum, reason);
80819261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
80919261079SEd Maste goto out;
81019261079SEd Maste }
81119261079SEd Maste /* success */
81219261079SEd Maste if (principalsp != NULL) {
81319261079SEd Maste *principalsp = principals;
81419261079SEd Maste principals = NULL; /* transferred */
81519261079SEd Maste }
81619261079SEd Maste if (sigoptsp != NULL) {
81719261079SEd Maste *sigoptsp = sigopts;
81819261079SEd Maste sigopts = NULL; /* transferred */
81919261079SEd Maste }
82019261079SEd Maste if (keyp != NULL) {
82119261079SEd Maste *keyp = key;
82219261079SEd Maste key = NULL; /* transferred */
82319261079SEd Maste }
82419261079SEd Maste r = 0;
82519261079SEd Maste out:
82619261079SEd Maste free(principals);
82719261079SEd Maste sshsigopt_free(sigopts);
82819261079SEd Maste sshkey_free(key);
82919261079SEd Maste return r;
83019261079SEd Maste }
83119261079SEd Maste
83219261079SEd Maste static int
cert_filter_principals(const char * path,u_long linenum,char ** principalsp,const struct sshkey * cert,uint64_t verify_time)8331323ec57SEd Maste cert_filter_principals(const char *path, u_long linenum,
8341323ec57SEd Maste char **principalsp, const struct sshkey *cert, uint64_t verify_time)
8351323ec57SEd Maste {
8361323ec57SEd Maste char *cp, *oprincipals, *principals;
8371323ec57SEd Maste const char *reason;
8381323ec57SEd Maste struct sshbuf *nprincipals;
8391323ec57SEd Maste int r = SSH_ERR_INTERNAL_ERROR, success = 0;
8401323ec57SEd Maste u_int i;
8411323ec57SEd Maste
8421323ec57SEd Maste oprincipals = principals = *principalsp;
8431323ec57SEd Maste *principalsp = NULL;
8441323ec57SEd Maste
8451323ec57SEd Maste if ((nprincipals = sshbuf_new()) == NULL) {
8461323ec57SEd Maste r = SSH_ERR_ALLOC_FAIL;
8471323ec57SEd Maste goto out;
8481323ec57SEd Maste }
8491323ec57SEd Maste
8501323ec57SEd Maste while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
8511323ec57SEd Maste /* Check certificate validity */
8521323ec57SEd Maste if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
8531323ec57SEd Maste verify_time, NULL, &reason)) != 0) {
8541323ec57SEd Maste debug("%s:%lu: principal \"%s\" not authorized: %s",
8551323ec57SEd Maste path, linenum, cp, reason);
8561323ec57SEd Maste continue;
8571323ec57SEd Maste }
8581323ec57SEd Maste /* Return all matching principal names from the cert */
8591323ec57SEd Maste for (i = 0; i < cert->cert->nprincipals; i++) {
8601323ec57SEd Maste if (match_pattern(cert->cert->principals[i], cp)) {
8611323ec57SEd Maste if ((r = sshbuf_putf(nprincipals, "%s%s",
8621323ec57SEd Maste sshbuf_len(nprincipals) != 0 ? "," : "",
8631323ec57SEd Maste cert->cert->principals[i])) != 0) {
8641323ec57SEd Maste error_f("buffer error");
8651323ec57SEd Maste goto out;
8661323ec57SEd Maste }
8671323ec57SEd Maste }
8681323ec57SEd Maste }
8691323ec57SEd Maste }
8701323ec57SEd Maste if (sshbuf_len(nprincipals) == 0) {
8711323ec57SEd Maste error("%s:%lu: no valid principals found", path, linenum);
8721323ec57SEd Maste r = SSH_ERR_KEY_CERT_INVALID;
8731323ec57SEd Maste goto out;
8741323ec57SEd Maste }
8751323ec57SEd Maste if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
8761323ec57SEd Maste error_f("buffer error");
8771323ec57SEd Maste goto out;
8781323ec57SEd Maste }
8791323ec57SEd Maste /* success */
8801323ec57SEd Maste success = 1;
8811323ec57SEd Maste *principalsp = principals;
8821323ec57SEd Maste out:
8831323ec57SEd Maste sshbuf_free(nprincipals);
8841323ec57SEd Maste free(oprincipals);
8851323ec57SEd Maste return success ? 0 : r;
8861323ec57SEd Maste }
8871323ec57SEd Maste
8881323ec57SEd Maste 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)88919261079SEd Maste check_allowed_keys_line(const char *path, u_long linenum, char *line,
89019261079SEd Maste const struct sshkey *sign_key, const char *principal,
8911323ec57SEd Maste const char *sig_namespace, uint64_t verify_time, char **principalsp)
89219261079SEd Maste {
89319261079SEd Maste struct sshkey *found_key = NULL;
8941323ec57SEd Maste char *principals = NULL;
89519261079SEd Maste int r, success = 0;
89619261079SEd Maste const char *reason = NULL;
89719261079SEd Maste struct sshsigopt *sigopts = NULL;
89819261079SEd Maste char tvalid[64], tverify[64];
89919261079SEd Maste
9001323ec57SEd Maste if (principalsp != NULL)
9011323ec57SEd Maste *principalsp = NULL;
9021323ec57SEd Maste
90319261079SEd Maste /* Parse the line */
90419261079SEd Maste if ((r = parse_principals_key_and_options(path, linenum, line,
9051323ec57SEd Maste principal, &principals, &found_key, &sigopts)) != 0) {
90619261079SEd Maste /* error already logged */
90719261079SEd Maste goto done;
90819261079SEd Maste }
90919261079SEd Maste
91019261079SEd Maste if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
91119261079SEd Maste /* Exact match of key */
91219261079SEd Maste debug("%s:%lu: matched key", path, linenum);
91319261079SEd Maste } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
91419261079SEd Maste sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
9151323ec57SEd Maste if (principal) {
9161323ec57SEd Maste /* Match certificate CA key with specified principal */
91719261079SEd Maste if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
91819261079SEd Maste verify_time, principal, &reason)) != 0) {
91919261079SEd Maste error("%s:%lu: certificate not authorized: %s",
92019261079SEd Maste path, linenum, reason);
92119261079SEd Maste goto done;
92219261079SEd Maste }
9231323ec57SEd Maste debug("%s:%lu: matched certificate CA key",
9241323ec57SEd Maste path, linenum);
9251323ec57SEd Maste } else {
9261323ec57SEd Maste /* No principal specified - find all matching ones */
9271323ec57SEd Maste if ((r = cert_filter_principals(path, linenum,
9281323ec57SEd Maste &principals, sign_key, verify_time)) != 0) {
9291323ec57SEd Maste /* error already displayed */
9301323ec57SEd Maste debug_r(r, "%s:%lu: cert_filter_principals",
9311323ec57SEd Maste path, linenum);
9321323ec57SEd Maste goto done;
9331323ec57SEd Maste }
9341323ec57SEd Maste debug("%s:%lu: matched certificate CA key",
9351323ec57SEd Maste path, linenum);
9361323ec57SEd Maste }
93719261079SEd Maste } else {
93819261079SEd Maste /* Didn't match key */
93919261079SEd Maste goto done;
94019261079SEd Maste }
94119261079SEd Maste
94219261079SEd Maste /* Check whether options preclude the use of this key */
9431323ec57SEd Maste if (sigopts->namespaces != NULL && sig_namespace != NULL &&
94419261079SEd Maste match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
94519261079SEd Maste error("%s:%lu: key is not permitted for use in signature "
94619261079SEd Maste "namespace \"%s\"", path, linenum, sig_namespace);
94719261079SEd Maste goto done;
94819261079SEd Maste }
94919261079SEd Maste
95019261079SEd Maste /* check key time validity */
95119261079SEd Maste format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
95219261079SEd Maste if (sigopts->valid_after != 0 &&
95319261079SEd Maste (uint64_t)verify_time < sigopts->valid_after) {
95419261079SEd Maste format_absolute_time(sigopts->valid_after,
95519261079SEd Maste tvalid, sizeof(tvalid));
95619261079SEd Maste error("%s:%lu: key is not yet valid: "
95719261079SEd Maste "verify time %s < valid-after %s", path, linenum,
95819261079SEd Maste tverify, tvalid);
95919261079SEd Maste goto done;
96019261079SEd Maste }
96119261079SEd Maste if (sigopts->valid_before != 0 &&
96219261079SEd Maste (uint64_t)verify_time > sigopts->valid_before) {
96319261079SEd Maste format_absolute_time(sigopts->valid_before,
96419261079SEd Maste tvalid, sizeof(tvalid));
96519261079SEd Maste error("%s:%lu: key has expired: "
96619261079SEd Maste "verify time %s > valid-before %s", path, linenum,
96719261079SEd Maste tverify, tvalid);
96819261079SEd Maste goto done;
96919261079SEd Maste }
97019261079SEd Maste success = 1;
97119261079SEd Maste
97219261079SEd Maste done:
9731323ec57SEd Maste if (success && principalsp != NULL) {
9741323ec57SEd Maste *principalsp = principals;
9751323ec57SEd Maste principals = NULL; /* transferred */
9761323ec57SEd Maste }
9771323ec57SEd Maste free(principals);
97819261079SEd Maste sshkey_free(found_key);
97919261079SEd Maste sshsigopt_free(sigopts);
98019261079SEd Maste return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
98119261079SEd Maste }
98219261079SEd Maste
98319261079SEd Maste int
sshsig_check_allowed_keys(const char * path,const struct sshkey * sign_key,const char * principal,const char * sig_namespace,uint64_t verify_time)98419261079SEd Maste sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
98519261079SEd Maste const char *principal, const char *sig_namespace, uint64_t verify_time)
98619261079SEd Maste {
98719261079SEd Maste FILE *f = NULL;
98819261079SEd Maste char *line = NULL;
98919261079SEd Maste size_t linesize = 0;
99019261079SEd Maste u_long linenum = 0;
991535af610SEd Maste int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
99219261079SEd Maste
99319261079SEd Maste /* Check key and principal against file */
99419261079SEd Maste if ((f = fopen(path, "r")) == NULL) {
99519261079SEd Maste oerrno = errno;
99619261079SEd Maste error("Unable to open allowed keys file \"%s\": %s",
99719261079SEd Maste path, strerror(errno));
99819261079SEd Maste errno = oerrno;
99919261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
100019261079SEd Maste }
100119261079SEd Maste
100219261079SEd Maste while (getline(&line, &linesize, f) != -1) {
100319261079SEd Maste linenum++;
100419261079SEd Maste r = check_allowed_keys_line(path, linenum, line, sign_key,
10051323ec57SEd Maste principal, sig_namespace, verify_time, NULL);
100619261079SEd Maste free(line);
100719261079SEd Maste line = NULL;
100819261079SEd Maste linesize = 0;
100919261079SEd Maste if (r == SSH_ERR_KEY_NOT_FOUND)
101019261079SEd Maste continue;
101119261079SEd Maste else if (r == 0) {
101219261079SEd Maste /* success */
101319261079SEd Maste fclose(f);
101419261079SEd Maste return 0;
101519261079SEd Maste } else
101619261079SEd Maste break;
101719261079SEd Maste }
101819261079SEd Maste /* Either we hit an error parsing or we simply didn't find the key */
101919261079SEd Maste fclose(f);
102019261079SEd Maste free(line);
1021535af610SEd Maste return r;
102219261079SEd Maste }
102319261079SEd Maste
102419261079SEd Maste int
sshsig_find_principals(const char * path,const struct sshkey * sign_key,uint64_t verify_time,char ** principals)102519261079SEd Maste sshsig_find_principals(const char *path, const struct sshkey *sign_key,
102619261079SEd Maste uint64_t verify_time, char **principals)
102719261079SEd Maste {
102819261079SEd Maste FILE *f = NULL;
102919261079SEd Maste char *line = NULL;
103019261079SEd Maste size_t linesize = 0;
103119261079SEd Maste u_long linenum = 0;
1032535af610SEd Maste int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
103319261079SEd Maste
103419261079SEd Maste if ((f = fopen(path, "r")) == NULL) {
103519261079SEd Maste oerrno = errno;
103619261079SEd Maste error("Unable to open allowed keys file \"%s\": %s",
103719261079SEd Maste path, strerror(errno));
103819261079SEd Maste errno = oerrno;
103919261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
104019261079SEd Maste }
104119261079SEd Maste
104219261079SEd Maste while (getline(&line, &linesize, f) != -1) {
104319261079SEd Maste linenum++;
10441323ec57SEd Maste r = check_allowed_keys_line(path, linenum, line,
10451323ec57SEd Maste sign_key, NULL, NULL, verify_time, principals);
104619261079SEd Maste free(line);
104719261079SEd Maste line = NULL;
104819261079SEd Maste linesize = 0;
104919261079SEd Maste if (r == SSH_ERR_KEY_NOT_FOUND)
105019261079SEd Maste continue;
105119261079SEd Maste else if (r == 0) {
105219261079SEd Maste /* success */
105319261079SEd Maste fclose(f);
105419261079SEd Maste return 0;
105519261079SEd Maste } else
105619261079SEd Maste break;
105719261079SEd Maste }
105819261079SEd Maste free(line);
105919261079SEd Maste /* Either we hit an error parsing or we simply didn't find the key */
106019261079SEd Maste if (ferror(f) != 0) {
106119261079SEd Maste oerrno = errno;
106219261079SEd Maste fclose(f);
106319261079SEd Maste error("Unable to read allowed keys file \"%s\": %s",
106419261079SEd Maste path, strerror(errno));
106519261079SEd Maste errno = oerrno;
106619261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
106719261079SEd Maste }
106819261079SEd Maste fclose(f);
1069535af610SEd Maste return r;
107019261079SEd Maste }
107119261079SEd Maste
107219261079SEd Maste int
sshsig_match_principals(const char * path,const char * principal,char *** principalsp,size_t * nprincipalsp)10731323ec57SEd Maste sshsig_match_principals(const char *path, const char *principal,
10741323ec57SEd Maste char ***principalsp, size_t *nprincipalsp)
10751323ec57SEd Maste {
10761323ec57SEd Maste FILE *f = NULL;
10771323ec57SEd Maste char *found, *line = NULL, **principals = NULL, **tmp;
10781323ec57SEd Maste size_t i, nprincipals = 0, linesize = 0;
10791323ec57SEd Maste u_long linenum = 0;
10801323ec57SEd Maste int oerrno = 0, r, ret = 0;
10811323ec57SEd Maste
10821323ec57SEd Maste if (principalsp != NULL)
10831323ec57SEd Maste *principalsp = NULL;
10841323ec57SEd Maste if (nprincipalsp != NULL)
10851323ec57SEd Maste *nprincipalsp = 0;
10861323ec57SEd Maste
10871323ec57SEd Maste /* Check key and principal against file */
10881323ec57SEd Maste if ((f = fopen(path, "r")) == NULL) {
10891323ec57SEd Maste oerrno = errno;
10901323ec57SEd Maste error("Unable to open allowed keys file \"%s\": %s",
10911323ec57SEd Maste path, strerror(errno));
10921323ec57SEd Maste errno = oerrno;
10931323ec57SEd Maste return SSH_ERR_SYSTEM_ERROR;
10941323ec57SEd Maste }
10951323ec57SEd Maste
10961323ec57SEd Maste while (getline(&line, &linesize, f) != -1) {
10971323ec57SEd Maste linenum++;
10981323ec57SEd Maste /* Parse the line */
10991323ec57SEd Maste if ((r = parse_principals_key_and_options(path, linenum, line,
11001323ec57SEd Maste principal, &found, NULL, NULL)) != 0) {
11011323ec57SEd Maste if (r == SSH_ERR_KEY_NOT_FOUND)
11021323ec57SEd Maste continue;
11031323ec57SEd Maste ret = r;
11041323ec57SEd Maste oerrno = errno;
11051323ec57SEd Maste break; /* unexpected error */
11061323ec57SEd Maste }
11071323ec57SEd Maste if ((tmp = recallocarray(principals, nprincipals,
11081323ec57SEd Maste nprincipals + 1, sizeof(*principals))) == NULL) {
11091323ec57SEd Maste ret = SSH_ERR_ALLOC_FAIL;
11101323ec57SEd Maste free(found);
11111323ec57SEd Maste break;
11121323ec57SEd Maste }
11131323ec57SEd Maste principals = tmp;
11141323ec57SEd Maste principals[nprincipals++] = found; /* transferred */
11151323ec57SEd Maste free(line);
11161323ec57SEd Maste line = NULL;
11171323ec57SEd Maste linesize = 0;
11181323ec57SEd Maste }
11191323ec57SEd Maste fclose(f);
11201323ec57SEd Maste
11211323ec57SEd Maste if (ret == 0) {
11221323ec57SEd Maste if (nprincipals == 0)
11231323ec57SEd Maste ret = SSH_ERR_KEY_NOT_FOUND;
1124069ac184SEd Maste if (nprincipalsp != 0)
1125069ac184SEd Maste *nprincipalsp = nprincipals;
11261323ec57SEd Maste if (principalsp != NULL) {
11271323ec57SEd Maste *principalsp = principals;
11281323ec57SEd Maste principals = NULL; /* transferred */
11291323ec57SEd Maste nprincipals = 0;
11301323ec57SEd Maste }
11311323ec57SEd Maste }
11321323ec57SEd Maste
11331323ec57SEd Maste for (i = 0; i < nprincipals; i++)
11341323ec57SEd Maste free(principals[i]);
11351323ec57SEd Maste free(principals);
11361323ec57SEd Maste
11371323ec57SEd Maste errno = oerrno;
11381323ec57SEd Maste return ret;
11391323ec57SEd Maste }
11401323ec57SEd Maste
11411323ec57SEd Maste int
sshsig_get_pubkey(struct sshbuf * signature,struct sshkey ** pubkey)114219261079SEd Maste sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
114319261079SEd Maste {
114419261079SEd Maste struct sshkey *pk = NULL;
114519261079SEd Maste int r = SSH_ERR_SIGNATURE_INVALID;
114619261079SEd Maste
114719261079SEd Maste if (pubkey == NULL)
114819261079SEd Maste return SSH_ERR_INTERNAL_ERROR;
114919261079SEd Maste if ((r = sshsig_parse_preamble(signature)) != 0)
115019261079SEd Maste return r;
115119261079SEd Maste if ((r = sshkey_froms(signature, &pk)) != 0)
115219261079SEd Maste return r;
115319261079SEd Maste
115419261079SEd Maste *pubkey = pk;
115519261079SEd Maste pk = NULL;
115619261079SEd Maste return 0;
115719261079SEd Maste }
1158