123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- /*
- * OpenConnect (SSL + DTLS) VPN client
- *
- * Copyright © 2008-2015 Intel Corporation.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- */
- #include <config.h>
- #include "openconnect-internal.h"
- #include <ctype.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #define NAME_TAG 0x71
- #define NAME_LIST_TAG 0x72
- #define KEY_TAG 0x73
- #define CHALLENGE_TAG 0x74
- #define RESPONSE_TAG 0x75
- #define T_RESPONSE_TAG 0x76
- #define NO_RESPONSE_TAG 0x77
- #define PROPERTY_TAG 0x78
- #define VERSION_TAG 0x79
- #define IMF_TAG 0x7a
- #define PUT_INS 0x01
- #define DELETE_INS 0x02
- #define SET_CODE_INS 0x03
- #define RESET_INS 0x04
- #define LIST_INS 0xa1
- #define CALCULATE_INS 0xa2
- #define VALIDATE_INS 0xa3
- #define CALCULATE_ALL_INS 0xa4
- #define SEND_REMAINING_INS 0xa5
- static const unsigned char appselect[] = { 0x00, 0xa4, 0x04, 0x00, 0x07,
- 0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01 };
- static const unsigned char list_keys[] = { 0x00, LIST_INS, 0x00, 0x00 };
- static const unsigned char send_remaining[] = { 0x00, SEND_REMAINING_INS, 0x00, 0x00 };
- #ifdef _WIN32
- #define scard_error(st) openconnect__win32_strerror(st)
- #define free_scard_error(str) free(str)
- #else
- #define scard_error(st) ((char *)pcsc_stringify_error(st))
- #define free_scard_error(str)
- #endif
- #ifdef __APPLE__
- #include <PCSC/wintypes.h>
- #include <PCSC/winscard.h>
- #else
- #include <winscard.h>
- #endif
- struct oc_pcsc_ctx {
- SCARDHANDLE pcsc_ctx, pcsc_card;
- char *yubikey_objname;
- int yubikey_pw_set;
- int yubikey_mode;
- };
- static int yubikey_cmd(struct openconnect_info *vpninfo, SCARDHANDLE card, int errlvl,
- const char *desc,
- const unsigned char *out, size_t outlen, struct oc_text_buf *buf)
- {
- DWORD status;
- buf_truncate(buf);
- do {
- DWORD respsize = 258;
- if (buf_ensure_space(buf, 258))
- return -ENOMEM;
- status = SCardTransmit (card, SCARD_PCI_T1, out, outlen,
- NULL, (unsigned char *)&buf->data[buf->pos], &respsize);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, errlvl,
- _("Failed to send \"%s\" to ykneo-oath applet: %s\n"),
- desc, pcsc_err);
- free_scard_error(pcsc_err);
- return -EIO;
- }
- if (respsize < 2) {
- vpn_progress(vpninfo, errlvl,
- _("Invalid short response to \"%s\" from ykneo-oath applet\n"),
- desc);
- return -EIO;
- }
- buf->pos += respsize - 2;
- /* Continuation */
- out = send_remaining;
- outlen = sizeof(send_remaining);
- } while (buf->data[buf->pos] == 0x61);
- status = load_be16(buf->data + buf->pos);
- if (status == 0x9000)
- return 0;
- vpn_progress(vpninfo, errlvl,
- _("Failure response to \"%s\": %04x\n"),
- desc, (unsigned)status);
- switch (status) {
- case 0x6a80:
- return -EINVAL;
- default:
- return -EIO;
- }
- }
- static int buf_tlv(struct oc_text_buf *buf, int *loc, unsigned char *type)
- {
- int len;
- int left = buf->pos - *loc;
- if (left < 2)
- return -EINVAL;
- *type = (unsigned char)buf->data[(*loc)++];
- len = (unsigned char)buf->data[(*loc)++];
- left -= 2;
- if (len > 0x82)
- return -EINVAL;
- else if (len == 0x81) {
- if (left < 1)
- return -EINVAL;
- len = (unsigned char)buf->data[(*loc)++];
- left--;
- } else if (len == 0x82) {
- if (left < 2)
- return -EINVAL;
- len = (unsigned char)buf->data[(*loc)++];
- len <<= 8;
- len |= (unsigned char)buf->data[(*loc)++];
- left -= 2;
- }
- if (left < len)
- return -EINVAL;
- return len;
- }
- static int select_yubioath_applet(struct openconnect_info *vpninfo,
- SCARDHANDLE pcsc_card, struct oc_text_buf *buf)
- {
- int ret, tlvlen, tlvpos, id_len, chall_len;
- unsigned char type;
- unsigned char applet_id[16], challenge[16];
- unsigned char *applet_ver;
- char *pin = NULL;
- int pin_len = 0;
- ret = yubikey_cmd(vpninfo, pcsc_card, PRG_DEBUG, _("select applet command"),
- appselect, sizeof(appselect), buf);
- if (ret)
- return ret;
- tlvpos = 0;
- tlvlen = buf_tlv(buf, &tlvpos, &type);
- if (tlvlen < 0 || type != VERSION_TAG || tlvlen != 3) {
- bad_applet:
- vpn_progress(vpninfo, PRG_ERR,
- _("Unrecognised response from ykneo-oath applet\n"));
- return -EIO;
- }
- applet_ver = (void *)&buf->data[tlvpos];
- tlvpos += tlvlen;
- tlvlen = buf_tlv(buf, &tlvpos, &type);
- if (tlvlen < 0 || type != NAME_TAG || tlvlen > sizeof(applet_id))
- goto bad_applet;
- memcpy(applet_id, &buf->data[tlvpos], tlvlen);
- id_len = tlvlen;
- tlvpos += tlvlen;
- /* Only print this during the first discovery loop */
- if (!vpninfo->pcsc)
- vpn_progress(vpninfo, PRG_INFO, _("Found ykneo-oath applet v%d.%d.%d.\n"),
- applet_ver[0], applet_ver[1], applet_ver[2]);
- if (tlvpos != buf->pos) {
- unsigned char chalresp[7 + SHA1_SIZE + 10];
- tlvlen = buf_tlv(buf, &tlvpos, &type);
- if (tlvlen < 0 || type != CHALLENGE_TAG || tlvlen > sizeof(challenge))
- goto bad_applet;
- memcpy(challenge, &buf->data[tlvpos], tlvlen);
- chall_len = tlvlen;
- /* On later invocations, we know there must have been a
- * successful authentication in the past. So try the same
- * hash first, and only retry in the loop on failure. */
- if (!vpninfo->pcsc) {
- struct oc_auth_form f;
- struct oc_form_opt o;
- retry_pass:
- memset(&f, 0, sizeof(f));
- f.auth_id = (char *)"yubikey_oath_pin";
- f.opts = &o;
- f.message = (char *)_("PIN required for Yubikey OATH applet");
- o.next = NULL;
- o.type = OC_FORM_OPT_PASSWORD;
- o.name = (char *)"yubikey_pin";
- o.label = (char *)_("Yubikey PIN:");
- o._value = NULL;
- ret = process_auth_form(vpninfo, &f);
- if (ret)
- goto out;
- if (!o._value) {
- ret = -EPERM;
- goto out;
- }
- if (pin) {
- memset(pin, 0, pin_len);
- free(pin);
- }
- pin = o._value;
- pin_len = strlen(pin);
- /* This *should* be UTF-8 but see the workaround below. */
- ret = openconnect_hash_yubikey_password(vpninfo, o._value, pin_len,
- applet_id, id_len);
- if (ret)
- goto out;
- }
- retry_hash:
- if (openconnect_yubikey_chalresp(vpninfo, &challenge, chall_len,
- chalresp + 7)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to calculate Yubikey unlock response\n"));
- ret = -EIO;
- goto out;
- }
- chalresp[0] = 0;
- chalresp[1] = VALIDATE_INS;
- chalresp[2] = 0;
- chalresp[3] = 0;
- chalresp[4] = sizeof(chalresp) - 5;
- chalresp[5] = RESPONSE_TAG;
- chalresp[6] = SHA1_SIZE;
- /* Response is already filled in */
- chalresp[7 + SHA1_SIZE] = CHALLENGE_TAG;
- chalresp[8 + SHA1_SIZE] = 8;
- memset(chalresp + 9 + SHA1_SIZE, 0xff, 8);
- ret = yubikey_cmd(vpninfo, pcsc_card, PRG_ERR, _("unlock command"),
- chalresp, sizeof(chalresp), buf);
- if (ret == -EINVAL) {
- memset(vpninfo->yubikey_pwhash, 0, sizeof(vpninfo->yubikey_pwhash));
- if (pin) {
- /* Try working around pre-KitKat PBKDF2 bug discussed at
- * https://forum.yubico.com/viewtopica454-3.html?f=26&t=1601#p6807 and
- * https://android-developers.googleblog.com/2013/12/changes-to-secretkeyfactory-api-in.html */
- const char *in;
- char *out;
- /* Convert the UTF-8 PIN to byte-truncated form in-place */
- in = out = pin;
- while (*in) {
- int c = get_utf8char(&in);
- if (c < 0) {
- /* Screw it. Break out of the loop in such a fashion
- * that we don't try the 'converted' result. */
- in = out;
- break;
- }
- *(out++) = c;
- }
- /* If out == in then the string only contained ASCII so
- * there was no conversion to be done (or was invalid
- * UTF-8 and hit the error case above). So don't try. */
- if (out != in &&
- !openconnect_hash_yubikey_password(vpninfo, pin, out - pin,
- applet_id, id_len)) {
- /* We'll have printed with PRG_ERR when the proper
- * encoding failed. So use PRG_ERR here too. */
- vpn_progress(vpninfo, PRG_ERR,
- _("Trying truncated-char PBKBF2 variant of Yubikey PIN\n"));
- goto retry_hash;
- }
- }
- goto retry_pass;
- }
- }
- out:
- free_pass(&pin);
- return ret;
- }
- #ifdef _WIN32
- #define reader_len wcslen
- #else
- #define SCardListReadersW SCardListReaders
- #define SCardConnectW SCardConnect
- #define reader_len strlen
- #endif
- int set_yubikey_mode(struct openconnect_info *vpninfo, const char *token_str)
- {
- SCARDHANDLE pcsc_ctx, pcsc_card;
- LONG status;
- #ifdef _WIN32
- wchar_t *readers = NULL, *reader;
- #else
- char *readers = NULL, *reader;
- #endif
- DWORD readers_size, proto;
- int ret, tlvlen, tlvpos;
- struct oc_text_buf *buf = NULL;
- status = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &pcsc_ctx);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, PRG_ERR, _("Failed to establish PC/SC context: %s\n"),
- pcsc_err);
- free_scard_error(pcsc_err);
- return -EIO;
- }
- vpn_progress(vpninfo, PRG_TRACE, _("Established PC/SC context\n"));
- ret = -ENOENT;
- status = SCardListReadersW(pcsc_ctx, NULL, NULL, &readers_size);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, PRG_ERR, _("Failed to query reader list: %s\n"),
- pcsc_err);
- free_scard_error(pcsc_err);
- goto out_ctx;
- }
- readers = calloc(readers_size, sizeof(readers[0]));
- if (!readers)
- goto out_ctx;
- status = SCardListReadersW(pcsc_ctx, NULL, readers, &readers_size);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, PRG_ERR, _("Failed to query reader list: %s\n"),
- pcsc_err);
- free_scard_error(pcsc_err);
- goto out_ctx;
- }
- buf = buf_alloc();
- reader = readers;
- while (reader[0]) {
- unsigned char type;
- #ifdef _WIN32
- char *reader_utf8;
- int reader_len;
- reader_len = WideCharToMultiByte(CP_UTF8, 0, reader, -1, NULL, 0, NULL, NULL);
- reader_utf8 = malloc(reader_len);
- if (!reader_utf8)
- goto next_reader;
- WideCharToMultiByte(CP_UTF8, 0, reader, -1, reader_utf8, reader_len, NULL, NULL);
- #else
- #define reader_utf8 reader
- #endif
- status = SCardConnectW(pcsc_ctx, reader, SCARD_SHARE_SHARED,
- SCARD_PROTOCOL_T1,
- &pcsc_card, &proto);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, PRG_ERR, _("Failed to connect to PC/SC reader '%s': %s\n"),
- reader_utf8, pcsc_err);
- free_scard_error(pcsc_err);
- goto free_reader_utf8;
- }
- vpn_progress(vpninfo, PRG_TRACE, _("Connected PC/SC reader '%s'\n"), reader_utf8);
- status = SCardBeginTransaction(pcsc_card);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, PRG_ERR, _("Failed to obtain exclusive access to reader '%s': %s\n"),
- reader_utf8, pcsc_err);
- free_scard_error(pcsc_err);
- goto disconnect;
- }
- ret = select_yubioath_applet(vpninfo, pcsc_card, buf);
- if (ret)
- goto end_trans;
- ret = yubikey_cmd(vpninfo, pcsc_card, PRG_ERR, _("list keys command"), list_keys, sizeof(list_keys), buf);
- if (ret)
- goto end_trans;
- tlvpos = 0;
- while (tlvpos < buf->pos) {
- unsigned char mode, hash;
- tlvlen = buf_tlv(buf, &tlvpos, &type);
- if (type != NAME_LIST_TAG || tlvlen < 1) {
- bad_applet:
- vpn_progress(vpninfo, PRG_ERR,
- _("Unrecognised response from ykneo-oath applet\n"));
- goto end_trans;
- }
- mode = buf->data[tlvpos] & 0xf0;
- hash = buf->data[tlvpos] & 0x0f;
- if (mode != 0x10 && mode != 0x20)
- goto bad_applet;
- if (hash != 0x01 && hash != 0x02)
- goto bad_applet;
- if (!token_str ||
- ((tlvlen - 1 == strlen(token_str)) && !memcmp(token_str, &buf->data[tlvpos+1], tlvlen-1))) {
- char *objname = strndup(&buf->data[tlvpos+1], tlvlen-1);
- if (!objname) {
- ret = -ENOMEM;
- SCardEndTransaction(pcsc_card, SCARD_LEAVE_CARD);
- SCardDisconnect(pcsc_card, SCARD_LEAVE_CARD);
- goto out_ctx;
- }
- /* Translators: This is filled in with mode and hash type, and the key identifier.
- e.g. "Found HOTP/SHA1 key: 'Work VPN key'\n" */
- vpn_progress(vpninfo, PRG_INFO, _("Found %s/%s key '%s' on '%s'\n"),
- (mode == 0x20) ? "TOTP" : "HOTP",
- (hash == 0x2) ? "SHA256" : "SHA1",
- objname, reader_utf8);
- vpninfo->pcsc = calloc(1, sizeof(*vpninfo->pcsc));
- if (!vpninfo->pcsc) {
- free(objname);
- goto out_ctx;
- }
- vpninfo->pcsc->yubikey_objname = objname;
- vpninfo->pcsc->yubikey_mode = mode;
- vpninfo->pcsc->pcsc_ctx = pcsc_ctx;
- vpninfo->pcsc->pcsc_card = pcsc_card;
- vpninfo->token_mode = OC_TOKEN_MODE_YUBIOATH;
- SCardEndTransaction(pcsc_card, SCARD_LEAVE_CARD);
- goto success;
- }
- tlvpos += tlvlen;
- }
- if (token_str) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Token '%s' not found on Yubikey '%s'. Searching for another Yubikey...\n"),
- token_str, reader_utf8);
- }
- end_trans:
- SCardEndTransaction(pcsc_card, SCARD_LEAVE_CARD);
- disconnect:
- SCardDisconnect(pcsc_card, SCARD_LEAVE_CARD);
- free_reader_utf8:
- #ifdef _WIN32
- free(reader_utf8);
- next_reader:
- #endif
- while (*reader)
- reader++;
- reader++;
- }
- ret = -ENOENT;
- out_ctx:
- SCardReleaseContext(pcsc_ctx);
- success:
- free(readers);
- buf_free(buf);
- return ret;
- }
- /* Return value:
- * < 0, if unable to generate a tokencode
- * = 0, on success
- */
- int can_gen_yubikey_code(struct openconnect_info *vpninfo,
- struct oc_auth_form *form,
- struct oc_form_opt *opt)
- {
- if (vpninfo->token_bypassed)
- return -EINVAL;
- if (vpninfo->token_tries == 0) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("OK to generate INITIAL tokencode\n"));
- vpninfo->token_time = 0;
- } else if (vpninfo->token_tries == 1) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("OK to generate NEXT tokencode\n"));
- vpninfo->token_time += 30;
- } else {
- /* limit the number of retries, to avoid account lockouts */
- vpn_progress(vpninfo, PRG_INFO,
- _("Server is rejecting the Yubikey token; switching to manual entry\n"));
- return -ENOENT;
- }
- return 0;
- }
- static int tlvlen_len(int tlvlen)
- {
- if (tlvlen < 0x80)
- return 1;
- else if (tlvlen < 0x100)
- return 2;
- else
- return 3;
- }
- static int append_tlvlen(unsigned char *p, int tlvlen)
- {
- if (tlvlen < 0x80) {
- if (p)
- p[0] = tlvlen;
- return 1;
- } else if (tlvlen < 0x100) {
- if (p) {
- p[0] = 0x81;
- p[1] = tlvlen;
- }
- return 2;
- } else {
- if (p) {
- p[0] = 0x82;
- store_be16(p + 1, tlvlen);
- }
- return 3;
- }
- }
- int do_gen_yubikey_code(struct openconnect_info *vpninfo,
- struct oc_auth_form *form,
- struct oc_form_opt *opt)
- {
- struct oc_text_buf *respbuf = NULL;
- DWORD status;
- int name_len = strlen(vpninfo->pcsc->yubikey_objname);
- int name_tlvlen;
- int calc_tlvlen;
- unsigned char *reqbuf = NULL;
- int tokval;
- int i = 0;
- int ret;
- if (!vpninfo->token_time)
- vpninfo->token_time = time(NULL);
- vpn_progress(vpninfo, PRG_INFO, _("Generating Yubikey token code\n"));
- status = SCardBeginTransaction(vpninfo->pcsc->pcsc_card);
- if (status != SCARD_S_SUCCESS) {
- char *pcsc_err = scard_error(status);
- vpn_progress(vpninfo, PRG_ERR, _("Failed to obtain exclusive access to Yubikey: %s\n"),
- pcsc_err);
- free_scard_error(pcsc_err);
- return -EIO;
- }
- respbuf = buf_alloc();
- ret = select_yubioath_applet(vpninfo, vpninfo->pcsc->pcsc_card, respbuf);
- if (ret)
- goto out;
- name_tlvlen = tlvlen_len(strlen(vpninfo->pcsc->yubikey_objname));
- calc_tlvlen = 1 /* NAME_TAG */ + name_tlvlen + name_len +
- 1 /* CHALLENGE_TAG */ + 1 /* Challenge TLV len */;
- if (vpninfo->pcsc->yubikey_mode == 0x20)
- calc_tlvlen += 8; /* TOTP needs the time as challenge */
- reqbuf = malloc(4 + tlvlen_len(calc_tlvlen) + calc_tlvlen);
- if (!reqbuf)
- goto out;
- reqbuf[i++] = 0;
- reqbuf[i++] = CALCULATE_INS;
- reqbuf[i++] = 0;
- reqbuf[i++] = 1;
- i += append_tlvlen(reqbuf + i, calc_tlvlen);
- reqbuf[i++] = NAME_TAG;
- i += append_tlvlen(reqbuf + i, name_len);
- memcpy(reqbuf + i, vpninfo->pcsc->yubikey_objname, name_len);
- i += name_len;
- reqbuf[i++] = CHALLENGE_TAG;
- if (vpninfo->pcsc->yubikey_mode == 0x20) {
- long token_steps = vpninfo->token_time / 30;
- reqbuf[i++] = 8;
- reqbuf[i++] = 0;
- reqbuf[i++] = 0;
- reqbuf[i++] = 0;
- reqbuf[i++] = 0;
- store_be32(reqbuf + i, token_steps);
- i += 4;
- } else {
- reqbuf[i++] = 0; /* HOTP mode, zero-length challenge */
- }
- ret = yubikey_cmd(vpninfo, vpninfo->pcsc->pcsc_card, PRG_ERR, _("calculate command"),
- reqbuf, i, respbuf);
- if (ret)
- goto out;
- if (respbuf->pos != 7 || (unsigned char)respbuf->data[0] != T_RESPONSE_TAG ||
- respbuf->data[1] != 5 || respbuf->data[2] > 8 || respbuf->data[2] < 6) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Unrecognised response from Yubikey when generating tokencode\n"));
- ret = -EIO;
- goto out;
- }
- tokval = load_be32(respbuf->data + 3);
- opt->_value = malloc(respbuf->data[2] + 1);
- if (!opt->_value) {
- ret = -ENOMEM;
- goto out;
- }
- i = respbuf->data[2];
- opt->_value[i] = 0;
- while (i--) {
- opt->_value[i] = '0' + tokval % 10;
- tokval /= 10;
- }
- vpninfo->token_tries++;
- out:
- SCardEndTransaction(vpninfo->pcsc->pcsc_card, SCARD_LEAVE_CARD);
- buf_free(respbuf);
- free(reqbuf);
- return ret;
- }
- void release_pcsc_ctx(struct openconnect_info *vpninfo)
- {
- if (!vpninfo->pcsc)
- return;
- if (vpninfo->token_mode == OC_TOKEN_MODE_YUBIOATH) {
- SCardDisconnect(vpninfo->pcsc->pcsc_card, SCARD_LEAVE_CARD);
- SCardReleaseContext(vpninfo->pcsc->pcsc_ctx);
- }
- memset(vpninfo->yubikey_pwhash, 0, sizeof(vpninfo->yubikey_pwhash));
- free(vpninfo->pcsc->yubikey_objname);
- free(vpninfo->pcsc);
- vpninfo->pcsc = NULL;
- }
|