int _ykp_json_import_cfg(YKP_CONFIG *cfg, const char *json, size_t len) { int ret_code = 0; if(cfg) { json_object *jobj = json_tokener_parse(json); json_object *yprod_json = json_object_object_get(jobj, "yubiProdConfig"); json_object *jmode = json_object_object_get(yprod_json, "mode"); json_object *options = json_object_object_get(yprod_json, "options"); json_object *jtarget; const char *raw_mode; int mode = MODE_OTP_YUBICO; struct map_st *p; if(!jobj || !yprod_json || !jmode || !options) { ykp_errno = YKP_EINVAL; goto out; } jtarget = json_object_object_get(yprod_json, "targetConfig"); if(jtarget) { int target_config = json_object_get_int(jtarget); int command; if(target_config == 1) { command = SLOT_CONFIG; } else if(target_config == 2) { command = SLOT_CONFIG2; } else { ykp_errno = YKP_EINVAL; goto out; } if(ykp_command(cfg) == 0) { ykp_configure_command(cfg, command); } else if(ykp_command(cfg) != command) { ykp_errno = YKP_EINVAL; goto out; } } raw_mode = json_object_get_string(jmode); for(p = _modes_map; p->flag; p++) { if(strcmp(raw_mode, p->json_text) == 0) { mode = p->flag; break; } } if(mode == MODE_OATH_HOTP) { json_object *jdigits = json_object_object_get(options, "oathDigits"); json_object *jrandom = json_object_object_get(options, "randomSeed"); ykp_set_tktflag_OATH_HOTP(cfg, true); if(jdigits) { int digits = json_object_get_int(jdigits); if(digits == 8) { ykp_set_cfgflag_OATH_HOTP8(cfg, true); } } if(jrandom) { int random = json_object_get_boolean(jrandom); int seed = 0; if(random == 1) { /* XXX: add random seed.. */ } else { json_object *jseed = json_object_object_get(options, "fixedSeedvalue"); if(jseed) { seed = json_object_get_int(jseed); } } ykp_set_oath_imf(cfg, (long unsigned int)seed); } } else if(mode == MODE_CHAL_HMAC) { ykp_set_tktflag_CHAL_RESP(cfg, true); ykp_set_cfgflag_CHAL_HMAC(cfg, true); } else if(mode == MODE_CHAL_YUBICO) { ykp_set_tktflag_CHAL_RESP(cfg, true); ykp_set_cfgflag_CHAL_YUBICO(cfg, true); } else if(mode == MODE_STATIC_TICKET) { ykp_set_cfgflag_STATIC_TICKET(cfg, true); } for(p = _ticket_flags_map; p->flag; p++) { if(!p->json_text) { continue; } if(p->mode && (mode & p->mode) == mode) { json_object *joption = json_object_object_get(options, p->json_text); if(joption && json_object_get_type(joption) == json_type_boolean) { int value = json_object_get_boolean(joption); if(value == 1) { p->setter(cfg, true); } } } } for(p = _config_flags_map; p->flag; p++) { if(!p->json_text) { continue; } if(p->mode && (mode & p->mode) == mode) { json_object *joption = json_object_object_get(options, p->json_text); if(joption && json_object_get_type(joption) == json_type_boolean) { int value = json_object_get_boolean(joption); if(value == 1) { p->setter(cfg, true); } } } } for(p = _extended_flags_map; p->flag; p++) { if(!p->json_text) { continue; } if(p->mode && (mode & p->mode) == mode) { json_object *joption = json_object_object_get(options, p->json_text); if(joption && json_object_get_type(joption) == json_type_boolean) { int value = json_object_get_boolean(joption); if(value == 1) { p->setter(cfg, true); } } } } ret_code = 1; out: if(jobj) { json_object_put(jobj); } } ykp_errno = YKP_EINVAL; return ret_code; }
void YubiKeyWriter::writeConfig(YubiKeyConfig *ykConfig) { YubiKeyFinder::getInstance()->stop(); YK_KEY *yk = 0; YK_STATUS *ykst = YubiKeyFinder::getInstance()->status(); YKP_CONFIG *cfg = ykp_alloc(); bool error = false; qDebug() << "-------------------------"; qDebug() << "Starting write config"; qDebug() << "-------------------------"; try { if (!(yk = yk_open_first_key())) { throw 0; } else if (!yk_get_status(yk, ykst)) { throw 0; } ykp_configure_version(cfg, ykst); qDebug() << "writer:configuration slot:" << ykConfig->configSlot(); bool useAccessCode; unsigned char accessCode[MAX_SIZE]; if(!assembleConfig(ykConfig, cfg, &useAccessCode, accessCode)) { throw 0; } //Log configuration... qDebug() << "-------------------------"; qDebug() << "Config data to be written to key configuration..."; char conf_buf[1024]; ykp_export_config(cfg, conf_buf, 1024, YKP_FORMAT_LEGACY); qDebug() << conf_buf; qDebug() << "-------------------------"; // Write configuration... if (!yk_write_command(yk, ykp_core_config(cfg), ykp_command(cfg), useAccessCode ? accessCode : NULL)) { qDebug() << "Failure"; throw 0; } qDebug() << "Success... config written"; emit diagnostics(QString("Successfully wrote config to slot %1").arg(ykp_config_num(cfg))); YubiKeyLogger::logConfig(ykConfig); emit configWritten(true, NULL); } catch(...) { error = true; } if (cfg) { ykp_free_config(cfg); } if (yk && !yk_close_key(yk)) { error = true; } YubiKeyFinder::getInstance()->start(); if(error) { qDebug() << "Config not written"; QString errMsg = reportError(); emit configWritten(false, errMsg); } qDebug() << "-------------------------"; qDebug() << "Stopping write config"; qDebug() << "-------------------------"; }
/* * Parse all arguments supplied to this program and turn it into mainly * a YKP_CONFIG (but return some other parameters as well, like * access_code, verbose etc.). * * Done in this way to be testable (see tests/test_args_to_config.c). */ int args_to_config(int argc, char **argv, YKP_CONFIG *cfg, YK_KEY *yk, const char **infname, const char **outfname, bool *autocommit, char *salt, YK_STATUS *st, bool *verbose, unsigned char *access_code, bool *use_access_code, bool *aesviahash, char *ndef_type, char *ndef, unsigned char *usb_mode, bool *zap, unsigned char *scan_bin, int *exit_code) { int c; const char *aeshash = NULL; bool new_access_code = false; bool slot_chosen = false; bool mode_chosen = false; bool option_seen = false; bool swap_seen = false; bool update_seen = false; bool ndef_seen = false; bool usb_mode_seen = false; bool scan_map_seen = false; struct config_st *ycfg; ykp_configure_version(cfg, st); ycfg = (struct config_st *) ykp_core_config(cfg); while((c = getopt(argc, argv, optstring)) != -1) { if (c == 'o') { if (strcmp(optarg, "oath-hotp") == 0 || strcmp(optarg, "chal-resp") == 0) { if (mode_chosen) { fprintf(stderr, "You may only choose mode (-ooath-hotp / " "-ochal-resp) once.\n"); *exit_code = 1; return 0; } if (option_seen) { fprintf(stderr, "Mode choosing flags (oath-hotp / chal-resp) " "must be set prior to any other options (-o).\n"); *exit_code = 1; return 0; } /* The default flags (particularly for slot 2) does not apply to * these new modes of operation found in Yubikey >= 2.1. Therefor, * we reset them here and, as a consequence of that, require the * mode choosing options to be specified before any other. */ ycfg->tktFlags = 0; ycfg->cfgFlags = 0; ycfg->extFlags = 0; mode_chosen = 1; } option_seen = true; } switch (c) { case 'u': if (slot_chosen) { fprintf(stderr, "You must use update before slot (-1 / -2).\n"); *exit_code = 1; return 0; } if (swap_seen) { fprintf(stderr, "Update (-u) and swap (-x) can't be combined.\n"); *exit_code = 1; return 0; } if (ndef_seen) { fprintf(stderr, "Update (-u) can not be combined with ndef (-n).\n"); *exit_code = 1; return 0; } update_seen = true; break; case '1': case '2': { int command; if (slot_chosen) { fprintf(stderr, "You may only choose slot (-1 / -2) once.\n"); *exit_code = 1; return 0; } if (option_seen) { fprintf(stderr, "You must choose slot before any options (-o).\n"); *exit_code = 1; return 0; } if (swap_seen) { fprintf(stderr, "You can not combine slot swap (-x) with configuring a slot.\n"); *exit_code = 1; return 0; } ykp_set_tktflag_APPEND_CR(cfg, true); if (update_seen) { ykp_set_extflag_ALLOW_UPDATE(cfg, true); if(c == '1') { command = SLOT_UPDATE1; } else if(c == '2') { command = SLOT_UPDATE2; } } else if (c == '1') { command = SLOT_CONFIG; } else if (c == '2') { command = SLOT_CONFIG2; ykp_set_cfgflag_STATIC_TICKET(cfg, true); ykp_set_cfgflag_STRONG_PW1(cfg, true); ykp_set_cfgflag_STRONG_PW2(cfg, true); ykp_set_cfgflag_MAN_UPDATE(cfg, true); } if (!ykp_configure_command(cfg, command)) return 0; slot_chosen = true; break; } case 'x': if (slot_chosen || option_seen || update_seen || ndef_seen || *zap || usb_mode_seen || scan_map_seen) { fprintf(stderr, "Slot swap (-x) can not be used with other options.\n"); *exit_code = 1; return 0; } if (!ykp_configure_command(cfg, SLOT_SWAP)) { return 0; } swap_seen = true; break; case 'z': if (swap_seen || update_seen || ndef_seen || usb_mode_seen || scan_map_seen) { fprintf(stderr, "Zap (-z) can only be used with a slot (-1 / -2).\n"); *exit_code = 1; return 0; } *zap = true; break; case 'i': *infname = optarg; break; case 's': *outfname = optarg; break; case 'a': *aesviahash = true; aeshash = optarg; break; case 'c': { size_t access_code_len = 0; int rc = hex_modhex_decode(access_code, &access_code_len, optarg, strlen(optarg), 12, 12, false); if (rc <= 0) { fprintf(stderr, "Invalid access code string: %s\n", optarg); *exit_code = 1; return 0; } if (!new_access_code) ykp_set_access_code(cfg, access_code, access_code_len); *use_access_code = true; break; } case 't': *ndef_type = 'T'; case 'n': { int command; if(!*ndef_type) { *ndef_type = 'U'; } if (swap_seen || update_seen || option_seen || *zap || usb_mode_seen || scan_map_seen) { fprintf(stderr, "Ndef (-n/-t) can only be used with a slot (-1/-2).\n"); *exit_code = 1; return 0; } if(ykp_command(cfg) == SLOT_CONFIG) { command = SLOT_NDEF; } else if(ykp_command(cfg) == SLOT_CONFIG2) { command = SLOT_NDEF2; } else { command = SLOT_NDEF; } if (!ykp_configure_command(cfg, command)) { return 0; } memcpy(ndef, optarg, strlen(optarg)); ndef_seen = true; break; } case 'm': if(slot_chosen || swap_seen || update_seen || option_seen || ndef_seen || *zap || scan_map_seen) { fprintf(stderr, "USB mode (-m) can not be combined with other options.\n"); *exit_code = 1; return 0; } if(optarg[1] != '\0') { *usb_mode = (optarg[0] - '0') << 4; optarg++; } if(optarg[1] == '\0') { int mode = optarg[0] - '0'; if(mode >= 0 && mode < MODE_MASK) { *usb_mode |= mode; usb_mode_seen = true; } } /* Only true if we've parsed a valid USB mode number */ if(!usb_mode_seen) { fprintf(stderr, "Invalid USB operation mode.\n"); *exit_code = 1; return 0; } if (!ykp_configure_command(cfg, SLOT_DEVICE_CONFIG)) return 0; break; case 'S': { size_t scanlength = strlen(SCAN_MAP); if(slot_chosen || swap_seen || update_seen || option_seen || ndef_seen || *zap || usb_mode_seen) { fprintf(stderr, "Scanmap (-S) can not be combined with other options.\n"); *exit_code = 1; return 0; } if(optarg) { size_t scanbinlen; size_t scanlen = strlen (optarg); int rc = hex_modhex_decode(scan_bin, &scanbinlen, optarg, scanlen, scanlength * 2, scanlength * 2, false); if (rc <= 0) { fprintf(stderr, "Invalid scanmap string %s\n", optarg); *exit_code = 1; return 0; } } else { memset(scan_bin, 0, scanlength); } scan_map_seen = true; } if (!ykp_configure_command(cfg, SLOT_SCAN_MAP)) return 0; break; case 'o': if (*zap) { fprintf(stderr, "No options can be given with zap (-z).\n"); *exit_code = 1; return 0; } if (strncmp(optarg, "salt=", 5) == 0) salt = strdup(optarg+5); else if (strncmp(optarg, "fixed=", 6) == 0) { if (_set_fixed(optarg + 6, cfg) != 1) { fprintf(stderr, "Invalid fixed string: %s\n", optarg + 6); *exit_code = 1; return 0; } } else if (strncmp(optarg, "uid=", 4) == 0) { const char *uid = optarg+4; size_t uidlen = strlen (uid); unsigned char uidbin[256]; size_t uidbinlen = 0; int rc = hex_modhex_decode(uidbin, &uidbinlen, uid, uidlen, 12, 12, false); if (rc <= 0) { fprintf(stderr, "Invalid uid string: %s\n", uid); *exit_code = 1; return 0; } /* for OATH-HOTP and CHAL-RESP, uid is not applicable */ if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP || (ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP) { fprintf(stderr, "Option uid= not valid with -ooath-hotp or -ochal-resp.\n" ); *exit_code = 1; return 0; } ykp_set_uid(cfg, uidbin, uidbinlen); } else if (strncmp(optarg, "access=", 7) == 0) { const char *acc = optarg+7; size_t acclen = strlen (acc); unsigned char accbin[256]; size_t accbinlen = 0; int rc = hex_modhex_decode (accbin, &accbinlen, acc, acclen, 12, 12, false); if (rc <= 0) { fprintf(stderr, "Invalid access code string: %s\n", acc); *exit_code = 1; return 0; } ykp_set_access_code(cfg, accbin, accbinlen); new_access_code = true; } #define TKTFLAG(o, f) \ else if (strcmp(optarg, o) == 0) { \ if (!ykp_set_tktflag_##f(cfg, true)) { \ *exit_code = 1; \ return 0; \ } \ } else if (strcmp(optarg, "-" o) == 0) { \ if (! ykp_set_tktflag_##f(cfg, false)) { \ *exit_code = 1; \ return 0; \ } \ } TKTFLAG("tab-first", TAB_FIRST) TKTFLAG("append-tab1", APPEND_TAB1) TKTFLAG("append-tab2", APPEND_TAB2) TKTFLAG("append-delay1", APPEND_DELAY1) TKTFLAG("append-delay2", APPEND_DELAY2) TKTFLAG("append-cr", APPEND_CR) TKTFLAG("protect-cfg2", PROTECT_CFG2) TKTFLAG("oath-hotp", OATH_HOTP) TKTFLAG("chal-resp", CHAL_RESP) #undef TKTFLAG #define CFGFLAG(o, f) \ else if (strcmp(optarg, o) == 0) { \ if (! ykp_set_cfgflag_##f(cfg, true)) { \ *exit_code = 1; \ return 0; \ } \ } else if (strcmp(optarg, "-" o) == 0) { \ if (! ykp_set_cfgflag_##f(cfg, false)) { \ *exit_code = 1; \ return 0; \ } \ } CFGFLAG("send-ref", SEND_REF) CFGFLAG("ticket-first", TICKET_FIRST) CFGFLAG("pacing-10ms", PACING_10MS) CFGFLAG("pacing-20ms", PACING_20MS) CFGFLAG("allow-hidtrig", ALLOW_HIDTRIG) CFGFLAG("static-ticket", STATIC_TICKET) CFGFLAG("short-ticket", SHORT_TICKET) CFGFLAG("strong-pw1", STRONG_PW1) CFGFLAG("strong-pw2", STRONG_PW2) CFGFLAG("man-update", MAN_UPDATE) CFGFLAG("oath-hotp8", OATH_HOTP8) CFGFLAG("oath-fixed-modhex1", OATH_FIXED_MODHEX1) CFGFLAG("oath-fixed-modhex2", OATH_FIXED_MODHEX2) CFGFLAG("oath-fixed-modhex", OATH_FIXED_MODHEX) CFGFLAG("chal-yubico", CHAL_YUBICO) CFGFLAG("chal-hmac", CHAL_HMAC) CFGFLAG("hmac-lt64", HMAC_LT64) CFGFLAG("chal-btn-trig", CHAL_BTN_TRIG) #undef CFGFLAG else if (strncmp(optarg, "oath-imf=", 9) == 0) { unsigned long imf; if (!(ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) { fprintf(stderr, "Option oath-imf= only valid with -ooath-hotp or -ooath-hotp8.\n" ); *exit_code = 1; return 0; } if (sscanf(optarg+9, "%lu", &imf) != 1 || /* yubikey limitations */ imf > 65535*16 || imf % 16 != 0) { fprintf(stderr, "Invalid value %s for oath-imf=.\n", optarg+9 ); *exit_code = 1; return 0; } if (! ykp_set_oath_imf(cfg, imf)) { *exit_code = 1; return 0; } } else if (strncmp(optarg, "oath-id=", 8) == 0 || strcmp(optarg, "oath-id") == 0) { if (_set_oath_id(optarg, cfg, ycfg, yk, st) != 1) { *exit_code = 1; return 0; } } #define EXTFLAG(o, f) \ else if (strcmp(optarg, o) == 0) { \ if (! ykp_set_extflag_##f(cfg, true)) { \ *exit_code = 1; \ return 0; \ } \ } else if (strcmp(optarg, "-" o) == 0) { \ if (! ykp_set_extflag_##f(cfg, false)) { \ *exit_code = 1; \ return 0; \ } \ } EXTFLAG("serial-btn-visible", SERIAL_BTN_VISIBLE) EXTFLAG("serial-usb-visible", SERIAL_USB_VISIBLE) EXTFLAG("serial-api-visible", SERIAL_API_VISIBLE) EXTFLAG("use-numeric-keypad", USE_NUMERIC_KEYPAD) EXTFLAG("fast-trig", FAST_TRIG) EXTFLAG("allow-update", ALLOW_UPDATE) EXTFLAG("dormant", DORMANT) #undef EXTFLAG else { fprintf(stderr, "Unknown option '%s'\n", optarg); fputs(usage, stderr); *exit_code = 1; return 0; } break; case 'v': *verbose = true; break; case 'y': *autocommit = true; break; case 'h': default: fputs(usage, stderr); *exit_code = 0; return 0; } } if (!slot_chosen && !ndef_seen && !swap_seen && !usb_mode_seen && !scan_map_seen) { fprintf(stderr, "A slot must be chosen with -1 or -2.\n"); *exit_code = 1; return 0; } if (update_seen) { struct config_st *core_config = (struct config_st *) ykp_core_config(cfg); if ((core_config->tktFlags & TKTFLAG_UPDATE_MASK) != core_config->tktFlags) { fprintf(stderr, "Unallowed ticket flags with update.\n"); *exit_code = 1; return 0; } if ((core_config->cfgFlags & CFGFLAG_UPDATE_MASK) != core_config->cfgFlags) { fprintf(stderr, "Unallowed cfg flags with update.\n"); *exit_code = 1; return 0; } if ((core_config->extFlags & EXTFLAG_UPDATE_MASK) != core_config->extFlags) { fprintf(stderr, "Unallowed ext flags with update.\n"); *exit_code = 1; return 0; } } if (*aesviahash) { bool long_key_valid = false; int res = 0; /* for OATH-HOTP, 160 bits key is also valid */ if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) long_key_valid = true; /* for HMAC (not Yubico) challenge-response, 160 bits key is also valid */ if ((ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP && (ycfg->cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) { long_key_valid = true; } if (long_key_valid && strlen(aeshash) == 40) { res = ykp_HMAC_key_from_hex(cfg, aeshash); } else { res = ykp_AES_key_from_hex(cfg, aeshash); } if (res) { fprintf(stderr, "Bad %s key: %s\n", long_key_valid ? "HMAC":"AES", aeshash); fflush(stderr); return 0; } } return 1; }