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