1 /* $OpenBSD: sshsig.c,v 1.37 2024/11/26 22:05:51 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
46 int
sshsig_armor(const struct sshbuf * blob,struct sshbuf ** out)47 sshsig_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
85 int
sshsig_dearmor(struct sshbuf * sig,struct sshbuf ** out)86 sshsig_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 */
154 done:
155 sshbuf_free(buf);
156 sshbuf_free(sbuf);
157 free(b64);
158 return r;
159 }
160
161 static int
sshsig_wrap_sign(struct sshkey * key,const char * hashalg,const char * sk_provider,const char * sk_pin,const struct sshbuf * h_message,const char * sig_namespace,struct sshbuf ** out,sshsig_signer * signer,void * signer_ctx)162 sshsig_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;
231 done:
232 free(sig);
233 sshbuf_free(blob);
234 sshbuf_free(tosign);
235 return r;
236 }
237
238 /* Check preamble and version. */
239 static int
sshsig_parse_preamble(struct sshbuf * buf)240 sshsig_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
260 static int
sshsig_check_hashalg(const char * hashalg)261 sshsig_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
270 static int
sshsig_peek_hashalg(struct sshbuf * signature,char ** hashalgp)271 sshsig_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
302 static int
sshsig_wrap_verify(struct sshbuf * signature,const char * hashalg,const struct sshbuf * h_message,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)303 sshsig_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 }
393 done:
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
403 static int
hash_buffer(const struct sshbuf * m,const char * hashalg,struct sshbuf ** bp)404 hash_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
445 int
sshsig_signb(struct sshkey * key,const char * hashalg,const char * sk_provider,const char * sk_pin,const struct sshbuf * message,const char * sig_namespace,struct sshbuf ** out,sshsig_signer * signer,void * signer_ctx)446 sshsig_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
472 int
sshsig_verifyb(struct sshbuf * signature,const struct sshbuf * message,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)473 sshsig_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
503 static int
hash_file(int fd,const char * hashalg,struct sshbuf ** bp)504 hash_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
573 int
sshsig_sign_fd(struct sshkey * key,const char * hashalg,const char * sk_provider,const char * sk_pin,int fd,const char * sig_namespace,struct sshbuf ** out,sshsig_signer * signer,void * signer_ctx)574 sshsig_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
600 int
sshsig_verify_fd(struct sshbuf * signature,int fd,const char * expect_namespace,struct sshkey ** sign_keyp,struct sshkey_sig_details ** sig_details)601 sshsig_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
631 struct sshsigopt {
632 int ca;
633 char *namespaces;
634 uint64_t valid_after, valid_before;
635 };
636
637 struct sshsigopt *
sshsigopt_parse(const char * opts,const char * path,u_long linenum,const char ** errstrp)638 sshsigopt_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
724 void
sshsigopt_free(struct sshsigopt * opts)725 sshsigopt_free(struct sshsigopt *opts)
726 {
727 if (opts == NULL)
728 return;
729 free(opts->namespaces);
730 free(opts);
731 }
732
733 static int
parse_principals_key_and_options(const char * path,u_long linenum,char * line,const char * required_principal,char ** principalsp,struct sshkey ** keyp,struct sshsigopt ** sigoptsp)734 parse_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
835 static int
cert_filter_principals(const char * path,u_long linenum,char ** principalsp,const struct sshkey * cert,uint64_t verify_time)836 cert_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, 1, 0,
856 verify_time, 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 goto out;
881 }
882 /* success */
883 success = 1;
884 *principalsp = principals;
885 out:
886 sshbuf_free(nprincipals);
887 free(oprincipals);
888 return success ? 0 : r;
889 }
890
891 static int
check_allowed_keys_line(const char * path,u_long linenum,char * line,const struct sshkey * sign_key,const char * principal,const char * sig_namespace,uint64_t verify_time,char ** principalsp)892 check_allowed_keys_line(const char *path, u_long linenum, char *line,
893 const struct sshkey *sign_key, const char *principal,
894 const char *sig_namespace, uint64_t verify_time, char **principalsp)
895 {
896 struct sshkey *found_key = NULL;
897 char *principals = NULL;
898 int r, success = 0;
899 const char *reason = NULL;
900 struct sshsigopt *sigopts = NULL;
901 char tvalid[64], tverify[64];
902
903 if (principalsp != NULL)
904 *principalsp = NULL;
905
906 /* Parse the line */
907 if ((r = parse_principals_key_and_options(path, linenum, line,
908 principal, &principals, &found_key, &sigopts)) != 0) {
909 /* error already logged */
910 goto done;
911 }
912
913 if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
914 /* Exact match of key */
915 debug("%s:%lu: matched key", path, linenum);
916 } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
917 sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
918 if (principal) {
919 /* Match certificate CA key with specified principal */
920 if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
921 verify_time, principal, &reason)) != 0) {
922 error("%s:%lu: certificate not authorized: %s",
923 path, linenum, reason);
924 goto done;
925 }
926 debug("%s:%lu: matched certificate CA key",
927 path, linenum);
928 } else {
929 /* No principal specified - find all matching ones */
930 if ((r = cert_filter_principals(path, linenum,
931 &principals, sign_key, verify_time)) != 0) {
932 /* error already displayed */
933 debug_r(r, "%s:%lu: cert_filter_principals",
934 path, linenum);
935 goto done;
936 }
937 debug("%s:%lu: matched certificate CA key",
938 path, linenum);
939 }
940 } else {
941 /* Didn't match key */
942 goto done;
943 }
944
945 /* Check whether options preclude the use of this key */
946 if (sigopts->namespaces != NULL && sig_namespace != NULL &&
947 match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
948 error("%s:%lu: key is not permitted for use in signature "
949 "namespace \"%s\"", path, linenum, sig_namespace);
950 goto done;
951 }
952
953 /* check key time validity */
954 format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
955 if (sigopts->valid_after != 0 &&
956 (uint64_t)verify_time < sigopts->valid_after) {
957 format_absolute_time(sigopts->valid_after,
958 tvalid, sizeof(tvalid));
959 error("%s:%lu: key is not yet valid: "
960 "verify time %s < valid-after %s", path, linenum,
961 tverify, tvalid);
962 goto done;
963 }
964 if (sigopts->valid_before != 0 &&
965 (uint64_t)verify_time > sigopts->valid_before) {
966 format_absolute_time(sigopts->valid_before,
967 tvalid, sizeof(tvalid));
968 error("%s:%lu: key has expired: "
969 "verify time %s > valid-before %s", path, linenum,
970 tverify, tvalid);
971 goto done;
972 }
973 success = 1;
974
975 done:
976 if (success && principalsp != NULL) {
977 *principalsp = principals;
978 principals = NULL; /* transferred */
979 }
980 free(principals);
981 sshkey_free(found_key);
982 sshsigopt_free(sigopts);
983 return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
984 }
985
986 int
sshsig_check_allowed_keys(const char * path,const struct sshkey * sign_key,const char * principal,const char * sig_namespace,uint64_t verify_time)987 sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
988 const char *principal, const char *sig_namespace, uint64_t verify_time)
989 {
990 FILE *f = NULL;
991 char *line = NULL;
992 size_t linesize = 0;
993 u_long linenum = 0;
994 int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
995
996 /* Check key and principal against file */
997 if ((f = fopen(path, "r")) == NULL) {
998 oerrno = errno;
999 error("Unable to open allowed keys file \"%s\": %s",
1000 path, strerror(errno));
1001 errno = oerrno;
1002 return SSH_ERR_SYSTEM_ERROR;
1003 }
1004
1005 while (getline(&line, &linesize, f) != -1) {
1006 linenum++;
1007 r = check_allowed_keys_line(path, linenum, line, sign_key,
1008 principal, sig_namespace, verify_time, NULL);
1009 free(line);
1010 line = NULL;
1011 linesize = 0;
1012 if (r == SSH_ERR_KEY_NOT_FOUND)
1013 continue;
1014 else if (r == 0) {
1015 /* success */
1016 fclose(f);
1017 return 0;
1018 } else
1019 break;
1020 }
1021 /* Either we hit an error parsing or we simply didn't find the key */
1022 fclose(f);
1023 free(line);
1024 return r;
1025 }
1026
1027 int
sshsig_find_principals(const char * path,const struct sshkey * sign_key,uint64_t verify_time,char ** principals)1028 sshsig_find_principals(const char *path, const struct sshkey *sign_key,
1029 uint64_t verify_time, char **principals)
1030 {
1031 FILE *f = NULL;
1032 char *line = NULL;
1033 size_t linesize = 0;
1034 u_long linenum = 0;
1035 int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
1036
1037 if ((f = fopen(path, "r")) == NULL) {
1038 oerrno = errno;
1039 error("Unable to open allowed keys file \"%s\": %s",
1040 path, strerror(errno));
1041 errno = oerrno;
1042 return SSH_ERR_SYSTEM_ERROR;
1043 }
1044
1045 while (getline(&line, &linesize, f) != -1) {
1046 linenum++;
1047 r = check_allowed_keys_line(path, linenum, line,
1048 sign_key, NULL, NULL, verify_time, principals);
1049 free(line);
1050 line = NULL;
1051 linesize = 0;
1052 if (r == SSH_ERR_KEY_NOT_FOUND)
1053 continue;
1054 else if (r == 0) {
1055 /* success */
1056 fclose(f);
1057 return 0;
1058 } else
1059 break;
1060 }
1061 free(line);
1062 /* Either we hit an error parsing or we simply didn't find the key */
1063 if (ferror(f) != 0) {
1064 oerrno = errno;
1065 fclose(f);
1066 error("Unable to read allowed keys file \"%s\": %s",
1067 path, strerror(errno));
1068 errno = oerrno;
1069 return SSH_ERR_SYSTEM_ERROR;
1070 }
1071 fclose(f);
1072 return r;
1073 }
1074
1075 int
sshsig_match_principals(const char * path,const char * principal,char *** principalsp,size_t * nprincipalsp)1076 sshsig_match_principals(const char *path, const char *principal,
1077 char ***principalsp, size_t *nprincipalsp)
1078 {
1079 FILE *f = NULL;
1080 char *found, *line = NULL, **principals = NULL, **tmp;
1081 size_t i, nprincipals = 0, linesize = 0;
1082 u_long linenum = 0;
1083 int oerrno = 0, r, ret = 0;
1084
1085 if (principalsp != NULL)
1086 *principalsp = NULL;
1087 if (nprincipalsp != NULL)
1088 *nprincipalsp = 0;
1089
1090 /* Check key and principal against file */
1091 if ((f = fopen(path, "r")) == NULL) {
1092 oerrno = errno;
1093 error("Unable to open allowed keys file \"%s\": %s",
1094 path, strerror(errno));
1095 errno = oerrno;
1096 return SSH_ERR_SYSTEM_ERROR;
1097 }
1098
1099 while (getline(&line, &linesize, f) != -1) {
1100 linenum++;
1101 /* Parse the line */
1102 if ((r = parse_principals_key_and_options(path, linenum, line,
1103 principal, &found, NULL, NULL)) != 0) {
1104 if (r == SSH_ERR_KEY_NOT_FOUND)
1105 continue;
1106 ret = r;
1107 oerrno = errno;
1108 break; /* unexpected error */
1109 }
1110 if ((tmp = recallocarray(principals, nprincipals,
1111 nprincipals + 1, sizeof(*principals))) == NULL) {
1112 ret = SSH_ERR_ALLOC_FAIL;
1113 free(found);
1114 break;
1115 }
1116 principals = tmp;
1117 principals[nprincipals++] = found; /* transferred */
1118 free(line);
1119 line = NULL;
1120 linesize = 0;
1121 }
1122 fclose(f);
1123
1124 if (ret == 0) {
1125 if (nprincipals == 0)
1126 ret = SSH_ERR_KEY_NOT_FOUND;
1127 if (nprincipalsp != 0)
1128 *nprincipalsp = nprincipals;
1129 if (principalsp != NULL) {
1130 *principalsp = principals;
1131 principals = NULL; /* transferred */
1132 nprincipals = 0;
1133 }
1134 }
1135
1136 for (i = 0; i < nprincipals; i++)
1137 free(principals[i]);
1138 free(principals);
1139
1140 errno = oerrno;
1141 return ret;
1142 }
1143
1144 int
sshsig_get_pubkey(struct sshbuf * signature,struct sshkey ** pubkey)1145 sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
1146 {
1147 struct sshkey *pk = NULL;
1148 int r = SSH_ERR_SIGNATURE_INVALID;
1149
1150 if (pubkey == NULL)
1151 return SSH_ERR_INTERNAL_ERROR;
1152 if ((r = sshsig_parse_preamble(signature)) != 0)
1153 return r;
1154 if ((r = sshkey_froms(signature, &pk)) != 0)
1155 return r;
1156
1157 *pubkey = pk;
1158 pk = NULL;
1159 return 0;
1160 }
1161