Skip to content
Snippets Groups Projects
yubikey_handler.c 3.75 KiB
Newer Older
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>

#include <ykdef.h>
#include <ykcore.h>
#include <yubikey.h>
#include "ykpers.h"

#define REQ_RESPONSE_LENGTH 64

static int
Emily Ehlert's avatar
Emily Ehlert committed
configure_cfg(YKP_CONFIG *cfg, YK_STATUS *st, char *server_key, int slot)
{
	// Generate Config
	ykp_configure_version(cfg, st);
    
Emily Ehlert's avatar
Emily Ehlert committed
	if(!ykp_configure_command(cfg, ((slot == 1) ? SLOT_CONFIG : SLOT_CONFIG2)))
	{
        fprintf(stderr,"Couldn't configure command");
		return false;
	}
	
	if(!ykp_set_tktflag_CHAL_RESP(cfg, true))
	{
        fprintf(stderr,"Couldn't set CHAL_RESP flag");
		return false;
	}
    if(!ykp_set_cfgflag_CHAL_HMAC(cfg, true))
	{
        fprintf(stderr,"Couldn't set CHAL_HMAC flag");
		return false;
	}

    int res = ykp_HMAC_key_from_raw(cfg, server_key);
    if (res) {
        fprintf(stderr,"Bad HMAC key");
        return false;
    }

    size_t key_bytes = (size_t)ykp_get_supported_key_length(cfg);
    if(key_bytes != 20)
    {
        fprintf(stderr,"Yubikey doesn't support HMAC Challenge");
        return false;
    }


	return true;
}

int
challenge_response(YK_KEY *yk, int slot, bool may_block, unsigned int challenge_len, const unsigned char *challenge,
                   unsigned char *response, int verbosity)
{
    if (verbosity > 6)
    {
        fprintf(stdout, "Sending %i bytes HMAC challenge to slot %i\n", challenge_len, slot);
    }

    int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2;

    if(! yk_challenge_response(yk, yk_cmd, may_block, challenge_len,
                               challenge, REQ_RESPONSE_LENGTH, response))
    {
        return false;
    }

    if(verbosity > 6)
    {
        unsigned char output_buf[(SHA1_MAX_BLOCK_SIZE * 2) + 1] = { 0 };
        yubikey_hex_encode((char *)output_buf, (char *)response, 20);
        printf("%s\n", output_buf);
    }

    return true;
}

int
key_init(YK_KEY **yk, YK_STATUS *st)
{
    if (!yk_init()) {
        return false;
    }
    if (!(*yk = yk_open_first_key())) {
        printf("Could not open YubiKey. Is the YubiKey connected to the computer?");
        return false;
    }

    if (!yk_get_status(*yk, st))
    {
        return false;
    }

    if (!(yk_check_firmware_version2(st)))
    {
        return false;
    }

    return true;
}

int
Emily Ehlert's avatar
Emily Ehlert committed
import_server_key(YK_KEY *yk, YK_STATUS *st, char *server_key, char *acc_code_hex, int verbosity, int slot)
{
    YKP_CONFIG *cfg = ykp_alloc();
    char data[1024] = {0};
Emily Ehlert's avatar
Emily Ehlert committed
    unsigned char *access_code = NULL;
    int exit_code = OPENVPN_PLUGIN_FUNC_ERROR;

Emily Ehlert's avatar
Emily Ehlert committed
    configure_cfg(cfg, st, server_key, slot);

    if(verbosity > 6)
    {
        ykp_export_config(cfg, data, 1024, YKP_FORMAT_LEGACY);
        fwrite(data, 1, strlen(data), stdout);
    }

    if (acc_code_hex)
    {
        if(!yubikey_hex_p(acc_code_hex))
        {
            fprintf(stderr,
                    "Invalid access code string: %s\n",
                    acc_code_hex);
            goto import_err;
        }
        unsigned int access_code_len = strlen(acc_code_hex) / 2;
        access_code = calloc(access_code_len, sizeof(unsigned char));
		yubikey_hex_decode(access_code, acc_code_hex, access_code_len);
        ykp_set_access_code(cfg, access_code, access_code_len);
	}

    // Apply Config
    YK_CONFIG *ycfg = NULL;
    ycfg = ykp_core_config(cfg);

    if (!yk_write_command(yk,
                          ycfg,
                          ykp_command(cfg),
                          acc_code_hex ? access_code : NULL))
    {
        fprintf(stderr, "Failed writing server key to YubiKey\n");
        goto import_err;
    }
    if(verbosity > 3)
    {
        puts("Successfully wrote server key to YubiKey");
    }
    exit_code = OPENVPN_PLUGIN_FUNC_SUCCESS;
import_err:
    if (cfg)
        ykp_free_config(cfg);
    free(access_code);
    return exit_code;
}