[BACK]Return to sk-usbhid.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/sk-usbhid.c between version 1.21 and 1.22

version 1.21, 2020/08/27 01:06:18 version 1.22, 2020/08/27 01:07:51
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 2019 Markus Friedl   * Copyright (c) 2019 Markus Friedl
    * Copyright (c) 2020 Pedro Martelletto
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 37 
Line 38 
 #ifndef SK_STANDALONE  #ifndef SK_STANDALONE
 # include "log.h"  # include "log.h"
 # include "xmalloc.h"  # include "xmalloc.h"
   # include "misc.h"
 /*  /*
  * If building as part of OpenSSH, then rename exported functions.   * If building as part of OpenSSH, then rename exported functions.
  * This must be done before including sk-api.h.   * This must be done before including sk-api.h.
Line 57 
Line 59 
 #define SSH_FIDO_INIT_ARG       0  #define SSH_FIDO_INIT_ARG       0
 #endif  #endif
   
 #define MAX_FIDO_DEVICES        256  #define MAX_FIDO_DEVICES        8
   #define FIDO_POLL_MS            50
   #define SELECT_MS               15000
   #define POLL_SLEEP_NS           200000000
   
 /* Compatibility with OpenSSH 1.0.x */  /* Compatibility with OpenSSH 1.0.x */
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)  #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
Line 68 
Line 73 
         } while (0)          } while (0)
 #endif  #endif
   
   struct sk_usbhid {
           fido_dev_t *dev;
           char *path;
   };
   
 /* Return the version of the middleware API */  /* Return the version of the middleware API */
 uint32_t sk_api_version(void);  uint32_t sk_api_version(void);
   
