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