oath.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. * Copyright © 2013 John Morrissey <jwm@horde.net>
  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. static int b32_char(char in)
  24. {
  25. if (in >= 'A' && in <= 'Z')
  26. return in - 'A';
  27. if (in >= 'a' && in <= 'z')
  28. return in - 'a';
  29. if (in >= '2' && in <= '7')
  30. return in - '2' + 26;
  31. if (in == '=')
  32. return -2;
  33. return -1;
  34. }
  35. /* Take a group of 8 base32 chars, convert to (up to) 5 bytes of data */
  36. static int decode_b32_group(unsigned char *out, const char *in)
  37. {
  38. uint32_t d = 0;
  39. int c, i, len;
  40. for (i = 0; i < 8; i++) {
  41. c = b32_char(in[i]);
  42. if (c == -1)
  43. return -EINVAL;
  44. if (c == -2)
  45. break;
  46. d <<= 5;
  47. d |= c;
  48. /* Write the top bits before they disappear off the top
  49. of 'd' which is only a uint32_t */
  50. if (i == 1)
  51. out[0] = d >> 2;
  52. }
  53. len = i;
  54. /* There must be at least two base32 chars to provide one byte of data.
  55. * Bail out early to avoid an undefined shift. */
  56. if (len < 2)
  57. return -EINVAL;
  58. if (i < 8) {
  59. d <<= 5 * (8 - i);
  60. while (++i < 8) {
  61. if (in[i] != '=')
  62. return -EINVAL;
  63. }
  64. }
  65. store_be32(out + 1, d);
  66. switch(len) {
  67. case 8:
  68. return 5;
  69. case 7:
  70. return 4;
  71. case 5:
  72. return 3;
  73. case 4:
  74. return 2;
  75. case 2:
  76. return 1;
  77. default:
  78. return -EINVAL;
  79. }
  80. }
  81. static int decode_base32(struct openconnect_info *vpninfo, const char *b32, int len)
  82. {
  83. unsigned char *output = NULL;
  84. int inpos, outpos;
  85. int outlen;
  86. int ret;
  87. if (len % 8) {
  88. invalid:
  89. vpn_progress(vpninfo, PRG_ERR,
  90. _("Invalid base32 token string\n"));
  91. free(output);
  92. return -EINVAL;
  93. }
  94. outlen = len / 8 * 5;
  95. output = malloc(outlen);
  96. if (!output) {
  97. vpn_progress(vpninfo, PRG_ERR,
  98. _("Failed to allocate memory to decode OATH secret\n"));
  99. return -ENOMEM;
  100. }
  101. outpos = inpos = 0;
  102. while (inpos < len) {
  103. ret = decode_b32_group(output + outpos, b32 + inpos);
  104. if (ret < 0)
  105. goto invalid;
  106. inpos += 8;
  107. if (ret != 5 && inpos != len)
  108. goto invalid;
  109. outpos += ret;
  110. }
  111. vpninfo->oath_secret = (void *)output;
  112. vpninfo->oath_secret_len = outpos;
  113. return 0;
  114. }
  115. static char *parse_hex(const char *tok, int len)
  116. {
  117. unsigned char *data, *p;
  118. if (len <= 1)
  119. return NULL;
  120. data = malloc((len + 1) / 2);
  121. if (!data)
  122. return NULL;
  123. p = data;
  124. if (len & 1) {
  125. char b[2] = { '0', tok[0] };
  126. if (!isxdigit((int)(unsigned char)tok[0])) {
  127. free(data);
  128. return NULL;
  129. }
  130. *(p++) = unhex(b);
  131. tok++;
  132. len--;
  133. }
  134. while (len) {
  135. if (!isxdigit((int)(unsigned char)tok[0]) ||
  136. !isxdigit((int)(unsigned char)tok[1])) {
  137. free(data);
  138. return NULL;
  139. }
  140. *(p++) = unhex(tok);
  141. tok += 2;
  142. len -= 2;
  143. }
  144. return (char *)data;
  145. }
  146. static int pskc_decode(struct openconnect_info *vpninfo, const char *token_str,
  147. int toklen, int mode)
  148. {
  149. #ifdef HAVE_LIBPSKC
  150. pskc_t *container;
  151. pskc_key_t *key;
  152. const char *key_algo;
  153. const char *want_algo;
  154. size_t klen;
  155. if (pskc_global_init())
  156. return -EIO;
  157. if (pskc_init(&container))
  158. return -ENOMEM;
  159. if (pskc_parse_from_memory(container, toklen, token_str))
  160. return -EINVAL;
  161. key = pskc_get_keypackage(container, 0);
  162. if (!key) {
  163. pskc_done(container);
  164. return -EINVAL;
  165. }
  166. if (mode == OC_TOKEN_MODE_HOTP)
  167. want_algo = "urn:ietf:params:xml:ns:keyprov:pskc:hotp";
  168. else
  169. want_algo = "urn:ietf:params:xml:ns:keyprov:pskc:totp";
  170. key_algo = pskc_get_key_algorithm(key);
  171. if (!key_algo || strcmp(key_algo, want_algo)) {
  172. pskc_done(container);
  173. return -EINVAL;
  174. }
  175. vpninfo->oath_secret = (char *)pskc_get_key_data_secret(key, &klen);
  176. vpninfo->oath_secret_len = klen;
  177. if (!vpninfo->oath_secret) {
  178. pskc_done(container);
  179. return -EINVAL;
  180. }
  181. vpninfo->token_time = pskc_get_key_data_counter(key, NULL);
  182. vpninfo->pskc = container;
  183. vpninfo->pskc_key = key;
  184. return 0;
  185. #else /* !HAVE_LIBPSKC */
  186. vpn_progress(vpninfo, PRG_ERR,
  187. _("This version of OpenConnect was built without PSKC support\n"));
  188. return -EINVAL;
  189. #endif /* HAVE_LIBPSKC */
  190. }
  191. int set_oath_mode(struct openconnect_info *vpninfo, const char *token_str,
  192. oc_token_mode_t token_mode)
  193. {
  194. int ret, toklen;
  195. if (!token_str)
  196. return -EINVAL;
  197. toklen = strlen(token_str);
  198. while (toklen && isspace((int)(unsigned char)token_str[toklen-1]))
  199. toklen--;
  200. if (strncmp(token_str, "<?xml", 5) == 0) {
  201. vpninfo->hotp_secret_format = HOTP_SECRET_PSKC;
  202. ret = pskc_decode(vpninfo, token_str, toklen, token_mode);
  203. if (ret)
  204. return -EINVAL;
  205. vpninfo->token_mode = token_mode;
  206. return 0;
  207. }
  208. if (!strncasecmp(token_str, "sha1:", 5)) {
  209. token_str += 5;
  210. toklen -= 5;
  211. vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA1;
  212. } else if (!strncasecmp(token_str, "sha256:", 7)) {
  213. token_str += 7;
  214. toklen -= 7;
  215. vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA256;
  216. } else if (!strncasecmp(token_str, "sha512:", 7)) {
  217. toklen -= 7;
  218. token_str += 7;
  219. vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA512;
  220. } else
  221. vpninfo->oath_hmac_alg = OATH_ALG_HMAC_SHA1;
  222. if (token_mode == OC_TOKEN_MODE_HOTP) {
  223. char *p = strrchr(token_str, ',');
  224. if (p) {
  225. long counter;
  226. toklen = p - token_str;
  227. p++;
  228. counter = strtol(p, &p, 0);
  229. if (counter < 0)
  230. return -EINVAL;
  231. while (*p) {
  232. if (isspace((int)(unsigned char)*p))
  233. p++;
  234. else
  235. return -EINVAL;
  236. }
  237. vpninfo->token_time = counter;
  238. } else {
  239. while (toklen &&
  240. isspace((int)(unsigned char)token_str[toklen-1]))
  241. toklen--;
  242. }
  243. }
  244. if (strncasecmp(token_str, "base32:", strlen("base32:")) == 0) {
  245. vpninfo->hotp_secret_format = HOTP_SECRET_BASE32;
  246. ret = decode_base32(vpninfo, token_str + strlen("base32:"),
  247. toklen - strlen("base32:"));
  248. if (ret)
  249. return ret;
  250. } else if (strncmp(token_str, "0x", 2) == 0) {
  251. vpninfo->hotp_secret_format = HOTP_SECRET_HEX;
  252. vpninfo->oath_secret_len = (toklen - 2) / 2;
  253. vpninfo->oath_secret = parse_hex(token_str + 2, toklen - 2);
  254. if (!vpninfo->oath_secret)
  255. return -EINVAL;
  256. } else {
  257. vpninfo->hotp_secret_format = HOTP_SECRET_RAW;
  258. vpninfo->oath_secret = strdup(token_str);
  259. vpninfo->oath_secret_len = toklen;
  260. }
  261. vpninfo->token_mode = token_mode;
  262. return 0;
  263. }
  264. /* Return value:
  265. * < 0, if unable to generate a tokencode
  266. * = 0, on success
  267. */
  268. int can_gen_totp_code(struct openconnect_info *vpninfo,
  269. struct oc_auth_form *form,
  270. struct oc_form_opt *opt)
  271. {
  272. if (vpninfo->token_tries == 0) {
  273. vpn_progress(vpninfo, PRG_DEBUG,
  274. _("OK to generate INITIAL tokencode\n"));
  275. vpninfo->token_time = 0;
  276. } else if (vpninfo->token_tries == 1) {
  277. vpn_progress(vpninfo, PRG_DEBUG,
  278. _("OK to generate NEXT tokencode\n"));
  279. vpninfo->token_time += 30;
  280. } else {
  281. /* limit the number of retries, to avoid account lockouts */
  282. vpn_progress(vpninfo, PRG_INFO,
  283. _("Server is rejecting the soft token; switching to manual entry\n"));
  284. return -ENOENT;
  285. }
  286. return 0;
  287. }
  288. /* Return value:
  289. * < 0, if unable to generate a tokencode
  290. * = 0, on success
  291. */
  292. int can_gen_hotp_code(struct openconnect_info *vpninfo,
  293. struct oc_auth_form *form,
  294. struct oc_form_opt *opt)
  295. {
  296. if (vpninfo->token_tries == 0) {
  297. vpn_progress(vpninfo, PRG_DEBUG,
  298. _("OK to generate INITIAL tokencode\n"));
  299. } else if (vpninfo->token_tries == 1) {
  300. vpn_progress(vpninfo, PRG_DEBUG,
  301. _("OK to generate NEXT tokencode\n"));
  302. } else {
  303. /* limit the number of retries, to avoid account lockouts */
  304. vpn_progress(vpninfo, PRG_INFO,
  305. _("Server is rejecting the soft token; switching to manual entry\n"));
  306. return -ENOENT;
  307. }
  308. return 0;
  309. }
  310. static int gen_hotp(struct openconnect_info *vpninfo, uint64_t data, char *output)
  311. {
  312. uint32_t data_be[2];
  313. int digest;
  314. data_be[0] = htonl(data >> 32);
  315. data_be[1] = htonl(data);
  316. digest = hotp_hmac(vpninfo, data_be);
  317. if (digest < 0)
  318. return digest;
  319. digest %= 1000000;
  320. snprintf(output, 7, "%06d", digest);
  321. return 0;
  322. }
  323. int do_gen_totp_code(struct openconnect_info *vpninfo,
  324. struct oc_auth_form *form,
  325. struct oc_form_opt *opt)
  326. {
  327. char tokencode[7];
  328. uint64_t challenge;
  329. if (!vpninfo->token_time)
  330. vpninfo->token_time = time(NULL);
  331. vpn_progress(vpninfo, PRG_INFO, _("Generating OATH TOTP token code\n"));
  332. /* XXX: Support non-standard start time and step size */
  333. challenge = vpninfo->token_time / 30;
  334. if (gen_hotp(vpninfo, challenge, tokencode))
  335. return -EIO;
  336. vpninfo->token_tries++;
  337. opt->_value = strdup(tokencode);
  338. return opt->_value ? 0 : -ENOMEM;
  339. }
  340. static void buf_append_base32(struct oc_text_buf *buf, void *data, int len)
  341. {
  342. static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  343. unsigned char *bytes = data;
  344. int i, j, b32_len = ((len + 4) / 5) * 8;
  345. uint32_t d;
  346. char b32[8];
  347. if (buf_ensure_space(buf, b32_len + 1))
  348. return;
  349. for (i = 0; i < (len - 4); i += 5) {
  350. /* Load low 4 input bytes into 'd' */
  351. d = load_be32(&bytes[i + 1]);
  352. /* Loop backwardd over output group, emitting low
  353. * 5 bits of 'd' each time and shifting. */
  354. for (j = 7; j >= 0; j--) {
  355. b32[j] = alphabet[d & 31];
  356. d >>= 5;
  357. /* Mask in the last input byte when we can fit it */
  358. if (j == 5)
  359. d |= bytes[i] << 17;
  360. }
  361. buf_append_bytes(buf, b32, 8);
  362. }
  363. if (i < len) {
  364. d = 0;
  365. /* This is basically load_be32(bytes + i) but substituting
  366. * zeroes instead of reading off the end. */
  367. for (j = 0; j < 4; j++) {
  368. d <<= 8;
  369. if (i + j < len)
  370. d |= bytes[i + j];
  371. }
  372. /* Now, work out how much '=' padding we need */
  373. memset(b32, '=', 8);
  374. b32_len = (((len - i) * 8) + 4) / 5;
  375. memset(b32 + b32_len, '=', 8 - b32_len);
  376. /* If we need 7 characters of data then put the seventh
  377. * in manually because the LSB of 'd' is actually bit 3
  378. * of the output character. */
  379. if (b32_len == 7) {
  380. b32[6] = alphabet[(d & 3) << 3];
  381. b32_len--;
  382. }
  383. /* Now shift bits into the right place and do the simple
  384. * loop emitting characters from the low 5 bits of 'd'. */
  385. d >>= ((8 - b32_len) * 5) - 8;
  386. for (j = b32_len - 1; j >= 0; j--) {
  387. b32[j] = alphabet[d & 31];
  388. d >>= 5;
  389. }
  390. buf_append_bytes(buf, b32, 8);
  391. }
  392. }
  393. static char *regen_hotp_secret(struct openconnect_info *vpninfo)
  394. {
  395. char *new_secret = NULL;
  396. struct oc_text_buf *buf;
  397. switch (vpninfo->hotp_secret_format) {
  398. case HOTP_SECRET_BASE32:
  399. buf = buf_alloc();
  400. buf_append(buf, "base32:");
  401. buf_append_base32(buf, vpninfo->oath_secret,
  402. vpninfo->oath_secret_len);
  403. break;
  404. case HOTP_SECRET_HEX:
  405. buf = buf_alloc();
  406. buf_append(buf, "0x");
  407. buf_append_hex(buf, vpninfo->oath_secret, vpninfo->oath_secret_len);
  408. break;
  409. case HOTP_SECRET_RAW:
  410. buf = buf_alloc();
  411. buf_append_bytes(buf, vpninfo->oath_secret,
  412. vpninfo->oath_secret_len);
  413. break;
  414. case HOTP_SECRET_PSKC:
  415. #ifdef HAVE_LIBPSKC
  416. {
  417. size_t len;
  418. if (!vpninfo->pskc_key || !vpninfo->pskc)
  419. return NULL;
  420. pskc_set_key_data_counter(vpninfo->pskc_key, vpninfo->token_time);
  421. pskc_build_xml(vpninfo->pskc, &new_secret, &len);
  422. /* FFS #1: libpskc craps all over itself on pskc_build_xml().
  423. https://bugzilla.redhat.com/show_bug.cgi?id=1129491
  424. Hopefully this will be fixed by 2.4.2 but make it
  425. unconditional for now... */
  426. if (1 || !pskc_check_version("2.4.2")) {
  427. pskc_done(vpninfo->pskc);
  428. vpninfo->pskc = NULL;
  429. vpninfo->pskc_key = NULL;
  430. if (pskc_init(&vpninfo->pskc) ||
  431. pskc_parse_from_memory(vpninfo->pskc, len, new_secret)) {
  432. pskc_done(vpninfo->pskc);
  433. vpninfo->pskc = NULL;
  434. } else {
  435. vpninfo->pskc_key = pskc_get_keypackage(vpninfo->pskc, 0);
  436. vpninfo->oath_secret = (char *)pskc_get_key_data_secret(vpninfo->pskc_key, NULL);
  437. }
  438. }
  439. /* FFS #2: No terminating NUL byte */
  440. realloc_inplace(new_secret, len + 1);
  441. if (new_secret)
  442. new_secret[len] = 0;
  443. return new_secret;
  444. }
  445. #endif
  446. default:
  447. return NULL;
  448. }
  449. buf_append(buf,",%ld", (long)vpninfo->token_time);
  450. if (!buf_error(buf)) {
  451. new_secret = buf->data;
  452. buf->data = NULL;
  453. }
  454. buf_free(buf);
  455. return new_secret;
  456. }
  457. int do_gen_hotp_code(struct openconnect_info *vpninfo,
  458. struct oc_auth_form *form,
  459. struct oc_form_opt *opt)
  460. {
  461. char tokencode[7];
  462. int ret;
  463. vpn_progress(vpninfo, PRG_INFO, _("Generating OATH HOTP token code\n"));
  464. if (vpninfo->lock_token) {
  465. /* This may call openconnect_set_token_mode() again to update
  466. * the token if it's changed. */
  467. ret = vpninfo->lock_token(vpninfo->tok_cbdata);
  468. if (ret)
  469. return ret;
  470. }
  471. if (gen_hotp(vpninfo, vpninfo->token_time, tokencode))
  472. return -EIO;
  473. vpninfo->token_time++;
  474. vpninfo->token_tries++;
  475. opt->_value = strdup(tokencode);
  476. if (vpninfo->unlock_token) {
  477. char *new_tok = regen_hotp_secret(vpninfo);
  478. vpninfo->unlock_token(vpninfo->tok_cbdata, new_tok);
  479. free(new_tok);
  480. }
  481. return opt->_value ? 0 : -ENOMEM;
  482. }