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