util.c revision 1.1.1.3 1 /*
2 * Copyright (c) 2018 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 return fido_dev_has_pin(dev) && (r == FIDO_ERR_PIN_REQUIRED ||
579 r == FIDO_ERR_UV_INVALID || r == FIDO_ERR_UNAUTHORIZED_PERM);
580 }
581