static void crc_test3 (void) { unsigned char buf[] = { 0x55, 0xaa, 0, 0xff }; uint16_t crc = yubikey_crc16 (buf, sizeof (buf)); assert (crc == 52149); printf ("CRC-3 success\n"); }
static void crc_test2 (void) { unsigned char buf[] = { 0xfe }; uint16_t crc = yubikey_crc16 (buf, sizeof (buf)); assert (crc == 4470); printf ("CRC-2 success\n"); }
static void crc_test1 (void) { unsigned char buf[] = { 0, 1, 2, 3, 4 }; uint16_t crc = yubikey_crc16 (buf, sizeof (buf)); assert (crc == 62919); printf ("CRC-1 success\n"); }
static void crc_test4 (void) { unsigned char buf[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x30, 0x75, 0x00, 0x09, 0x3d, 0xfa, 0x60, 0xea }; uint16_t crc = yubikey_crc16 (buf, sizeof (buf)); assert (crc == 35339); printf ("CRC-4 success\n"); }
/* * Send something to the YubiKey. The command, as well as the slot, is * given in the 'slot' parameter (e.g. SLOT_CHAL_HMAC2 to send a HMAC-SHA1 * challenge to slot 2). */ int yk_write_to_key(YK_KEY *yk, uint8_t slot, const void *buf, int bufcount) { YK_FRAME frame; unsigned char repbuf[FEATURE_RPT_SIZE]; int i, seq; unsigned char *ptr, *end; if (bufcount > sizeof(frame.payload)) { yk_errno = YK_EWRONGSIZ; return 0; } /* Insert data and set slot # */ memset(&frame, 0, sizeof(frame)); memcpy(frame.payload, buf, bufcount); frame.slot = slot; /* Append slot checksum */ i = yubikey_crc16 (frame.payload, sizeof(frame.payload)); frame.crc = yk_endian_swap_16(i); /* Chop up the data into parts that fits into the payload of a feature report. Set the sequence number | 0x80 in the end of the feature report. When the Yubikey has processed it, it will clear this byte, signaling that the next part can be sent */ ptr = (unsigned char *) &frame; end = (unsigned char *) &frame + sizeof(frame); #ifdef YK_DEBUG fprintf(stderr, "YK_DEBUG: Write %i bytes to YubiKey :\n", bufcount); #endif for (seq = 0; ptr < end; seq++) { int all_zeros = 1; /* Ignore parts that are all zeroes except first and last to speed up the transfer */ for (i = 0; i < (FEATURE_RPT_SIZE - 1); i++) { if ((repbuf[i] = *ptr++)) all_zeros = 0; } if (all_zeros && (seq > 0) && (ptr < end)) continue; /* sequence number goes into lower bits of last byte */ repbuf[i] = seq | SLOT_WRITE_FLAG; /* When the Yubikey clears the SLOT_WRITE_FLAG, the * next part can be sent. */ if (! yk_wait_for_key_status(yk, slot, 0, WAIT_FOR_WRITE_FLAG, false, SLOT_WRITE_FLAG, NULL)) return 0; #ifdef YK_DEBUG _yk_hexdump(repbuf, FEATURE_RPT_SIZE); #endif if (!_ykusb_write(yk, REPORT_TYPE_FEATURE, 0, (char *)repbuf, FEATURE_RPT_SIZE)) return 0; } return 1; }
/* Read one or more feature reports from a Yubikey and put them together. * * Bufsize must be able to hold at least 2 more bytes than you are expecting * (the CRC), but since all read requests return 7 bytes of data bufsize needs * to be up to 7 bytes more than you expect. * * If the key returns more data than bufsize, we fail and set yk_errno to * YK_EWRONGSIZ. If that happens there will be partial data in buf. * * If we read a response from a Yubikey that is configured to block and wait for * a button press (in challenge response), this function will abort unless * flags contain YK_FLAG_MAYBLOCK, in which case it might take up to 15 seconds * for this function to return. * * The slot parameter is here for future purposes only. */ int yk_read_response_from_key(YK_KEY *yk, uint8_t slot, unsigned int flags, void *buf, unsigned int bufsize, unsigned int expect_bytes, unsigned int *bytes_read) { unsigned char data[FEATURE_RPT_SIZE]; memset(data, 0, sizeof(data)); memset(buf, 0, bufsize); *bytes_read = 0; #ifdef YK_DEBUG fprintf(stderr, "YK_DEBUG: Read %i bytes from YubiKey :\n", expect_bytes); #endif /* Wait for the key to turn on RESP_PENDING_FLAG */ if (! yk_wait_for_key_status(yk, slot, flags, 1000, true, RESP_PENDING_FLAG, (unsigned char *) &data)) return 0; /* The first part of the response was read by yk_wait_for_key_status(). We need * to copy it to buf. */ memcpy((char*)buf + *bytes_read, data, sizeof(data) - 1); *bytes_read += sizeof(data) - 1; while (*bytes_read + FEATURE_RPT_SIZE <= bufsize) { memset(data, 0, sizeof(data)); if (!_ykusb_read(yk, REPORT_TYPE_FEATURE, 0, (char *)data, FEATURE_RPT_SIZE)) return 0; #ifdef YK_DEBUG _yk_hexdump(data, FEATURE_RPT_SIZE); #endif if (data[FEATURE_RPT_SIZE - 1] & RESP_PENDING_FLAG) { /* The lower five bits of the status byte has the response sequence * number. If that gets reset to zero we are done. */ if ((data[FEATURE_RPT_SIZE - 1] & 31) == 0) { if (expect_bytes > 0) { /* Size of response is known. Verify CRC. */ int crc = yubikey_crc16(buf, expect_bytes + 2); if (crc != YK_CRC_OK_RESIDUAL) { yk_errno = YK_ECHECKSUM; return 0; } } /* Reset read mode of Yubikey before returning. */ yk_force_key_update(yk); return 1; } memcpy((char*)buf + *bytes_read, data, sizeof(data) - 1); *bytes_read += sizeof(data) - 1; } else { /* Reset read mode of Yubikey before returning. */ yk_force_key_update(yk); return 0; } } /* We're out of buffer space, abort reading */ yk_force_key_update(yk); yk_errno = YK_EWRONGSIZ; return 0; }
int main (int argc, char *argv[]) { uint8_t buf[128]; uint8_t key[YUBIKEY_KEY_SIZE]; char *aeskey, *token; yubikey_token_st tok; /* Parse command line parameters. */ if (argc <= 2) { printf ("Usage: %s <aeskey> <token>\n", argv[0]); printf (" AESKEY:\tHex encoded AES-key.\n"); printf (" TOKEN:\t\tModHex encoded token.\n"); return EXIT_FAILURE; } aeskey = argv[1]; token = argv[2]; if (strlen (aeskey) != 32) { printf ("error: Hex encoded AES-key must be 32 characters.\n"); return EXIT_FAILURE; } if (strlen (token) > 32) { printf ("warning: overlong token, ignoring prefix: %.*s\n", (int) strlen (token) - 32, token); token = token + (strlen (token) - 32); } if (strlen (token) != 32) { printf ("error: ModHex encoded token must be 32 characters.\n"); return EXIT_FAILURE; } /* Debug. */ printf ("Input:\n"); printf (" token: %s\n", token); yubikey_modhex_decode ((char *) key, token, YUBIKEY_KEY_SIZE); { size_t i; printf (" "); for (i = 0; i < YUBIKEY_KEY_SIZE; i++) printf ("%02x ", key[i] & 0xFF); printf ("\n"); } printf (" aeskey: %s\n", aeskey); yubikey_hex_decode ((char *) key, aeskey, YUBIKEY_KEY_SIZE); { size_t i; printf (" "); for (i = 0; i < YUBIKEY_KEY_SIZE; i++) printf ("%02x ", key[i] & 0xFF); printf ("\n"); } /* Pack up dynamic password, decrypt it and verify checksum */ yubikey_parse ((uint8_t *) token, key, &tok); printf ("Output:\n"); { size_t i; printf (" "); for (i = 0; i < YUBIKEY_BLOCK_SIZE; i++) printf ("%02x ", ((uint8_t *) & tok)[i] & 0xFF); printf ("\n"); } printf ("\nStruct:\n"); /* Debug */ { size_t i; printf (" uid: "); for (i = 0; i < YUBIKEY_UID_SIZE; i++) printf ("%02x ", tok.uid[i] & 0xFF); printf ("\n"); } printf (" counter: %d (0x%04x)\n", tok.ctr, tok.ctr); printf (" timestamp (low): %d (0x%04x)\n", tok.tstpl, tok.tstpl); printf (" timestamp (high): %d (0x%02x)\n", tok.tstph, tok.tstph); printf (" session use: %d (0x%02x)\n", tok.use, tok.use); printf (" random: %d (0x%02x)\n", tok.rnd, tok.rnd); printf (" crc: %d (0x%04x)\n", tok.crc, tok.crc); printf ("\nDerived:\n"); printf (" cleaned counter: %d (0x%04x)\n", yubikey_counter (tok.ctr), yubikey_counter (tok.ctr)); yubikey_modhex_encode ((char *) buf, (char *) tok.uid, YUBIKEY_UID_SIZE); printf (" modhex uid: %s\n", buf); printf (" triggered by caps lock: %s\n", yubikey_capslock (tok.ctr) ? "yes" : "no"); printf (" crc: %04X\n", yubikey_crc16 ((void *) &tok, YUBIKEY_KEY_SIZE)); printf (" crc check: "); if (yubikey_crc_ok_p ((uint8_t *) & tok)) { printf ("ok\n"); return EXIT_SUCCESS; } printf ("fail\n"); return EXIT_FAILURE; }
static int yubikey_auth_core(myConf_t *myConf, REQUEST *request) { int passLen = 0, session = 0, counter = 0, i = 0; MD5_CTX ctx; int result = 0; char *filename = "/usr/local/etc/raddb/yubico/users"; //get password by removing the last 32 characters of the password if (strlen(request->password->vp_strvalue) <= 32) { DEBUG("rlm_yubikey: Password too short."); return RLM_MODULE_REJECT; } passLen = strlen(request->password->vp_strvalue) - 32; strncpy(myConf->pass, request->password->vp_strvalue, passLen); myConf->pass[passLen] = '\0'; strncpy(myConf->token, request->password->vp_strvalue + passLen, 32); myConf->token[32] = '\0'; //md5 stuff MD5Init(&ctx); DEBUG("rlm_yubikey: length: %d, string: %s", passLen, myConf->pass); MD5Update(&ctx, (unsigned char *)myConf->pass, passLen); MD5Final(&ctx); MD5toString(&ctx, myConf->md5ComputedString); myConf->md5ComputedString[32] = '\0'; DEBUG("rlm_yubikey: MD5string of your pass: %s", myConf->md5ComputedString); DEBUG("rlm_yubikey: Username: %s", request->username->vp_strvalue); //read file result = config_read_file(&(myConf->config), filename); if (result != CONFIG_TRUE) { DEBUG("rlm_yubikey: Failed to parse configuration file: config_read_file (&config, filename);"); DEBUG("config_error_text()= %s and config_error_line()=%d", config_error_text(&(myConf->config)), config_error_line(&(myConf->config))); return RLM_MODULE_FAIL; } //parse file myConf->config_setting = config_lookup(&(myConf->config), USERS_PATH); if (myConf->config_setting == NULL) { DEBUG("rlm_yubikey: Failed to parse configuration file: config_lookup failed to find the users node"); return RLM_MODULE_FAIL; } //go through the list of users for (i = 0; i < config_setting_length(myConf->config_setting); i++) { DEBUG("Trying i: %d", i); config_setting_t *tmpSetting = NULL; tmpSetting = config_setting_get_elem(myConf->config_setting, i); if (tmpSetting == NULL) { DEBUG("rlm_yubikey: Failed to parse configuration file: config_setting_get_elem(config_setting,i);"); return RLM_MODULE_FAIL; } if ((config_setting_get_string_elem(tmpSetting, 0) == NULL) || (config_setting_get_string_elem(tmpSetting, 1) == NULL) || (config_setting_get_string_elem(tmpSetting, 2) == NULL)) { DEBUG("rlm_yubikey: Failed to parse configuration file while reading the username/password/aeskey triplet "); return RLM_MODULE_FAIL; } //check usernames are equal if (strcmp(request->username->vp_strvalue, config_setting_get_string_elem(tmpSetting, 0)) != 0) { //users do not match. No need to debug this //Go to next iteration continue; } //check passwords are equal if (strcmp(myConf->md5ComputedString, config_setting_get_string_elem(tmpSetting, 1)) != 0) { //passwords do not match. We debug DEBUG("rlm_yubikey: Password does not match for user %s", request->username->vp_strvalue); //Go to next iteration continue; } //check aes stuff - mostly copied from the ykdebug.c that comes with the low-level yubikey library uint8_t buf[128]; const char *aeskey = config_setting_get_string_elem(tmpSetting, 2); char *token = myConf->token; uint8_t key[YUBIKEY_KEY_SIZE]; yubikey_token_st tok; yubikey_modhex_decode((char*) key, token, YUBIKEY_KEY_SIZE); DEBUG("rlm_yubikey: aeskey: %s", aeskey); yubikey_hex_decode((char*) key, aeskey, YUBIKEY_KEY_SIZE); /* Pack up dynamic password, decrypt it and verify checksum */ yubikey_parse((uint8_t*) token, key, &tok); DEBUG("rlm_yubikey: Struct:"); size_t i; char *tmp = (char*) malloc(1024); for (i = 0; i < YUBIKEY_UID_SIZE; i++) { sprintf(tmp + i, "%c ", tok.uid[i] & 0xFF); } tmp[YUBIKEY_UID_SIZE + i] = 0; DEBUG("rlm_yubikey: uid:%s", tmp); free(tmp); DEBUG("rlm_yubikey: counter: %d (0x%04x)", tok.ctr, tok.ctr); DEBUG("rlm_yubikey: timestamp (low): %d (0x%04x)", tok.tstpl, tok.tstpl); DEBUG("rlm_yubikey: timestamp (high): %d (0x%02x)", tok.tstph, tok.tstph); DEBUG("rlm_yubikey: session use: %d (0x%02x)", tok.use, tok.use); DEBUG("rlm_yubikey: random: %d (0x%02x)", tok.rnd, tok.rnd); DEBUG("rlm_yubikey: crc: %d (0x%04x)", tok.crc, tok.crc); DEBUG("rlm_yubikey: Derived:"); DEBUG("rlm_yubikey: cleaned counter: %d (0x%04x)",yubikey_counter(tok.ctr), yubikey_counter(tok.ctr)); yubikey_modhex_encode((char*) buf, (char*) tok.uid, YUBIKEY_UID_SIZE); DEBUG("rlm_yubikey: modhex uid: %s", buf); DEBUG("rlm_yubikey: triggered by caps lock: %s", yubikey_capslock(tok.ctr) ? "yes" : "no"); DEBUG("rlm_yubikey: crc: %04X", yubikey_crc16((void*) & tok, YUBIKEY_KEY_SIZE)); DEBUG("rlm_yubikey: crc check: "); if (yubikey_crc_ok_p((uint8_t*) & tok)) { DEBUG("rlm_yubikey: ok"); char *tmppath = KEYS_PATH; char *path = (char*) malloc(strlen(tmppath) + 32 + 1); strcpy(path, tmppath); strcat(path, aeskey); myConf->config_setting = config_lookup(&(myConf->config), path); if (myConf->config_setting == NULL) { DEBUG("rlm_yubikey: Error parsing file: %s not found", path); return RLM_MODULE_FAIL; } //checking counter and session and update them if necessary counter = config_setting_get_int_elem(myConf->config_setting, 0); session = config_setting_get_int_elem(myConf->config_setting, 1); DEBUG("rlm_yubikey: Read counter: %d, session: %d", counter, session); if ((tok.ctr < counter)||((tok.ctr == counter) && (tok.use <= session))) { DEBUG("rlm_yubikey: someone tried to login with an old generated hash"); return RLM_MODULE_REJECT; } //updating config file with counter and session config_setting_set_int_elem(myConf->config_setting, 0, tok.ctr); config_setting_set_int_elem(myConf->config_setting, 1, tok.use); DEBUG("rlm_yubikey: Written element: %ld", config_setting_get_int_elem(myConf->config_setting, 0)); DEBUG("rlm_yubikey: Written element: %ld", config_setting_get_int_elem(myConf->config_setting, 1)); if (CONFIG_FALSE == config_write_file(&(myConf->config), filename)) { DEBUG("rlm_yubikey: Failed to write the file."); return RLM_MODULE_FAIL; } return RLM_MODULE_OK; } DEBUG("rlm_yubikey: fail"); } DEBUG("rlm_yubikey: Authenticating with password %s", request->password->vp_strvalue); return RLM_MODULE_REJECT; }