diff options
Diffstat (limited to 'ssh-sk.c')
-rw-r--r-- | ssh-sk.c | 121 |
1 files changed, 104 insertions, 17 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.c,v 1.23 2019/12/30 09:24:45 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.c,v 1.24 2020/01/06 02:00:47 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -53,29 +53,32 @@ struct sshsk_provider { | |||
53 | /* Enroll a U2F key (private key generation) */ | 53 | /* Enroll a U2F key (private key generation) */ |
54 | int (*sk_enroll)(int alg, const uint8_t *challenge, | 54 | int (*sk_enroll)(int alg, const uint8_t *challenge, |
55 | size_t challenge_len, const char *application, uint8_t flags, | 55 | size_t challenge_len, const char *application, uint8_t flags, |
56 | const char *pin, struct sk_enroll_response **enroll_response); | 56 | const char *pin, struct sk_option **opts, |
57 | struct sk_enroll_response **enroll_response); | ||
57 | 58 | ||
58 | /* Sign a challenge */ | 59 | /* Sign a challenge */ |
59 | int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, | 60 | int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, |
60 | const char *application, | 61 | const char *application, |
61 | const uint8_t *key_handle, size_t key_handle_len, | 62 | const uint8_t *key_handle, size_t key_handle_len, |
62 | uint8_t flags, const char *pin, | 63 | uint8_t flags, const char *pin, struct sk_option **opts, |
63 | struct sk_sign_response **sign_response); | 64 | struct sk_sign_response **sign_response); |
64 | 65 | ||
65 | /* Enumerate resident keys */ | 66 | /* Enumerate resident keys */ |
66 | int (*sk_load_resident_keys)(const char *pin, | 67 | int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, |
67 | struct sk_resident_key ***rks, size_t *nrks); | 68 | struct sk_resident_key ***rks, size_t *nrks); |
68 | }; | 69 | }; |
69 | 70 | ||
70 | /* Built-in version */ | 71 | /* Built-in version */ |
71 | int ssh_sk_enroll(int alg, const uint8_t *challenge, | 72 | int ssh_sk_enroll(int alg, const uint8_t *challenge, |
72 | size_t challenge_len, const char *application, uint8_t flags, | 73 | size_t challenge_len, const char *application, uint8_t flags, |
73 | const char *pin, struct sk_enroll_response **enroll_response); | 74 | const char *pin, struct sk_option **opts, |
75 | struct sk_enroll_response **enroll_response); | ||
74 | int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, | 76 | int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, |
75 | const char *application, | 77 | const char *application, |
76 | const uint8_t *key_handle, size_t key_handle_len, | 78 | const uint8_t *key_handle, size_t key_handle_len, |
77 | uint8_t flags, const char *pin, struct sk_sign_response **sign_response); | 79 | uint8_t flags, const char *pin, struct sk_option **opts, |
78 | int ssh_sk_load_resident_keys(const char *pin, | 80 | struct sk_sign_response **sign_response); |
81 | int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, | ||
79 | struct sk_resident_key ***rks, size_t *nrks); | 82 | struct sk_resident_key ***rks, size_t *nrks); |
80 | 83 | ||
81 | static void | 84 | static void |
@@ -339,9 +342,80 @@ skerr_to_ssherr(int skerr) | |||
339 | } | 342 | } |
340 | } | 343 | } |
341 | 344 | ||
345 | static void | ||
346 | sshsk_free_options(struct sk_option **opts) | ||
347 | { | ||
348 | size_t i; | ||
349 | |||
350 | if (opts == NULL) | ||
351 | return; | ||
352 | for (i = 0; opts[i] != NULL; i++) { | ||
353 | free(opts[i]->name); | ||
354 | free(opts[i]->value); | ||
355 | free(opts[i]); | ||
356 | } | ||
357 | free(opts); | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, | ||
362 | const char *name, const char *value, uint8_t required) | ||
363 | { | ||
364 | struct sk_option **opts = *optsp; | ||
365 | size_t nopts = *noptsp; | ||
366 | |||
367 | if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ | ||
368 | sizeof(*opts))) == NULL) { | ||
369 | error("%s: array alloc failed", __func__); | ||
370 | return SSH_ERR_ALLOC_FAIL; | ||
371 | } | ||
372 | *optsp = opts; | ||
373 | *noptsp = nopts + 1; | ||
374 | if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { | ||
375 | error("%s: alloc failed", __func__); | ||
376 | return SSH_ERR_ALLOC_FAIL; | ||
377 | } | ||
378 | if ((opts[nopts]->name = strdup(name)) == NULL || | ||
379 | (opts[nopts]->value = strdup(value)) == NULL) { | ||
380 | error("%s: alloc failed", __func__); | ||
381 | return SSH_ERR_ALLOC_FAIL; | ||
382 | } | ||
383 | opts[nopts]->required = required; | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static int | ||
388 | make_options(const char *device, const char *user_id, | ||
389 | struct sk_option ***optsp) | ||
390 | { | ||
391 | struct sk_option **opts = NULL; | ||
392 | size_t nopts = 0; | ||
393 | int r, ret = SSH_ERR_INTERNAL_ERROR; | ||
394 | |||
395 | if (device != NULL && | ||
396 | (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { | ||
397 | ret = r; | ||
398 | goto out; | ||
399 | } | ||
400 | if (user_id != NULL && | ||
401 | (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { | ||
402 | ret = r; | ||
403 | goto out; | ||
404 | } | ||
405 | /* success */ | ||
406 | *optsp = opts; | ||
407 | opts = NULL; | ||
408 | nopts = 0; | ||
409 | ret = 0; | ||
410 | out: | ||
411 | sshsk_free_options(opts); | ||
412 | return ret; | ||
413 | } | ||
414 | |||
342 | int | 415 | int |
343 | sshsk_enroll(int type, const char *provider_path, const char *application, | 416 | sshsk_enroll(int type, const char *provider_path, const char *device, |
344 | uint8_t flags, const char *pin, struct sshbuf *challenge_buf, | 417 | const char *application, const char *userid, uint8_t flags, |
418 | const char *pin, struct sshbuf *challenge_buf, | ||
345 | struct sshkey **keyp, struct sshbuf *attest) | 419 | struct sshkey **keyp, struct sshbuf *attest) |
346 | { | 420 | { |
347 | struct sshsk_provider *skp = NULL; | 421 | struct sshsk_provider *skp = NULL; |
@@ -350,17 +424,23 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
350 | const u_char *challenge; | 424 | const u_char *challenge; |
351 | size_t challenge_len; | 425 | size_t challenge_len; |
352 | struct sk_enroll_response *resp = NULL; | 426 | struct sk_enroll_response *resp = NULL; |
427 | struct sk_option **opts = NULL; | ||
353 | int r = SSH_ERR_INTERNAL_ERROR; | 428 | int r = SSH_ERR_INTERNAL_ERROR; |
354 | int alg; | 429 | int alg; |
355 | 430 | ||
356 | debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, " | 431 | debug("%s: provider \"%s\", device \"%s\", application \"%s\", " |
357 | "challenge len %zu%s", __func__, provider_path, application, | 432 | "userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__, |
358 | flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), | 433 | provider_path, device, application, userid, flags, |
434 | challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), | ||
359 | (pin != NULL && *pin != '\0') ? " with-pin" : ""); | 435 | (pin != NULL && *pin != '\0') ? " with-pin" : ""); |
360 | 436 | ||
361 | *keyp = NULL; | 437 | *keyp = NULL; |
362 | if (attest) | 438 | if (attest) |
363 | sshbuf_reset(attest); | 439 | sshbuf_reset(attest); |
440 | |||
441 | if ((r = make_options(device, userid, &opts)) != 0) | ||
442 | goto out; | ||
443 | |||
364 | switch (type) { | 444 | switch (type) { |
365 | #ifdef WITH_OPENSSL | 445 | #ifdef WITH_OPENSSL |
366 | case KEY_ECDSA_SK: | 446 | case KEY_ECDSA_SK: |
@@ -407,7 +487,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
407 | /* XXX validate flags? */ | 487 | /* XXX validate flags? */ |
408 | /* enroll key */ | 488 | /* enroll key */ |
409 | if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, | 489 | if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, |
410 | flags, pin, &resp)) != 0) { | 490 | flags, pin, opts, &resp)) != 0) { |
411 | error("Security key provider \"%s\" returned failure %d", | 491 | error("Security key provider \"%s\" returned failure %d", |
412 | provider_path, r); | 492 | provider_path, r); |
413 | r = skerr_to_ssherr(r); | 493 | r = skerr_to_ssherr(r); |
@@ -437,6 +517,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
437 | key = NULL; /* transferred */ | 517 | key = NULL; /* transferred */ |
438 | r = 0; | 518 | r = 0; |
439 | out: | 519 | out: |
520 | sshsk_free_options(opts); | ||
440 | sshsk_free(skp); | 521 | sshsk_free(skp); |
441 | sshkey_free(key); | 522 | sshkey_free(key); |
442 | sshsk_free_enroll_response(resp); | 523 | sshsk_free_enroll_response(resp); |
@@ -528,6 +609,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
528 | struct sk_sign_response *resp = NULL; | 609 | struct sk_sign_response *resp = NULL; |
529 | struct sshbuf *inner_sig = NULL, *sig = NULL; | 610 | struct sshbuf *inner_sig = NULL, *sig = NULL; |
530 | uint8_t message[32]; | 611 | uint8_t message[32]; |
612 | struct sk_option **opts = NULL; | ||
531 | 613 | ||
532 | debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, | 614 | debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, |
533 | provider_path, sshkey_type(key), key->sk_flags, | 615 | provider_path, sshkey_type(key), key->sk_flags, |
@@ -571,7 +653,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
571 | if ((r = skp->sk_sign(alg, message, sizeof(message), | 653 | if ((r = skp->sk_sign(alg, message, sizeof(message), |
572 | key->sk_application, | 654 | key->sk_application, |
573 | sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), | 655 | sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), |
574 | key->sk_flags, pin, &resp)) != 0) { | 656 | key->sk_flags, pin, opts, &resp)) != 0) { |
575 | debug("%s: sk_sign failed with code %d", __func__, r); | 657 | debug("%s: sk_sign failed with code %d", __func__, r); |
576 | r = skerr_to_ssherr(r); | 658 | r = skerr_to_ssherr(r); |
577 | goto out; | 659 | goto out; |
@@ -617,6 +699,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
617 | /* success */ | 699 | /* success */ |
618 | r = 0; | 700 | r = 0; |
619 | out: | 701 | out: |
702 | sshsk_free_options(opts); | ||
620 | explicit_bzero(message, sizeof(message)); | 703 | explicit_bzero(message, sizeof(message)); |
621 | sshsk_free(skp); | 704 | sshsk_free(skp); |
622 | sshsk_free_sign_response(resp); | 705 | sshsk_free_sign_response(resp); |
@@ -645,8 +728,8 @@ sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) | |||
645 | } | 728 | } |
646 | 729 | ||
647 | int | 730 | int |
648 | sshsk_load_resident(const char *provider_path, const char *pin, | 731 | sshsk_load_resident(const char *provider_path, const char *device, |
649 | struct sshkey ***keysp, size_t *nkeysp) | 732 | const char *pin, struct sshkey ***keysp, size_t *nkeysp) |
650 | { | 733 | { |
651 | struct sshsk_provider *skp = NULL; | 734 | struct sshsk_provider *skp = NULL; |
652 | int r = SSH_ERR_INTERNAL_ERROR; | 735 | int r = SSH_ERR_INTERNAL_ERROR; |
@@ -654,6 +737,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
654 | size_t i, nrks = 0, nkeys = 0; | 737 | size_t i, nrks = 0, nkeys = 0; |
655 | struct sshkey *key = NULL, **keys = NULL, **tmp; | 738 | struct sshkey *key = NULL, **keys = NULL, **tmp; |
656 | uint8_t flags; | 739 | uint8_t flags; |
740 | struct sk_option **opts = NULL; | ||
657 | 741 | ||
658 | debug("%s: provider \"%s\"%s", __func__, provider_path, | 742 | debug("%s: provider \"%s\"%s", __func__, provider_path, |
659 | (pin != NULL && *pin != '\0') ? ", have-pin": ""); | 743 | (pin != NULL && *pin != '\0') ? ", have-pin": ""); |
@@ -663,11 +747,13 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
663 | *keysp = NULL; | 747 | *keysp = NULL; |
664 | *nkeysp = 0; | 748 | *nkeysp = 0; |
665 | 749 | ||
750 | if ((r = make_options(device, NULL, &opts)) != 0) | ||
751 | goto out; | ||
666 | if ((skp = sshsk_open(provider_path)) == NULL) { | 752 | if ((skp = sshsk_open(provider_path)) == NULL) { |
667 | r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ | 753 | r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ |
668 | goto out; | 754 | goto out; |
669 | } | 755 | } |
670 | if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) { | 756 | if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { |
671 | error("Security key provider \"%s\" returned failure %d", | 757 | error("Security key provider \"%s\" returned failure %d", |
672 | provider_path, r); | 758 | provider_path, r); |
673 | r = skerr_to_ssherr(r); | 759 | r = skerr_to_ssherr(r); |
@@ -710,6 +796,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
710 | nkeys = 0; | 796 | nkeys = 0; |
711 | r = 0; | 797 | r = 0; |
712 | out: | 798 | out: |
799 | sshsk_free_options(opts); | ||
713 | sshsk_free(skp); | 800 | sshsk_free(skp); |
714 | sshsk_free_sk_resident_keys(rks, nrks); | 801 | sshsk_free_sk_resident_keys(rks, nrks); |
715 | sshkey_free(key); | 802 | sshkey_free(key); |