Line 121 
Line 131 
         return SSH_SK_VERSION_MAJOR;          return SSH_SK_VERSION_MAJOR;
 }  }
   
 /* Select the first identified FIDO device attached to the system */  static struct sk_usbhid *
 static char *  sk_open(const char *path)
 pick_first_device(void)  
 {  {
         char *ret = NULL;          struct sk_usbhid *sk;
         fido_dev_info_t *devlist = NULL;  
         size_t olen = 0;  
         int r;          int r;
         const fido_dev_info_t *di;  
   
         if ((devlist = fido_dev_info_new(1)) == NULL) {          if (path == NULL) {
                 skdebug(__func__, "fido_dev_info_new failed");                  skdebug(__func__, "path == NULL");
                 goto out;                  return NULL;
         }          }
         if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {          if ((sk = calloc(1, sizeof(*sk))) == NULL) {
                 skdebug(__func__, "fido_dev_info_manifest failed: %s",                  skdebug(__func__, "calloc sk failed");
                   return NULL;
           }
           if ((sk->path = strdup(path)) == NULL) {
                   skdebug(__func__, "strdup path failed");
                   free(sk);
                   return NULL;
           }
           if ((sk->dev = fido_dev_new()) == NULL) {
                   skdebug(__func__, "fido_dev_new failed");
                   free(sk->path);
                   free(sk);
                   return NULL;
           }
           if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
                   skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
                     fido_strerr(r));                      fido_strerr(r));
                 goto out;                  fido_dev_free(&sk->dev);
                   free(sk->path);
                   free(sk);
                   return NULL;
         }          }
         if (olen != 1) {          return sk;
                 skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);  }
                 goto out;  
   static void
   sk_close(struct sk_usbhid *sk)
   {
           if (sk == NULL)
                   return;
           fido_dev_cancel(sk->dev); /* cancel any pending operation */
           fido_dev_close(sk->dev);
           fido_dev_free(&sk->dev);
           free(sk->path);
           free(sk);
   }
   
   static struct sk_usbhid **
   sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
   {
           const fido_dev_info_t *di;
           struct sk_usbhid **skv;
           size_t i;
   
           *nopen = 0;
           if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
                   skdebug(__func__, "calloc skv failed");
                   return NULL;
         }          }
         di = fido_dev_info_ptr(devlist, 0);          for (i = 0; i < ndevs; i++) {
         if ((ret = strdup(fido_dev_info_path(di))) == NULL) {                  if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
                 skdebug(__func__, "fido_dev_info_path failed");                          skdebug(__func__, "fido_dev_info_ptr failed");
                 goto out;                  else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
                           skdebug(__func__, "sk_open failed");
                   else
                           (*nopen)++;
         }          }
  out:          if (*nopen == 0) {
         fido_dev_info_free(&devlist, 1);                  for (i = 0; i < ndevs; i++)
         return ret;                          sk_close(skv[i]);
                   free(skv);
                   skv = NULL;
           }
   
           return skv;
 }  }
   
 /* Check if the specified key handle exists on a given device. */  static void
   sk_closev(struct sk_usbhid **skv, size_t nsk)
   {
           size_t i;
   
           for (i = 0; i < nsk; i++)
                   sk_close(skv[i]);
           free(skv);
   }
   
 static int  static int
 try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,  sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
     const char *application, const uint8_t *key_handle, size_t key_handle_len,  
     uint8_t flags, const char *pin)  
 {  {
           size_t i, ok = 0;
           int r;
   
           for (i = 0; i < nsk; i++)
                   if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
                           skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
                               " %s", skv[i]->path, fido_strerr(r));
                   else
                           ok++;
   
           return ok ? 0 : -1;
   }
   
   static int
   sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
   {
           struct timespec ts_pause;
           size_t npoll, i;
           int r;
   
           ts_pause.tv_sec = 0;
           ts_pause.tv_nsec = POLL_SLEEP_NS;
           nanosleep(&ts_pause, NULL);
           npoll = nsk;
           for (i = 0; i < nsk; i++) {
                   if (skv[i] == NULL)
                           continue; /* device discarded */
                   skdebug(__func__, "polling %s", skv[i]->path);
                   if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
                       FIDO_POLL_MS)) != FIDO_OK) {
                           skdebug(__func__, "fido_dev_get_touch_status %s: %s",
                               skv[i]->path, fido_strerr(r));
                           sk_close(skv[i]); /* discard device */
                           skv[i] = NULL;
                           if (--npoll == 0) {
                                   skdebug(__func__, "no device left to poll");
                                   return -1;
                           }
                   } else if (*touch) {
                           *idx = i;
                           return 0;
                   }
           }
           *touch = 0;
           return 0;
   }
   
   /* Calculate SHA256(m) */
   static int
   sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
   {
   #ifdef WITH_OPENSSL
           u_int mdlen;
   #endif
   
           if (dlen != 32)
                   return -1;
   #ifdef WITH_OPENSSL
           mdlen = dlen;
           if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
                   return -1;
   #else
           SHA256Data(m, mlen, d);
   #endif
           return 0;
   }
   
   /* Check if the specified key handle exists on a given sk. */
   static int
   sk_try(const struct sk_usbhid *sk, const char *application,
       const uint8_t *key_handle, size_t key_handle_len)
   {
         fido_assert_t *assert = NULL;          fido_assert_t *assert = NULL;
           /* generate an invalid signature on FIDO2 tokens */
           const char *data = "";
           uint8_t message[32];
         int r = FIDO_ERR_INTERNAL;          int r = FIDO_ERR_INTERNAL;
   
           if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) {
                   skdebug(__func__, "hash message failed");
                   goto out;
           }
         if ((assert = fido_assert_new()) == NULL) {          if ((assert = fido_assert_new()) == NULL) {
                 skdebug(__func__, "fido_assert_new failed");                  skdebug(__func__, "fido_assert_new failed");
                 goto out;                  goto out;
         }          }
         if ((r = fido_assert_set_clientdata_hash(assert, message,          if ((r = fido_assert_set_clientdata_hash(assert, message,
             message_len)) != FIDO_OK) {              sizeof(message))) != FIDO_OK) {
                 skdebug(__func__, "fido_assert_set_clientdata_hash: %s",                  skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
                     fido_strerr(r));                      fido_strerr(r));
                 goto out;                  goto out;
Line 186 
Line 327 
                 skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));                  skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
                 goto out;                  goto out;
         }          }
         r = fido_dev_get_assert(dev, assert, pin);          r = fido_dev_get_assert(sk->dev, assert, NULL);
         skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));          skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
         if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {          if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
                 /* U2F tokens may return this */                  /* U2F tokens may return this */
Line 198 
Line 339 
         return r != FIDO_OK ? -1 : 0;          return r != FIDO_OK ? -1 : 0;
 }  }
   
 /* Iterate over configured devices looking for a specific key handle */  static struct sk_usbhid *
 static fido_dev_t *  sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
 find_device(const char *path, const uint8_t *message, size_t message_len,      const char *application, const uint8_t *key_handle, size_t key_handle_len)
     const char *application, const uint8_t *key_handle, size_t key_handle_len,  
     uint8_t flags, const char *pin)  
 {  {
         fido_dev_info_t *devlist = NULL;          struct sk_usbhid **skv, *sk;
         fido_dev_t *dev = NULL;          size_t skvcnt, i;
         size_t devlist_len = 0, i;  
         int r;  
   
         if (path != NULL) {          if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
                 if ((dev = fido_dev_new()) == NULL) {                  skdebug(__func__, "sk_openv failed");
                         skdebug(__func__, "fido_dev_new failed");                  return NULL;
                         return NULL;  
                 }  
                 if ((r = fido_dev_open(dev, path)) != FIDO_OK) {  
                         skdebug(__func__, "fido_dev_open failed");  
                         fido_dev_free(&dev);  
                         return NULL;  
                 }  
                 return dev;  
         }          }
           sk = NULL;
           for (i = 0; i < skvcnt; i++)
                   if (sk_try(skv[i], application, key_handle,
                       key_handle_len) == 0) {
                           sk = skv[i];
                           skv[i] = NULL;
                           skdebug(__func__, "found key in %s", sk->path);
                           break;
                   }
           sk_closev(skv, skvcnt);
           return sk;
   }
   
         if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {  static struct sk_usbhid *
                 skdebug(__func__, "fido_dev_info_new failed");  sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
   {
           struct sk_usbhid **skv, *sk;
           struct timeval tv_start, tv_now, tv_delta;
           size_t skvcnt, idx;
           int touch, ms_remain;
   
           if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
                   skdebug(__func__, "sk_openv failed");
                   return NULL;
           }
           sk = NULL;
           if (skvcnt < 2) {
                   if (skvcnt == 1) {
                           /* single candidate */
                           sk = skv[0];
                           skv[0] = NULL;
                   }
                 goto out;                  goto out;
         }          }
         if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,          if (sk_touch_begin(skv, skvcnt) == -1) {
             &devlist_len)) != FIDO_OK) {                  skdebug(__func__, "sk_touch_begin failed");
                 skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));  
                 goto out;                  goto out;
         }          }
           monotime_tv(&tv_start);
         skdebug(__func__, "found %zu device(s)", devlist_len);          do {
                   if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
         for (i = 0; i < devlist_len; i++) {                          skdebug(__func__, "sk_touch_poll failed");
                 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);                          goto out;
   
                 if (di == NULL) {  
                         skdebug(__func__, "fido_dev_info_ptr %zu failed", i);  
                         continue;  
                 }                  }
                 if ((path = fido_dev_info_path(di)) == NULL) {                  if (touch) {
                         skdebug(__func__, "fido_dev_info_path %zu failed", i);                          sk = skv[idx];
                         continue;                          skv[idx] = NULL;
                           goto out;
                 }                  }
                 skdebug(__func__, "trying device %zu: %s", i, path);                  monotime_tv(&tv_now);
                 if ((dev = fido_dev_new()) == NULL) {                  timersub(&tv_now, &tv_start, &tv_delta);
                         skdebug(__func__, "fido_dev_new failed");                  ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
                         continue;                      tv_delta.tv_usec / 1000;
                 }          } while (ms_remain >= FIDO_POLL_MS);
                 if ((r = fido_dev_open(dev, path)) != FIDO_OK) {          skdebug(__func__, "timeout");
                         skdebug(__func__, "fido_dev_open failed");  out:
                         fido_dev_free(&dev);          sk_closev(skv, skvcnt);
                         continue;          return sk;
                 }  }
                 if (try_device(dev, message, message_len, application,  
                     key_handle, key_handle_len, flags, pin) == 0) {  
                         skdebug(__func__, "found key");  
                         break;  
                 }  
                 fido_dev_close(dev);  
                 fido_dev_free(&dev);  
         }  
   
  out:  static struct sk_usbhid *
         if (devlist != NULL)  sk_probe(const char *application, const uint8_t *key_handle,
                 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);      size_t key_handle_len)
   {
           struct sk_usbhid *sk;
           fido_dev_info_t *devlist;
           size_t ndevs;
           int r;
   
         return dev;          if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
                   skdebug(__func__, "fido_dev_info_new failed");
                   return NULL;
           }
           if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
               &ndevs)) != FIDO_OK) {
                   skdebug(__func__, "fido_dev_info_manifest failed: %s",
                       fido_strerr(r));
                   fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
                   return NULL;
           }
           skdebug(__func__, "%zu device(s) detected", ndevs);
           if (ndevs == 0) {
                   sk = NULL;
           } else if (application != NULL && key_handle != NULL) {
                   skdebug(__func__, "selecting sk by cred");
                   sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
                       key_handle_len);
           } else {
                   skdebug(__func__, "selecting sk by touch");
                   sk = sk_select_by_touch(devlist, ndevs);
           }
           fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
           return sk;
 }  }
   
 #ifdef WITH_OPENSSL  #ifdef WITH_OPENSSL
