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