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