jcs's openbsd hax
openbsd
at jcs 1162 lines 30 kB view raw
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}