int write_chalresp_state(FILE *f, CR_STATE *state) { char challenge_hex[CR_CHALLENGE_SIZE * 2 + 1], response_hex[CR_RESPONSE_SIZE * 2 + 1]; int fd; memset(challenge_hex, 0, sizeof(challenge_hex)); memset(response_hex, 0, sizeof(response_hex)); yubikey_hex_encode(challenge_hex, (char *)state->challenge, state->challenge_len); yubikey_hex_encode(response_hex, (char *)state->response, state->response_len); rewind(f); fd = fileno(f); if (fd == -1) goto out; if (ftruncate(fd, 0)) goto out; fprintf(f, "v1:%s:%s:%d\n", challenge_hex, response_hex, state->slot); if (fflush(f) < 0) goto out; if (fsync(fd) < 0) goto out; return 1; out: return 0; }
int write_chalresp_state(FILE *f, CR_STATE *state) { char challenge_hex[CR_CHALLENGE_SIZE * 2 + 1], response_hex[CR_RESPONSE_SIZE * 2 + 1]; char salt_hex[CR_SALT_SIZE * 2 + 1], hashed_hex[CR_RESPONSE_SIZE * 2 + 1]; unsigned char salt[CR_SALT_SIZE], hash[CR_RESPONSE_SIZE]; YK_PRF_METHOD prf_method = {20, yk_hmac_sha1}; unsigned int iterations = CR_DEFAULT_ITERATIONS; int fd; memset(challenge_hex, 0, sizeof(challenge_hex)); memset(response_hex, 0, sizeof(response_hex)); memset(salt_hex, 0, sizeof(salt_hex)); memset(hashed_hex, 0, sizeof(hashed_hex)); yubikey_hex_encode(challenge_hex, (char *)state->challenge, state->challenge_len); yubikey_hex_encode(response_hex, (char *)state->response, state->response_len); if(state->iterations > 0) { iterations = state->iterations; } generate_random(salt, CR_SALT_SIZE); yk_pbkdf2(response_hex, salt, CR_SALT_SIZE, iterations, hash, CR_RESPONSE_SIZE, &prf_method); yubikey_hex_encode(hashed_hex, (char *)hash, CR_RESPONSE_SIZE); yubikey_hex_encode(salt_hex, (char *)salt, CR_SALT_SIZE); rewind(f); fd = fileno(f); if (fd == -1) goto out; if (ftruncate(fd, 0)) goto out; fprintf(f, "v2:%s:%s:%s:%u:%d\n", challenge_hex, hashed_hex, salt_hex, iterations, state->slot); if (fflush(f) < 0) goto out; if (fsync(fd) < 0) goto out; return 1; out: return 0; }
const std::string YubikoOtpKeyConfig::getSecretKey() const { BOOST_LOG_NAMED_SCOPE("YubikoOtpKeyConfig::getSecretKey()"); string myRetVal(K_SEC_KEY_SZ, '.'); yubikey_hex_encode(&myRetVal[0], reinterpret_cast<const char*>(&itsKey), YUBIKEY_KEY_SIZE); return string(myRetVal); }
/** * Getter. * * @return Hex-encoded string representing the private id Yubikey token part. */ const string YubikoOtpKeyConfig::getPrivateId() const { BOOST_LOG_NAMED_SCOPE("YubikoOtpKeyConfig::getPrivateId"); string myRetVal(K_YBK_PRIVATE_ID_LEN, '.'); yubikey_hex_encode(&myRetVal[0], reinterpret_cast<const char*>(&itsToken.uid), YUBIKEY_UID_SIZE); return string(myRetVal); }
/** * @see const string hex2Modhex(const string& p2Modhex) const; * @param p2Hex what to decode. * @return input from Modhex decoded. * */ const string YubikoOtpKeyConfig::modhex2Hex(const std::string& p2Hex) { const unsigned long mySz = p2Hex.size(); const unsigned long mySz2 = mySz / 2; vector<uint8_t> myBytes(mySz2, uint8_t(0)); yubikey_modhex_decode(reinterpret_cast<char*>(&myBytes[0]), p2Hex.c_str(), mySz2); string myPubId(mySz + 1, '\0'); yubikey_hex_encode(&myPubId[0], reinterpret_cast<char*>(&myBytes[0]), mySz2); myPubId.resize(mySz); return myPubId; }
int YubiKeyUtil::hexModhexEncode(char *result, size_t *resultLen, const unsigned char *str, size_t strLen, bool modhex) { *resultLen = strLen * 2; if (modhex) { yubikey_modhex_encode((char *)result, (char *)str, strLen); return 1; } else { yubikey_hex_encode((char *)result, (char *)str, strLen); return 1; } return 0; }
const string YubikoOtpKeyConfig::token2json() const { string myUid(YUBIKEY_UID_SIZE * 2 + 1, ' '); yubikey_hex_encode(&myUid[0], reinterpret_cast<const char*>(&getToken().uid), YUBIKEY_UID_SIZE); ostringstream myOstr; myOstr << "yubikey_token_st:{"; myOstr << " uid :\"" << myUid.c_str() << "\""; myOstr << " ctr :\"" << int(getToken().ctr) << "\""; myOstr << " use :\"" << int(getToken().use) << "\""; myOstr << " rnd :\"" << int(getToken().rnd) << "\""; myOstr << " tstpl:\"" << int(getToken().tstpl) << "\""; myOstr << " tstph:\"" << int(getToken().tstph) << "\""; myOstr << " crc :\"" << int(getToken().crc) << "\""; myOstr << "}"; return myOstr.str(); }
static void hex_test2 (void) { char buf[1024]; char buf2[1024]; yubikey_hex_encode (buf, "test", 4); printf ("hex-encode(\"test\") = %s\n", buf); assert (strcmp (buf, "74657374") == 0); printf ("Hex-2.1 success\n"); printf ("hex-decode(\"%s\") = ", buf); yubikey_hex_decode (buf2, buf, sizeof (buf2)); printf ("%.*s\n", 4, buf2); assert (memcmp (buf2, "test", 4) == 0); printf ("Hex-2.2 success\n"); }
int _ykp_json_export_cfg(const YKP_CONFIG *cfg, char *json, size_t len) { json_object *jobj = json_object_new_object(); json_object *yprod_json = json_object_new_object(); json_object *options_json = json_object_new_object(); if(cfg) { YK_CONFIG ycfg = cfg->ykcore_config; int mode = MODE_OTP_YUBICO; struct map_st *p; json_object *target_config = NULL; json_object *prot_obj = NULL; int protection = ykp_get_acccode_type(cfg); if((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP){ if((ycfg.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) { mode = MODE_CHAL_HMAC; } else if((ycfg.cfgFlags & CFGFLAG_CHAL_YUBICO) == CFGFLAG_CHAL_YUBICO) { mode = MODE_CHAL_YUBICO; } else { mode = MODE_OATH_HOTP; } } else if((ycfg.cfgFlags & CFGFLAG_STATIC_TICKET) == CFGFLAG_STATIC_TICKET) { mode = MODE_STATIC_TICKET; } for(p = _modes_map; p->flag; p++) { if(p->flag == mode) { json_object *jmode = json_object_new_string(p->json_text); json_object_object_add(yprod_json, "mode", jmode); break; } } if(cfg->command == SLOT_CONFIG) { target_config = json_object_new_int(1); } else if(cfg->command == SLOT_CONFIG2) { target_config = json_object_new_int(2); } if(target_config) { json_object_object_add(yprod_json, "targetConfig", target_config); } if(protection == YKP_ACCCODE_NONE) { prot_obj = json_object_new_string("none"); } else if(protection == YKP_ACCCODE_RANDOM) { prot_obj = json_object_new_string("random"); } else if(protection == YKP_ACCCODE_SERIAL) { prot_obj = json_object_new_string("id"); } if(prot_obj) { json_object_object_add(yprod_json, "protection", prot_obj); } json_object_object_add(jobj, "yubiProdConfig", yprod_json); json_object_object_add(yprod_json, "options", options_json); if(ycfg.fixedSize != 0 && mode != MODE_STATIC_TICKET) { json_object *jPrefix; char prefix[5] = {0}; json_object *scope; if(mode == MODE_OTP_YUBICO && ycfg.fixed[0] == 0x00 && ycfg.fixed[1] == 0x00) { scope = json_object_new_string("yubiCloud"); } else { scope = json_object_new_string("privatePrefix"); } json_object_object_add(yprod_json, "scope", scope); yubikey_modhex_encode(prefix, (const char*)ycfg.fixed, 2); if(mode == MODE_OATH_HOTP) { int flag = ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX; json_object *fixed_modhex = json_object_new_boolean( flag == CFGFLAG_OATH_FIXED_MODHEX ? 1 : 0); json_object_object_add(options_json, "fixedModhex", fixed_modhex); if(flag == 0) { yubikey_hex_encode(prefix, (const char*)ycfg.fixed, 2); } else if(flag == CFGFLAG_OATH_FIXED_MODHEX1) { yubikey_hex_encode(prefix + 2, (const char*)ycfg.fixed + 1, 1); } } jPrefix = json_object_new_string(prefix); json_object_object_add(yprod_json, "prefix", jPrefix); } else if(mode != MODE_STATIC_TICKET) { json_object *scope = json_object_new_string("noPublicId"); json_object_object_add(yprod_json, "scope", scope); } if(mode == MODE_OATH_HOTP) { json_object *oathDigits; json_object *randomSeed; if((ycfg.cfgFlags & CFGFLAG_OATH_HOTP8) == CFGFLAG_OATH_HOTP8) { oathDigits = json_object_new_int(8); } else { oathDigits = json_object_new_int(6); } json_object_object_add(options_json, "oathDigits", oathDigits); if((ycfg.uid[5] == 0x01 || ycfg.uid[5] == 0x00) && ycfg.uid[4] == 0x00) { json_object *fixedSeedvalue = json_object_new_int(ycfg.uid[5] << 4); json_object_object_add(options_json, "fixedSeedvalue", fixedSeedvalue); randomSeed = json_object_new_boolean(0); } else { randomSeed = json_object_new_boolean(1); } json_object_object_add(options_json, "randomSeed", randomSeed); } for(p = _ticket_flags_map; p->flag; p++) { if(!p->json_text) { continue; } if(p->mode && (mode & p->mode) == mode) { int set = (ycfg.tktFlags & p->flag) == p->flag; json_object *jsetting = json_object_new_boolean(set); json_object_object_add(options_json, p->json_text, jsetting); } } for(p = _config_flags_map; p->flag; p++) { if(!p->json_text) { continue; } if(p->mode && (mode & p->mode) == mode) { int set = (ycfg.cfgFlags & p->flag) == p->flag; json_object *jsetting = json_object_new_boolean(set); json_object_object_add(options_json, p->json_text, jsetting); } } for(p = _extended_flags_map; p->flag; p++) { if(!p->json_text) { continue; } if(p->mode && (mode & p->mode) == mode) { int set = (ycfg.extFlags & p->flag) == p->flag; json_object *jsetting = json_object_new_boolean(set); json_object_object_add(options_json, p->json_text, jsetting); } } } #ifdef HAVE_JSON_OBJECT_TO_JSON_STRING_EXT strncpy(json, json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY), len); #else strncpy(json, json_object_to_json_string(jobj), len); #endif /* free the root object, will free all children */ json_object_put(jobj); return strlen(json); }
static int _ykp_legacy_export_config(const YKP_CONFIG *cfg, char *buf, size_t len) { if (cfg) { char buffer[256]; struct map_st *p; unsigned char t_flags; bool key_bits_in_uid = false; YK_CONFIG ycfg = cfg->ykcore_config; int mode = MODE_OTP_YUBICO; int pos = 0; if((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP){ if((ycfg.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) { mode = MODE_CHAL_HMAC; } else if((ycfg.cfgFlags & CFGFLAG_CHAL_YUBICO) == CFGFLAG_CHAL_YUBICO) { mode = MODE_CHAL_YUBICO; } else { mode = MODE_OATH_HOTP; } } else if((ycfg.cfgFlags & CFGFLAG_STATIC_TICKET) == CFGFLAG_STATIC_TICKET) { mode = MODE_STATIC_TICKET; } /* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes * additional key data in the uid field */ key_bits_in_uid = (_get_supported_key_length(cfg) == 20); /* fixed: or OATH id: */ if ((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP && ycfg.fixedSize) { /* First byte (vendor id) */ if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX1) == CFGFLAG_OATH_FIXED_MODHEX1 || (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX2) == CFGFLAG_OATH_FIXED_MODHEX2 || (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) { yubikey_modhex_encode(buffer, (const char *)ycfg.fixed, 1); } else { yubikey_hex_encode(buffer, (const char *)ycfg.fixed, 1); } /* Second byte (token type) */ if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX2) == CFGFLAG_OATH_FIXED_MODHEX2 || (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) { yubikey_modhex_encode(buffer + 2, (const char *)ycfg.fixed + 1, 1); } else { yubikey_hex_encode(buffer + 2, (const char *)ycfg.fixed + 1, 1); } /* bytes 3-12 - MUI */ if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) { yubikey_modhex_encode(buffer + 4, (const char *)ycfg.fixed + 2, 8); } else { yubikey_hex_encode(buffer + 4, (const char *)ycfg.fixed + 2, 8); } buffer[12] = 0; pos += snprintf(buf, len - (size_t)pos, "%s%s%s\n", str_oath_id, str_key_value_separator, buffer); } else { yubikey_modhex_encode(buffer, (const char *)ycfg.fixed, ycfg.fixedSize); pos += snprintf(buf, len - (size_t)pos, "%s%s%s%s\n", str_fixed, str_key_value_separator, str_modhex_prefix, buffer); } /* uid: */ if (key_bits_in_uid) { strncpy(buffer, "n/a", 3); } else { yubikey_hex_encode(buffer, (const char *)ycfg.uid, UID_SIZE); } pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_uid, str_key_value_separator, buffer); /* key: */ yubikey_hex_encode(buffer, (const char *)ycfg.key, KEY_SIZE); if (key_bits_in_uid) { yubikey_hex_encode(buffer + KEY_SIZE * 2, (const char *)ycfg.uid, 4); } pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%s\n", str_key, str_key_value_separator, str_hex_prefix, buffer); /* acc_code: */ yubikey_hex_encode(buffer, (const char*)ycfg.accCode, ACC_CODE_SIZE); pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%s\n", str_acc_code, str_key_value_separator, str_hex_prefix, buffer); /* OATH IMF: */ if ((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP && capability_has_oath_imf(cfg)) { pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%lx\n", str_oath_imf, str_key_value_separator, str_hex_prefix, ykp_get_oath_imf(cfg)); } /* ticket_flags: */ buffer[0] = '\0'; for (p = _ticket_flags_map; p->flag; p++) { if ((ycfg.tktFlags & p->flag) == p->flag && p->capability(cfg) && (mode & p->mode) == mode) { if (*buffer) { strcat(buffer, str_flags_separator); strcat(buffer, p->flag_text); } else { strcpy(buffer, p->flag_text); } } } pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_ticket_flags, str_key_value_separator, buffer); /* config_flags: */ buffer[0] = '\0'; t_flags = ycfg.cfgFlags; for (p = _config_flags_map; p->flag; p++) { if ((t_flags & p->flag) == p->flag && p->capability(cfg) && (mode & p->mode) == mode) { if (*buffer) { strcat(buffer, str_flags_separator); strcat(buffer, p->flag_text); } else { strcpy(buffer, p->flag_text); } /* make sure we don't show more than one cfgFlag per value - some cfgflags share value in different contexts */ t_flags -= p->flag; } } pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_config_flags, str_key_value_separator, buffer); /* extended_flags: */ buffer[0] = '\0'; for (p = _extended_flags_map; p->flag; p++) { if ((ycfg.extFlags & p->flag) == p->flag && p->capability(cfg) && (mode & p->mode) == mode) { if (*buffer) { strcat(buffer, str_flags_separator); strcat(buffer, p->flag_text); } else { strcpy(buffer, p->flag_text); } } } pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_extended_flags, str_key_value_separator, buffer); return pos; } return 0; }
int main (void) { char buf[1024]; size_t i; int rc; yubikey_token_st tok; /* Test Modhex */ yubikey_modhex_encode (buf, "test", 4); printf ("modhex-encode(\"test\") = %s\n", buf); if (strcmp (buf, "ifhgieif") != 0) { printf ("ModHex failure\n"); return 1; } printf ("Modhex-1 success\n"); printf ("modhex-decode(\"%s\") = ", buf); yubikey_modhex_decode (buf, buf, strlen ((char *) buf)); printf ("%.*s\n", 4, buf); if (memcmp (buf, "test", 4) != 0) { printf ("ModHex failure\n"); return 1; } printf ("Modhex-2 success\n"); strcpy (buf, "cbdefghijklnrtuv"); rc = yubikey_modhex_p (buf); printf ("hex-p(\"%s\") = %d\n", buf, rc); if (!rc) { printf ("Hex_p failure\n"); return 1; } printf ("Hex-3 success\n"); strcpy (buf, "0123Xabc"); rc = yubikey_hex_p (buf); printf ("hex-p(\"%s\") = %d\n", buf, rc); if (rc) { printf ("Hex_p failure\n"); return 1; } printf ("Hex-3 success\n"); /* Test Hex */ yubikey_hex_encode (buf, "test", 4); printf ("hex-encode(\"test\") = %s\n", buf); if (strcmp (buf, "74657374") != 0) { printf ("Hex failure\n"); return 1; } printf ("Hex-1 success\n"); printf ("hex-decode(\"%s\") = ", buf); yubikey_hex_decode (buf, buf, strlen ((char *) buf)); printf ("%.*s\n", 4, buf); if (memcmp (buf, "test", 4) != 0) { printf ("Hex failure\n"); return 1; } printf ("Hex-2 success\n"); strcpy (buf, "0123456789abcdef"); rc = yubikey_hex_p (buf); printf ("hex-p(\"%s\") = %d\n", buf, rc); if (!rc) { printf ("Hex_p failure\n"); return 1; } printf ("Hex-3 success\n"); strcpy (buf, "0123Xabc"); rc = yubikey_hex_p (buf); printf ("hex-p(\"%s\") = %d\n", buf, rc); if (rc) { printf ("Hex_p failure\n"); return 1; } printf ("Hex-3 success\n"); /* Test AES */ { uint8_t buf[1024]; char out[1024]; uint8_t key[16 + 1]; memcpy (buf, "0123456789abcdef\0", 17); memcpy (key, "abcdef0123456789\0", 17); printf ("aes-decrypt (data=%s, key=%s)\n => ", (char *) buf, (char *) key); yubikey_aes_decrypt (buf, key); for (i = 0; i < 16; i++) printf ("%02x", buf[i] & 0xFF); printf ("\n"); if (memcmp (buf, "\x83\x8a\x46\x7f\x34\x63\x95\x51" "\x75\x5b\xd3\x2a\x4a\x2f\x15\xe1", 16) != 0) { printf ("AES failure\n"); return 1; } printf ("AES-1 success\n"); yubikey_aes_encrypt (buf, key); if (memcmp (buf, "0123456789abcdef", 16) != 0) { printf ("AES encryption failure\n"); return 1; } printf ("AES-2 success\n"); /* Test OTP */ memcpy ((void *) &tok, "\x16\xe1\xe5\xd9\xd3\x99\x10\x04\x45\x20\x07\xe3\x02\x00\x00", 16); memcpy (key, "abcdef0123456789", 16); yubikey_generate ((void *) &tok, key, out); yubikey_parse ((uint8_t *) out, key, &tok); if (memcmp (&tok, "\x16\xe1\xe5\xd9\xd3\x99\x10\x04\x45\x20\x07\xe3\x02\x00\x00", 16) != 0) { printf ("OTP generation - parse failure\n"); return 1; } printf ("OTP-1 success\n"); } return 0; }
int main(int argc, char **argv) { unsigned int version = 0, help = 0; char challenge_old[CHALLENGELEN + 1], challenge_new[CHALLENGELEN + 1], response_old[RESPONSELEN], response_new[RESPONSELEN], passphrase_old[PASSPHRASELEN + 1], passphrase_new[PASSPHRASELEN + 1]; const char * tmp; char challengefilename[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 1], challengefiletmpname[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 7 /* -XXXXXX */ + 1]; int challengefile = 0, challengefiletmp = 0; struct timeval tv; int i; size_t len; int8_t rc = EXIT_FAILURE; /* cryptsetup */ const char * device_name; int8_t luks_slot = -1; struct crypt_device *cryptdevice; crypt_status_info cryptstatus; crypt_keyslot_info cryptkeyslot; char * passphrase = NULL; /* keyutils */ key_serial_t key; void * payload = NULL; char * second_factor = NULL, * new_2nd_factor = NULL, * new_2nd_factor_verify = NULL; /* yubikey */ YK_KEY * yk; uint8_t yk_slot = SLOT_CHAL_HMAC2; unsigned int serial = 0; /* iniparser */ dictionary * ini; char section_ykslot[10 /* unsigned int in char */ + 1 + sizeof(CONFYKSLOT) + 1]; char section_luksslot[10 + 1 + sizeof(CONFLUKSSLOT) + 1]; /* get command line options */ while ((i = getopt_long(argc, argv, optstring, options_long, NULL)) != -1) switch (i) { case 'h': help++; break; case 'n': case 'N': if (new_2nd_factor != NULL) { fprintf(stderr, "We already have a new second factor. Did you specify it twice?\n"); goto out10; } if (optarg == NULL) { /* N */ if ((new_2nd_factor = ask_secret("new second factor")) == NULL) goto out10; if ((new_2nd_factor_verify = ask_secret("new second factor for verification")) == NULL) goto out10; if (strcmp(new_2nd_factor, new_2nd_factor_verify) != 0) { fprintf(stderr, "Verification failed, given strings do not match.\n"); goto out10; } } else { /* n */ new_2nd_factor = strdup(optarg); memset(optarg, '*', strlen(optarg)); } break; case 's': case 'S': if (second_factor != NULL) { fprintf(stderr, "We already have a second factor. Did you specify it twice?\n"); goto out10; } if (optarg == NULL) { /* S */ second_factor = ask_secret("current second factor"); } else { /* s */ second_factor = strdup(optarg); memset(optarg, '*', strlen(optarg)); } break; case 'V': version++; break; } if (version > 0) printf("%s: %s v%s (compiled: " __DATE__ ", " __TIME__ ")\n", argv[0], PROGNAME, VERSION); if (help > 0) fprintf(stderr, "usage: %s [-h|--help] [-n|--new-2nd-factor <new-2nd-factor>] [-N|--ask-new-2nd-factor]\n" " [-s|--2nd-factor <2nd-factor>] [-S|--ask-2nd-factor] [-V|--version]\n", argv[0]); if (version > 0 || help > 0) return EXIT_SUCCESS; /* initialize random seed */ gettimeofday(&tv, NULL); srand(tv.tv_usec * tv.tv_sec); /* initialize static buffers */ memset(challenge_old, 0, CHALLENGELEN + 1); memset(challenge_new, 0, CHALLENGELEN + 1); memset(response_old, 0, RESPONSELEN); memset(response_new, 0, RESPONSELEN); memset(passphrase_old, 0, PASSPHRASELEN + 1); memset(passphrase_new, 0, PASSPHRASELEN + 1); if ((ini = iniparser_load(CONFIGFILE)) == NULL) { fprintf(stderr, "Could not parse configuration file.\n"); goto out10; } if ((device_name = iniparser_getstring(ini, "general:" CONFDEVNAME, NULL)) == NULL) { /* read from crypttab? */ /* get device from currently open devices? */ fprintf(stderr, "Could not read LUKS device from configuration file.\n"); goto out20; } /* init and open first Yubikey */ if (yk_init() == 0) { perror("yk_init() failed"); goto out20; } if ((yk = yk_open_first_key()) == NULL) { fprintf(stderr, "No Yubikey available.\n"); goto out30; } /* read the serial number from key */ if (yk_get_serial(yk, 0, 0, &serial) == 0) { perror("yk_get_serial() failed"); goto out40; } /* get the yk slot */ sprintf(section_ykslot, "%d:" CONFYKSLOT, serial); yk_slot = iniparser_getint(ini, "general:" CONFYKSLOT, yk_slot); yk_slot = iniparser_getint(ini, section_ykslot, yk_slot); switch (yk_slot) { case 1: case SLOT_CHAL_HMAC1: yk_slot = SLOT_CHAL_HMAC1; break; case 2: case SLOT_CHAL_HMAC2: default: yk_slot = SLOT_CHAL_HMAC2; break; } /* get the luks slot */ sprintf(section_luksslot, "%d:" CONFLUKSSLOT, serial); luks_slot = iniparser_getint(ini, section_luksslot, luks_slot); if (luks_slot < 0) { fprintf(stderr, "Please set LUKS key slot for Yubikey with serial %d!\n" "Add something like this to " CONFIGFILE ":\n\n" "[%d]\nluks slot = 1\n", serial, serial); goto out40; } if (second_factor == NULL) { /* get second factor from key store */ if ((key = request_key("user", "ykfde-2f", NULL, 0)) < 0) fprintf(stderr, "Failed requesting key. That's ok if you do not use\n" "second factor. Give it manually if required.\n"); if (key > -1) { /* if we have a key id we have a key - so this should succeed */ if (keyctl_read_alloc(key, &payload) < 0) { perror("Failed reading payload from key"); goto out40; } second_factor = payload; } else second_factor = strdup(""); } /* warn when second factor is not enabled in config */ if ((*second_factor != 0 || new_2nd_factor != NULL) && iniparser_getboolean(ini, "general:" CONF2NDFACTOR, 0) == 0) fprintf(stderr, "Warning: Processing second factor, but not enabled in config!\n"); /* get random number and limit to printable ASCII character (32 to 126) */ for(i = 0; i < CHALLENGELEN; i++) challenge_new[i] = (rand() % (126 - 32)) + 32; /* these are the filenames for challenge * we need this for reading and writing */ sprintf(challengefilename, CHALLENGEDIR "/challenge-%d", serial); sprintf(challengefiletmpname, CHALLENGEDIR "/challenge-%d-XXXXXX", serial); /* write new challenge to file */ if ((challengefiletmp = mkstemp(challengefiletmpname)) < 0) { fprintf(stderr, "Could not open file %s for writing.\n", challengefiletmpname); goto out40; } if (write(challengefiletmp, challenge_new, CHALLENGELEN) < 0) { fprintf(stderr, "Failed to write challenge to file.\n"); goto out50; } challengefiletmp = close(challengefiletmp); /* now that the new challenge has been written to file... * add second factor to new challenge */ tmp = new_2nd_factor ? new_2nd_factor : second_factor; len = strlen(tmp); memcpy(challenge_new, tmp, len < MAX2FLEN ? len : MAX2FLEN); /* do challenge/response and encode to hex */ if (yk_challenge_response(yk, yk_slot, true, CHALLENGELEN, (unsigned char *) challenge_new, RESPONSELEN, (unsigned char *) response_new) == 0) { perror("yk_challenge_response() failed"); goto out50; } yubikey_hex_encode((char *) passphrase_new, (char *) response_new, SHA1_DIGEST_SIZE); /* get status of crypt device * We expect this to be active (or busy). It is the actual root device, no? */ cryptstatus = crypt_status(cryptdevice, device_name); if (cryptstatus != CRYPT_ACTIVE && cryptstatus != CRYPT_BUSY) { fprintf(stderr, "Device %s is invalid or inactive.\n", device_name); goto out50; } /* initialize crypt device */ if (crypt_init_by_name(&cryptdevice, device_name) < 0) { fprintf(stderr, "Device %s failed to initialize.\n", device_name); goto out60; } cryptkeyslot = crypt_keyslot_status(cryptdevice, luks_slot); if (cryptkeyslot == CRYPT_SLOT_INVALID) { fprintf(stderr, "Key slot %d is invalid.\n", luks_slot); goto out60; } else if (cryptkeyslot == CRYPT_SLOT_ACTIVE || cryptkeyslot == CRYPT_SLOT_ACTIVE_LAST) { /* read challenge from file */ if ((challengefile = open(challengefilename, O_RDONLY)) < 0) { perror("Failed opening challenge file for reading"); goto out60; } if (read(challengefile, challenge_old, CHALLENGELEN) < 0) { perror("Failed reading challenge from file"); goto out60; } challengefile = close(challengefile); /* finished reading challenge */ /* copy the second factor */ len = strlen(second_factor); memcpy(challenge_old, second_factor, len < MAX2FLEN ? len : MAX2FLEN); /* do challenge/response and encode to hex */ if (yk_challenge_response(yk, yk_slot, true, CHALLENGELEN, (unsigned char *) challenge_old, RESPONSELEN, (unsigned char *) response_old) == 0) { perror("yk_challenge_response() failed"); goto out60; } yubikey_hex_encode((char *) passphrase_old, (char *) response_old, SHA1_DIGEST_SIZE); if (crypt_keyslot_change_by_passphrase(cryptdevice, luks_slot, luks_slot, passphrase_old, PASSPHRASELEN, passphrase_new, PASSPHRASELEN) < 0) { fprintf(stderr, "Could not update passphrase for key slot %d.\n", luks_slot); goto out60; } if (unlink(challengefilename) < 0) { fprintf(stderr, "Failed to delete old challenge file.\n"); goto out60; } } else { /* ck == CRYPT_SLOT_INACTIVE */ if ((passphrase = ask_secret("existing LUKS passphrase")) == NULL) goto out60; if (crypt_keyslot_add_by_passphrase(cryptdevice, luks_slot, passphrase, strlen(passphrase), passphrase_new, PASSPHRASELEN) < 0) { fprintf(stderr, "Could not add passphrase for key slot %d.\n", luks_slot); goto out60; } } if (rename(challengefiletmpname, challengefilename) < 0) { fprintf(stderr, "Failed to rename new challenge file.\n"); goto out60; } rc = EXIT_SUCCESS; out60: /* free crypt context */ crypt_free(cryptdevice); out50: /* close the challenge file */ if (challengefile) close(challengefile); if (challengefiletmp) close(challengefiletmp); if (access(challengefiletmpname, F_OK) == 0) unlink(challengefiletmpname); out40: /* close Yubikey */ if (yk_close_key(yk) == 0) perror("yk_close_key() failed"); out30: /* release Yubikey */ if (yk_release() == 0) perror("yk_release() failed"); out20: /* free iniparser dictionary */ iniparser_freedict(ini); out10: /* wipe response (cleartext password!) from memory */ /* This is statically allocated and always save to wipe! */ memset(challenge_old, 0, CHALLENGELEN + 1); memset(challenge_new, 0, CHALLENGELEN + 1); memset(response_old, 0, RESPONSELEN); memset(response_new, 0, RESPONSELEN); memset(passphrase_old, 0, PASSPHRASELEN + 1); memset(passphrase_new, 0, PASSPHRASELEN + 1); free(passphrase); free(new_2nd_factor_verify); free(new_2nd_factor); free(second_factor); return rc; }
static int yubikey_login(const char *username, const char *password) { char fn[MAXPATHLEN]; char hexkey[33], key[YUBIKEY_KEY_SIZE]; char hexuid[13], uid[YUBIKEY_UID_SIZE]; FILE *f; yubikey_token_st tok; u_int32_t last_ctr = 0, ctr; int r, i = 0, mapok = 0, crcok = 0; snprintf(fn, sizeof(fn), "%s/%s.uid", path, username); if ((f = fopen(fn, "r")) == NULL) { syslog(LOG_ERR, "user %s: fopen: %s: %m", username, fn); return (AUTH_FAILED); } if (fscanf(f, "%12s", hexuid) != 1) { syslog(LOG_ERR, "user %s: fscanf: %s: %m", username, fn); fclose(f); return (AUTH_FAILED); } fclose(f); snprintf(fn, sizeof(fn), "%s/%s.key", path, username); if ((f = fopen(fn, "r")) == NULL) { syslog(LOG_ERR, "user %s: fopen: %s: %m", username, fn); return (AUTH_FAILED); } if (fscanf(f, "%32s", hexkey) != 1) { syslog(LOG_ERR, "user %s: fscanf: %s: %m", username, fn); fclose(f); return (AUTH_FAILED); } fclose(f); if (strlen(hexkey) != 32) { syslog(LOG_ERR, "user %s: key len != 32", username); return (AUTH_FAILED); } snprintf(fn, sizeof(fn), "%s/%s.ctr", path, username); if ((f = fopen(fn, "r")) != NULL) { if (fscanf(f, "%u", &last_ctr) != 1) last_ctr = 0; fclose(f); } yubikey_hex_decode(uid, hexuid, YUBIKEY_UID_SIZE); yubikey_hex_decode(key, hexkey, YUBIKEY_KEY_SIZE); /* * Cycle through the key mapping table. * XXX brute force, unoptimized; a lookup table for valid mappings may * be appropriate. */ while (1) { r = yubikey_parse((uint8_t *)password, (uint8_t *)key, &tok, i++); switch (r) { case EMSGSIZE: syslog(LOG_INFO, "user %s failed: password too short.", username); return (AUTH_FAILED); case EINVAL: /* keyboard mapping invalid */ continue; case 0: /* found a valid keyboard mapping */ mapok++; if (!yubikey_crc_ok_p((uint8_t *)&tok)) continue; /* try another one */ crcok++; syslog(LOG_DEBUG, "user %s: crc %04x ok", username, tok.crc); if (memcmp(tok.uid, uid, YUBIKEY_UID_SIZE)) { char h[13]; yubikey_hex_encode(h, (const char *)tok.uid, YUBIKEY_UID_SIZE); syslog(LOG_DEBUG, "user %s: uid %s != %s", username, h, hexuid); continue; /* try another one */ } break; /* uid matches */ case -1: syslog(LOG_INFO, "user %s: could not decode password " "with any keymap (%d crc ok)", username, crcok); return (AUTH_FAILED); default: syslog(LOG_DEBUG, "user %s failed: %s", username, strerror(r)); return (AUTH_FAILED); } break; /* only reached through the bottom of case 0 */ } syslog(LOG_INFO, "user %s uid %s: %d matching keymaps (%d checked), " "%d crc ok", username, hexuid, mapok, i, crcok); ctr = ((u_int32_t)yubikey_counter(tok.ctr) << 8) | tok.use; if (ctr <= last_ctr) { syslog(LOG_INFO, "user %s: counter %u.%u <= %u.%u " "(REPLAY ATTACK!)", username, ctr / 256, ctr % 256, last_ctr / 256, last_ctr % 256); return (AUTH_FAILED); } syslog(LOG_INFO, "user %s: counter %u.%u > %u.%u", username, ctr / 256, ctr % 256, last_ctr / 256, last_ctr % 256); umask(S_IRWXO); if ((f = fopen(fn, "w")) == NULL) { syslog(LOG_ERR, "user %s: fopen: %s: %m", username, fn); return (AUTH_FAILED); } fprintf(f, "%u", ctr); fclose(f); return (AUTH_OK); }
int ykp_write_config(const YKP_CONFIG *cfg, int (*writer)(const char *buf, size_t count, void *userdata), void *userdata) { if (cfg) { char buffer[256]; struct map_st *p; unsigned char t_flags; bool key_bits_in_uid = false; /* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes * additional key data in the uid field */ key_bits_in_uid = (_get_supported_key_length(cfg) == 20); /* fixed: */ writer(str_fixed, strlen(str_fixed), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); /* XXX print OATH-HOTP fixed differently based on oath-fixed-modhex etc. */ writer(str_modhex_prefix, strlen(str_key_value_separator), userdata); yubikey_modhex_encode(buffer, (char *)cfg->ykcore_config.fixed, cfg->ykcore_config.fixedSize); writer(buffer, strlen(buffer), userdata); writer("\n", 1, userdata); /* uid: */ writer(str_uid, strlen(str_uid), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); if (key_bits_in_uid) { writer("n/a", 3, userdata); } else { writer(str_hex_prefix, strlen(str_key_value_separator), userdata); yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.uid, UID_SIZE); writer(buffer, strlen(buffer), userdata); } writer("\n", 1, userdata); /* key: */ writer(str_key, strlen(str_key), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); writer(str_hex_prefix, strlen(str_key_value_separator), userdata); yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.key, KEY_SIZE); if (key_bits_in_uid) { yubikey_hex_encode(buffer + KEY_SIZE * 2, (char *)cfg->ykcore_config.uid, 4); } writer(buffer, strlen(buffer), userdata); writer("\n", 1, userdata); /* acc_code: */ writer(str_acc_code, strlen(str_acc_code), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); writer(str_hex_prefix, strlen(str_key_value_separator), userdata); yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.accCode, ACC_CODE_SIZE); writer(buffer, strlen(buffer), userdata); writer("\n", 1, userdata); /* ticket_flags: */ buffer[0] = '\0'; for (p = ticket_flags_map; p->flag; p++) { if ((cfg->ykcore_config.tktFlags & p->flag) == p->flag && p->vcheck(cfg)) { if (*buffer) { strcat(buffer, str_flags_separator); strcat(buffer, p->flag_text); } else { strcpy(buffer, p->flag_text); } } } writer(str_ticket_flags, strlen(str_ticket_flags), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); writer(buffer, strlen(buffer), userdata); writer("\n", 1, userdata); /* config_flags: */ buffer[0] = '\0'; t_flags = cfg->ykcore_config.cfgFlags; for (p = config_flags_map; p->flag; p++) { if ((t_flags & p->flag) == p->flag && p->vcheck(cfg) && (cfg->ykcore_config.tktFlags & p->tkt_context) == p->tkt_context) { if (*buffer) { strcat(buffer, str_flags_separator); strcat(buffer, p->flag_text); } else { strcpy(buffer, p->flag_text); } /* make sure we don't show more than one cfgFlag per value - some cfgflags share value in different contexts */ t_flags -= p->flag; } } writer(str_config_flags, strlen(str_config_flags), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); writer(buffer, strlen(buffer), userdata); writer("\n", 1, userdata); /* extended_flags: */ buffer[0] = '\0'; for (p = extended_flags_map; p->flag; p++) { if ((cfg->ykcore_config.extFlags & p->flag) == p->flag && p->vcheck(cfg)) { if (*buffer) { strcat(buffer, str_flags_separator); strcat(buffer, p->flag_text); } else { strcpy(buffer, p->flag_text); } } } writer(str_extended_flags, strlen(str_extended_flags), userdata); writer(str_key_value_separator, strlen(str_key_value_separator), userdata); writer(buffer, strlen(buffer), userdata); writer("\n", 1, userdata); return 1; } return 0; }
static int do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) { char *userfile = NULL, *tmpfile = NULL; FILE *f = NULL; char buf[CR_RESPONSE_SIZE + 16], response_hex[CR_RESPONSE_SIZE * 2 + 1]; int ret, fd; unsigned int flags = 0; unsigned int response_len = 0; YK_KEY *yk = NULL; CR_STATE state; char *errstr = NULL; struct passwd *p; struct stat st; ret = PAM_AUTH_ERR; flags |= YK_FLAG_MAYBLOCK; if (! init_yubikey(&yk)) { D(("Failed initializing YubiKey")); goto out; } if (! check_firmware_version(yk, false, true)) { D(("YubiKey does not support Challenge-Response (version 2.2 required)")); goto out; } if (! get_user_challenge_file (yk, cfg->chalresp_path, username, &userfile)) { D(("Failed getting user challenge file for user %s", username)); goto out; } DBG(("Loading challenge from file %s", userfile)); p = getpwnam (username); if (p == NULL) { DBG (("getpwnam: %s", strerror(errno))); goto out; } /* Drop privileges before opening user file. */ if (drop_privileges(p, pamh) < 0) { D (("could not drop privileges")); goto out; } fd = open(userfile, O_RDONLY, 0); if (fd < 0) { DBG (("Cannot open file: %s (%s)", userfile, strerror(errno))); goto out; } if (fstat(fd, &st) < 0) { DBG (("Cannot stat file: %s (%s)", userfile, strerror(errno))); close(fd); goto out; } if (!S_ISREG(st.st_mode)) { DBG (("%s is not a regular file", userfile)); close(fd); goto out; } f = fdopen(fd, "r"); if (f == NULL) { DBG (("fdopen: %s", strerror(errno))); close(fd); goto out; } if (! load_chalresp_state(f, &state, cfg->debug)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } f = NULL; if (restore_privileges(pamh) < 0) { DBG (("could not restore privileges")); goto out; } if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Challenge-response FAILED")); goto out; } /* * Check YubiKey response against the expected response */ yubikey_hex_encode(response_hex, buf, response_len); if (memcmp(buf, state.response, response_len) == 0) { ret = PAM_SUCCESS; } else { D(("Unexpected C/R response : %s", response_hex)); goto out; } DBG(("Got the expected response, generating new challenge (%i bytes).", CR_CHALLENGE_SIZE)); errstr = "Error generating new challenge, please check syslog or contact your system administrator"; if (generate_random(state.challenge, sizeof(state.challenge))) { D(("Failed generating new challenge!")); goto out; } errstr = "Error communicating with Yubikey, please check syslog or contact your system administrator"; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Second challenge-response FAILED")); goto out; } /* There is a bug that makes the YubiKey 2.2 send the same response for all challenges unless HMAC_LT64 is set, check for that here */ if (memcmp(buf, state.response, response_len) == 0) { errstr = "Same response for second challenge, YubiKey should be reconfigured with the option HMAC_LT64"; goto out; } /* the yk_* functions leave 'junk' in errno */ errno = 0; /* * Write the challenge and response we will expect the next time to the state file. */ if (response_len > sizeof(state.response)) { D(("Got too long response ??? (%u/%lu)", response_len, (unsigned long) sizeof(state.response))); goto out; } memcpy (state.response, buf, response_len); state.response_len = response_len; /* Drop privileges before creating new challenge file. */ if (drop_privileges(p, pamh) < 0) { D (("could not drop privileges")); goto out; } /* Write out the new file */ tmpfile = malloc(strlen(userfile) + 1 + 4); if (! tmpfile) goto out; strcpy(tmpfile, userfile); strcat(tmpfile, ".tmp"); fd = open(tmpfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { DBG (("Cannot open file: %s (%s)", tmpfile, strerror(errno))); goto out; } f = fdopen(fd, "w"); if (! f) { close(fd); goto out; } errstr = "Error updating Yubikey challenge, please check syslog or contact your system administrator"; if (! write_chalresp_state (f, &state)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } f = NULL; if (rename(tmpfile, userfile) < 0) { goto out; } if (restore_privileges(pamh) < 0) { DBG (("could not restore privileges")); goto out; } DBG(("Challenge-response success!")); errstr = NULL; errno = 0; out: if (yk_errno) { if (yk_errno == YK_EUSBERR) { syslog(LOG_ERR, "USB error: %s", yk_usb_strerror()); D(("USB error: %s", yk_usb_strerror())); } else { syslog(LOG_ERR, "Yubikey core error: %s", yk_strerror(yk_errno)); D(("Yubikey core error: %s", yk_strerror(yk_errno))); } } if (errstr) display_error(pamh, errstr); if (errno) { syslog(LOG_ERR, "Challenge response failed: %s", strerror(errno)); D(("Challenge response failed: %s", strerror(errno))); } if (yk) yk_close_key(yk); yk_release(); if (f) fclose(f); free(userfile); free(tmpfile); return ret; }
static int do_challenge_response(pam_handle_t *pamh, struct cfg *cfg, const char *username) { char *userfile = NULL, *tmpfile = NULL; FILE *f = NULL; unsigned char buf[CR_RESPONSE_SIZE + 16], response_hex[CR_RESPONSE_SIZE * 2 + 1]; int ret; unsigned int flags = 0; unsigned int response_len = 0; unsigned int expect_bytes = 0; YK_KEY *yk = NULL; CR_STATE state; int len; char *errstr = NULL; ret = PAM_AUTH_ERR; flags |= YK_FLAG_MAYBLOCK; if (! init_yubikey(&yk)) { D(("Failed initializing YubiKey")); goto out; } if (! check_firmware_version(yk, false, true)) { D(("YubiKey does not support Challenge-Response (version 2.2 required)")); goto out; } if (! get_user_challenge_file (yk, cfg->chalresp_path, username, &userfile)) { D(("Failed getting user challenge file for user %s", username)); goto out; } DBG(("Loading challenge from file %s", userfile)); /* XXX should drop root privileges before opening file in user's home directory */ f = fopen(userfile, "r"); if (! load_chalresp_state(f, &state)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } if (! challenge_response(yk, state.slot, state.challenge, state.challenge_len, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Challenge-response FAILED")); goto out; } /* * Check YubiKey response against the expected response */ yubikey_hex_encode(response_hex, (char *)buf, response_len); if (memcmp(buf, state.response, response_len) == 0) { ret = PAM_SUCCESS; } else { D(("Unexpected C/R response : %s", response_hex)); goto out; } DBG(("Got the expected response, generating new challenge (%i bytes).", CR_CHALLENGE_SIZE)); errstr = "Error generating new challenge, please check syslog or contact your system administrator"; if (generate_random(state.challenge, sizeof(state.challenge))) { D(("Failed generating new challenge!")); goto out; } errstr = "Error communicating with Yubikey, please check syslog or contact your system administrator"; if (! challenge_response(yk, state.slot, state.challenge, CR_CHALLENGE_SIZE, true, flags, false, buf, sizeof(buf), &response_len)) { D(("Second challenge-response FAILED")); goto out; } /* the yk_* functions leave 'junk' in errno */ errno = 0; /* * Write the challenge and response we will expect the next time to the state file. */ if (response_len > sizeof(state.response)) { D(("Got too long response ??? (%i/%i)", response_len, sizeof(state.response))); goto out; } memcpy (state.response, buf, response_len); state.response_len = response_len; /* Write out the new file */ tmpfile = malloc(strlen(userfile) + 1 + 4); if (! tmpfile) goto out; strcpy(tmpfile, userfile); strcat(tmpfile, ".tmp"); f = fopen(tmpfile, "w"); if (! f) goto out; errstr = "Error updating Yubikey challenge, please check syslog or contact your system administrator"; if (! write_chalresp_state (f, &state)) goto out; if (fclose(f) < 0) { f = NULL; goto out; } f = NULL; if (rename(tmpfile, userfile) < 0) { goto out; } DBG(("Challenge-response success!")); errstr = NULL; out: if (yk_errno) { if (yk_errno == YK_EUSBERR) { syslog(LOG_ERR, "USB error: %s", yk_usb_strerror()); D(("USB error: %s", yk_usb_strerror())); } else { syslog(LOG_ERR, "Yubikey core error: %s", yk_strerror(yk_errno)); D(("Yubikey core error: %s", yk_strerror(yk_errno))); } } if (errstr) display_error(pamh, errstr); if (errno) { syslog(LOG_ERR, "Challenge response failed: %s", strerror(errno)); D(("Challenge response failed: %s", strerror(errno))); } if (yk) yk_close_key(yk); yk_release(); if (f) fclose(f); free(userfile); free(tmpfile); return ret; }
int main(int argc, char **argv) { char challenge_old[CHALLENGELEN + 1], challenge_new[CHALLENGELEN + 1], repose_old[RESPONSELEN], repose_new[RESPONSELEN], passphrase_old[PASSPHRASELEN + 1], passphrase_new[PASSPHRASELEN + 1]; char challengefilename[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 1], challengefiletmpname[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 7 /* -XXXXXX */ + 1]; int challengefile = 0, challengefiletmp = 0; struct timeval tv; int i; int8_t rc = EXIT_FAILURE; /* cryptsetup */ char * device_name; int8_t luks_slot = -1; struct crypt_device *cd; crypt_status_info cs; crypt_keyslot_info ck; /* yubikey */ YK_KEY * yk; uint8_t yk_slot = SLOT_CHAL_HMAC2; unsigned int serial = 0; /* iniparser */ dictionary * ini; char section_ykslot[10 /* unsigned int in char */ + 1 + sizeof(CONFYKSLOT) + 1]; char section_luksslot[10 /* unsigned int in char */ + 1 + sizeof(CONFLUKSSLOT) + 1]; /* initialize random seed */ gettimeofday(&tv, NULL); srand(tv.tv_usec * tv.tv_sec); memset(challenge_old, 0, CHALLENGELEN + 1); memset(challenge_new, 0, CHALLENGELEN + 1); memset(repose_old, 0, RESPONSELEN); memset(repose_new, 0, RESPONSELEN); memset(passphrase_old, 0, PASSPHRASELEN + 1); memset(passphrase_new, 0, PASSPHRASELEN + 1); if ((ini = iniparser_load(CONFIGFILE)) == NULL) { rc = EXIT_FAILURE; fprintf(stderr, "Could not parse configuration file.\n"); goto out10; } if ((device_name = iniparser_getstring(ini, "general:" CONFDEVNAME, NULL)) == NULL) { rc = EXIT_FAILURE; /* read from crypttab? */ /* get device from currently open devices? */ fprintf(stderr, "Could not read LUKS device from configuration file.\n"); goto out20; } /* init and open first Yubikey */ if ((rc = yk_init()) < 0) { perror("yk_init() failed"); goto out20; } if ((yk = yk_open_first_key()) == NULL) { rc = EXIT_FAILURE; perror("yk_open_first_key() failed"); goto out30; } /* read the serial number from key */ if ((rc = yk_get_serial(yk, 0, 0, &serial)) < 0) { perror("yk_get_serial() failed"); goto out40; } /* get the yk slot */ sprintf(section_ykslot, "%d:" CONFYKSLOT, serial); yk_slot = iniparser_getint(ini, "general:" CONFYKSLOT, yk_slot); yk_slot = iniparser_getint(ini, section_ykslot, yk_slot); switch (yk_slot) { case 1: case SLOT_CHAL_HMAC1: yk_slot = SLOT_CHAL_HMAC1; break; case 2: case SLOT_CHAL_HMAC2: default: yk_slot = SLOT_CHAL_HMAC2; break; } /* get the luks slot */ sprintf(section_luksslot, "%d:" CONFLUKSSLOT, serial); luks_slot = iniparser_getint(ini, section_luksslot, luks_slot); if (luks_slot < 0) { rc = EXIT_FAILURE; fprintf(stderr, "Please set LUKS key slot for Yubikey with serial %d!\n", serial); printf("Add something like this to " CONFIGFILE ":\n\n[%d]\nluks slot = 1\n", serial); goto out40; } /* get random number and limit to printable ASCII character (32 to 126) */ for(i = 0; i < CHALLENGELEN; i++) challenge_new[i] = (rand() % (126 - 32)) + 32; /* do challenge/response and encode to hex */ if ((rc = yk_challenge_response(yk, yk_slot, true, CHALLENGELEN, (unsigned char *)challenge_new, RESPONSELEN, (unsigned char *)repose_new)) < 0) { perror("yk_challenge_response() failed"); goto out40; } yubikey_hex_encode((char *)passphrase_new, (char *)repose_new, 20); /* initialize crypt device */ if ((rc = crypt_init_by_name(&cd, device_name)) < 0) { fprintf(stderr, "Device %s failed to initialize.\n", device_name); goto out40; } /* these are the filenames for challenge * we need this for reading and writing */ sprintf(challengefilename, CHALLENGEDIR "/challenge-%d", serial); sprintf(challengefiletmpname, CHALLENGEDIR "/challenge-%d-XXXXXX", serial); /* write new challenge to file */ if ((rc = challengefiletmp = mkstemp(challengefiletmpname)) < 0) { fprintf(stderr, "Could not open file %s for writing.\n", challengefiletmpname); goto out50; } if ((rc = write(challengefiletmp, challenge_new, CHALLENGELEN)) < 0) { fprintf(stderr, "Failed to write challenge to file.\n"); goto out60; } challengefiletmp = close(challengefiletmp); /* get status of crypt device * We expect this to be active (or busy). It is the actual root device, no? */ cs = crypt_status(cd, device_name); if (cs != CRYPT_ACTIVE && cs != CRYPT_BUSY) { rc = EXIT_FAILURE; fprintf(stderr, "Device %s is invalid or inactive.\n", device_name); goto out60; } ck = crypt_keyslot_status(cd, luks_slot); if (ck == CRYPT_SLOT_INVALID) { rc = EXIT_FAILURE; fprintf(stderr, "Key slot %d is invalid.\n", luks_slot); goto out60; } else if (ck == CRYPT_SLOT_ACTIVE || ck == CRYPT_SLOT_ACTIVE_LAST) { /* read challenge from file */ if ((rc = challengefile = open(challengefilename, O_RDONLY)) < 0) { perror("Failed opening challenge file for reading"); goto out60; } if ((rc = read(challengefile, challenge_old, CHALLENGELEN)) < 0) { perror("Failed reading challenge from file"); goto out60; } challengefile = close(challengefile); /* finished reading challenge */ /* do challenge/response and encode to hex */ if ((rc = yk_challenge_response(yk, yk_slot, true, CHALLENGELEN, (unsigned char *)challenge_old, RESPONSELEN, (unsigned char *)repose_old)) < 0) { perror("yk_challenge_response() failed"); goto out60; } yubikey_hex_encode((char *)passphrase_old, (char *)repose_old, 20); if ((rc = crypt_keyslot_change_by_passphrase(cd, luks_slot, luks_slot, passphrase_old, PASSPHRASELEN, passphrase_new, PASSPHRASELEN)) < 0) { fprintf(stderr, "Could not update passphrase for key slot %d.\n", luks_slot); goto out60; } if ((rc = unlink(challengefilename)) < 0) { fprintf(stderr, "Failed to delete old challenge file.\n"); goto out60; } } else { /* ck == CRYPT_SLOT_INACTIVE */ if ((rc = crypt_keyslot_add_by_passphrase(cd, luks_slot, NULL, 0, passphrase_new, PASSPHRASELEN)) < 0) { fprintf(stderr, "Could add passphrase for key slot %d.\n", luks_slot); goto out60; } } if ((rc = rename(challengefiletmpname, challengefilename)) < 0) { fprintf(stderr, "Failed to rename new challenge file.\n"); goto out60; } rc = EXIT_SUCCESS; out60: /* close the challenge file */ if (challengefile) close(challengefile); if (challengefiletmp) close(challengefiletmp); if (access(challengefiletmpname, F_OK) == 0 ) unlink(challengefiletmpname); out50: /* free crypt context */ crypt_free(cd); out40: /* close Yubikey */ if (!yk_close_key(yk)) perror("yk_close_key() failed"); out30: /* release Yubikey */ if (!yk_release()) perror("yk_release() failed"); out20: /* free iniparser dictionary */ iniparser_freedict(ini); out10: /* wipe response (cleartext password!) from memory */ /* This is statically allocated and always save to wipe! */ memset(challenge_old, 0, CHALLENGELEN + 1); memset(challenge_new, 0, CHALLENGELEN + 1); memset(repose_old, 0, RESPONSELEN); memset(repose_new, 0, RESPONSELEN); memset(passphrase_old, 0, PASSPHRASELEN + 1); memset(passphrase_new, 0, PASSPHRASELEN + 1); return rc; }