diff options
-rw-r--r-- | sk-usbhid.c | 578 | ||||
-rw-r--r-- | ssh-keygen.c | 7 |
2 files changed, 332 insertions, 253 deletions
diff --git a/sk-usbhid.c b/sk-usbhid.c index 1dd834883..2efb377c5 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2019 Markus Friedl | 2 | * Copyright (c) 2019 Markus Friedl |
3 | * Copyright (c) 2020 Pedro Martelletto | ||
3 | * | 4 | * |
4 | * Permission to use, copy, modify, and distribute this software for any | 5 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above | 6 | * purpose with or without fee is hereby granted, provided that the above |
@@ -43,6 +44,7 @@ | |||
43 | #ifndef SK_STANDALONE | 44 | #ifndef SK_STANDALONE |
44 | # include "log.h" | 45 | # include "log.h" |
45 | # include "xmalloc.h" | 46 | # include "xmalloc.h" |
47 | # include "misc.h" | ||
46 | /* | 48 | /* |
47 | * If building as part of OpenSSH, then rename exported functions. | 49 | * If building as part of OpenSSH, then rename exported functions. |
48 | * This must be done before including sk-api.h. | 50 | * This must be done before including sk-api.h. |
@@ -63,7 +65,10 @@ | |||
63 | #define SSH_FIDO_INIT_ARG 0 | 65 | #define SSH_FIDO_INIT_ARG 0 |
64 | #endif | 66 | #endif |
65 | 67 | ||
66 | #define MAX_FIDO_DEVICES 256 | 68 | #define MAX_FIDO_DEVICES 8 |
69 | #define FIDO_POLL_MS 50 | ||
70 | #define SELECT_MS 15000 | ||
71 | #define POLL_SLEEP_NS 200000000 | ||
67 | 72 | ||
68 | /* Compatibility with OpenSSH 1.0.x */ | 73 | /* Compatibility with OpenSSH 1.0.x */ |
69 | #if (OPENSSL_VERSION_NUMBER < 0x10100000L) | 74 | #if (OPENSSL_VERSION_NUMBER < 0x10100000L) |
@@ -74,6 +79,11 @@ | |||
74 | } while (0) | 79 | } while (0) |
75 | #endif | 80 | #endif |
76 | 81 | ||
82 | struct sk_usbhid { | ||
83 | fido_dev_t *dev; | ||
84 | char *path; | ||
85 | }; | ||
86 | |||
77 | /* Return the version of the middleware API */ | 87 | /* Return the version of the middleware API */ |
78 | uint32_t sk_api_version(void); | 88 | uint32_t sk_api_version(void); |
79 | 89 | ||
@@ -127,54 +137,185 @@ sk_api_version(void) | |||
127 | return SSH_SK_VERSION_MAJOR; | 137 | return SSH_SK_VERSION_MAJOR; |
128 | } | 138 | } |
129 | 139 | ||
130 | /* Select the first identified FIDO device attached to the system */ | 140 | static struct sk_usbhid * |
131 | static char * | 141 | sk_open(const char *path) |
132 | pick_first_device(void) | ||
133 | { | 142 | { |
134 | char *ret = NULL; | 143 | struct sk_usbhid *sk; |
135 | fido_dev_info_t *devlist = NULL; | ||
136 | size_t olen = 0; | ||
137 | int r; | 144 | int r; |
138 | const fido_dev_info_t *di; | ||
139 | 145 | ||
140 | if ((devlist = fido_dev_info_new(1)) == NULL) { | 146 | if (path == NULL) { |
141 | skdebug(__func__, "fido_dev_info_new failed"); | 147 | skdebug(__func__, "path == NULL"); |
142 | goto out; | 148 | return NULL; |
143 | } | 149 | } |
144 | if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { | 150 | if ((sk = calloc(1, sizeof(*sk))) == NULL) { |
145 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | 151 | skdebug(__func__, "calloc sk failed"); |
152 | return NULL; | ||
153 | } | ||
154 | if ((sk->path = strdup(path)) == NULL) { | ||
155 | skdebug(__func__, "strdup path failed"); | ||
156 | free(sk); | ||
157 | return NULL; | ||
158 | } | ||
159 | if ((sk->dev = fido_dev_new()) == NULL) { | ||
160 | skdebug(__func__, "fido_dev_new failed"); | ||
161 | free(sk->path); | ||
162 | free(sk); | ||
163 | return NULL; | ||
164 | } | ||
165 | if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) { | ||
166 | skdebug(__func__, "fido_dev_open %s failed: %s", sk->path, | ||
146 | fido_strerr(r)); | 167 | fido_strerr(r)); |
147 | goto out; | 168 | fido_dev_free(&sk->dev); |
169 | free(sk->path); | ||
170 | free(sk); | ||
171 | return NULL; | ||
148 | } | 172 | } |
149 | if (olen != 1) { | 173 | return sk; |
150 | skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); | 174 | } |
151 | goto out; | 175 | |
176 | static void | ||
177 | sk_close(struct sk_usbhid *sk) | ||
178 | { | ||
179 | if (sk == NULL) | ||
180 | return; | ||
181 | fido_dev_cancel(sk->dev); /* cancel any pending operation */ | ||
182 | fido_dev_close(sk->dev); | ||
183 | fido_dev_free(&sk->dev); | ||
184 | free(sk->path); | ||
185 | free(sk); | ||
186 | } | ||
187 | |||
188 | static struct sk_usbhid ** | ||
189 | sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen) | ||
190 | { | ||
191 | const fido_dev_info_t *di; | ||
192 | struct sk_usbhid **skv; | ||
193 | size_t i; | ||
194 | |||
195 | *nopen = 0; | ||
196 | if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) { | ||
197 | skdebug(__func__, "calloc skv failed"); | ||
198 | return NULL; | ||
152 | } | 199 | } |
153 | di = fido_dev_info_ptr(devlist, 0); | 200 | for (i = 0; i < ndevs; i++) { |
154 | if ((ret = strdup(fido_dev_info_path(di))) == NULL) { | 201 | if ((di = fido_dev_info_ptr(devlist, i)) == NULL) |
155 | skdebug(__func__, "fido_dev_info_path failed"); | 202 | skdebug(__func__, "fido_dev_info_ptr failed"); |
156 | goto out; | 203 | else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL) |
204 | skdebug(__func__, "sk_open failed"); | ||
205 | else | ||
206 | (*nopen)++; | ||
157 | } | 207 | } |
158 | out: | 208 | if (*nopen == 0) { |
159 | fido_dev_info_free(&devlist, 1); | 209 | for (i = 0; i < ndevs; i++) |
160 | return ret; | 210 | sk_close(skv[i]); |
211 | free(skv); | ||
212 | skv = NULL; | ||
213 | } | ||
214 | |||
215 | return skv; | ||
216 | } | ||
217 | |||
218 | static void | ||
219 | sk_closev(struct sk_usbhid **skv, size_t nsk) | ||
220 | { | ||
221 | size_t i; | ||
222 | |||
223 | for (i = 0; i < nsk; i++) | ||
224 | sk_close(skv[i]); | ||
225 | free(skv); | ||
161 | } | 226 | } |
162 | 227 | ||
163 | /* Check if the specified key handle exists on a given device. */ | ||
164 | static int | 228 | static int |
165 | try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, | 229 | sk_touch_begin(struct sk_usbhid **skv, size_t nsk) |
166 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | 230 | { |
167 | uint8_t flags, const char *pin) | 231 | size_t i, ok = 0; |
232 | int r; | ||
233 | |||
234 | for (i = 0; i < nsk; i++) | ||
235 | if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK) | ||
236 | skdebug(__func__, "fido_dev_get_touch_begin %s failed:" | ||
237 | " %s", skv[i]->path, fido_strerr(r)); | ||
238 | else | ||
239 | ok++; | ||
240 | |||
241 | return ok ? 0 : -1; | ||
242 | } | ||
243 | |||
244 | static int | ||
245 | sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx) | ||
246 | { | ||
247 | struct timespec ts_pause; | ||
248 | size_t npoll, i; | ||
249 | int r; | ||
250 | |||
251 | ts_pause.tv_sec = 0; | ||
252 | ts_pause.tv_nsec = POLL_SLEEP_NS; | ||
253 | nanosleep(&ts_pause, NULL); | ||
254 | npoll = nsk; | ||
255 | for (i = 0; i < nsk; i++) { | ||
256 | if (skv[i] == NULL) | ||
257 | continue; /* device discarded */ | ||
258 | skdebug(__func__, "polling %s", skv[i]->path); | ||
259 | if ((r = fido_dev_get_touch_status(skv[i]->dev, touch, | ||
260 | FIDO_POLL_MS)) != FIDO_OK) { | ||
261 | skdebug(__func__, "fido_dev_get_touch_status %s: %s", | ||
262 | skv[i]->path, fido_strerr(r)); | ||
263 | sk_close(skv[i]); /* discard device */ | ||
264 | skv[i] = NULL; | ||
265 | if (--npoll == 0) { | ||
266 | skdebug(__func__, "no device left to poll"); | ||
267 | return -1; | ||
268 | } | ||
269 | } else if (*touch) { | ||
270 | *idx = i; | ||
271 | return 0; | ||
272 | } | ||
273 | } | ||
274 | *touch = 0; | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /* Calculate SHA256(m) */ | ||
279 | static int | ||
280 | sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen) | ||
281 | { | ||
282 | #ifdef WITH_OPENSSL | ||
283 | u_int mdlen; | ||
284 | #endif | ||
285 | |||
286 | if (dlen != 32) | ||
287 | return -1; | ||
288 | #ifdef WITH_OPENSSL | ||
289 | mdlen = dlen; | ||
290 | if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL)) | ||
291 | return -1; | ||
292 | #else | ||
293 | SHA256Data(m, mlen, d); | ||
294 | #endif | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | /* Check if the specified key handle exists on a given sk. */ | ||
299 | static int | ||
300 | sk_try(const struct sk_usbhid *sk, const char *application, | ||
301 | const uint8_t *key_handle, size_t key_handle_len) | ||
168 | { | 302 | { |
169 | fido_assert_t *assert = NULL; | 303 | fido_assert_t *assert = NULL; |
304 | /* generate an invalid signature on FIDO2 tokens */ | ||
305 | const char *data = ""; | ||
306 | uint8_t message[32]; | ||
170 | int r = FIDO_ERR_INTERNAL; | 307 | int r = FIDO_ERR_INTERNAL; |
171 | 308 | ||
309 | if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) { | ||
310 | skdebug(__func__, "hash message failed"); | ||
311 | goto out; | ||
312 | } | ||
172 | if ((assert = fido_assert_new()) == NULL) { | 313 | if ((assert = fido_assert_new()) == NULL) { |
173 | skdebug(__func__, "fido_assert_new failed"); | 314 | skdebug(__func__, "fido_assert_new failed"); |
174 | goto out; | 315 | goto out; |
175 | } | 316 | } |
176 | if ((r = fido_assert_set_clientdata_hash(assert, message, | 317 | if ((r = fido_assert_set_clientdata_hash(assert, message, |
177 | message_len)) != FIDO_OK) { | 318 | sizeof(message))) != FIDO_OK) { |
178 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | 319 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", |
179 | fido_strerr(r)); | 320 | fido_strerr(r)); |
180 | goto out; | 321 | goto out; |
@@ -192,7 +333,7 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, | |||
192 | skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); | 333 | skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); |
193 | goto out; | 334 | goto out; |
194 | } | 335 | } |
195 | r = fido_dev_get_assert(dev, assert, pin); | 336 | r = fido_dev_get_assert(sk->dev, assert, NULL); |
196 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | 337 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); |
197 | if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { | 338 | if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { |
198 | /* U2F tokens may return this */ | 339 | /* U2F tokens may return this */ |
@@ -204,77 +345,110 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, | |||
204 | return r != FIDO_OK ? -1 : 0; | 345 | return r != FIDO_OK ? -1 : 0; |
205 | } | 346 | } |
206 | 347 | ||
207 | /* Iterate over configured devices looking for a specific key handle */ | 348 | static struct sk_usbhid * |
208 | static fido_dev_t * | 349 | sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs, |
209 | find_device(const char *path, const uint8_t *message, size_t message_len, | 350 | const char *application, const uint8_t *key_handle, size_t key_handle_len) |
210 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | ||
211 | uint8_t flags, const char *pin) | ||
212 | { | 351 | { |
213 | fido_dev_info_t *devlist = NULL; | 352 | struct sk_usbhid **skv, *sk; |
214 | fido_dev_t *dev = NULL; | 353 | size_t skvcnt, i; |
215 | size_t devlist_len = 0, i; | 354 | |
216 | int r; | 355 | if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { |
217 | 356 | skdebug(__func__, "sk_openv failed"); | |
218 | if (path != NULL) { | 357 | return NULL; |
219 | if ((dev = fido_dev_new()) == NULL) { | 358 | } |
220 | skdebug(__func__, "fido_dev_new failed"); | 359 | sk = NULL; |
221 | return NULL; | 360 | for (i = 0; i < skvcnt; i++) |
222 | } | 361 | if (sk_try(skv[i], application, key_handle, |
223 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) { | 362 | key_handle_len) == 0) { |
224 | skdebug(__func__, "fido_dev_open failed"); | 363 | sk = skv[i]; |
225 | fido_dev_free(&dev); | 364 | skv[i] = NULL; |
226 | return NULL; | 365 | skdebug(__func__, "found key in %s", sk->path); |
366 | break; | ||
227 | } | 367 | } |
228 | return dev; | 368 | sk_closev(skv, skvcnt); |
229 | } | 369 | return sk; |
370 | } | ||
230 | 371 | ||
231 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | 372 | static struct sk_usbhid * |
232 | skdebug(__func__, "fido_dev_info_new failed"); | 373 | sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs) |
374 | { | ||
375 | struct sk_usbhid **skv, *sk; | ||
376 | struct timeval tv_start, tv_now, tv_delta; | ||
377 | size_t skvcnt, idx; | ||
378 | int touch, ms_remain; | ||
379 | |||
380 | if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { | ||
381 | skdebug(__func__, "sk_openv failed"); | ||
382 | return NULL; | ||
383 | } | ||
384 | sk = NULL; | ||
385 | if (skvcnt < 2) { | ||
386 | if (skvcnt == 1) { | ||
387 | /* single candidate */ | ||
388 | sk = skv[0]; | ||
389 | skv[0] = NULL; | ||
390 | } | ||
233 | goto out; | 391 | goto out; |
234 | } | 392 | } |
235 | if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, | 393 | if (sk_touch_begin(skv, skvcnt) == -1) { |
236 | &devlist_len)) != FIDO_OK) { | 394 | skdebug(__func__, "sk_touch_begin failed"); |
237 | skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); | ||
238 | goto out; | 395 | goto out; |
239 | } | 396 | } |
240 | 397 | monotime_tv(&tv_start); | |
241 | skdebug(__func__, "found %zu device(s)", devlist_len); | 398 | do { |
242 | 399 | if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) { | |
243 | for (i = 0; i < devlist_len; i++) { | 400 | skdebug(__func__, "sk_touch_poll failed"); |
244 | const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); | 401 | goto out; |
245 | |||
246 | if (di == NULL) { | ||
247 | skdebug(__func__, "fido_dev_info_ptr %zu failed", i); | ||
248 | continue; | ||
249 | } | ||
250 | if ((path = fido_dev_info_path(di)) == NULL) { | ||
251 | skdebug(__func__, "fido_dev_info_path %zu failed", i); | ||
252 | continue; | ||
253 | } | ||
254 | skdebug(__func__, "trying device %zu: %s", i, path); | ||
255 | if ((dev = fido_dev_new()) == NULL) { | ||
256 | skdebug(__func__, "fido_dev_new failed"); | ||
257 | continue; | ||
258 | } | ||
259 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) { | ||
260 | skdebug(__func__, "fido_dev_open failed"); | ||
261 | fido_dev_free(&dev); | ||
262 | continue; | ||
263 | } | 402 | } |
264 | if (try_device(dev, message, message_len, application, | 403 | if (touch) { |
265 | key_handle, key_handle_len, flags, pin) == 0) { | 404 | sk = skv[idx]; |
266 | skdebug(__func__, "found key"); | 405 | skv[idx] = NULL; |
267 | break; | 406 | goto out; |
268 | } | 407 | } |
269 | fido_dev_close(dev); | 408 | monotime_tv(&tv_now); |
270 | fido_dev_free(&dev); | 409 | timersub(&tv_now, &tv_start, &tv_delta); |
271 | } | 410 | ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 - |
411 | tv_delta.tv_usec / 1000; | ||
412 | } while (ms_remain >= FIDO_POLL_MS); | ||
413 | skdebug(__func__, "timeout"); | ||
414 | out: | ||
415 | sk_closev(skv, skvcnt); | ||
416 | return sk; | ||
417 | } | ||
272 | 418 | ||
273 | out: | 419 | static struct sk_usbhid * |
274 | if (devlist != NULL) | 420 | sk_probe(const char *application, const uint8_t *key_handle, |
275 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | 421 | size_t key_handle_len) |
422 | { | ||
423 | struct sk_usbhid *sk; | ||
424 | fido_dev_info_t *devlist; | ||
425 | size_t ndevs; | ||
426 | int r; | ||
276 | 427 | ||
277 | return dev; | 428 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { |
429 | skdebug(__func__, "fido_dev_info_new failed"); | ||
430 | return NULL; | ||
431 | } | ||
432 | if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, | ||
433 | &ndevs)) != FIDO_OK) { | ||
434 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
435 | fido_strerr(r)); | ||
436 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
437 | return NULL; | ||
438 | } | ||
439 | skdebug(__func__, "%zu device(s) detected", ndevs); | ||
440 | if (ndevs == 0) { | ||
441 | sk = NULL; | ||
442 | } else if (application != NULL && key_handle != NULL) { | ||
443 | skdebug(__func__, "selecting sk by cred"); | ||
444 | sk = sk_select_by_cred(devlist, ndevs, application, key_handle, | ||
445 | key_handle_len); | ||
446 | } else { | ||
447 | skdebug(__func__, "selecting sk by touch"); | ||
448 | sk = sk_select_by_touch(devlist, ndevs); | ||
449 | } | ||
450 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
451 | return sk; | ||
278 | } | 452 | } |
279 | 453 | ||
280 | #ifdef WITH_OPENSSL | 454 | #ifdef WITH_OPENSSL |
@@ -451,52 +625,15 @@ check_enroll_options(struct sk_option **options, char **devicep, | |||
451 | return 0; | 625 | return 0; |
452 | } | 626 | } |
453 | 627 | ||
454 | static int | ||
455 | check_sk_extensions(fido_dev_t *dev, const char *ext, int *ret) | ||
456 | { | ||
457 | fido_cbor_info_t *info; | ||
458 | char * const *ptr; | ||
459 | size_t len, i; | ||
460 | int r; | ||
461 | |||
462 | *ret = 0; | ||
463 | |||
464 | if (!fido_dev_is_fido2(dev)) { | ||
465 | skdebug(__func__, "device is not fido2"); | ||
466 | return 0; | ||
467 | } | ||
468 | if ((info = fido_cbor_info_new()) == NULL) { | ||
469 | skdebug(__func__, "fido_cbor_info_new failed"); | ||
470 | return -1; | ||
471 | } | ||
472 | if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) { | ||
473 | skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r)); | ||
474 | fido_cbor_info_free(&info); | ||
475 | return -1; | ||
476 | } | ||
477 | ptr = fido_cbor_info_extensions_ptr(info); | ||
478 | len = fido_cbor_info_extensions_len(info); | ||
479 | for (i = 0; i < len; i++) { | ||
480 | if (!strcmp(ptr[i], ext)) { | ||
481 | *ret = 1; | ||
482 | break; | ||
483 | } | ||
484 | } | ||
485 | fido_cbor_info_free(&info); | ||
486 | skdebug(__func__, "extension %s %s", ext, *ret ? "present" : "absent"); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | int | 628 | int |
492 | sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | 629 | sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, |
493 | const char *application, uint8_t flags, const char *pin, | 630 | const char *application, uint8_t flags, const char *pin, |
494 | struct sk_option **options, struct sk_enroll_response **enroll_response) | 631 | struct sk_option **options, struct sk_enroll_response **enroll_response) |
495 | { | 632 | { |
496 | fido_cred_t *cred = NULL; | 633 | fido_cred_t *cred = NULL; |
497 | fido_dev_t *dev = NULL; | ||
498 | const uint8_t *ptr; | 634 | const uint8_t *ptr; |
499 | uint8_t user_id[32]; | 635 | uint8_t user_id[32]; |
636 | struct sk_usbhid *sk = NULL; | ||
500 | struct sk_enroll_response *response = NULL; | 637 | struct sk_enroll_response *response = NULL; |
501 | size_t len; | 638 | size_t len; |
502 | int credprot; | 639 | int credprot; |
@@ -511,12 +648,12 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
511 | skdebug(__func__, "enroll_response == NULL"); | 648 | skdebug(__func__, "enroll_response == NULL"); |
512 | goto out; | 649 | goto out; |
513 | } | 650 | } |
651 | *enroll_response = NULL; | ||
514 | memset(user_id, 0, sizeof(user_id)); | 652 | memset(user_id, 0, sizeof(user_id)); |
515 | if (check_enroll_options(options, &device, | 653 | if (check_enroll_options(options, &device, user_id, |
516 | user_id, sizeof(user_id)) != 0) | 654 | sizeof(user_id)) != 0) |
517 | goto out; /* error already logged */ | 655 | goto out; /* error already logged */ |
518 | 656 | ||
519 | *enroll_response = NULL; | ||
520 | switch(alg) { | 657 | switch(alg) { |
521 | #ifdef WITH_OPENSSL | 658 | #ifdef WITH_OPENSSL |
522 | case SSH_SK_ECDSA: | 659 | case SSH_SK_ECDSA: |
@@ -530,12 +667,15 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
530 | skdebug(__func__, "unsupported key type %d", alg); | 667 | skdebug(__func__, "unsupported key type %d", alg); |
531 | goto out; | 668 | goto out; |
532 | } | 669 | } |
533 | if (device == NULL && (device = pick_first_device()) == NULL) { | 670 | if (device != NULL) |
534 | ret = SSH_SK_ERR_DEVICE_NOT_FOUND; | 671 | sk = sk_open(device); |
535 | skdebug(__func__, "pick_first_device failed"); | 672 | else |
673 | sk = sk_probe(NULL, NULL, 0); | ||
674 | if (sk == NULL) { | ||
675 | skdebug(__func__, "failed to find sk"); | ||
536 | goto out; | 676 | goto out; |
537 | } | 677 | } |
538 | skdebug(__func__, "using device %s", device); | 678 | skdebug(__func__, "using device %s", sk->path); |
539 | if ((cred = fido_cred_new()) == NULL) { | 679 | if ((cred = fido_cred_new()) == NULL) { |
540 | skdebug(__func__, "fido_cred_new failed"); | 680 | skdebug(__func__, "fido_cred_new failed"); |
541 | goto out; | 681 | goto out; |
@@ -564,22 +704,11 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
564 | skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); | 704 | skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); |
565 | goto out; | 705 | goto out; |
566 | } | 706 | } |
567 | if ((dev = fido_dev_new()) == NULL) { | ||
568 | skdebug(__func__, "fido_dev_new failed"); | ||
569 | goto out; | ||
570 | } | ||
571 | if ((r = fido_dev_open(dev, device)) != FIDO_OK) { | ||
572 | skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); | ||
573 | goto out; | ||
574 | } | ||
575 | if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) { | 707 | if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) { |
576 | if (check_sk_extensions(dev, "credProtect", &credprot) < 0) { | 708 | if (!fido_dev_supports_cred_prot(sk->dev)) { |
577 | skdebug(__func__, "check_sk_extensions failed"); | 709 | skdebug(__func__, "%s does not support credprot, " |
578 | goto out; | 710 | "refusing to create unprotected " |
579 | } | 711 | "resident/verify-required key", sk->path); |
580 | if (credprot == 0) { | ||
581 | skdebug(__func__, "refusing to create unprotected " | ||
582 | "resident/verify-required key"); | ||
583 | ret = SSH_SK_ERR_UNSUPPORTED; | 712 | ret = SSH_SK_ERR_UNSUPPORTED; |
584 | goto out; | 713 | goto out; |
585 | } | 714 | } |
@@ -595,7 +724,7 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
595 | goto out; | 724 | goto out; |
596 | } | 725 | } |
597 | } | 726 | } |
598 | if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) { | 727 | if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) { |
599 | skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); | 728 | skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); |
600 | ret = fidoerr_to_skerr(r); | 729 | ret = fidoerr_to_skerr(r); |
601 | goto out; | 730 | goto out; |
@@ -662,13 +791,8 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | |||
662 | free(response->attestation_cert); | 791 | free(response->attestation_cert); |
663 | free(response); | 792 | free(response); |
664 | } | 793 | } |
665 | if (dev != NULL) { | 794 | sk_close(sk); |
666 | fido_dev_close(dev); | 795 | fido_cred_free(&cred); |
667 | fido_dev_free(&dev); | ||
668 | } | ||
669 | if (cred != NULL) { | ||
670 | fido_cred_free(&cred); | ||
671 | } | ||
672 | return ret; | 796 | return ret; |
673 | } | 797 | } |
674 | 798 | ||
@@ -782,26 +906,6 @@ check_sign_load_resident_options(struct sk_option **options, char **devicep) | |||
782 | return 0; | 906 | return 0; |
783 | } | 907 | } |
784 | 908 | ||
785 | /* Calculate SHA256(m) */ | ||
786 | static int | ||
787 | sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen) | ||
788 | { | ||
789 | #ifdef WITH_OPENSSL | ||
790 | u_int mdlen; | ||
791 | #endif | ||
792 | |||
793 | if (dlen != 32) | ||
794 | return -1; | ||
795 | #ifdef WITH_OPENSSL | ||
796 | mdlen = dlen; | ||
797 | if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL)) | ||
798 | return -1; | ||
799 | #else | ||
800 | SHA256Data(m, mlen, d); | ||
801 | #endif | ||
802 | return 0; | ||
803 | } | ||
804 | |||
805 | int | 909 | int |
806 | sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, | 910 | sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, |
807 | const char *application, | 911 | const char *application, |
@@ -811,7 +915,7 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, | |||
811 | { | 915 | { |
812 | fido_assert_t *assert = NULL; | 916 | fido_assert_t *assert = NULL; |
813 | char *device = NULL; | 917 | char *device = NULL; |
814 | fido_dev_t *dev = NULL; | 918 | struct sk_usbhid *sk = NULL; |
815 | struct sk_sign_response *response = NULL; | 919 | struct sk_sign_response *response = NULL; |
816 | uint8_t message[32]; | 920 | uint8_t message[32]; |
817 | int ret = SSH_SK_ERR_GENERAL; | 921 | int ret = SSH_SK_ERR_GENERAL; |
@@ -831,9 +935,14 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, | |||
831 | skdebug(__func__, "hash message failed"); | 935 | skdebug(__func__, "hash message failed"); |
832 | goto out; | 936 | goto out; |
833 | } | 937 | } |
834 | if ((dev = find_device(device, message, sizeof(message), | 938 | if (device != NULL) |
835 | application, key_handle, key_handle_len, flags, pin)) == NULL) { | 939 | sk = sk_open(device); |
836 | skdebug(__func__, "couldn't find device for key handle"); | 940 | else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD)) |
941 | sk = sk_probe(NULL, NULL, 0); | ||
942 | else | ||
943 | sk = sk_probe(application, key_handle, key_handle_len); | ||
944 | if (sk == NULL) { | ||
945 | skdebug(__func__, "failed to find sk"); | ||
837 | goto out; | 946 | goto out; |
838 | } | 947 | } |
839 | if ((assert = fido_assert_new()) == NULL) { | 948 | if ((assert = fido_assert_new()) == NULL) { |
@@ -867,7 +976,7 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, | |||
867 | ret = FIDO_ERR_PIN_REQUIRED; | 976 | ret = FIDO_ERR_PIN_REQUIRED; |
868 | goto out; | 977 | goto out; |
869 | } | 978 | } |
870 | if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) { | 979 | if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) { |
871 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | 980 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); |
872 | ret = fidoerr_to_skerr(r); | 981 | ret = fidoerr_to_skerr(r); |
873 | goto out; | 982 | goto out; |
@@ -893,22 +1002,16 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, | |||
893 | free(response->sig_s); | 1002 | free(response->sig_s); |
894 | free(response); | 1003 | free(response); |
895 | } | 1004 | } |
896 | if (dev != NULL) { | 1005 | sk_close(sk); |
897 | fido_dev_close(dev); | 1006 | fido_assert_free(&assert); |
898 | fido_dev_free(&dev); | ||
899 | } | ||
900 | if (assert != NULL) { | ||
901 | fido_assert_free(&assert); | ||
902 | } | ||
903 | return ret; | 1007 | return ret; |
904 | } | 1008 | } |
905 | 1009 | ||
906 | static int | 1010 | static int |
907 | read_rks(const char *devpath, const char *pin, | 1011 | read_rks(struct sk_usbhid *sk, const char *pin, |
908 | struct sk_resident_key ***rksp, size_t *nrksp) | 1012 | struct sk_resident_key ***rksp, size_t *nrksp) |
909 | { | 1013 | { |
910 | int ret = SSH_SK_ERR_GENERAL, r = -1; | 1014 | int ret = SSH_SK_ERR_GENERAL, r = -1; |
911 | fido_dev_t *dev = NULL; | ||
912 | fido_credman_metadata_t *metadata = NULL; | 1015 | fido_credman_metadata_t *metadata = NULL; |
913 | fido_credman_rp_t *rp = NULL; | 1016 | fido_credman_rp_t *rp = NULL; |
914 | fido_credman_rk_t *rk = NULL; | 1017 | fido_credman_rk_t *rk = NULL; |
@@ -916,30 +1019,25 @@ read_rks(const char *devpath, const char *pin, | |||
916 | const fido_cred_t *cred; | 1019 | const fido_cred_t *cred; |
917 | struct sk_resident_key *srk = NULL, **tmp; | 1020 | struct sk_resident_key *srk = NULL, **tmp; |
918 | 1021 | ||
919 | if ((dev = fido_dev_new()) == NULL) { | 1022 | if (pin == NULL) { |
920 | skdebug(__func__, "fido_dev_new failed"); | 1023 | skdebug(__func__, "no PIN specified"); |
921 | return ret; | 1024 | ret = SSH_SK_ERR_PIN_REQUIRED; |
922 | } | 1025 | goto out; |
923 | if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) { | ||
924 | skdebug(__func__, "fido_dev_open %s failed: %s", | ||
925 | devpath, fido_strerr(r)); | ||
926 | fido_dev_free(&dev); | ||
927 | return ret; | ||
928 | } | 1026 | } |
929 | if ((metadata = fido_credman_metadata_new()) == NULL) { | 1027 | if ((metadata = fido_credman_metadata_new()) == NULL) { |
930 | skdebug(__func__, "alloc failed"); | 1028 | skdebug(__func__, "alloc failed"); |
931 | goto out; | 1029 | goto out; |
932 | } | 1030 | } |
933 | 1031 | ||
934 | if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) { | 1032 | if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) { |
935 | if (r == FIDO_ERR_INVALID_COMMAND) { | 1033 | if (r == FIDO_ERR_INVALID_COMMAND) { |
936 | skdebug(__func__, "device %s does not support " | 1034 | skdebug(__func__, "device %s does not support " |
937 | "resident keys", devpath); | 1035 | "resident keys", sk->path); |
938 | ret = 0; | 1036 | ret = 0; |
939 | goto out; | 1037 | goto out; |
940 | } | 1038 | } |
941 | skdebug(__func__, "get metadata for %s failed: %s", | 1039 | skdebug(__func__, "get metadata for %s failed: %s", |
942 | devpath, fido_strerr(r)); | 1040 | sk->path, fido_strerr(r)); |
943 | ret = fidoerr_to_skerr(r); | 1041 | ret = fidoerr_to_skerr(r); |
944 | goto out; | 1042 | goto out; |
945 | } | 1043 | } |
@@ -950,14 +1048,14 @@ read_rks(const char *devpath, const char *pin, | |||
950 | skdebug(__func__, "alloc rp failed"); | 1048 | skdebug(__func__, "alloc rp failed"); |
951 | goto out; | 1049 | goto out; |
952 | } | 1050 | } |
953 | if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) { | 1051 | if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) { |
954 | skdebug(__func__, "get RPs for %s failed: %s", | 1052 | skdebug(__func__, "get RPs for %s failed: %s", |
955 | devpath, fido_strerr(r)); | 1053 | sk->path, fido_strerr(r)); |
956 | goto out; | 1054 | goto out; |
957 | } | 1055 | } |
958 | nrp = fido_credman_rp_count(rp); | 1056 | nrp = fido_credman_rp_count(rp); |
959 | skdebug(__func__, "Device %s has resident keys for %zu RPs", | 1057 | skdebug(__func__, "Device %s has resident keys for %zu RPs", |
960 | devpath, nrp); | 1058 | sk->path, nrp); |
961 | 1059 | ||
962 | /* Iterate over RP IDs that have resident keys */ | 1060 | /* Iterate over RP IDs that have resident keys */ |
963 | for (i = 0; i < nrp; i++) { | 1061 | for (i = 0; i < nrp; i++) { |
@@ -974,10 +1072,10 @@ read_rks(const char *devpath, const char *pin, | |||
974 | skdebug(__func__, "alloc rk failed"); | 1072 | skdebug(__func__, "alloc rk failed"); |
975 | goto out; | 1073 | goto out; |
976 | } | 1074 | } |
977 | if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i), | 1075 | if ((r = fido_credman_get_dev_rk(sk->dev, |
978 | rk, pin)) != 0) { | 1076 | fido_credman_rp_id(rp, i), rk, pin)) != 0) { |
979 | skdebug(__func__, "get RKs for %s slot %zu failed: %s", | 1077 | skdebug(__func__, "get RKs for %s slot %zu failed: %s", |
980 | devpath, i, fido_strerr(r)); | 1078 | sk->path, i, fido_strerr(r)); |
981 | goto out; | 1079 | goto out; |
982 | } | 1080 | } |
983 | nrk = fido_credman_rk_count(rk); | 1081 | nrk = fido_credman_rk_count(rk); |
@@ -991,7 +1089,7 @@ read_rks(const char *devpath, const char *pin, | |||
991 | continue; | 1089 | continue; |
992 | } | 1090 | } |
993 | skdebug(__func__, "Device %s RP \"%s\" slot %zu: " | 1091 | skdebug(__func__, "Device %s RP \"%s\" slot %zu: " |
994 | "type %d flags 0x%02x prot 0x%02x", devpath, | 1092 | "type %d flags 0x%02x prot 0x%02x", sk->path, |
995 | fido_credman_rp_id(rp, i), j, fido_cred_type(cred), | 1093 | fido_credman_rp_id(rp, i), j, fido_cred_type(cred), |
996 | fido_cred_flags(cred), fido_cred_prot(cred)); | 1094 | fido_cred_flags(cred), fido_cred_prot(cred)); |
997 | 1095 | ||
@@ -1050,8 +1148,6 @@ read_rks(const char *devpath, const char *pin, | |||
1050 | } | 1148 | } |
1051 | fido_credman_rp_free(&rp); | 1149 | fido_credman_rp_free(&rp); |
1052 | fido_credman_rk_free(&rk); | 1150 | fido_credman_rk_free(&rk); |
1053 | fido_dev_close(dev); | ||
1054 | fido_dev_free(&dev); | ||
1055 | fido_credman_metadata_free(&metadata); | 1151 | fido_credman_metadata_free(&metadata); |
1056 | return ret; | 1152 | return ret; |
1057 | } | 1153 | } |
@@ -1061,11 +1157,11 @@ sk_load_resident_keys(const char *pin, struct sk_option **options, | |||
1061 | struct sk_resident_key ***rksp, size_t *nrksp) | 1157 | struct sk_resident_key ***rksp, size_t *nrksp) |
1062 | { | 1158 | { |
1063 | int ret = SSH_SK_ERR_GENERAL, r = -1; | 1159 | int ret = SSH_SK_ERR_GENERAL, r = -1; |
1064 | fido_dev_info_t *devlist = NULL; | 1160 | size_t i, nrks = 0; |
1065 | size_t i, ndev = 0, nrks = 0; | ||
1066 | const fido_dev_info_t *di; | ||
1067 | struct sk_resident_key **rks = NULL; | 1161 | struct sk_resident_key **rks = NULL; |
1162 | struct sk_usbhid *sk = NULL; | ||
1068 | char *device = NULL; | 1163 | char *device = NULL; |
1164 | |||
1069 | *rksp = NULL; | 1165 | *rksp = NULL; |
1070 | *nrksp = 0; | 1166 | *nrksp = 0; |
1071 | 1167 | ||
@@ -1073,40 +1169,19 @@ sk_load_resident_keys(const char *pin, struct sk_option **options, | |||
1073 | 1169 | ||
1074 | if (check_sign_load_resident_options(options, &device) != 0) | 1170 | if (check_sign_load_resident_options(options, &device) != 0) |
1075 | goto out; /* error already logged */ | 1171 | goto out; /* error already logged */ |
1076 | if (device != NULL) { | 1172 | if (device != NULL) |
1077 | skdebug(__func__, "trying %s", device); | 1173 | sk = sk_open(device); |
1078 | if ((r = read_rks(device, pin, &rks, &nrks)) != 0) { | 1174 | else |
1079 | skdebug(__func__, "read_rks failed for %s", device); | 1175 | sk = sk_probe(NULL, NULL, 0); |
1080 | ret = r; | 1176 | if (sk == NULL) { |
1081 | goto out; | 1177 | skdebug(__func__, "failed to find sk"); |
1082 | } | 1178 | goto out; |
1083 | } else { | 1179 | } |
1084 | /* Try all devices */ | 1180 | skdebug(__func__, "trying %s", sk->path); |
1085 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | 1181 | if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) { |
1086 | skdebug(__func__, "fido_dev_info_new failed"); | 1182 | skdebug(__func__, "read_rks failed for %s", sk->path); |
1087 | goto out; | 1183 | ret = r; |
1088 | } | 1184 | goto out; |
1089 | if ((r = fido_dev_info_manifest(devlist, | ||
1090 | MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { | ||
1091 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
1092 | fido_strerr(r)); | ||
1093 | goto out; | ||
1094 | } | ||
1095 | for (i = 0; i < ndev; i++) { | ||
1096 | if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { | ||
1097 | skdebug(__func__, "no dev info at %zu", i); | ||
1098 | continue; | ||
1099 | } | ||
1100 | skdebug(__func__, "trying %s", fido_dev_info_path(di)); | ||
1101 | if ((r = read_rks(fido_dev_info_path(di), pin, | ||
1102 | &rks, &nrks)) != 0) { | ||
1103 | skdebug(__func__, "read_rks failed for %s", | ||
1104 | fido_dev_info_path(di)); | ||
1105 | /* remember last error */ | ||
1106 | ret = r; | ||
1107 | continue; | ||
1108 | } | ||
1109 | } | ||
1110 | } | 1185 | } |
1111 | /* success, unless we have no keys but a specific error */ | 1186 | /* success, unless we have no keys but a specific error */ |
1112 | if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) | 1187 | if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) |
@@ -1116,7 +1191,7 @@ sk_load_resident_keys(const char *pin, struct sk_option **options, | |||
1116 | rks = NULL; | 1191 | rks = NULL; |
1117 | nrks = 0; | 1192 | nrks = 0; |
1118 | out: | 1193 | out: |
1119 | free(device); | 1194 | sk_close(sk); |
1120 | for (i = 0; i < nrks; i++) { | 1195 | for (i = 0; i < nrks; i++) { |
1121 | free(rks[i]->application); | 1196 | free(rks[i]->application); |
1122 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); | 1197 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); |
@@ -1124,7 +1199,6 @@ sk_load_resident_keys(const char *pin, struct sk_option **options, | |||
1124 | freezero(rks[i], sizeof(*rks[i])); | 1199 | freezero(rks[i], sizeof(*rks[i])); |
1125 | } | 1200 | } |
1126 | free(rks); | 1201 | free(rks); |
1127 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
1128 | return ret; | 1202 | return ret; |
1129 | } | 1203 | } |
1130 | 1204 | ||
diff --git a/ssh-keygen.c b/ssh-keygen.c index 89ef9a143..1d6234c1c 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.416 2020/08/27 01:06:18 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.417 2020/08/27 01:07:51 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -3632,6 +3632,11 @@ main(int argc, char **argv) | |||
3632 | fatal("Too many incorrect PINs"); | 3632 | fatal("Too many incorrect PINs"); |
3633 | passphrase = read_passphrase("Enter PIN for " | 3633 | passphrase = read_passphrase("Enter PIN for " |
3634 | "authenticator: ", RP_ALLOW_STDIN); | 3634 | "authenticator: ", RP_ALLOW_STDIN); |
3635 | if (!quiet) { | ||
3636 | printf("You may need to touch your " | ||
3637 | "authenticator (again) to authorize " | ||
3638 | "key generation.\n"); | ||
3639 | } | ||
3635 | } | 3640 | } |
3636 | if (passphrase != NULL) { | 3641 | if (passphrase != NULL) { |
3637 | freezero(passphrase, strlen(passphrase)); | 3642 | freezero(passphrase, strlen(passphrase)); |