Exemplo n.º 1
0
Arquivo: zxpw.c Projeto: kiwiroy/zx
/* Called by:  zx_password_authn */
int zx_yubikey_authn(const char* cpath, char* uid, const char* passw, const char* pin)
{
  unsigned char uidpath[256];
  unsigned char pw_buf[256];
  unsigned char pw_hash[120];
  yubikey_token_st yktok;
  int len = strlen(uid);

  strcpy((char*)pw_hash, uid + len - 32);
  uid[len - 32] = 0;
  D("yubikey user(%s) ticket(%s) pin(%s)", uid, pw_hash, STRNULLCHK(pin));
  
  snprintf((char*)uidpath, sizeof(uidpath)-1, "%s" ZXID_UID_DIR "%s", cpath, uid);
  uidpath[sizeof(uidpath)-1] = 0;
  len = read_all(sizeof(pw_buf), (char*)pw_buf, "ykspent", 0, "%s/.ykspent/%s", uidpath, pw_hash);
  if (len) {
    ERR("The One Time Password has already been spent. ticket(%s%s) pw_buf(%.*s)", uid, pw_hash, len, pw_buf);
    return 0;
  }
  if (!write_all_path("ykspent", "%s/.ykspent/%s", (char*)uidpath, (char*)pw_hash, 1, "1"))
    return 0;
  
  len = read_all(sizeof(pw_buf), (char*)pw_buf, "ykaes", 1, "%s/.yk", uidpath);
  D("buf    (%s) got=%d", pw_buf, len);
  if (len < 32) {
    ERR("User's %s/.yk file must contain aes128 key as 32 hexadecimal characters. Too few characters %d ticket(%s)", uid, len, pw_hash);
    return 0;
  }
  if (len > 32) {
    INFO("User's %s/.yk file must contain aes128 key as 32 hexadecimal characters. Too many characters %d ticket(%s). Truncating.", uid, len, pw_hash);
    len = 32;
    pw_buf[len] = 0;
  }
  zx_hexdec((char*)pw_buf, (char*)pw_buf, len, hex_trans);
  ZERO(&yktok, sizeof(yktok));
  zx_hexdec((void*)&yktok, (char*)pw_hash, 32, ykmodhex_trans);
  yubikey_aes_decrypt((void*)&yktok, pw_buf);
  D("internal uid %02x %02x %02x %02x %02x %02x counter=%d 0x%x timestamp=%d (hi=%x lo=%x) use=%d 0x%x rnd=0x%x crc=0x%x", yktok.uid[0], yktok.uid[1], yktok.uid[2], yktok.uid[3], yktok.uid[4], yktok.uid[5], yktok.ctr, yktok.ctr, (yktok.tstph << 16) | yktok.tstpl, yktok.tstph, yktok.tstpl, yktok.use, yktok.use, yktok.rnd, yktok.crc);
  
  if (!yubikey_crc_ok_p((unsigned char*)&yktok)) {
    ERR("yubikey ticket validation failure %d", 0);
    return 0;
  }

  if (pin && *pin) { /* Pin supplied, may be we can perform two factor authn? */
    len = read_all(sizeof(pw_buf), (char*)pw_buf, "pin", 1, "%s/.pin", uidpath);
    if (zx_pw_chk(uid, (char*)pw_buf, pin, 0)) {
      D("Two factor pin+yubikey successful. %d", 1);
      return 4;
    }
    ERR("pin validation failure (after successful yubikey) %d", 0);
    return 0;
  }

  return 3;
}
Exemplo n.º 2
0
int check_token(yubikey_token_st *tok, const char *uid,
		int counter, int session)
{
  char tok_uid[2*YUBIKEY_UID_SIZE+1];
  if (!yubikey_crc_ok_p ((uint8_t *)  tok))
    return CHK_FAIL;
  from_bin_to_string(tok->uid, tok_uid, YUBIKEY_UID_SIZE);

  if(strncmp(tok_uid, uid, YUBIKEY_UID_SIZE))
    return CHK_FAIL;
  if (yubikey_counter (tok->ctr) > counter)
    return CHK_OK;
  if (yubikey_counter (tok->ctr) < counter)
    return CHK_FAIL;
  if (tok->use > session)
    return CHK_OK;

  return CHK_FAIL;
}
Exemplo n.º 3
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;
}
Exemplo n.º 4
0
/** Decrypt a Yubikey OTP AES block
 *
 * @param inst Module configuration.
 * @param passcode string to decrypt.
 * @return one of the RLM_RCODE_* constants.
 */
rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char const *passcode)
{
	uint32_t counter;
	yubikey_token_st token;

	DICT_ATTR const *da;

	char private_id[(YUBIKEY_UID_SIZE * 2) + 1];
	VALUE_PAIR *key, *vp;

	da = dict_attrbyname("Yubikey-Key");
	if (!da) {
		REDEBUG("Dictionary missing entry for 'Yubikey-Key'");
		return RLM_MODULE_FAIL;
	}

	key = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY);
	if (!key) {
		REDEBUG("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
		return RLM_MODULE_INVALID;
	}

	if (key->length != YUBIKEY_KEY_SIZE) {
		REDEBUG("Yubikey-Key length incorrect, expected %u got %zu", YUBIKEY_KEY_SIZE, key->length);
		return RLM_MODULE_INVALID;
	}

	yubikey_parse((uint8_t const *) passcode + inst->id_len, key->vp_octets, &token);

	/*
	 *	Apparently this just uses byte offsets...
	 */
	if (!yubikey_crc_ok_p((uint8_t *) &token)) {
		REDEBUG("Decrypting OTP token data failed, rejecting");
		return RLM_MODULE_REJECT;
	}

	RDEBUG("Token data decrypted successfully");

	if (request->log.lvl && request->log.func) {
		(void) fr_bin2hex((char *) &private_id, (uint8_t*) &token.uid, YUBIKEY_UID_SIZE);
		RDEBUG2("Private ID	: 0x%s", private_id);
		RDEBUG2("Session counter   : %u", yubikey_counter(token.ctr));
		RDEBUG2("# used in session : %u", token.use);
		RDEBUG2("Token timestamp    : %u",
			(token.tstph << 16) | token.tstpl);
		RDEBUG2("Random data       : %u", token.rnd);
		RDEBUG2("CRC data          : 0x%x", token.crc);
	}

	/*
	 *	Private ID used for validation purposes
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Private-ID");

		return RLM_MODULE_FAIL;
	}
	pairmemcpy(vp, token.uid, YUBIKEY_UID_SIZE);

	/*
	 *	Token timestamp
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Timestamp");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = (token.tstph << 16) | token.tstpl;
	vp->length = 4;

	/*
	 *	Token random
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Random");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = token.rnd;
	vp->length = 4;

	/*
	 *	Combine the two counter fields together so we can do
	 *	replay attack checks.
	 */
	counter = (yubikey_counter(token.ctr) << 16) | token.use;

	vp = pairmake(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Counter");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = counter;
	vp->length = 4;

	/*
	 *	Now we check for replay attacks
	 */
	vp = pairfind(request->config_items, vp->da->attr, vp->da->vendor, TAG_ANY);
	if (!vp) {
		RWDEBUG("Yubikey-Counter not found in control list, skipping replay attack checks");
		return RLM_MODULE_OK;
	}

	if (counter <= vp->vp_integer) {
		REDEBUG("Replay attack detected! Counter value %u, is lt or eq to last known counter value %u",
			counter, vp->vp_integer);
		return RLM_MODULE_REJECT;
	}

	return RLM_MODULE_OK;
}
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;
}
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);
}
Exemplo n.º 7
0
/*
 *	Authenticate the user with the given password.
 */
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
{
	rlm_yubikey_t *inst = instance;
	
	char *passcode;
	size_t i, len;
	uint32_t counter;

	const DICT_ATTR *da;	
	VALUE_PAIR *key, *vp;
	yubikey_token_st token;
	
	char private_id[(YUBIKEY_UID_SIZE * 2) + 1];
	
	/*
	 *	Can't do yubikey auth if there's no password.
	 */
	if (!request->password || (request->password->da->attr != PW_USER_PASSWORD)) {
		RDEBUGE("No Clear-Text password in the request. Can't do Yubikey authentication.");
		return RLM_MODULE_FAIL;
	}
	
	passcode = request->password->vp_strvalue;
	len = request->password->length;
	/*
	 *	Verify the passcode is the correct length (in its raw
	 *	modhex encoded form).
	 *
	 *	<public_id (6-16 bytes)> + <aes-block (32 bytes)>
	 */
	if (len != (inst->id_len + 32)) {
		RDEBUGE("User-Password value is not the correct length, expected %u, got %zu", inst->id_len + 32, len);
		return RLM_MODULE_FAIL;	
	}

	for (i = inst->id_len; i < len; i++) {
		if (!is_modhex(*passcode)) {
			RDEBUG2("User-Password (aes-block) value contains non modhex chars");
			return RLM_MODULE_FAIL;	
		}
	}
	
	da = dict_attrbyname("Yubikey-Key");
	key = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY);
	if (!key) {
		RDEBUGE("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
		return RLM_MODULE_FAIL;
	}

	if (key->length != YUBIKEY_KEY_SIZE) {
		RDEBUGE("Yubikey-Key length incorrect, expected %u got %zu", YUBIKEY_KEY_SIZE, key->length);
		return RLM_MODULE_FAIL;	
	}
	
	yubikey_parse(request->password->vp_octets + inst->id_len,
		      key->vp_octets, &token);

	/*
	 *	Apparently this just uses byte offsets...
	 */
	if (!yubikey_crc_ok_p((uint8_t *) &token)) {
		RDEBUGE("Decrypting OTP token data failed, rejecting");	
		return RLM_MODULE_REJECT;
	}
	
	RDEBUG("Token data decrypted successfully");
	
	if (request->options && request->radlog) {
		(void) fr_bin2hex((uint8_t*) &token.uid,
				  (char *) &private_id, YUBIKEY_UID_SIZE);
		RDEBUG2("Private ID	: 0x%s", private_id);
		RDEBUG2("Session counter   : %u", yubikey_counter(token.ctr));
		RDEBUG2("# used in session : %u", token.use);
		RDEBUG2("Token timetamp    : %u",
			(token.tstph << 16) | token.tstpl);
		RDEBUG2("Random data       : %u", token.rnd);
		RDEBUG2("CRC data          : 0x%x", token.crc);
	}

	/*
	 *	Private ID used for validation purposes
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);	
	memcpy(vp->vp_octets, token.uid, YUBIKEY_UID_SIZE);
	vp->length = YUBIKEY_UID_SIZE;
	
	/*
	 *	Token timestamp
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
	vp->vp_integer = (token.tstph << 16) | token.tstpl;
	vp->length = 4;
	
	/*
	 *	Token random
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
	vp->vp_integer = token.rnd;
	vp->length = 4;
	
	/*
	 *	Combine the two counter fields together so we can do
	 *	replay attack checks.
	 */
	counter = (yubikey_counter(token.ctr) << 16) | token.use;
	
	vp = pairmake(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
	vp->vp_integer = counter;
	vp->length = 4;
	
	/*
	 *	Now we check for replay attacks
	 */
	vp = pairfind(request->config_items, vp->da->attr, vp->da->vendor, TAG_ANY);
	if (!vp) {
		RDEBUGW("Yubikey-Counter not found in control list, skipping replay attack checks");
		return RLM_MODULE_OK;
	}

	if (counter <= vp->vp_integer) {
		RDEBUGE("Replay attack detected! Counter value %u, is lt or eq to last known counter value %u",
			counter, vp->vp_integer);
		return RLM_MODULE_REJECT;	
	}

	return RLM_MODULE_OK;
}