Line 445 
Line 619 
         return 0;          return 0;
 }  }
   
 static int  
 check_sk_extensions(fido_dev_t *dev, const char *ext, int *ret)  
 {  
         fido_cbor_info_t *info;  
         char * const *ptr;  
         size_t len, i;  
         int r;  
   
         *ret = 0;  
   
         if (!fido_dev_is_fido2(dev)) {  
                 skdebug(__func__, "device is not fido2");  
                 return 0;  
         }  
         if ((info = fido_cbor_info_new()) == NULL) {  
                 skdebug(__func__, "fido_cbor_info_new failed");  
                 return -1;  
         }  
         if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {  
                 skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));  
                 fido_cbor_info_free(&info);  
                 return -1;  
         }  
         ptr = fido_cbor_info_extensions_ptr(info);  
         len = fido_cbor_info_extensions_len(info);  
         for (i = 0; i < len; i++) {  
                 if (!strcmp(ptr[i], ext)) {  
                         *ret = 1;  
                         break;  
                 }  
         }  
         fido_cbor_info_free(&info);  
         skdebug(__func__, "extension %s %s", ext, *ret ? "present" : "absent");  
   
         return 0;  
 }  
   
 int  int
 sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,  sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
     const char *application, uint8_t flags, const char *pin,      const char *application, uint8_t flags, const char *pin,
     struct sk_option **options, struct sk_enroll_response **enroll_response)      struct sk_option **options, struct sk_enroll_response **enroll_response)
 {  {
         fido_cred_t *cred = NULL;          fido_cred_t *cred = NULL;
         fido_dev_t *dev = NULL;  
         const uint8_t *ptr;          const uint8_t *ptr;
         uint8_t user_id[32];          uint8_t user_id[32];
           struct sk_usbhid *sk = NULL;
         struct sk_enroll_response *response = NULL;          struct sk_enroll_response *response = NULL;
         size_t len;          size_t len;
         int credprot;          int credprot;
Line 505 
Line 642 
                 skdebug(__func__, "enroll_response == NULL");                  skdebug(__func__, "enroll_response == NULL");
                 goto out;                  goto out;
         }          }
           *enroll_response = NULL;
         memset(user_id, 0, sizeof(user_id));          memset(user_id, 0, sizeof(user_id));
         if (check_enroll_options(options, &device,          if (check_enroll_options(options, &device, user_id,
             user_id, sizeof(user_id)) != 0)              sizeof(user_id)) != 0)
                 goto out; /* error already logged */                  goto out; /* error already logged */
   
         *enroll_response = NULL;  
         switch(alg) {          switch(alg) {
 #ifdef WITH_OPENSSL  #ifdef WITH_OPENSSL
         case SSH_SK_ECDSA:          case SSH_SK_ECDSA:
Line 524 
Line 661 
                 skdebug(__func__, "unsupported key type %d", alg);                  skdebug(__func__, "unsupported key type %d", alg);
                 goto out;                  goto out;
         }          }
         if (device == NULL && (device = pick_first_device()) == NULL) {          if (device != NULL)
                 ret = SSH_SK_ERR_DEVICE_NOT_FOUND;                  sk = sk_open(device);
                 skdebug(__func__, "pick_first_device failed");          else
                   sk = sk_probe(NULL, NULL, 0);
           if (sk == NULL) {
                   skdebug(__func__, "failed to find sk");
                 goto out;                  goto out;
         }          }
         skdebug(__func__, "using device %s", device);          skdebug(__func__, "using device %s", sk->path);
         if ((cred = fido_cred_new()) == NULL) {          if ((cred = fido_cred_new()) == NULL) {
                 skdebug(__func__, "fido_cred_new failed");                  skdebug(__func__, "fido_cred_new failed");
                 goto out;                  goto out;
Line 558 
Line 698 
                 skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));                  skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
                 goto out;                  goto out;
         }          }
         if ((dev = fido_dev_new()) == NULL) {  
                 skdebug(__func__, "fido_dev_new failed");  
                 goto out;  
         }  
         if ((r = fido_dev_open(dev, device)) != FIDO_OK) {  
                 skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));  
                 goto out;  
         }  
         if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {          if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
                 if (check_sk_extensions(dev, "credProtect", &credprot) < 0) {                  if (!fido_dev_supports_cred_prot(sk->dev)) {
                         skdebug(__func__, "check_sk_extensions failed");                          skdebug(__func__, "%s does not support credprot, "
                         goto out;                              "refusing to create unprotected "
                 }                              "resident/verify-required key", sk->path);
                 if (credprot == 0) {  
                         skdebug(__func__, "refusing to create unprotected "  
                             "resident/verify-required key");  
                         ret = SSH_SK_ERR_UNSUPPORTED;                          ret = SSH_SK_ERR_UNSUPPORTED;
                         goto out;                          goto out;
                 }                  }
Line 589 
Line 718 
                         goto out;                          goto out;
                 }                  }
         }          }
         if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {          if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
                 skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));                  skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
                 ret = fidoerr_to_skerr(r);                  ret = fidoerr_to_skerr(r);
                 goto out;                  goto out;
