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