gnutls_tpm.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. /*
  18. * TPM code based on client-tpm.c from
  19. * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder
  20. */
  21. #include <config.h>
  22. #include "openconnect-internal.h"
  23. #include "gnutls.h"
  24. #include <gnutls/gnutls.h>
  25. #include <errno.h>
  26. #include <string.h>
  27. #ifdef HAVE_TROUSERS
  28. #include <trousers/tss.h>
  29. #include <trousers/trousers.h>
  30. struct oc_tpm1_ctx {
  31. TSS_HCONTEXT tpm_context;
  32. TSS_HKEY srk;
  33. TSS_HPOLICY srk_policy;
  34. TSS_HKEY tpm_key;
  35. TSS_HPOLICY tpm_key_policy;
  36. };
  37. /* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */
  38. static int tpm_sign_fn(gnutls_privkey_t key, void *_certinfo,
  39. const gnutls_datum_t *data, gnutls_datum_t *sig)
  40. {
  41. struct cert_info *certinfo = _certinfo;
  42. struct openconnect_info *vpninfo = certinfo->vpninfo;
  43. TSS_HHASH hash;
  44. int err;
  45. vpn_progress(vpninfo, PRG_DEBUG,
  46. _("TPM sign function called for %d bytes.\n"),
  47. data->size);
  48. err = Tspi_Context_CreateObject(certinfo->tpm1->tpm_context, TSS_OBJECT_TYPE_HASH,
  49. TSS_HASH_OTHER, &hash);
  50. if (err) {
  51. vpn_progress(vpninfo, PRG_ERR,
  52. _("Failed to create TPM hash object: %s\n"),
  53. Trspi_Error_String(err));
  54. return GNUTLS_E_PK_SIGN_FAILED;
  55. }
  56. err = Tspi_Hash_SetHashValue(hash, data->size, data->data);
  57. if (err) {
  58. vpn_progress(vpninfo, PRG_ERR,
  59. _("Failed to set value in TPM hash object: %s\n"),
  60. Trspi_Error_String(err));
  61. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, hash);
  62. return GNUTLS_E_PK_SIGN_FAILED;
  63. }
  64. err = Tspi_Hash_Sign(hash, certinfo->tpm1->tpm_key, &sig->size, &sig->data);
  65. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, hash);
  66. if (err) {
  67. if (certinfo->tpm1->tpm_key_policy || err != TPM_E_AUTHFAIL)
  68. vpn_progress(vpninfo, PRG_ERR,
  69. _("TPM hash signature failed: %s\n"),
  70. Trspi_Error_String(err));
  71. if (err == TPM_E_AUTHFAIL)
  72. return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
  73. else
  74. return GNUTLS_E_PK_SIGN_FAILED;
  75. }
  76. return 0;
  77. }
  78. int load_tpm1_key(struct openconnect_info *vpninfo, struct cert_info *certinfo,
  79. gnutls_datum_t *fdata, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig)
  80. {
  81. static const TSS_UUID SRK_UUID = TSS_UUID_SRK;
  82. gnutls_datum_t asn1;
  83. unsigned int tss_len;
  84. char *pass;
  85. int ofs, err;
  86. err = gnutls_pem_base64_decode_alloc("TSS KEY BLOB", fdata, &asn1);
  87. if (err) {
  88. vpn_progress(vpninfo, PRG_ERR,
  89. _("Error decoding TSS key blob: %s\n"),
  90. gnutls_strerror(err));
  91. return -EINVAL;
  92. }
  93. certinfo->tpm1 = calloc(1, sizeof(*certinfo->tpm1));
  94. /* Ick. We have to parse the ASN1 OCTET_STRING for ourselves. */
  95. if (asn1.size < 2 || asn1.data[0] != 0x04 /* OCTET_STRING */) {
  96. vpn_progress(vpninfo, PRG_ERR,
  97. _("Error in TSS key blob\n"));
  98. goto out_blob;
  99. }
  100. tss_len = asn1.data[1];
  101. ofs = 2;
  102. if (tss_len & 0x80) {
  103. int lenlen = tss_len & 0x7f;
  104. if (asn1.size < 2 + lenlen || lenlen > 3) {
  105. vpn_progress(vpninfo, PRG_ERR,
  106. _("Error in TSS key blob\n"));
  107. goto out_blob;
  108. }
  109. tss_len = 0;
  110. while (lenlen) {
  111. tss_len <<= 8;
  112. tss_len |= asn1.data[ofs++];
  113. lenlen--;
  114. }
  115. }
  116. if (tss_len + ofs != asn1.size) {
  117. vpn_progress(vpninfo, PRG_ERR,
  118. _("Error in TSS key blob\n"));
  119. goto out_blob;
  120. }
  121. err = Tspi_Context_Create(&certinfo->tpm1->tpm_context);
  122. if (err) {
  123. vpn_progress(vpninfo, PRG_ERR,
  124. _("Failed to create TPM context: %s\n"),
  125. Trspi_Error_String(err));
  126. goto out_blob;
  127. }
  128. err = Tspi_Context_Connect(certinfo->tpm1->tpm_context, NULL);
  129. if (err) {
  130. vpn_progress(vpninfo, PRG_ERR,
  131. _("Failed to connect TPM context: %s\n"),
  132. Trspi_Error_String(err));
  133. goto out_context;
  134. }
  135. err = Tspi_Context_LoadKeyByUUID(certinfo->tpm1->tpm_context, TSS_PS_TYPE_SYSTEM,
  136. SRK_UUID, &certinfo->tpm1->srk);
  137. if (err) {
  138. vpn_progress(vpninfo, PRG_ERR,
  139. _("Failed to load TPM SRK key: %s\n"),
  140. Trspi_Error_String(err));
  141. goto out_context;
  142. }
  143. err = Tspi_GetPolicyObject(certinfo->tpm1->srk, TSS_POLICY_USAGE, &certinfo->tpm1->srk_policy);
  144. if (err) {
  145. vpn_progress(vpninfo, PRG_ERR,
  146. _("Failed to load TPM SRK policy object: %s\n"),
  147. Trspi_Error_String(err));
  148. goto out_srk;
  149. }
  150. pass = certinfo->password;
  151. certinfo->password = NULL;
  152. while (1) {
  153. static const char nullpass[20];
  154. /* We don't seem to get the error here... */
  155. if (pass)
  156. err = Tspi_Policy_SetSecret(certinfo->tpm1->srk_policy,
  157. TSS_SECRET_MODE_PLAIN,
  158. strlen(pass), (BYTE *)pass);
  159. else /* Well-known NULL key */
  160. err = Tspi_Policy_SetSecret(certinfo->tpm1->srk_policy,
  161. TSS_SECRET_MODE_SHA1,
  162. sizeof(nullpass), (BYTE *)nullpass);
  163. if (err) {
  164. vpn_progress(vpninfo, PRG_ERR,
  165. _("Failed to set TPM PIN: %s\n"),
  166. Trspi_Error_String(err));
  167. goto out_srkpol;
  168. }
  169. free_pass(&pass);
  170. /* ... we get it here instead. */
  171. err = Tspi_Context_LoadKeyByBlob(certinfo->tpm1->tpm_context, certinfo->tpm1->srk,
  172. tss_len, asn1.data + ofs,
  173. &certinfo->tpm1->tpm_key);
  174. if (!err)
  175. break;
  176. if (pass)
  177. vpn_progress(vpninfo, PRG_ERR,
  178. _("Failed to load TPM key blob: %s\n"),
  179. Trspi_Error_String(err));
  180. if (err != TPM_E_AUTHFAIL)
  181. goto out_srkpol;
  182. err = request_passphrase(vpninfo, "openconnect_tpm_srk",
  183. &pass, _("Enter TPM SRK PIN:"));
  184. if (err)
  185. goto out_srkpol;
  186. }
  187. gnutls_privkey_init(pkey);
  188. /* This would be nicer if there was a destructor callback. I could
  189. allocate a data structure with the TPM handles and the vpninfo
  190. pointer, and destroy that properly when the key is destroyed. */
  191. gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, certinfo, tpm_sign_fn, NULL, 0);
  192. retry_sign:
  193. err = gnutls_privkey_sign_data(*pkey, GNUTLS_DIG_SHA1, 0, fdata, pkey_sig);
  194. if (err == GNUTLS_E_INSUFFICIENT_CREDENTIALS) {
  195. if (!certinfo->tpm1->tpm_key_policy) {
  196. err = Tspi_Context_CreateObject(certinfo->tpm1->tpm_context,
  197. TSS_OBJECT_TYPE_POLICY,
  198. TSS_POLICY_USAGE,
  199. &certinfo->tpm1->tpm_key_policy);
  200. if (err) {
  201. vpn_progress(vpninfo, PRG_ERR,
  202. _("Failed to create key policy object: %s\n"),
  203. Trspi_Error_String(err));
  204. goto out_key;
  205. }
  206. err = Tspi_Policy_AssignToObject(certinfo->tpm1->tpm_key_policy,
  207. certinfo->tpm1->tpm_key);
  208. if (err) {
  209. vpn_progress(vpninfo, PRG_ERR,
  210. _("Failed to assign policy to key: %s\n"),
  211. Trspi_Error_String(err));
  212. goto out_key_policy;
  213. }
  214. }
  215. err = request_passphrase(vpninfo,
  216. certinfo_string(certinfo, "openconnect_tpm_key",
  217. "openconnect_secondary_tpm_key"),
  218. &pass,
  219. certinfo_string(certinfo, _("Enter TPM key PIN:"),
  220. _("Enter secondary key TPM PIN:")));
  221. if (err)
  222. goto out_key_policy;
  223. err = Tspi_Policy_SetSecret(certinfo->tpm1->tpm_key_policy,
  224. TSS_SECRET_MODE_PLAIN,
  225. strlen(pass), (void *)pass);
  226. free_pass(&pass);
  227. if (err) {
  228. vpn_progress(vpninfo, PRG_ERR,
  229. _("Failed to set key PIN: %s\n"),
  230. Trspi_Error_String(err));
  231. goto out_key_policy;
  232. }
  233. goto retry_sign;
  234. }
  235. free(asn1.data);
  236. return 0;
  237. out_key_policy:
  238. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key_policy);
  239. certinfo->tpm1->tpm_key_policy = 0;
  240. out_key:
  241. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key);
  242. certinfo->tpm1->tpm_key = 0;
  243. out_srkpol:
  244. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk_policy);
  245. certinfo->tpm1->srk_policy = 0;
  246. out_srk:
  247. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk);
  248. certinfo->tpm1->srk = 0;
  249. out_context:
  250. Tspi_Context_Close(certinfo->tpm1->tpm_context);
  251. certinfo->tpm1->tpm_context = 0;
  252. out_blob:
  253. free(asn1.data);
  254. free(certinfo->tpm1);
  255. certinfo->tpm1 = NULL;
  256. return -EIO;
  257. }
  258. void release_tpm1_ctx(struct openconnect_info *vpninfo, struct cert_info *certinfo)
  259. {
  260. if (!certinfo->tpm1)
  261. return;
  262. if (certinfo->tpm1->tpm_key_policy) {
  263. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key_policy);
  264. certinfo->tpm1->tpm_key = 0;
  265. }
  266. if (certinfo->tpm1->tpm_key) {
  267. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key);
  268. certinfo->tpm1->tpm_key = 0;
  269. }
  270. if (certinfo->tpm1->srk_policy) {
  271. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk_policy);
  272. certinfo->tpm1->srk_policy = 0;
  273. }
  274. if (certinfo->tpm1->srk) {
  275. Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk);
  276. certinfo->tpm1->srk = 0;
  277. }
  278. if (certinfo->tpm1->tpm_context) {
  279. Tspi_Context_Close(certinfo->tpm1->tpm_context);
  280. certinfo->tpm1->tpm_context = 0;
  281. }
  282. free(certinfo->tpm1);
  283. certinfo->tpm1 = NULL;
  284. };
  285. #endif /* HAVE_TROUSERS */