Home | History | Annotate | Line # | Download | only in pamu2fcfg
pamu2fcfg.c revision 1.1.1.3
      1      1.1  christos /*
      2  1.1.1.3  christos  * Copyright (C) 2014-2022 Yubico AB - See COPYING
      3      1.1  christos  */
      4      1.1  christos 
      5      1.1  christos #define BUFSIZE 1024
      6      1.1  christos #define PAM_PREFIX "pam://"
      7      1.1  christos #define TIMEOUT 15
      8      1.1  christos #define FREQUENCY 1
      9      1.1  christos 
     10  1.1.1.3  christos #define PIN_SET 0x01
     11  1.1.1.3  christos #define PIN_UNSET 0x02
     12  1.1.1.3  christos #define UV_SET 0x04
     13  1.1.1.3  christos #define UV_UNSET 0x08
     14  1.1.1.3  christos #define UV_REQD 0x10
     15  1.1.1.3  christos #define UV_NOT_REQD 0x20
     16  1.1.1.3  christos 
     17      1.1  christos #include <fido.h>
     18      1.1  christos 
     19      1.1  christos #include <stdio.h>
     20      1.1  christos #include <stdlib.h>
     21      1.1  christos #include <string.h>
     22      1.1  christos #include <getopt.h>
     23      1.1  christos #include <unistd.h>
     24      1.1  christos #include <sys/types.h>
     25      1.1  christos #include <pwd.h>
     26  1.1.1.3  christos #include <err.h>
     27      1.1  christos 
     28      1.1  christos #include "b64.h"
     29      1.1  christos #include "util.h"
     30      1.1  christos 
     31  1.1.1.2  christos #include "openbsd-compat.h"
     32  1.1.1.2  christos 
     33  1.1.1.3  christos #ifndef FIDO_ERR_UV_BLOCKED /* XXX: compat libfido2 <1.5.0 */
     34  1.1.1.3  christos #define FIDO_ERR_UV_BLOCKED 0x3c
     35  1.1.1.3  christos #endif
     36  1.1.1.3  christos 
     37  1.1.1.3  christos struct args {
     38  1.1.1.3  christos   const char *appid;
     39  1.1.1.3  christos   const char *origin;
     40  1.1.1.3  christos   const char *type;
     41  1.1.1.3  christos   const char *username;
     42  1.1.1.3  christos   int resident;
     43  1.1.1.3  christos   int no_user_presence;
     44  1.1.1.3  christos   int pin_verification;
     45  1.1.1.3  christos   int user_verification;
     46  1.1.1.3  christos   int debug;
     47  1.1.1.3  christos   int verbose;
     48  1.1.1.3  christos   int nouser;
     49  1.1.1.3  christos };
     50  1.1.1.3  christos 
     51  1.1.1.3  christos static fido_cred_t *prepare_cred(const struct args *const args) {
     52      1.1  christos   fido_cred_t *cred = NULL;
     53  1.1.1.3  christos   const char *appid = NULL;
     54  1.1.1.3  christos   const char *user = NULL;
     55      1.1  christos   struct passwd *passwd;
     56      1.1  christos   unsigned char userid[32];
     57  1.1.1.2  christos   unsigned char cdh[32];
     58  1.1.1.2  christos   char origin[BUFSIZE];
     59  1.1.1.2  christos   int type;
     60  1.1.1.2  christos   int ok = -1;
     61  1.1.1.2  christos   size_t n;
     62  1.1.1.2  christos   int r;
     63      1.1  christos 
     64  1.1.1.2  christos   if ((cred = fido_cred_new()) == NULL) {
     65      1.1  christos     fprintf(stderr, "fido_cred_new failed\n");
     66  1.1.1.2  christos     goto err;
     67      1.1  christos   }
     68      1.1  christos 
     69  1.1.1.2  christos   type = COSE_ES256; /* default */
     70  1.1.1.3  christos   if (args->type && !cose_type(args->type, &type)) {
     71  1.1.1.3  christos     fprintf(stderr, "Unknown COSE type '%s'.\n", args->type);
     72  1.1.1.3  christos     goto err;
     73  1.1.1.2  christos   }
     74      1.1  christos 
     75  1.1.1.2  christos   if ((r = fido_cred_set_type(cred, type)) != FIDO_OK) {
     76      1.1  christos     fprintf(stderr, "error: fido_cred_set_type (%d): %s\n", r, fido_strerr(r));
     77  1.1.1.2  christos     goto err;
     78      1.1  christos   }
     79      1.1  christos 
     80  1.1.1.2  christos   if (!random_bytes(cdh, sizeof(cdh))) {
     81  1.1.1.2  christos     fprintf(stderr, "random_bytes failed\n");
     82  1.1.1.2  christos     goto err;
     83  1.1.1.2  christos   }
     84  1.1.1.2  christos 
     85  1.1.1.2  christos   if ((r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh))) != FIDO_OK) {
     86      1.1  christos     fprintf(stderr, "error: fido_cred_set_clientdata_hash (%d): %s\n", r,
     87      1.1  christos             fido_strerr(r));
     88  1.1.1.2  christos     goto err;
     89      1.1  christos   }
     90      1.1  christos 
     91  1.1.1.3  christos   if (args->origin) {
     92  1.1.1.3  christos     if (strlcpy(origin, args->origin, sizeof(origin)) >= sizeof(origin)) {
     93  1.1.1.2  christos       fprintf(stderr, "error: strlcpy failed\n");
     94  1.1.1.2  christos       goto err;
     95  1.1.1.2  christos     }
     96  1.1.1.2  christos   } else {
     97  1.1.1.2  christos     if ((n = strlcpy(origin, PAM_PREFIX, sizeof(origin))) >= sizeof(origin)) {
     98  1.1.1.2  christos       fprintf(stderr, "error: strlcpy failed\n");
     99  1.1.1.2  christos       goto err;
    100      1.1  christos     }
    101  1.1.1.2  christos     if (gethostname(origin + n, sizeof(origin) - n) == -1) {
    102      1.1  christos       perror("gethostname");
    103  1.1.1.2  christos       goto err;
    104      1.1  christos     }
    105      1.1  christos   }
    106      1.1  christos 
    107  1.1.1.3  christos   if (args->appid) {
    108  1.1.1.3  christos     appid = args->appid;
    109  1.1.1.2  christos   } else {
    110      1.1  christos     appid = origin;
    111      1.1  christos   }
    112      1.1  christos 
    113  1.1.1.3  christos   if (args->verbose) {
    114  1.1.1.2  christos     fprintf(stderr, "Setting origin to %s\n", origin);
    115      1.1  christos     fprintf(stderr, "Setting appid to %s\n", appid);
    116  1.1.1.2  christos   }
    117      1.1  christos 
    118  1.1.1.2  christos   if ((r = fido_cred_set_rp(cred, origin, appid)) != FIDO_OK) {
    119      1.1  christos     fprintf(stderr, "error: fido_cred_set_rp (%d) %s\n", r, fido_strerr(r));
    120  1.1.1.2  christos     goto err;
    121      1.1  christos   }
    122      1.1  christos 
    123  1.1.1.3  christos   if (args->username) {
    124  1.1.1.3  christos     user = args->username;
    125  1.1.1.2  christos   } else {
    126  1.1.1.2  christos     if ((passwd = getpwuid(getuid())) == NULL) {
    127      1.1  christos       perror("getpwuid");
    128  1.1.1.2  christos       goto err;
    129      1.1  christos     }
    130      1.1  christos     user = passwd->pw_name;
    131      1.1  christos   }
    132      1.1  christos 
    133      1.1  christos   if (!random_bytes(userid, sizeof(userid))) {
    134      1.1  christos     fprintf(stderr, "random_bytes failed\n");
    135  1.1.1.2  christos     goto err;
    136      1.1  christos   }
    137      1.1  christos 
    138  1.1.1.3  christos   if (args->verbose) {
    139      1.1  christos     fprintf(stderr, "Setting user to %s\n", user);
    140      1.1  christos     fprintf(stderr, "Setting user id to ");
    141      1.1  christos     for (size_t i = 0; i < sizeof(userid); i++)
    142      1.1  christos       fprintf(stderr, "%02x", userid[i]);
    143      1.1  christos     fprintf(stderr, "\n");
    144      1.1  christos   }
    145      1.1  christos 
    146  1.1.1.2  christos   if ((r = fido_cred_set_user(cred, userid, sizeof(userid), user, user,
    147  1.1.1.2  christos                               NULL)) != FIDO_OK) {
    148      1.1  christos     fprintf(stderr, "error: fido_cred_set_user (%d) %s\n", r, fido_strerr(r));
    149  1.1.1.2  christos     goto err;
    150      1.1  christos   }
    151      1.1  christos 
    152  1.1.1.3  christos   if ((r = fido_cred_set_rk(cred, args->resident ? FIDO_OPT_TRUE
    153  1.1.1.3  christos                                                  : FIDO_OPT_OMIT)) != FIDO_OK) {
    154      1.1  christos     fprintf(stderr, "error: fido_cred_set_rk (%d) %s\n", r, fido_strerr(r));
    155  1.1.1.2  christos     goto err;
    156      1.1  christos   }
    157      1.1  christos 
    158  1.1.1.2  christos   if ((r = fido_cred_set_uv(cred, FIDO_OPT_OMIT)) != FIDO_OK) {
    159      1.1  christos     fprintf(stderr, "error: fido_cred_set_uv (%d) %s\n", r, fido_strerr(r));
    160  1.1.1.2  christos     goto err;
    161  1.1.1.2  christos   }
    162  1.1.1.2  christos 
    163  1.1.1.2  christos   ok = 0;
    164  1.1.1.2  christos 
    165  1.1.1.2  christos err:
    166  1.1.1.2  christos   if (ok != 0) {
    167  1.1.1.2  christos     fido_cred_free(&cred);
    168  1.1.1.2  christos   }
    169  1.1.1.2  christos 
    170  1.1.1.2  christos   return cred;
    171  1.1.1.2  christos }
    172  1.1.1.2  christos 
    173  1.1.1.3  christos static int make_cred(const struct args *args, const char *path, fido_dev_t *dev,
    174  1.1.1.3  christos                      fido_cred_t *cred, int devopts) {
    175  1.1.1.2  christos   char prompt[BUFSIZE];
    176  1.1.1.2  christos   char pin[BUFSIZE];
    177  1.1.1.2  christos   int n;
    178  1.1.1.2  christos   int r;
    179  1.1.1.2  christos 
    180  1.1.1.2  christos   if (path == NULL || dev == NULL || cred == NULL) {
    181  1.1.1.2  christos     fprintf(stderr, "%s: args\n", __func__);
    182  1.1.1.2  christos     return -1;
    183  1.1.1.2  christos   }
    184  1.1.1.2  christos 
    185  1.1.1.3  christos   /* Some form of UV required; built-in UV is available. */
    186  1.1.1.3  christos   if (args->user_verification || (devopts & (UV_SET | UV_NOT_REQD)) == UV_SET) {
    187  1.1.1.3  christos     if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) {
    188  1.1.1.3  christos       fprintf(stderr, "error: fido_cred_set_uv: %s (%d)\n", fido_strerr(r), r);
    189  1.1.1.3  christos       return -1;
    190  1.1.1.3  christos     }
    191  1.1.1.3  christos   }
    192  1.1.1.3  christos 
    193  1.1.1.3  christos   /* Let built-in UV have precedence over PIN. No UV also handled here. */
    194  1.1.1.3  christos   if (args->user_verification || !args->pin_verification) {
    195  1.1.1.3  christos     r = fido_dev_make_cred(dev, cred, NULL);
    196  1.1.1.3  christos   } else {
    197  1.1.1.3  christos     r = FIDO_ERR_PIN_REQUIRED;
    198  1.1.1.3  christos   }
    199  1.1.1.3  christos 
    200  1.1.1.3  christos   /* Some form of UV required; built-in UV failed or is not available. */
    201  1.1.1.3  christos   if ((devopts & PIN_SET) &&
    202  1.1.1.3  christos       (r == FIDO_ERR_PIN_REQUIRED || r == FIDO_ERR_UV_BLOCKED ||
    203  1.1.1.3  christos        r == FIDO_ERR_PIN_BLOCKED)) {
    204  1.1.1.2  christos     n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path);
    205  1.1.1.2  christos     if (n < 0 || (size_t) n >= sizeof(prompt)) {
    206  1.1.1.2  christos       fprintf(stderr, "error: snprintf prompt");
    207  1.1.1.2  christos       return -1;
    208  1.1.1.2  christos     }
    209  1.1.1.2  christos     if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) {
    210  1.1.1.2  christos       fprintf(stderr, "error: failed to read pin");
    211  1.1.1.2  christos       explicit_bzero(pin, sizeof(pin));
    212  1.1.1.2  christos       return -1;
    213  1.1.1.2  christos     }
    214  1.1.1.2  christos     r = fido_dev_make_cred(dev, cred, pin);
    215  1.1.1.2  christos   }
    216  1.1.1.2  christos   explicit_bzero(pin, sizeof(pin));
    217  1.1.1.2  christos 
    218  1.1.1.2  christos   if (r != FIDO_OK) {
    219  1.1.1.2  christos     fprintf(stderr, "error: fido_dev_make_cred (%d) %s\n", r, fido_strerr(r));
    220  1.1.1.2  christos     return -1;
    221  1.1.1.2  christos   }
    222  1.1.1.2  christos 
    223  1.1.1.2  christos   return 0;
    224  1.1.1.2  christos }
    225  1.1.1.2  christos 
    226  1.1.1.2  christos static int verify_cred(const fido_cred_t *const cred) {
    227  1.1.1.2  christos   int r;
    228  1.1.1.2  christos 
    229  1.1.1.2  christos   if (cred == NULL) {
    230  1.1.1.2  christos     fprintf(stderr, "%s: args\n", __func__);
    231  1.1.1.2  christos     return -1;
    232  1.1.1.2  christos   }
    233  1.1.1.2  christos 
    234  1.1.1.2  christos   if (fido_cred_x5c_ptr(cred) == NULL) {
    235  1.1.1.2  christos     if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
    236  1.1.1.2  christos       fprintf(stderr, "error: fido_cred_verify_self (%d) %s\n", r,
    237  1.1.1.2  christos               fido_strerr(r));
    238  1.1.1.2  christos       return -1;
    239  1.1.1.2  christos     }
    240  1.1.1.2  christos   } else {
    241  1.1.1.2  christos     if ((r = fido_cred_verify(cred)) != FIDO_OK) {
    242  1.1.1.2  christos       fprintf(stderr, "error: fido_cred_verify (%d) %s\n", r, fido_strerr(r));
    243  1.1.1.2  christos       return -1;
    244  1.1.1.2  christos     }
    245  1.1.1.2  christos   }
    246  1.1.1.2  christos 
    247  1.1.1.2  christos   return 0;
    248  1.1.1.2  christos }
    249  1.1.1.2  christos 
    250  1.1.1.3  christos static int print_authfile_line(const struct args *const args,
    251  1.1.1.2  christos                                const fido_cred_t *const cred) {
    252  1.1.1.2  christos   const unsigned char *kh = NULL;
    253  1.1.1.2  christos   const unsigned char *pk = NULL;
    254  1.1.1.2  christos   const char *user = NULL;
    255  1.1.1.2  christos   char *b64_kh = NULL;
    256  1.1.1.2  christos   char *b64_pk = NULL;
    257  1.1.1.2  christos   size_t kh_len;
    258  1.1.1.2  christos   size_t pk_len;
    259  1.1.1.2  christos   int ok = -1;
    260  1.1.1.2  christos 
    261  1.1.1.2  christos   if ((kh = fido_cred_id_ptr(cred)) == NULL) {
    262  1.1.1.2  christos     fprintf(stderr, "error: fido_cred_id_ptr returned NULL\n");
    263  1.1.1.2  christos     goto err;
    264  1.1.1.2  christos   }
    265  1.1.1.2  christos 
    266  1.1.1.2  christos   if ((kh_len = fido_cred_id_len(cred)) == 0) {
    267  1.1.1.2  christos     fprintf(stderr, "error: fido_cred_id_len returned 0\n");
    268  1.1.1.2  christos     goto err;
    269  1.1.1.2  christos   }
    270  1.1.1.2  christos 
    271  1.1.1.2  christos   if ((pk = fido_cred_pubkey_ptr(cred)) == NULL) {
    272  1.1.1.2  christos     fprintf(stderr, "error: fido_cred_pubkey_ptr returned NULL\n");
    273  1.1.1.2  christos     goto err;
    274  1.1.1.2  christos   }
    275  1.1.1.2  christos 
    276  1.1.1.2  christos   if ((pk_len = fido_cred_pubkey_len(cred)) == 0) {
    277  1.1.1.2  christos     fprintf(stderr, "error: fido_cred_pubkey_len returned 0\n");
    278  1.1.1.2  christos     goto err;
    279  1.1.1.2  christos   }
    280  1.1.1.2  christos 
    281  1.1.1.2  christos   if (!b64_encode(kh, kh_len, &b64_kh)) {
    282  1.1.1.2  christos     fprintf(stderr, "error: failed to encode key handle\n");
    283  1.1.1.2  christos     goto err;
    284  1.1.1.2  christos   }
    285  1.1.1.2  christos 
    286  1.1.1.2  christos   if (!b64_encode(pk, pk_len, &b64_pk)) {
    287  1.1.1.2  christos     fprintf(stderr, "error: failed to encode public key\n");
    288  1.1.1.2  christos     goto err;
    289  1.1.1.2  christos   }
    290  1.1.1.2  christos 
    291  1.1.1.3  christos   if (!args->nouser) {
    292  1.1.1.2  christos     if ((user = fido_cred_user_name(cred)) == NULL) {
    293  1.1.1.2  christos       fprintf(stderr, "error: fido_cred_user_name returned NULL\n");
    294  1.1.1.2  christos       goto err;
    295  1.1.1.2  christos     }
    296  1.1.1.2  christos     printf("%s", user);
    297  1.1.1.2  christos   }
    298  1.1.1.2  christos 
    299  1.1.1.3  christos   printf(":%s,%s,%s,%s%s%s", args->resident ? "*" : b64_kh, b64_pk,
    300  1.1.1.2  christos          cose_string(fido_cred_type(cred)),
    301  1.1.1.3  christos          !args->no_user_presence ? "+presence" : "",
    302  1.1.1.3  christos          args->user_verification ? "+verification" : "",
    303  1.1.1.3  christos          args->pin_verification ? "+pin" : "");
    304  1.1.1.2  christos 
    305  1.1.1.2  christos   ok = 0;
    306  1.1.1.2  christos 
    307  1.1.1.2  christos err:
    308  1.1.1.2  christos   free(b64_kh);
    309  1.1.1.2  christos   free(b64_pk);
    310  1.1.1.2  christos 
    311  1.1.1.2  christos   return ok;
    312  1.1.1.2  christos }
    313  1.1.1.2  christos 
    314  1.1.1.3  christos static int get_device_options(fido_dev_t *dev, int *devopts) {
    315  1.1.1.3  christos   char *const *opts;
    316  1.1.1.3  christos   const bool *vals;
    317  1.1.1.3  christos   fido_cbor_info_t *info;
    318  1.1.1.3  christos   int r;
    319  1.1.1.3  christos 
    320  1.1.1.3  christos   *devopts = 0;
    321  1.1.1.3  christos 
    322  1.1.1.3  christos   if (!fido_dev_is_fido2(dev))
    323  1.1.1.3  christos     return 0;
    324  1.1.1.3  christos 
    325  1.1.1.3  christos   if ((info = fido_cbor_info_new()) == NULL) {
    326  1.1.1.3  christos     fprintf(stderr, "fido_cbor_info_new failed\n");
    327  1.1.1.3  christos     return -1;
    328  1.1.1.3  christos   }
    329  1.1.1.3  christos   if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
    330  1.1.1.3  christos     fprintf(stderr, "fido_dev_get_cbor_info: %s (%d)\n", fido_strerr(r), r);
    331  1.1.1.3  christos     fido_cbor_info_free(&info);
    332  1.1.1.3  christos     return -1;
    333  1.1.1.3  christos   }
    334  1.1.1.3  christos 
    335  1.1.1.3  christos   opts = fido_cbor_info_options_name_ptr(info);
    336  1.1.1.3  christos   vals = fido_cbor_info_options_value_ptr(info);
    337  1.1.1.3  christos   for (size_t i = 0; i < fido_cbor_info_options_len(info); i++) {
    338  1.1.1.3  christos     if (strcmp(opts[i], "clientPin") == 0) {
    339  1.1.1.3  christos       *devopts |= vals[i] ? PIN_SET : PIN_UNSET;
    340  1.1.1.3  christos     } else if (strcmp(opts[i], "uv") == 0) {
    341  1.1.1.3  christos       *devopts |= vals[i] ? UV_SET : UV_UNSET;
    342  1.1.1.3  christos     } else if (strcmp(opts[i], "makeCredUvNotRqd") == 0) {
    343  1.1.1.3  christos       *devopts |= vals[i] ? UV_NOT_REQD : UV_REQD;
    344  1.1.1.3  christos     }
    345  1.1.1.3  christos   }
    346  1.1.1.3  christos 
    347  1.1.1.3  christos   fido_cbor_info_free(&info);
    348  1.1.1.3  christos 
    349  1.1.1.3  christos   return 0;
    350  1.1.1.3  christos }
    351  1.1.1.3  christos 
    352  1.1.1.3  christos static void parse_args(int argc, char *argv[], struct args *args) {
    353  1.1.1.3  christos   int c;
    354  1.1.1.3  christos   enum {
    355  1.1.1.3  christos     OPT_VERSION = 0x100,
    356  1.1.1.3  christos   };
    357  1.1.1.3  christos   /* clang-format off */
    358  1.1.1.3  christos   static const struct option options[] = {
    359  1.1.1.3  christos     { "help",              no_argument,       NULL, 'h'         },
    360  1.1.1.3  christos     { "version",           no_argument,       NULL, OPT_VERSION },
    361  1.1.1.3  christos     { "origin",            required_argument, NULL, 'o'         },
    362  1.1.1.3  christos     { "appid",             required_argument, NULL, 'i'         },
    363  1.1.1.3  christos     { "type",              required_argument, NULL, 't'         },
    364  1.1.1.3  christos     { "resident",          no_argument,       NULL, 'r'         },
    365  1.1.1.3  christos     { "no-user-presence",  no_argument,       NULL, 'P'         },
    366  1.1.1.3  christos     { "pin-verification",  no_argument,       NULL, 'N'         },
    367  1.1.1.3  christos     { "user-verification", no_argument,       NULL, 'V'         },
    368  1.1.1.3  christos     { "debug",             no_argument,       NULL, 'd'         },
    369  1.1.1.3  christos     { "verbose",           no_argument,       NULL, 'v'         },
    370  1.1.1.3  christos     { "username",          required_argument, NULL, 'u'         },
    371  1.1.1.3  christos     { "nouser",            no_argument,       NULL, 'n'         },
    372  1.1.1.3  christos     { 0,                   0,                 0,    0           }
    373  1.1.1.3  christos   };
    374  1.1.1.3  christos   const char *usage =
    375  1.1.1.3  christos "Usage: pamu2fcfg [OPTION]...\n"
    376  1.1.1.3  christos "Perform a FIDO2/U2F registration operation and print a configuration line that\n"
    377  1.1.1.3  christos "can be used with the pam_u2f module.\n"
    378  1.1.1.3  christos "\n"
    379  1.1.1.3  christos "  -h, --help               Print help and exit\n"
    380  1.1.1.3  christos "      --version            Print version and exit\n"
    381  1.1.1.3  christos "  -o, --origin=STRING      Relying party ID to use during registration,\n"
    382  1.1.1.3  christos "                             defaults to pam://hostname\n"
    383  1.1.1.3  christos "  -i, --appid=STRING       Relying party name to use during registration,\n"
    384  1.1.1.3  christos "                             defaults to the value of origin\n"
    385  1.1.1.3  christos "  -t, --type=STRING        COSE type to use during registration (ES256, EDDSA,\n"
    386  1.1.1.3  christos "                             or RS256), defaults to ES256\n"
    387  1.1.1.3  christos "  -r, --resident           Generate a resident (discoverable) credential\n"
    388  1.1.1.3  christos "  -P, --no-user-presence   Allow the credential to be used without ensuring the\n"
    389  1.1.1.3  christos "                             user's presence\n"
    390  1.1.1.3  christos "  -N, --pin-verification   Require PIN verification during authentication\n"
    391  1.1.1.3  christos "  -V, --user-verification  Require user verification during authentication\n"
    392  1.1.1.3  christos "  -d, --debug              Print debug information\n"
    393  1.1.1.3  christos "  -v, --verbose            Print information about chosen origin and appid\n"
    394  1.1.1.3  christos "  -u, --username=STRING    The name of the user registering the device,\n"
    395  1.1.1.3  christos "                             defaults to the current user name\n"
    396  1.1.1.3  christos "  -n, --nouser             Print only registration information (key handle,\n"
    397  1.1.1.3  christos "                             public key, and options), useful for appending\n"
    398  1.1.1.3  christos "\n"
    399  1.1.1.3  christos "Report bugs at <" PACKAGE_BUGREPORT ">.\n";
    400  1.1.1.3  christos   /* clang-format on */
    401  1.1.1.3  christos 
    402  1.1.1.3  christos   while ((c = getopt_long(argc, argv, "ho:i:t:rPNVdvu:n", options, NULL)) !=
    403  1.1.1.3  christos          -1) {
    404  1.1.1.3  christos     switch (c) {
    405  1.1.1.3  christos       case 'h':
    406  1.1.1.3  christos         printf("%s", usage);
    407  1.1.1.3  christos         exit(EXIT_SUCCESS);
    408  1.1.1.3  christos       case 'o':
    409  1.1.1.3  christos         args->origin = optarg;
    410  1.1.1.3  christos         break;
    411  1.1.1.3  christos       case 'i':
    412  1.1.1.3  christos         args->appid = optarg;
    413  1.1.1.3  christos         break;
    414  1.1.1.3  christos       case 't':
    415  1.1.1.3  christos         args->type = optarg;
    416  1.1.1.3  christos         break;
    417  1.1.1.3  christos       case 'u':
    418  1.1.1.3  christos         args->username = optarg;
    419  1.1.1.3  christos         break;
    420  1.1.1.3  christos       case 'r':
    421  1.1.1.3  christos         args->resident = 1;
    422  1.1.1.3  christos         break;
    423  1.1.1.3  christos       case 'P':
    424  1.1.1.3  christos         args->no_user_presence = 1;
    425  1.1.1.3  christos         break;
    426  1.1.1.3  christos       case 'N':
    427  1.1.1.3  christos         args->pin_verification = 1;
    428  1.1.1.3  christos         break;
    429  1.1.1.3  christos       case 'V':
    430  1.1.1.3  christos         args->user_verification = 1;
    431  1.1.1.3  christos         break;
    432  1.1.1.3  christos       case 'd':
    433  1.1.1.3  christos         args->debug = 1;
    434  1.1.1.3  christos         break;
    435  1.1.1.3  christos       case 'v':
    436  1.1.1.3  christos         args->verbose = 1;
    437  1.1.1.3  christos         break;
    438  1.1.1.3  christos       case 'n':
    439  1.1.1.3  christos         args->nouser = 1;
    440  1.1.1.3  christos         break;
    441  1.1.1.3  christos       case OPT_VERSION:
    442  1.1.1.3  christos         printf("pamu2fcfg " PACKAGE_VERSION "\n");
    443  1.1.1.3  christos         exit(EXIT_SUCCESS);
    444  1.1.1.3  christos       case '?':
    445  1.1.1.3  christos         exit(EXIT_FAILURE);
    446  1.1.1.3  christos       default:
    447  1.1.1.3  christos         errx(EXIT_FAILURE, "unknown option 0x%x", c);
    448  1.1.1.3  christos     }
    449  1.1.1.3  christos   }
    450  1.1.1.3  christos 
    451  1.1.1.3  christos   if (optind != argc)
    452  1.1.1.3  christos     errx(EXIT_FAILURE, "unsupported positional argument(s)");
    453  1.1.1.3  christos }
    454  1.1.1.3  christos 
    455  1.1.1.2  christos int main(int argc, char *argv[]) {
    456  1.1.1.2  christos   int exit_code = EXIT_FAILURE;
    457  1.1.1.3  christos   struct args args = {0};
    458  1.1.1.2  christos   fido_cred_t *cred = NULL;
    459  1.1.1.2  christos   fido_dev_info_t *devlist = NULL;
    460  1.1.1.2  christos   fido_dev_t *dev = NULL;
    461  1.1.1.2  christos   const fido_dev_info_t *di = NULL;
    462  1.1.1.2  christos   const char *path = NULL;
    463  1.1.1.2  christos   size_t ndevs = 0;
    464  1.1.1.3  christos   int devopts = 0;
    465  1.1.1.2  christos   int r;
    466  1.1.1.2  christos 
    467  1.1.1.3  christos   parse_args(argc, argv, &args);
    468  1.1.1.3  christos   fido_init(args.debug ? FIDO_DEBUG : 0);
    469  1.1.1.2  christos 
    470  1.1.1.3  christos   devlist = fido_dev_info_new(DEVLIST_LEN);
    471      1.1  christos   if (!devlist) {
    472      1.1  christos     fprintf(stderr, "error: fido_dev_info_new failed\n");
    473  1.1.1.2  christos     goto err;
    474      1.1  christos   }
    475      1.1  christos 
    476  1.1.1.3  christos   r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
    477      1.1  christos   if (r != FIDO_OK) {
    478      1.1  christos     fprintf(stderr, "Unable to discover device(s), %s (%d)\n", fido_strerr(r),
    479      1.1  christos             r);
    480  1.1.1.2  christos     goto err;
    481      1.1  christos   }
    482      1.1  christos 
    483      1.1  christos   if (ndevs == 0) {
    484  1.1.1.2  christos     for (int i = 0; i < TIMEOUT; i += FREQUENCY) {
    485      1.1  christos       fprintf(stderr,
    486      1.1  christos               "\rNo U2F device available, please insert one now, you "
    487      1.1  christos               "have %2d seconds",
    488      1.1  christos               TIMEOUT - i);
    489      1.1  christos       fflush(stderr);
    490      1.1  christos       sleep(FREQUENCY);
    491      1.1  christos 
    492  1.1.1.3  christos       r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
    493      1.1  christos       if (r != FIDO_OK) {
    494      1.1  christos         fprintf(stderr, "\nUnable to discover device(s), %s (%d)",
    495      1.1  christos                 fido_strerr(r), r);
    496  1.1.1.2  christos         goto err;
    497      1.1  christos       }
    498      1.1  christos 
    499      1.1  christos       if (ndevs != 0) {
    500      1.1  christos         fprintf(stderr, "\nDevice found!\n");
    501      1.1  christos         break;
    502      1.1  christos       }
    503      1.1  christos     }
    504      1.1  christos   }
    505      1.1  christos 
    506      1.1  christos   if (ndevs == 0) {
    507      1.1  christos     fprintf(stderr, "\rNo device found. Aborting.                              "
    508      1.1  christos                     "           \n");
    509  1.1.1.2  christos     goto err;
    510      1.1  christos   }
    511      1.1  christos 
    512      1.1  christos   /* XXX loop over every device? */
    513      1.1  christos   dev = fido_dev_new();
    514      1.1  christos   if (!dev) {
    515      1.1  christos     fprintf(stderr, "fido_dev_new failed\n");
    516  1.1.1.2  christos     goto err;
    517      1.1  christos   }
    518      1.1  christos 
    519      1.1  christos   di = fido_dev_info_ptr(devlist, 0);
    520      1.1  christos   if (!di) {
    521      1.1  christos     fprintf(stderr, "error: fido_dev_info_ptr returned NULL\n");
    522  1.1.1.2  christos     goto err;
    523      1.1  christos   }
    524      1.1  christos 
    525  1.1.1.2  christos   if ((path = fido_dev_info_path(di)) == NULL) {
    526  1.1.1.2  christos     fprintf(stderr, "error: fido_dev_path returned NULL\n");
    527  1.1.1.2  christos     goto err;
    528      1.1  christos   }
    529      1.1  christos 
    530  1.1.1.2  christos   r = fido_dev_open(dev, path);
    531      1.1  christos   if (r != FIDO_OK) {
    532  1.1.1.2  christos     fprintf(stderr, "error: fido_dev_open (%d) %s\n", r, fido_strerr(r));
    533  1.1.1.2  christos     goto err;
    534      1.1  christos   }
    535      1.1  christos 
    536  1.1.1.3  christos   if (get_device_options(dev, &devopts) != 0) {
    537  1.1.1.3  christos     goto err;
    538  1.1.1.3  christos   }
    539  1.1.1.3  christos   if (args.pin_verification && !(devopts & PIN_SET)) {
    540  1.1.1.3  christos     warnx("%s", devopts & PIN_UNSET ? "device has no PIN"
    541  1.1.1.3  christos                                     : "device does not support PIN");
    542  1.1.1.3  christos     goto err;
    543  1.1.1.3  christos   }
    544  1.1.1.3  christos   if (args.user_verification && !(devopts & UV_SET)) {
    545  1.1.1.3  christos     warnx("%s", devopts & UV_UNSET
    546  1.1.1.3  christos                   ? "device has no built-in user verification configured"
    547  1.1.1.3  christos                   : "device does not support built-in user verification");
    548  1.1.1.3  christos     goto err;
    549  1.1.1.3  christos   }
    550  1.1.1.3  christos   if ((devopts & (UV_REQD | PIN_SET | UV_SET)) == UV_REQD) {
    551  1.1.1.3  christos     warnx("%s", "some form of user verification required but none configured");
    552  1.1.1.3  christos     goto err;
    553  1.1.1.3  christos   }
    554  1.1.1.3  christos 
    555  1.1.1.3  christos   if ((cred = prepare_cred(&args)) == NULL)
    556  1.1.1.3  christos     goto err;
    557  1.1.1.3  christos 
    558  1.1.1.3  christos   if (make_cred(&args, path, dev, cred, devopts) != 0 ||
    559  1.1.1.3  christos       verify_cred(cred) != 0 || print_authfile_line(&args, cred) != 0)
    560  1.1.1.2  christos     goto err;
    561      1.1  christos 
    562      1.1  christos   exit_code = EXIT_SUCCESS;
    563      1.1  christos 
    564  1.1.1.2  christos err:
    565  1.1.1.2  christos   if (dev != NULL)
    566  1.1.1.2  christos     fido_dev_close(dev);
    567      1.1  christos   fido_dev_info_free(&devlist, ndevs);
    568      1.1  christos   fido_cred_free(&cred);
    569      1.1  christos   fido_dev_free(&dev);
    570      1.1  christos 
    571      1.1  christos   exit(exit_code);
    572      1.1  christos }
    573