Home | History | Annotate | Line # | Download | only in tools
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