1 /*
2 * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10
11 #include <openssl/ec.h>
12 #include <openssl/evp.h>
13 #include <openssl/pem.h>
14
15 #include <fido.h>
16 #include <fido/es256.h>
17 #include <fido/es384.h>
18 #include <fido/rs256.h>
19 #include <fido/eddsa.h>
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "../openbsd-compat/openbsd-compat.h"
31 #ifdef _MSC_VER
32 #include "../openbsd-compat/posix_win.h"
33 #endif
34
35 #include "extern.h"
36
37 char *
get_pin(const char * path)38 get_pin(const char *path)
39 {
40 char *pin;
41 char prompt[1024];
42 int r, ok = -1;
43
44 if ((pin = calloc(1, PINBUF_LEN)) == NULL) {
45 warn("%s: calloc", __func__);
46 return NULL;
47 }
48 if ((r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
49 path)) < 0 || (size_t)r >= sizeof(prompt)) {
50 warn("%s: snprintf", __func__);
51 goto out;
52 }
53 if (!readpassphrase(prompt, pin, PINBUF_LEN, RPP_ECHO_OFF)) {
54 warnx("%s: readpassphrase", __func__);
55 goto out;
56 }
57
58 ok = 0;
59 out:
60 if (ok < 0) {
61 freezero(pin, PINBUF_LEN);
62 pin = NULL;
63 }
64
65 return pin;
66 }
67
68 FILE *
open_write(const char * file)69 open_write(const char *file)
70 {
71 int fd;
72 FILE *f;
73
74 if (file == NULL || strcmp(file, "-") == 0)
75 return (stdout);
76 if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0)
77 err(1, "open %s", file);
78 if ((f = fdopen(fd, "w")) == NULL)
79 err(1, "fdopen %s", file);
80
81 return (f);
82 }
83
84 FILE *
open_read(const char * file)85 open_read(const char *file)
86 {
87 int fd;
88 FILE *f;
89
90 if (file == NULL || strcmp(file, "-") == 0) {
91 #ifdef FIDO_FUZZ
92 setvbuf(stdin, NULL, _IONBF, 0);
93 #endif
94 return (stdin);
95 }
96 if ((fd = open(file, O_RDONLY)) < 0)
97 err(1, "open %s", file);
98 if ((f = fdopen(fd, "r")) == NULL)
99 err(1, "fdopen %s", file);
100
101 return (f);
102 }
103
104 int
base10(const char * str)105 base10(const char *str)
106 {
107 char *ep;
108 long long ll;
109
110 ll = strtoll(str, &ep, 10);
111 if (str == ep || *ep != '\0')
112 return (-1);
113 else if (ll == LLONG_MIN && errno == ERANGE)
114 return (-1);
115 else if (ll == LLONG_MAX && errno == ERANGE)
116 return (-1);
117 else if (ll < 0 || ll > INT_MAX)
118 return (-1);
119
120 return ((int)ll);
121 }
122
123 void
xxd(const void * buf,size_t count)124 xxd(const void *buf, size_t count)
125 {
126 const uint8_t *ptr = buf;
127 size_t i;
128
129 fprintf(stderr, " ");
130
131 for (i = 0; i < count; i++) {
132 fprintf(stderr, "%02x ", *ptr++);
133 if ((i + 1) % 16 == 0 && i + 1 < count)
134 fprintf(stderr, "\n ");
135 }
136
137 fprintf(stderr, "\n");
138 fflush(stderr);
139 }
140
141 int
string_read(FILE * f,char ** out)142 string_read(FILE *f, char **out)
143 {
144 char *line = NULL;
145 size_t linesize = 0;
146 ssize_t n;
147
148 *out = NULL;
149
150 if ((n = getline(&line, &linesize, f)) <= 0 ||
151 (size_t)n != strlen(line)) {
152 free(line);
153 return (-1);
154 }
155
156 line[n - 1] = '\0'; /* trim \n */
157 *out = line;
158
159 return (0);
160 }
161
162 fido_dev_t *
open_dev(const char * path)163 open_dev(const char *path)
164 {
165 fido_dev_t *dev;
166 int r;
167
168 if ((dev = fido_dev_new()) == NULL)
169 errx(1, "fido_dev_new");
170
171 r = fido_dev_open(dev, path);
172 if (r != FIDO_OK)
173 errx(1, "fido_dev_open %s: %s", path, fido_strerr(r));
174
175 return (dev);
176 }
177
178 int
get_devopt(fido_dev_t * dev,const char * name,int * val)179 get_devopt(fido_dev_t *dev, const char *name, int *val)
180 {
181 fido_cbor_info_t *cbor_info;
182 char * const *names;
183 const bool *values;
184 int r, ok = -1;
185
186 if ((cbor_info = fido_cbor_info_new()) == NULL) {
187 warnx("fido_cbor_info_new");
188 goto out;
189 }
190
191 if ((r = fido_dev_get_cbor_info(dev, cbor_info)) != FIDO_OK) {
192 warnx("fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
193 goto out;
194 }
195
196 if ((names = fido_cbor_info_options_name_ptr(cbor_info)) == NULL ||
197 (values = fido_cbor_info_options_value_ptr(cbor_info)) == NULL) {
198 warnx("fido_dev_get_cbor_info: NULL name/value pointer");
199 goto out;
200 }
201
202 *val = -1;
203 for (size_t i = 0; i < fido_cbor_info_options_len(cbor_info); i++)
204 if (strcmp(names[i], name) == 0) {
205 *val = values[i];
206 break;
207 }
208
209 ok = 0;
210 out:
211 fido_cbor_info_free(&cbor_info);
212
213 return (ok);
214 }
215
216 EC_KEY *
read_ec_pubkey(const char * path)217 read_ec_pubkey(const char *path)
218 {
219 FILE *fp = NULL;
220 EVP_PKEY *pkey = NULL;
221 EC_KEY *ec = NULL;
222
223 if ((fp = fopen(path, "r")) == NULL) {
224 warn("fopen");
225 goto fail;
226 }
227
228 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
229 warnx("PEM_read_PUBKEY");
230 goto fail;
231 }
232 if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
233 warnx("EVP_PKEY_get1_EC_KEY");
234 goto fail;
235 }
236
237 fail:
238 if (fp) {
239 fclose(fp);
240 }
241 if (pkey) {
242 EVP_PKEY_free(pkey);
243 }
244
245 return (ec);
246 }
247
248 int
write_es256_pubkey(FILE * f,const void * ptr,size_t len)249 write_es256_pubkey(FILE *f, const void *ptr, size_t len)
250 {
251 EVP_PKEY *pkey = NULL;
252 es256_pk_t *pk = NULL;
253 int ok = -1;
254
255 if ((pk = es256_pk_new()) == NULL) {
256 warnx("es256_pk_new");
257 goto fail;
258 }
259
260 if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
261 warnx("es256_pk_from_ptr");
262 goto fail;
263 }
264
265 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
266 warnx("es256_pk_to_EVP_PKEY");
267 goto fail;
268 }
269
270 if (PEM_write_PUBKEY(f, pkey) == 0) {
271 warnx("PEM_write_PUBKEY");
272 goto fail;
273 }
274
275 ok = 0;
276 fail:
277 es256_pk_free(&pk);
278
279 if (pkey != NULL) {
280 EVP_PKEY_free(pkey);
281 }
282
283 return (ok);
284 }
285
286 int
write_es384_pubkey(FILE * f,const void * ptr,size_t len)287 write_es384_pubkey(FILE *f, const void *ptr, size_t len)
288 {
289 EVP_PKEY *pkey = NULL;
290 es384_pk_t *pk = NULL;
291 int ok = -1;
292
293 if ((pk = es384_pk_new()) == NULL) {
294 warnx("es384_pk_new");
295 goto fail;
296 }
297
298 if (es384_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
299 warnx("es384_pk_from_ptr");
300 goto fail;
301 }
302
303 if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) {
304 warnx("es384_pk_to_EVP_PKEY");
305 goto fail;
306 }
307
308 if (PEM_write_PUBKEY(f, pkey) == 0) {
309 warnx("PEM_write_PUBKEY");
310 goto fail;
311 }
312
313 ok = 0;
314 fail:
315 es384_pk_free(&pk);
316
317 if (pkey != NULL) {
318 EVP_PKEY_free(pkey);
319 }
320
321 return (ok);
322 }
323
324 RSA *
read_rsa_pubkey(const char * path)325 read_rsa_pubkey(const char *path)
326 {
327 FILE *fp = NULL;
328 EVP_PKEY *pkey = NULL;
329 RSA *rsa = NULL;
330
331 if ((fp = fopen(path, "r")) == NULL) {
332 warn("fopen");
333 goto fail;
334 }
335
336 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
337 warnx("PEM_read_PUBKEY");
338 goto fail;
339 }
340 if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
341 warnx("EVP_PKEY_get1_RSA");
342 goto fail;
343 }
344
345 fail:
346 if (fp) {
347 fclose(fp);
348 }
349 if (pkey) {
350 EVP_PKEY_free(pkey);
351 }
352
353 return (rsa);
354 }
355
356 int
write_rsa_pubkey(FILE * f,const void * ptr,size_t len)357 write_rsa_pubkey(FILE *f, const void *ptr, size_t len)
358 {
359 EVP_PKEY *pkey = NULL;
360 rs256_pk_t *pk = NULL;
361 int ok = -1;
362
363 if ((pk = rs256_pk_new()) == NULL) {
364 warnx("rs256_pk_new");
365 goto fail;
366 }
367
368 if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
369 warnx("rs256_pk_from_ptr");
370 goto fail;
371 }
372
373 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
374 warnx("rs256_pk_to_EVP_PKEY");
375 goto fail;
376 }
377
378 if (PEM_write_PUBKEY(f, pkey) == 0) {
379 warnx("PEM_write_PUBKEY");
380 goto fail;
381 }
382
383 ok = 0;
384 fail:
385 rs256_pk_free(&pk);
386
387 if (pkey != NULL) {
388 EVP_PKEY_free(pkey);
389 }
390
391 return (ok);
392 }
393
394 EVP_PKEY *
read_eddsa_pubkey(const char * path)395 read_eddsa_pubkey(const char *path)
396 {
397 FILE *fp = NULL;
398 EVP_PKEY *pkey = NULL;
399
400 if ((fp = fopen(path, "r")) == NULL) {
401 warn("fopen");
402 goto fail;
403 }
404
405 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
406 warnx("PEM_read_PUBKEY");
407 goto fail;
408 }
409
410 fail:
411 if (fp) {
412 fclose(fp);
413 }
414
415 return (pkey);
416 }
417
418 int
write_eddsa_pubkey(FILE * f,const void * ptr,size_t len)419 write_eddsa_pubkey(FILE *f, const void *ptr, size_t len)
420 {
421 EVP_PKEY *pkey = NULL;
422 eddsa_pk_t *pk = NULL;
423 int ok = -1;
424
425 if ((pk = eddsa_pk_new()) == NULL) {
426 warnx("eddsa_pk_new");
427 goto fail;
428 }
429
430 if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
431 warnx("eddsa_pk_from_ptr");
432 goto fail;
433 }
434
435 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
436 warnx("eddsa_pk_to_EVP_PKEY");
437 goto fail;
438 }
439
440 if (PEM_write_PUBKEY(f, pkey) == 0) {
441 warnx("PEM_write_PUBKEY");
442 goto fail;
443 }
444
445 ok = 0;
446 fail:
447 eddsa_pk_free(&pk);
448
449 if (pkey != NULL) {
450 EVP_PKEY_free(pkey);
451 }
452
453 return (ok);
454 }
455
456 void
print_cred(FILE * out_f,int type,const fido_cred_t * cred)457 print_cred(FILE *out_f, int type, const fido_cred_t *cred)
458 {
459 char *id;
460 int r;
461
462 r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id);
463 if (r < 0)
464 errx(1, "output error");
465
466 fprintf(out_f, "%s\n", id);
467
468 switch (type) {
469 case COSE_ES256:
470 write_es256_pubkey(out_f, fido_cred_pubkey_ptr(cred),
471 fido_cred_pubkey_len(cred));
472 break;
473 case COSE_ES384:
474 write_es384_pubkey(out_f, fido_cred_pubkey_ptr(cred),
475 fido_cred_pubkey_len(cred));
476 break;
477 case COSE_RS256:
478 write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
479 fido_cred_pubkey_len(cred));
480 break;
481 case COSE_EDDSA:
482 write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
483 fido_cred_pubkey_len(cred));
484 break;
485 default:
486 errx(1, "print_cred: unknown type");
487 }
488
489 free(id);
490 }
491
492 int
cose_type(const char * str,int * type)493 cose_type(const char *str, int *type)
494 {
495 if (strcmp(str, "es256") == 0)
496 *type = COSE_ES256;
497 else if (strcmp(str, "es384") == 0)
498 *type = COSE_ES384;
499 else if (strcmp(str, "rs256") == 0)
500 *type = COSE_RS256;
501 else if (strcmp(str, "eddsa") == 0)
502 *type = COSE_EDDSA;
503 else {
504 *type = 0;
505 return (-1);
506 }
507
508 return (0);
509 }
510
511 const char *
cose_string(int type)512 cose_string(int type)
513 {
514 switch (type) {
515 case COSE_ES256:
516 return ("es256");
517 case COSE_ES384:
518 return ("es384");
519 case COSE_RS256:
520 return ("rs256");
521 case COSE_EDDSA:
522 return ("eddsa");
523 default:
524 return ("unknown");
525 }
526 }
527
528 const char *
prot_string(int prot)529 prot_string(int prot)
530 {
531 switch (prot) {
532 case FIDO_CRED_PROT_UV_OPTIONAL:
533 return ("uvopt");
534 case FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID:
535 return ("uvopt+id");
536 case FIDO_CRED_PROT_UV_REQUIRED:
537 return ("uvreq");
538 default:
539 return ("unknown");
540 }
541 }
542
543 int
read_file(const char * path,u_char ** ptr,size_t * len)544 read_file(const char *path, u_char **ptr, size_t *len)
545 {
546 int fd, ok = -1;
547 struct stat st;
548 ssize_t n;
549
550 *ptr = NULL;
551 *len = 0;
552
553 if ((fd = open(path, O_RDONLY)) < 0) {
554 warn("%s: open %s", __func__, path);
555 goto fail;
556 }
557 if (fstat(fd, &st) < 0) {
558 warn("%s: stat %s", __func__, path);
559 goto fail;
560 }
561 if (st.st_size < 0) {
562 warnx("%s: stat %s: invalid size", __func__, path);
563 goto fail;
564 }
565 *len = (size_t)st.st_size;
566 if ((*ptr = malloc(*len)) == NULL) {
567 warn("%s: malloc", __func__);
568 goto fail;
569 }
570 if ((n = read(fd, *ptr, *len)) < 0) {
571 warn("%s: read", __func__);
572 goto fail;
573 }
574 if ((size_t)n != *len) {
575 warnx("%s: read", __func__);
576 goto fail;
577 }
578
579 ok = 0;
580 fail:
581 if (fd != -1) {
582 close(fd);
583 }
584 if (ok < 0) {
585 free(*ptr);
586 *ptr = NULL;
587 *len = 0;
588 }
589
590 return ok;
591 }
592
593 int
write_file(const char * path,const u_char * ptr,size_t len)594 write_file(const char *path, const u_char *ptr, size_t len)
595 {
596 int fd, ok = -1;
597 ssize_t n;
598
599 if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) {
600 warn("%s: open %s", __func__, path);
601 goto fail;
602 }
603 if ((n = write(fd, ptr, len)) < 0) {
604 warn("%s: write", __func__);
605 goto fail;
606 }
607 if ((size_t)n != len) {
608 warnx("%s: write", __func__);
609 goto fail;
610 }
611
612 ok = 0;
613 fail:
614 if (fd != -1) {
615 close(fd);
616 }
617
618 return ok;
619 }
620
621 const char *
plural(size_t x)622 plural(size_t x)
623 {
624 return x == 1 ? "" : "s";
625 }
626
627 int
should_retry_with_pin(const fido_dev_t * dev,int r)628 should_retry_with_pin(const fido_dev_t *dev, int r)
629 {
630 if (fido_dev_has_pin(dev) == false) {
631 return 0;
632 }
633
634 switch (r) {
635 case FIDO_ERR_PIN_REQUIRED:
636 case FIDO_ERR_UNAUTHORIZED_PERM:
637 case FIDO_ERR_UV_BLOCKED:
638 case FIDO_ERR_UV_INVALID:
639 return 1;
640 }
641
642 return 0;
643 }
644