yubikey.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. *
  6. * Author: David Woodhouse <dwmw2@infradead.org>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * version 2.1, as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. #include <config.h>
  18. #include "openconnect-internal.h"
  19. #include <ctype.h>
  20. #include <errno.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #define NAME_TAG 0x71
  24. #define NAME_LIST_TAG 0x72
  25. #define KEY_TAG 0x73
  26. #define CHALLENGE_TAG 0x74
  27. #define RESPONSE_TAG 0x75
  28. #define T_RESPONSE_TAG 0x76
  29. #define NO_RESPONSE_TAG 0x77
  30. #define PROPERTY_TAG 0x78
  31. #define VERSION_TAG 0x79
  32. #define IMF_TAG 0x7a
  33. #define PUT_INS 0x01
  34. #define DELETE_INS 0x02
  35. #define SET_CODE_INS 0x03
  36. #define RESET_INS 0x04
  37. #define LIST_INS 0xa1
  38. #define CALCULATE_INS 0xa2
  39. #define VALIDATE_INS 0xa3
  40. #define CALCULATE_ALL_INS 0xa4
  41. #define SEND_REMAINING_INS 0xa5
  42. static const unsigned char appselect[] = { 0x00, 0xa4, 0x04, 0x00, 0x07,
  43. 0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01 };
  44. static const unsigned char list_keys[] = { 0x00, LIST_INS, 0x00, 0x00 };
  45. static const unsigned char send_remaining[] = { 0x00, SEND_REMAINING_INS, 0x00, 0x00 };
  46. #ifdef _WIN32
  47. #define scard_error(st) openconnect__win32_strerror(st)
  48. #define free_scard_error(str) free(str)
  49. #else
  50. #define scard_error(st) ((char *)pcsc_stringify_error(st))
  51. #define free_scard_error(str)
  52. #endif
  53. #ifdef __APPLE__
  54. #include <PCSC/wintypes.h>
  55. #include <PCSC/winscard.h>
  56. #else
  57. #include <winscard.h>
  58. #endif
  59. struct oc_pcsc_ctx {
  60. SCARDHANDLE pcsc_ctx, pcsc_card;
  61. char *yubikey_objname;
  62. int yubikey_pw_set;
  63. int yubikey_mode;
  64. };
  65. static int yubikey_cmd(struct openconnect_info *vpninfo, SCARDHANDLE card, int errlvl,
  66. const char *desc,
  67. const unsigned char *out, size_t outlen, struct oc_text_buf *buf)
  68. {
  69. DWORD status;
  70. buf_truncate(buf);
  71. do {
  72. DWORD respsize = 258;
  73. if (buf_ensure_space(buf, 258))
  74. return -ENOMEM;
  75. status = SCardTransmit (card, SCARD_PCI_T1, out, outlen,
  76. NULL, (unsigned char *)&buf->data[buf->pos], &respsize);
  77. if (status != SCARD_S_SUCCESS) {
  78. char *pcsc_err = scard_error(status);
  79. vpn_progress(vpninfo, errlvl,
  80. _("Failed to send \"%s\" to ykneo-oath applet: %s\n"),
  81. desc, pcsc_err);
  82. free_scard_error(pcsc_err);
  83. return -EIO;
  84. }
  85. if (respsize < 2) {
  86. vpn_progress(vpninfo, errlvl,
  87. _("Invalid short response to \"%s\" from ykneo-oath applet\n"),
  88. desc);
  89. return -EIO;
  90. }
  91. buf->pos += respsize - 2;
  92. /* Continuation */
  93. out = send_remaining;
  94. outlen = sizeof(send_remaining);
  95. } while (buf->data[buf->pos] == 0x61);
  96. status = load_be16(buf->data + buf->pos);
  97. if (status == 0x9000)
  98. return 0;
  99. vpn_progress(vpninfo, errlvl,
  100. _("Failure response to \"%s\": %04x\n"),
  101. desc, (unsigned)status);
  102. switch (status) {
  103. case 0x6a80:
  104. return -EINVAL;
  105. default:
  106. return -EIO;
  107. }
  108. }
  109. static int buf_tlv(struct oc_text_buf *buf, int *loc, unsigned char *type)
  110. {
  111. int len;
  112. int left = buf->pos - *loc;
  113. if (left < 2)
  114. return -EINVAL;
  115. *type = (unsigned char)buf->data[(*loc)++];
  116. len = (unsigned char)buf->data[(*loc)++];
  117. left -= 2;
  118. if (len > 0x82)
  119. return -EINVAL;
  120. else if (len == 0x81) {
  121. if (left < 1)
  122. return -EINVAL;
  123. len = (unsigned char)buf->data[(*loc)++];
  124. left--;
  125. } else if (len == 0x82) {
  126. if (left < 2)
  127. return -EINVAL;
  128. len = (unsigned char)buf->data[(*loc)++];
  129. len <<= 8;
  130. len |= (unsigned char)buf->data[(*loc)++];
  131. left -= 2;
  132. }
  133. if (left < len)
  134. return -EINVAL;
  135. return len;
  136. }
  137. static int select_yubioath_applet(struct openconnect_info *vpninfo,
  138. SCARDHANDLE pcsc_card, struct oc_text_buf *buf)
  139. {
  140. int ret, tlvlen, tlvpos, id_len, chall_len;
  141. unsigned char type;
  142. unsigned char applet_id[16], challenge[16];
  143. unsigned char *applet_ver;
  144. char *pin = NULL;
  145. int pin_len = 0;
  146. ret = yubikey_cmd(vpninfo, pcsc_card, PRG_DEBUG, _("select applet command"),
  147. appselect, sizeof(appselect), buf);
  148. if (ret)
  149. return ret;
  150. tlvpos = 0;
  151. tlvlen = buf_tlv(buf, &tlvpos, &type);
  152. if (tlvlen < 0 || type != VERSION_TAG || tlvlen != 3) {
  153. bad_applet:
  154. vpn_progress(vpninfo, PRG_ERR,
  155. _("Unrecognised response from ykneo-oath applet\n"));
  156. return -EIO;
  157. }
  158. applet_ver = (void *)&buf->data[tlvpos];
  159. tlvpos += tlvlen;
  160. tlvlen = buf_tlv(buf, &tlvpos, &type);
  161. if (tlvlen < 0 || type != NAME_TAG || tlvlen > sizeof(applet_id))
  162. goto bad_applet;
  163. memcpy(applet_id, &buf->data[tlvpos], tlvlen);
  164. id_len = tlvlen;
  165. tlvpos += tlvlen;
  166. /* Only print this during the first discovery loop */
  167. if (!vpninfo->pcsc)
  168. vpn_progress(vpninfo, PRG_INFO, _("Found ykneo-oath applet v%d.%d.%d.\n"),
  169. applet_ver[0], applet_ver[1], applet_ver[2]);
  170. if (tlvpos != buf->pos) {
  171. unsigned char chalresp[7 + SHA1_SIZE + 10];
  172. tlvlen = buf_tlv(buf, &tlvpos, &type);
  173. if (tlvlen < 0 || type != CHALLENGE_TAG || tlvlen > sizeof(challenge))
  174. goto bad_applet;
  175. memcpy(challenge, &buf->data[tlvpos], tlvlen);
  176. chall_len = tlvlen;
  177. /* On later invocations, we know there must have been a
  178. * successful authentication in the past. So try the same
  179. * hash first, and only retry in the loop on failure. */
  180. if (!vpninfo->pcsc) {
  181. struct oc_auth_form f;
  182. struct oc_form_opt o;
  183. retry_pass:
  184. memset(&f, 0, sizeof(f));
  185. f.auth_id = (char *)"yubikey_oath_pin";
  186. f.opts = &o;
  187. f.message = (char *)_("PIN required for Yubikey OATH applet");
  188. o.next = NULL;
  189. o.type = OC_FORM_OPT_PASSWORD;
  190. o.name = (char *)"yubikey_pin";
  191. o.label = (char *)_("Yubikey PIN:");
  192. o._value = NULL;
  193. ret = process_auth_form(vpninfo, &f);
  194. if (ret)
  195. goto out;
  196. if (!o._value) {
  197. ret = -EPERM;
  198. goto out;
  199. }
  200. if (pin) {
  201. memset(pin, 0, pin_len);
  202. free(pin);
  203. }
  204. pin = o._value;
  205. pin_len = strlen(pin);
  206. /* This *should* be UTF-8 but see the workaround below. */
  207. ret = openconnect_hash_yubikey_password(vpninfo, o._value, pin_len,
  208. applet_id, id_len);
  209. if (ret)
  210. goto out;
  211. }
  212. retry_hash:
  213. if (openconnect_yubikey_chalresp(vpninfo, &challenge, chall_len,
  214. chalresp + 7)) {
  215. vpn_progress(vpninfo, PRG_ERR,
  216. _("Failed to calculate Yubikey unlock response\n"));
  217. ret = -EIO;
  218. goto out;
  219. }
  220. chalresp[0] = 0;
  221. chalresp[1] = VALIDATE_INS;
  222. chalresp[2] = 0;
  223. chalresp[3] = 0;
  224. chalresp[4] = sizeof(chalresp) - 5;
  225. chalresp[5] = RESPONSE_TAG;
  226. chalresp[6] = SHA1_SIZE;
  227. /* Response is already filled in */
  228. chalresp[7 + SHA1_SIZE] = CHALLENGE_TAG;
  229. chalresp[8 + SHA1_SIZE] = 8;
  230. memset(chalresp + 9 + SHA1_SIZE, 0xff, 8);
  231. ret = yubikey_cmd(vpninfo, pcsc_card, PRG_ERR, _("unlock command"),
  232. chalresp, sizeof(chalresp), buf);
  233. if (ret == -EINVAL) {
  234. memset(vpninfo->yubikey_pwhash, 0, sizeof(vpninfo->yubikey_pwhash));
  235. if (pin) {
  236. /* Try working around pre-KitKat PBKDF2 bug discussed at
  237. * https://forum.yubico.com/viewtopica454-3.html?f=26&t=1601#p6807 and
  238. * https://android-developers.googleblog.com/2013/12/changes-to-secretkeyfactory-api-in.html */
  239. const char *in;
  240. char *out;
  241. /* Convert the UTF-8 PIN to byte-truncated form in-place */
  242. in = out = pin;
  243. while (*in) {
  244. int c = get_utf8char(&in);
  245. if (c < 0) {
  246. /* Screw it. Break out of the loop in such a fashion
  247. * that we don't try the 'converted' result. */
  248. in = out;
  249. break;
  250. }
  251. *(out++) = c;
  252. }
  253. /* If out == in then the string only contained ASCII so
  254. * there was no conversion to be done (or was invalid
  255. * UTF-8 and hit the error case above). So don't try. */
  256. if (out != in &&
  257. !openconnect_hash_yubikey_password(vpninfo, pin, out - pin,
  258. applet_id, id_len)) {
  259. /* We'll have printed with PRG_ERR when the proper
  260. * encoding failed. So use PRG_ERR here too. */
  261. vpn_progress(vpninfo, PRG_ERR,
  262. _("Trying truncated-char PBKBF2 variant of Yubikey PIN\n"));
  263. goto retry_hash;
  264. }
  265. }
  266. goto retry_pass;
  267. }
  268. }
  269. out:
  270. free_pass(&pin);
  271. return ret;
  272. }
  273. #ifdef _WIN32
  274. #define reader_len wcslen
  275. #else
  276. #define SCardListReadersW SCardListReaders
  277. #define SCardConnectW SCardConnect
  278. #define reader_len strlen
  279. #endif
  280. int set_yubikey_mode(struct openconnect_info *vpninfo, const char *token_str)
  281. {
  282. SCARDHANDLE pcsc_ctx, pcsc_card;
  283. LONG status;
  284. #ifdef _WIN32
  285. wchar_t *readers = NULL, *reader;
  286. #else
  287. char *readers = NULL, *reader;
  288. #endif
  289. DWORD readers_size, proto;
  290. int ret, tlvlen, tlvpos;
  291. struct oc_text_buf *buf = NULL;
  292. status = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &pcsc_ctx);
  293. if (status != SCARD_S_SUCCESS) {
  294. char *pcsc_err = scard_error(status);
  295. vpn_progress(vpninfo, PRG_ERR, _("Failed to establish PC/SC context: %s\n"),
  296. pcsc_err);
  297. free_scard_error(pcsc_err);
  298. return -EIO;
  299. }
  300. vpn_progress(vpninfo, PRG_TRACE, _("Established PC/SC context\n"));
  301. ret = -ENOENT;
  302. status = SCardListReadersW(pcsc_ctx, NULL, NULL, &readers_size);
  303. if (status != SCARD_S_SUCCESS) {
  304. char *pcsc_err = scard_error(status);
  305. vpn_progress(vpninfo, PRG_ERR, _("Failed to query reader list: %s\n"),
  306. pcsc_err);
  307. free_scard_error(pcsc_err);
  308. goto out_ctx;
  309. }
  310. readers = calloc(readers_size, sizeof(readers[0]));
  311. if (!readers)
  312. goto out_ctx;
  313. status = SCardListReadersW(pcsc_ctx, NULL, readers, &readers_size);
  314. if (status != SCARD_S_SUCCESS) {
  315. char *pcsc_err = scard_error(status);
  316. vpn_progress(vpninfo, PRG_ERR, _("Failed to query reader list: %s\n"),
  317. pcsc_err);
  318. free_scard_error(pcsc_err);
  319. goto out_ctx;
  320. }
  321. buf = buf_alloc();
  322. reader = readers;
  323. while (reader[0]) {
  324. unsigned char type;
  325. #ifdef _WIN32
  326. char *reader_utf8;
  327. int reader_len;
  328. reader_len = WideCharToMultiByte(CP_UTF8, 0, reader, -1, NULL, 0, NULL, NULL);
  329. reader_utf8 = malloc(reader_len);
  330. if (!reader_utf8)
  331. goto next_reader;
  332. WideCharToMultiByte(CP_UTF8, 0, reader, -1, reader_utf8, reader_len, NULL, NULL);
  333. #else
  334. #define reader_utf8 reader
  335. #endif
  336. status = SCardConnectW(pcsc_ctx, reader, SCARD_SHARE_SHARED,
  337. SCARD_PROTOCOL_T1,
  338. &pcsc_card, &proto);
  339. if (status != SCARD_S_SUCCESS) {
  340. char *pcsc_err = scard_error(status);
  341. vpn_progress(vpninfo, PRG_ERR, _("Failed to connect to PC/SC reader '%s': %s\n"),
  342. reader_utf8, pcsc_err);
  343. free_scard_error(pcsc_err);
  344. goto free_reader_utf8;
  345. }
  346. vpn_progress(vpninfo, PRG_TRACE, _("Connected PC/SC reader '%s'\n"), reader_utf8);
  347. status = SCardBeginTransaction(pcsc_card);
  348. if (status != SCARD_S_SUCCESS) {
  349. char *pcsc_err = scard_error(status);
  350. vpn_progress(vpninfo, PRG_ERR, _("Failed to obtain exclusive access to reader '%s': %s\n"),
  351. reader_utf8, pcsc_err);
  352. free_scard_error(pcsc_err);
  353. goto disconnect;
  354. }
  355. ret = select_yubioath_applet(vpninfo, pcsc_card, buf);
  356. if (ret)
  357. goto end_trans;
  358. ret = yubikey_cmd(vpninfo, pcsc_card, PRG_ERR, _("list keys command"), list_keys, sizeof(list_keys), buf);
  359. if (ret)
  360. goto end_trans;
  361. tlvpos = 0;
  362. while (tlvpos < buf->pos) {
  363. unsigned char mode, hash;
  364. tlvlen = buf_tlv(buf, &tlvpos, &type);
  365. if (type != NAME_LIST_TAG || tlvlen < 1) {
  366. bad_applet:
  367. vpn_progress(vpninfo, PRG_ERR,
  368. _("Unrecognised response from ykneo-oath applet\n"));
  369. goto end_trans;
  370. }
  371. mode = buf->data[tlvpos] & 0xf0;
  372. hash = buf->data[tlvpos] & 0x0f;
  373. if (mode != 0x10 && mode != 0x20)
  374. goto bad_applet;
  375. if (hash != 0x01 && hash != 0x02)
  376. goto bad_applet;
  377. if (!token_str ||
  378. ((tlvlen - 1 == strlen(token_str)) && !memcmp(token_str, &buf->data[tlvpos+1], tlvlen-1))) {
  379. char *objname = strndup(&buf->data[tlvpos+1], tlvlen-1);
  380. if (!objname) {
  381. ret = -ENOMEM;
  382. SCardEndTransaction(pcsc_card, SCARD_LEAVE_CARD);
  383. SCardDisconnect(pcsc_card, SCARD_LEAVE_CARD);
  384. goto out_ctx;
  385. }
  386. /* Translators: This is filled in with mode and hash type, and the key identifier.
  387. e.g. "Found HOTP/SHA1 key: 'Work VPN key'\n" */
  388. vpn_progress(vpninfo, PRG_INFO, _("Found %s/%s key '%s' on '%s'\n"),
  389. (mode == 0x20) ? "TOTP" : "HOTP",
  390. (hash == 0x2) ? "SHA256" : "SHA1",
  391. objname, reader_utf8);
  392. vpninfo->pcsc = calloc(1, sizeof(*vpninfo->pcsc));
  393. if (!vpninfo->pcsc) {
  394. free(objname);
  395. goto out_ctx;
  396. }
  397. vpninfo->pcsc->yubikey_objname = objname;
  398. vpninfo->pcsc->yubikey_mode = mode;
  399. vpninfo->pcsc->pcsc_ctx = pcsc_ctx;
  400. vpninfo->pcsc->pcsc_card = pcsc_card;
  401. vpninfo->token_mode = OC_TOKEN_MODE_YUBIOATH;
  402. SCardEndTransaction(pcsc_card, SCARD_LEAVE_CARD);
  403. goto success;
  404. }
  405. tlvpos += tlvlen;
  406. }
  407. if (token_str) {
  408. vpn_progress(vpninfo, PRG_ERR,
  409. _("Token '%s' not found on Yubikey '%s'. Searching for another Yubikey...\n"),
  410. token_str, reader_utf8);
  411. }
  412. end_trans:
  413. SCardEndTransaction(pcsc_card, SCARD_LEAVE_CARD);
  414. disconnect:
  415. SCardDisconnect(pcsc_card, SCARD_LEAVE_CARD);
  416. free_reader_utf8:
  417. #ifdef _WIN32
  418. free(reader_utf8);
  419. next_reader:
  420. #endif
  421. while (*reader)
  422. reader++;
  423. reader++;
  424. }
  425. ret = -ENOENT;
  426. out_ctx:
  427. SCardReleaseContext(pcsc_ctx);
  428. success:
  429. free(readers);
  430. buf_free(buf);
  431. return ret;
  432. }
  433. /* Return value:
  434. * < 0, if unable to generate a tokencode
  435. * = 0, on success
  436. */
  437. int can_gen_yubikey_code(struct openconnect_info *vpninfo,
  438. struct oc_auth_form *form,
  439. struct oc_form_opt *opt)
  440. {
  441. if (vpninfo->token_bypassed)
  442. return -EINVAL;
  443. if (vpninfo->token_tries == 0) {
  444. vpn_progress(vpninfo, PRG_DEBUG,
  445. _("OK to generate INITIAL tokencode\n"));
  446. vpninfo->token_time = 0;
  447. } else if (vpninfo->token_tries == 1) {
  448. vpn_progress(vpninfo, PRG_DEBUG,
  449. _("OK to generate NEXT tokencode\n"));
  450. vpninfo->token_time += 30;
  451. } else {
  452. /* limit the number of retries, to avoid account lockouts */
  453. vpn_progress(vpninfo, PRG_INFO,
  454. _("Server is rejecting the Yubikey token; switching to manual entry\n"));
  455. return -ENOENT;
  456. }
  457. return 0;
  458. }
  459. static int tlvlen_len(int tlvlen)
  460. {
  461. if (tlvlen < 0x80)
  462. return 1;
  463. else if (tlvlen < 0x100)
  464. return 2;
  465. else
  466. return 3;
  467. }
  468. static int append_tlvlen(unsigned char *p, int tlvlen)
  469. {
  470. if (tlvlen < 0x80) {
  471. if (p)
  472. p[0] = tlvlen;
  473. return 1;
  474. } else if (tlvlen < 0x100) {
  475. if (p) {
  476. p[0] = 0x81;
  477. p[1] = tlvlen;
  478. }
  479. return 2;
  480. } else {
  481. if (p) {
  482. p[0] = 0x82;
  483. store_be16(p + 1, tlvlen);
  484. }
  485. return 3;
  486. }
  487. }
  488. int do_gen_yubikey_code(struct openconnect_info *vpninfo,
  489. struct oc_auth_form *form,
  490. struct oc_form_opt *opt)
  491. {
  492. struct oc_text_buf *respbuf = NULL;
  493. DWORD status;
  494. int name_len = strlen(vpninfo->pcsc->yubikey_objname);
  495. int name_tlvlen;
  496. int calc_tlvlen;
  497. unsigned char *reqbuf = NULL;
  498. int tokval;
  499. int i = 0;
  500. int ret;
  501. if (!vpninfo->token_time)
  502. vpninfo->token_time = time(NULL);
  503. vpn_progress(vpninfo, PRG_INFO, _("Generating Yubikey token code\n"));
  504. status = SCardBeginTransaction(vpninfo->pcsc->pcsc_card);
  505. if (status != SCARD_S_SUCCESS) {
  506. char *pcsc_err = scard_error(status);
  507. vpn_progress(vpninfo, PRG_ERR, _("Failed to obtain exclusive access to Yubikey: %s\n"),
  508. pcsc_err);
  509. free_scard_error(pcsc_err);
  510. return -EIO;
  511. }
  512. respbuf = buf_alloc();
  513. ret = select_yubioath_applet(vpninfo, vpninfo->pcsc->pcsc_card, respbuf);
  514. if (ret)
  515. goto out;
  516. name_tlvlen = tlvlen_len(strlen(vpninfo->pcsc->yubikey_objname));
  517. calc_tlvlen = 1 /* NAME_TAG */ + name_tlvlen + name_len +
  518. 1 /* CHALLENGE_TAG */ + 1 /* Challenge TLV len */;
  519. if (vpninfo->pcsc->yubikey_mode == 0x20)
  520. calc_tlvlen += 8; /* TOTP needs the time as challenge */
  521. reqbuf = malloc(4 + tlvlen_len(calc_tlvlen) + calc_tlvlen);
  522. if (!reqbuf)
  523. goto out;
  524. reqbuf[i++] = 0;
  525. reqbuf[i++] = CALCULATE_INS;
  526. reqbuf[i++] = 0;
  527. reqbuf[i++] = 1;
  528. i += append_tlvlen(reqbuf + i, calc_tlvlen);
  529. reqbuf[i++] = NAME_TAG;
  530. i += append_tlvlen(reqbuf + i, name_len);
  531. memcpy(reqbuf + i, vpninfo->pcsc->yubikey_objname, name_len);
  532. i += name_len;
  533. reqbuf[i++] = CHALLENGE_TAG;
  534. if (vpninfo->pcsc->yubikey_mode == 0x20) {
  535. long token_steps = vpninfo->token_time / 30;
  536. reqbuf[i++] = 8;
  537. reqbuf[i++] = 0;
  538. reqbuf[i++] = 0;
  539. reqbuf[i++] = 0;
  540. reqbuf[i++] = 0;
  541. store_be32(reqbuf + i, token_steps);
  542. i += 4;
  543. } else {
  544. reqbuf[i++] = 0; /* HOTP mode, zero-length challenge */
  545. }
  546. ret = yubikey_cmd(vpninfo, vpninfo->pcsc->pcsc_card, PRG_ERR, _("calculate command"),
  547. reqbuf, i, respbuf);
  548. if (ret)
  549. goto out;
  550. if (respbuf->pos != 7 || (unsigned char)respbuf->data[0] != T_RESPONSE_TAG ||
  551. respbuf->data[1] != 5 || respbuf->data[2] > 8 || respbuf->data[2] < 6) {
  552. vpn_progress(vpninfo, PRG_ERR,
  553. _("Unrecognised response from Yubikey when generating tokencode\n"));
  554. ret = -EIO;
  555. goto out;
  556. }
  557. tokval = load_be32(respbuf->data + 3);
  558. opt->_value = malloc(respbuf->data[2] + 1);
  559. if (!opt->_value) {
  560. ret = -ENOMEM;
  561. goto out;
  562. }
  563. i = respbuf->data[2];
  564. opt->_value[i] = 0;
  565. while (i--) {
  566. opt->_value[i] = '0' + tokval % 10;
  567. tokval /= 10;
  568. }
  569. vpninfo->token_tries++;
  570. out:
  571. SCardEndTransaction(vpninfo->pcsc->pcsc_card, SCARD_LEAVE_CARD);
  572. buf_free(respbuf);
  573. free(reqbuf);
  574. return ret;
  575. }
  576. void release_pcsc_ctx(struct openconnect_info *vpninfo)
  577. {
  578. if (!vpninfo->pcsc)
  579. return;
  580. if (vpninfo->token_mode == OC_TOKEN_MODE_YUBIOATH) {
  581. SCardDisconnect(vpninfo->pcsc->pcsc_card, SCARD_LEAVE_CARD);
  582. SCardReleaseContext(vpninfo->pcsc->pcsc_ctx);
  583. }
  584. memset(vpninfo->yubikey_pwhash, 0, sizeof(vpninfo->yubikey_pwhash));
  585. free(vpninfo->pcsc->yubikey_objname);
  586. free(vpninfo->pcsc);
  587. vpninfo->pcsc = NULL;
  588. }