Line 656 
Line 785 
                 free(response->attestation_cert);                  free(response->attestation_cert);
                 free(response);                  free(response);
         }          }
         if (dev != NULL) {          sk_close(sk);
                 fido_dev_close(dev);          fido_cred_free(&cred);
                 fido_dev_free(&dev);  
         }  
         if (cred != NULL) {  
                 fido_cred_free(&cred);  
         }  
         return ret;          return ret;
 }  }
   
Line 776 
Line 900 
         return 0;          return 0;
 }  }
   
 /* Calculate SHA256(m) */  
 static int  
 sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)  
 {  
 #ifdef WITH_OPENSSL  
         u_int mdlen;  
 #endif  
   
         if (dlen != 32)  
                 return -1;  
 #ifdef WITH_OPENSSL  
         mdlen = dlen;  
         if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))  
                 return -1;  
 #else  
         SHA256Data(m, mlen, d);  
 #endif  
         return 0;  
 }  
   
 int  int
 sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,  sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
     const char *application,      const char *application,
Line 805 
Line 909 
 {  {
         fido_assert_t *assert = NULL;          fido_assert_t *assert = NULL;
         char *device = NULL;          char *device = NULL;
         fido_dev_t *dev = NULL;          struct sk_usbhid *sk = NULL;
         struct sk_sign_response *response = NULL;          struct sk_sign_response *response = NULL;
         uint8_t message[32];          uint8_t message[32];
         int ret = SSH_SK_ERR_GENERAL;          int ret = SSH_SK_ERR_GENERAL;
Line 825 
Line 929 
                 skdebug(__func__, "hash message failed");                  skdebug(__func__, "hash message failed");
                 goto out;                  goto out;
         }          }
         if ((dev = find_device(device, message, sizeof(message),          if (device != NULL)
             application, key_handle, key_handle_len, flags, pin)) == NULL) {                  sk = sk_open(device);
                 skdebug(__func__, "couldn't find device for key handle");          else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
                   sk = sk_probe(NULL, NULL, 0);
           else
                   sk = sk_probe(application, key_handle, key_handle_len);
           if (sk == NULL) {
                   skdebug(__func__, "failed to find sk");
                 goto out;                  goto out;
         }          }
         if ((assert = fido_assert_new()) == NULL) {          if ((assert = fido_assert_new()) == NULL) {
Line 861 
Line 970 
                 ret = FIDO_ERR_PIN_REQUIRED;                  ret = FIDO_ERR_PIN_REQUIRED;
                 goto out;                  goto out;
         }          }
         if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {          if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
                 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));                  skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
                 ret = fidoerr_to_skerr(r);                  ret = fidoerr_to_skerr(r);
                 goto out;                  goto out;
