vnc-tls.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. * QEMU VNC display driver: TLS helpers
  3. *
  4. * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
  5. * Copyright (C) 2006 Fabrice Bellard
  6. * Copyright (C) 2009 Red Hat, Inc
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include "qemu-x509.h"
  27. #include "vnc.h"
  28. #include "qemu/sockets.h"
  29. #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
  30. /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
  31. static void vnc_debug_gnutls_log(int level, const char* str) {
  32. VNC_DEBUG("%d %s", level, str);
  33. }
  34. #endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
  35. #define DH_BITS 1024
  36. static gnutls_dh_params_t dh_params;
  37. static int vnc_tls_initialize(void)
  38. {
  39. static int tlsinitialized = 0;
  40. if (tlsinitialized)
  41. return 1;
  42. if (gnutls_global_init () < 0)
  43. return 0;
  44. /* XXX ought to re-generate diffie-hellman params periodically */
  45. if (gnutls_dh_params_init (&dh_params) < 0)
  46. return 0;
  47. if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
  48. return 0;
  49. #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
  50. gnutls_global_set_log_level(10);
  51. gnutls_global_set_log_function(vnc_debug_gnutls_log);
  52. #endif
  53. tlsinitialized = 1;
  54. return 1;
  55. }
  56. static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
  57. const void *data,
  58. size_t len) {
  59. struct VncState *vs = (struct VncState *)transport;
  60. int ret;
  61. retry:
  62. ret = send(vs->csock, data, len, 0);
  63. if (ret < 0) {
  64. if (errno == EINTR)
  65. goto retry;
  66. return -1;
  67. }
  68. return ret;
  69. }
  70. static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
  71. void *data,
  72. size_t len) {
  73. struct VncState *vs = (struct VncState *)transport;
  74. int ret;
  75. retry:
  76. ret = qemu_recv(vs->csock, data, len, 0);
  77. if (ret < 0) {
  78. if (errno == EINTR)
  79. goto retry;
  80. return -1;
  81. }
  82. return ret;
  83. }
  84. static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void)
  85. {
  86. gnutls_anon_server_credentials_t anon_cred;
  87. int ret;
  88. if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
  89. VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
  90. return NULL;
  91. }
  92. gnutls_anon_set_server_dh_params(anon_cred, dh_params);
  93. return anon_cred;
  94. }
  95. static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
  96. {
  97. gnutls_certificate_credentials_t x509_cred;
  98. int ret;
  99. if (!vd->tls.x509cacert) {
  100. VNC_DEBUG("No CA x509 certificate specified\n");
  101. return NULL;
  102. }
  103. if (!vd->tls.x509cert) {
  104. VNC_DEBUG("No server x509 certificate specified\n");
  105. return NULL;
  106. }
  107. if (!vd->tls.x509key) {
  108. VNC_DEBUG("No server private key specified\n");
  109. return NULL;
  110. }
  111. if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
  112. VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
  113. return NULL;
  114. }
  115. if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
  116. vd->tls.x509cacert,
  117. GNUTLS_X509_FMT_PEM)) < 0) {
  118. VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
  119. gnutls_certificate_free_credentials(x509_cred);
  120. return NULL;
  121. }
  122. if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
  123. vd->tls.x509cert,
  124. vd->tls.x509key,
  125. GNUTLS_X509_FMT_PEM)) < 0) {
  126. VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
  127. gnutls_certificate_free_credentials(x509_cred);
  128. return NULL;
  129. }
  130. if (vd->tls.x509cacrl) {
  131. if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
  132. vd->tls.x509cacrl,
  133. GNUTLS_X509_FMT_PEM)) < 0) {
  134. VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
  135. gnutls_certificate_free_credentials(x509_cred);
  136. return NULL;
  137. }
  138. }
  139. gnutls_certificate_set_dh_params (x509_cred, dh_params);
  140. return x509_cred;
  141. }
  142. int vnc_tls_validate_certificate(struct VncState *vs)
  143. {
  144. int ret;
  145. unsigned int status;
  146. const gnutls_datum_t *certs;
  147. unsigned int nCerts, i;
  148. time_t now;
  149. VNC_DEBUG("Validating client certificate\n");
  150. if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
  151. VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
  152. return -1;
  153. }
  154. if ((now = time(NULL)) == ((time_t)-1)) {
  155. return -1;
  156. }
  157. if (status != 0) {
  158. if (status & GNUTLS_CERT_INVALID)
  159. VNC_DEBUG("The certificate is not trusted.\n");
  160. if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
  161. VNC_DEBUG("The certificate hasn't got a known issuer.\n");
  162. if (status & GNUTLS_CERT_REVOKED)
  163. VNC_DEBUG("The certificate has been revoked.\n");
  164. if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
  165. VNC_DEBUG("The certificate uses an insecure algorithm\n");
  166. return -1;
  167. } else {
  168. VNC_DEBUG("Certificate is valid!\n");
  169. }
  170. /* Only support x509 for now */
  171. if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
  172. return -1;
  173. if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
  174. return -1;
  175. for (i = 0 ; i < nCerts ; i++) {
  176. gnutls_x509_crt_t cert;
  177. VNC_DEBUG ("Checking certificate chain %d\n", i);
  178. if (gnutls_x509_crt_init (&cert) < 0)
  179. return -1;
  180. if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
  181. gnutls_x509_crt_deinit (cert);
  182. return -1;
  183. }
  184. if (gnutls_x509_crt_get_expiration_time (cert) < now) {
  185. VNC_DEBUG("The certificate has expired\n");
  186. gnutls_x509_crt_deinit (cert);
  187. return -1;
  188. }
  189. if (gnutls_x509_crt_get_activation_time (cert) > now) {
  190. VNC_DEBUG("The certificate is not yet activated\n");
  191. gnutls_x509_crt_deinit (cert);
  192. return -1;
  193. }
  194. if (gnutls_x509_crt_get_activation_time (cert) > now) {
  195. VNC_DEBUG("The certificate is not yet activated\n");
  196. gnutls_x509_crt_deinit (cert);
  197. return -1;
  198. }
  199. if (i == 0) {
  200. size_t dnameSize = 1024;
  201. vs->tls.dname = g_malloc(dnameSize);
  202. requery:
  203. if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
  204. if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
  205. vs->tls.dname = g_realloc(vs->tls.dname, dnameSize);
  206. goto requery;
  207. }
  208. gnutls_x509_crt_deinit (cert);
  209. VNC_DEBUG("Cannot get client distinguished name: %s",
  210. gnutls_strerror (ret));
  211. return -1;
  212. }
  213. if (vs->vd->tls.x509verify) {
  214. int allow;
  215. if (!vs->vd->tls.acl) {
  216. VNC_DEBUG("no ACL activated, allowing access");
  217. gnutls_x509_crt_deinit (cert);
  218. continue;
  219. }
  220. allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
  221. vs->tls.dname);
  222. VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
  223. vs->tls.dname, allow ? "allowed" : "denied");
  224. if (!allow) {
  225. gnutls_x509_crt_deinit (cert);
  226. return -1;
  227. }
  228. }
  229. }
  230. gnutls_x509_crt_deinit (cert);
  231. }
  232. return 0;
  233. }
  234. #if defined(GNUTLS_VERSION_NUMBER) && \
  235. GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */
  236. static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
  237. {
  238. const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH";
  239. int rc;
  240. rc = gnutls_priority_set_direct(s, priority, NULL);
  241. if (rc != GNUTLS_E_SUCCESS) {
  242. return -1;
  243. }
  244. return 0;
  245. }
  246. #else
  247. static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
  248. {
  249. static const int cert_types[] = { GNUTLS_CRT_X509, 0 };
  250. static const int protocols[] = {
  251. GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0
  252. };
  253. static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 };
  254. static const int kx_x509[] = {
  255. GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA,
  256. GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0
  257. };
  258. int rc;
  259. rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon);
  260. if (rc != GNUTLS_E_SUCCESS) {
  261. return -1;
  262. }
  263. rc = gnutls_certificate_type_set_priority(s, cert_types);
  264. if (rc != GNUTLS_E_SUCCESS) {
  265. return -1;
  266. }
  267. rc = gnutls_protocol_set_priority(s, protocols);
  268. if (rc != GNUTLS_E_SUCCESS) {
  269. return -1;
  270. }
  271. return 0;
  272. }
  273. #endif
  274. int vnc_tls_client_setup(struct VncState *vs,
  275. int needX509Creds) {
  276. VncStateTLS *tls;
  277. VNC_DEBUG("Do TLS setup\n");
  278. #ifdef CONFIG_VNC_WS
  279. if (vs->websocket) {
  280. tls = &vs->ws_tls;
  281. } else
  282. #endif /* CONFIG_VNC_WS */
  283. {
  284. tls = &vs->tls;
  285. }
  286. if (vnc_tls_initialize() < 0) {
  287. VNC_DEBUG("Failed to init TLS\n");
  288. vnc_client_error(vs);
  289. return -1;
  290. }
  291. if (tls->session == NULL) {
  292. if (gnutls_init(&tls->session, GNUTLS_SERVER) < 0) {
  293. vnc_client_error(vs);
  294. return -1;
  295. }
  296. if (gnutls_set_default_priority(tls->session) < 0) {
  297. gnutls_deinit(tls->session);
  298. tls->session = NULL;
  299. vnc_client_error(vs);
  300. return -1;
  301. }
  302. if (vnc_set_gnutls_priority(tls->session, needX509Creds) < 0) {
  303. gnutls_deinit(tls->session);
  304. tls->session = NULL;
  305. vnc_client_error(vs);
  306. return -1;
  307. }
  308. if (needX509Creds) {
  309. gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
  310. if (!x509_cred) {
  311. gnutls_deinit(tls->session);
  312. tls->session = NULL;
  313. vnc_client_error(vs);
  314. return -1;
  315. }
  316. if (gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
  317. gnutls_deinit(tls->session);
  318. tls->session = NULL;
  319. gnutls_certificate_free_credentials(x509_cred);
  320. vnc_client_error(vs);
  321. return -1;
  322. }
  323. if (vs->vd->tls.x509verify) {
  324. VNC_DEBUG("Requesting a client certificate\n");
  325. gnutls_certificate_server_set_request (tls->session, GNUTLS_CERT_REQUEST);
  326. }
  327. } else {
  328. gnutls_anon_server_credentials_t anon_cred = vnc_tls_initialize_anon_cred();
  329. if (!anon_cred) {
  330. gnutls_deinit(tls->session);
  331. tls->session = NULL;
  332. vnc_client_error(vs);
  333. return -1;
  334. }
  335. if (gnutls_credentials_set(tls->session, GNUTLS_CRD_ANON, anon_cred) < 0) {
  336. gnutls_deinit(tls->session);
  337. tls->session = NULL;
  338. gnutls_anon_free_server_credentials(anon_cred);
  339. vnc_client_error(vs);
  340. return -1;
  341. }
  342. }
  343. gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr_t)vs);
  344. gnutls_transport_set_push_function(tls->session, vnc_tls_push);
  345. gnutls_transport_set_pull_function(tls->session, vnc_tls_pull);
  346. }
  347. return 0;
  348. }
  349. void vnc_tls_client_cleanup(struct VncState *vs)
  350. {
  351. if (vs->tls.session) {
  352. gnutls_deinit(vs->tls.session);
  353. vs->tls.session = NULL;
  354. }
  355. vs->tls.wiremode = VNC_WIREMODE_CLEAR;
  356. g_free(vs->tls.dname);
  357. #ifdef CONFIG_VNC_WS
  358. if (vs->ws_tls.session) {
  359. gnutls_deinit(vs->ws_tls.session);
  360. vs->ws_tls.session = NULL;
  361. }
  362. vs->ws_tls.wiremode = VNC_WIREMODE_CLEAR;
  363. g_free(vs->ws_tls.dname);
  364. #endif /* CONFIG_VNC_WS */
  365. }
  366. static int vnc_set_x509_credential(VncDisplay *vd,
  367. const char *certdir,
  368. const char *filename,
  369. char **cred,
  370. int ignoreMissing)
  371. {
  372. struct stat sb;
  373. g_free(*cred);
  374. *cred = g_malloc(strlen(certdir) + strlen(filename) + 2);
  375. strcpy(*cred, certdir);
  376. strcat(*cred, "/");
  377. strcat(*cred, filename);
  378. VNC_DEBUG("Check %s\n", *cred);
  379. if (stat(*cred, &sb) < 0) {
  380. g_free(*cred);
  381. *cred = NULL;
  382. if (ignoreMissing && errno == ENOENT)
  383. return 0;
  384. return -1;
  385. }
  386. return 0;
  387. }
  388. int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
  389. const char *certdir)
  390. {
  391. if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
  392. goto cleanup;
  393. if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
  394. goto cleanup;
  395. if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
  396. goto cleanup;
  397. if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
  398. goto cleanup;
  399. return 0;
  400. cleanup:
  401. g_free(vd->tls.x509cacert);
  402. g_free(vd->tls.x509cacrl);
  403. g_free(vd->tls.x509cert);
  404. g_free(vd->tls.x509key);
  405. vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
  406. return -1;
  407. }