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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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