Line 887 
Line 996 
                 free(response->sig_s);                  free(response->sig_s);
                 free(response);                  free(response);
         }          }
         if (dev != NULL) {          sk_close(sk);
                 fido_dev_close(dev);          fido_assert_free(&assert);
                 fido_dev_free(&dev);  
         }  
         if (assert != NULL) {  
                 fido_assert_free(&assert);  
         }  
         return ret;          return ret;
 }  }
   
 static int  static int
 read_rks(const char *devpath, const char *pin,  read_rks(struct sk_usbhid *sk, const char *pin,
     struct sk_resident_key ***rksp, size_t *nrksp)      struct sk_resident_key ***rksp, size_t *nrksp)
 {  {
         int ret = SSH_SK_ERR_GENERAL, r = -1;          int ret = SSH_SK_ERR_GENERAL, r = -1;
         fido_dev_t *dev = NULL;  
         fido_credman_metadata_t *metadata = NULL;          fido_credman_metadata_t *metadata = NULL;
         fido_credman_rp_t *rp = NULL;          fido_credman_rp_t *rp = NULL;
         fido_credman_rk_t *rk = NULL;          fido_credman_rk_t *rk = NULL;
Line 910 
Line 1013 
         const fido_cred_t *cred;          const fido_cred_t *cred;
         struct sk_resident_key *srk = NULL, **tmp;          struct sk_resident_key *srk = NULL, **tmp;
   
         if ((dev = fido_dev_new()) == NULL) {          if (pin == NULL) {
                 skdebug(__func__, "fido_dev_new failed");                  skdebug(__func__, "no PIN specified");
                 return ret;                  ret = SSH_SK_ERR_PIN_REQUIRED;
                   goto out;
         }          }
         if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {  
                 skdebug(__func__, "fido_dev_open %s failed: %s",  
                     devpath, fido_strerr(r));  
                 fido_dev_free(&dev);  
                 return ret;  
         }  
         if ((metadata = fido_credman_metadata_new()) == NULL) {          if ((metadata = fido_credman_metadata_new()) == NULL) {
                 skdebug(__func__, "alloc failed");                  skdebug(__func__, "alloc failed");
                 goto out;                  goto out;
         }          }
   
         if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {          if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
                 if (r == FIDO_ERR_INVALID_COMMAND) {                  if (r == FIDO_ERR_INVALID_COMMAND) {
                         skdebug(__func__, "device %s does not support "                          skdebug(__func__, "device %s does not support "
                             "resident keys", devpath);                              "resident keys", sk->path);
                         ret = 0;                          ret = 0;
                         goto out;                          goto out;
                 }                  }
                 skdebug(__func__, "get metadata for %s failed: %s",                  skdebug(__func__, "get metadata for %s failed: %s",
                     devpath, fido_strerr(r));                      sk->path, fido_strerr(r));
                 ret = fidoerr_to_skerr(r);                  ret = fidoerr_to_skerr(r);
                 goto out;                  goto out;
         }          }
Line 944 
Line 1042 
                 skdebug(__func__, "alloc rp failed");                  skdebug(__func__, "alloc rp failed");
                 goto out;                  goto out;
         }          }
         if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {          if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
                 skdebug(__func__, "get RPs for %s failed: %s",                  skdebug(__func__, "get RPs for %s failed: %s",
                     devpath, fido_strerr(r));                      sk->path, fido_strerr(r));
                 goto out;                  goto out;
         }          }
         nrp = fido_credman_rp_count(rp);          nrp = fido_credman_rp_count(rp);
         skdebug(__func__, "Device %s has resident keys for %zu RPs",          skdebug(__func__, "Device %s has resident keys for %zu RPs",
             devpath, nrp);              sk->path, nrp);
   
         /* Iterate over RP IDs that have resident keys */          /* Iterate over RP IDs that have resident keys */
         for (i = 0; i < nrp; i++) {          for (i = 0; i < nrp; i++) {
Line 968 
Line 1066 
                         skdebug(__func__, "alloc rk failed");                          skdebug(__func__, "alloc rk failed");
                         goto out;                          goto out;
                 }                  }
                 if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),                  if ((r = fido_credman_get_dev_rk(sk->dev,
                     rk, pin)) != 0) {                      fido_credman_rp_id(rp, i), rk, pin)) != 0) {
                         skdebug(__func__, "get RKs for %s slot %zu failed: %s",                          skdebug(__func__, "get RKs for %s slot %zu failed: %s",
                             devpath, i, fido_strerr(r));                              sk->path, i, fido_strerr(r));
                         goto out;                          goto out;
                 }                  }
                 nrk = fido_credman_rk_count(rk);                  nrk = fido_credman_rk_count(rk);
