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