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