Line 985 
Line 1083 
                                 continue;                                  continue;
                         }                          }
                         skdebug(__func__, "Device %s RP \"%s\" slot %zu: "                          skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
                             "type %d flags 0x%02x prot 0x%02x", devpath,                              "type %d flags 0x%02x prot 0x%02x", sk->path,
                             fido_credman_rp_id(rp, i), j, fido_cred_type(cred),                              fido_credman_rp_id(rp, i), j, fido_cred_type(cred),
                             fido_cred_flags(cred), fido_cred_prot(cred));                              fido_cred_flags(cred), fido_cred_prot(cred));
   
Line 1044 
Line 1142 
         }          }
         fido_credman_rp_free(&rp);          fido_credman_rp_free(&rp);
         fido_credman_rk_free(&rk);          fido_credman_rk_free(&rk);
         fido_dev_close(dev);  
         fido_dev_free(&dev);  
         fido_credman_metadata_free(&metadata);          fido_credman_metadata_free(&metadata);
         return ret;          return ret;
 }  }
Line 1055 
Line 1151 
     struct sk_resident_key ***rksp, size_t *nrksp)      struct sk_resident_key ***rksp, size_t *nrksp)
 {  {
         int ret = SSH_SK_ERR_GENERAL, r = -1;          int ret = SSH_SK_ERR_GENERAL, r = -1;
         fido_dev_info_t *devlist = NULL;          size_t i, nrks = 0;
         size_t i, ndev = 0, nrks = 0;  
         const fido_dev_info_t *di;  
         struct sk_resident_key **rks = NULL;          struct sk_resident_key **rks = NULL;
           struct sk_usbhid *sk = NULL;
         char *device = NULL;          char *device = NULL;
   
         *rksp = NULL;          *rksp = NULL;
         *nrksp = 0;          *nrksp = 0;
   
Line 1067 
Line 1163 
   
         if (check_sign_load_resident_options(options, &device) != 0)          if (check_sign_load_resident_options(options, &device) != 0)
                 goto out; /* error already logged */                  goto out; /* error already logged */
         if (device != NULL) {          if (device != NULL)
                 skdebug(__func__, "trying %s", device);                  sk = sk_open(device);
                 if ((r = read_rks(device, pin, &rks, &nrks)) != 0) {          else
                         skdebug(__func__, "read_rks failed for %s", device);                  sk = sk_probe(NULL, NULL, 0);
                         ret = r;          if (sk == NULL) {
                         goto out;                  skdebug(__func__, "failed to find sk");
                 }                  goto out;
         } else {  
                 /* Try all devices */  
                 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {  
                         skdebug(__func__, "fido_dev_info_new failed");  
                         goto out;  
                 }  
                 if ((r = fido_dev_info_manifest(devlist,  
                     MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {  
                         skdebug(__func__, "fido_dev_info_manifest failed: %s",  
                             fido_strerr(r));  
                         goto out;  
                 }  
                 for (i = 0; i < ndev; i++) {  
                         if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {  
                                 skdebug(__func__, "no dev info at %zu", i);  
                                 continue;  
                         }  
                         skdebug(__func__, "trying %s", fido_dev_info_path(di));  
                         if ((r = read_rks(fido_dev_info_path(di), pin,  
                             &rks, &nrks)) != 0) {  
                                 skdebug(__func__, "read_rks failed for %s",  
                                     fido_dev_info_path(di));  
                                 /* remember last error */  
                                 ret = r;  
                                 continue;  
                         }  
                 }  
         }          }
           skdebug(__func__, "trying %s", sk->path);
           if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
                   skdebug(__func__, "read_rks failed for %s", sk->path);
                   ret = r;
                   goto out;
           }
         /* success, unless we have no keys but a specific error */          /* success, unless we have no keys but a specific error */
         if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)          if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
                 ret = 0;                  ret = 0;
Line 1110 
Line 1185 
         rks = NULL;          rks = NULL;
         nrks = 0;          nrks = 0;
  out:   out:
         free(device);          sk_close(sk);
         for (i = 0; i < nrks; i++) {          for (i = 0; i < nrks; i++) {
                 free(rks[i]->application);                  free(rks[i]->application);
                 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);                  freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
Line 1118 
Line 1193 
                 freezero(rks[i], sizeof(*rks[i]));                  freezero(rks[i], sizeof(*rks[i]));
         }          }
         free(rks);          free(rks);
         fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);  
         return ret;          return ret;
 }  }
   

Legend:
Removed from v.1.21  
changed lines
  Added in v.1.22