|
|
1.1 root 1: /*
2: * QEMU VNC display driver: TLS helpers
3: *
4: * Copyright (C) 2006 Anthony Liguori <[email protected]>
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:
27: #include "qemu-x509.h"
28: #include "vnc.h"
29: #include "qemu_socket.h"
30:
31: #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
32: /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
33: static void vnc_debug_gnutls_log(int level, const char* str) {
34: VNC_DEBUG("%d %s", level, str);
35: }
36: #endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
37:
38:
39: #define DH_BITS 1024
40: static gnutls_dh_params_t dh_params;
41:
42: static int vnc_tls_initialize(void)
43: {
44: static int tlsinitialized = 0;
45:
46: if (tlsinitialized)
47: return 1;
48:
49: if (gnutls_global_init () < 0)
50: return 0;
51:
52: /* XXX ought to re-generate diffie-hellmen params periodically */
53: if (gnutls_dh_params_init (&dh_params) < 0)
54: return 0;
55: if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
56: return 0;
57:
58: #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
59: gnutls_global_set_log_level(10);
60: gnutls_global_set_log_function(vnc_debug_gnutls_log);
61: #endif
62:
63: tlsinitialized = 1;
64:
65: return 1;
66: }
67:
68: static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
69: const void *data,
70: size_t len) {
71: struct VncState *vs = (struct VncState *)transport;
72: int ret;
73:
74: retry:
75: ret = send(vs->csock, data, len, 0);
76: if (ret < 0) {
77: if (errno == EINTR)
78: goto retry;
79: return -1;
80: }
81: return ret;
82: }
83:
84:
85: static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
86: void *data,
87: size_t len) {
88: struct VncState *vs = (struct VncState *)transport;
89: int ret;
90:
91: retry:
92: ret = recv(vs->csock, data, len, 0);
93: if (ret < 0) {
94: if (errno == EINTR)
95: goto retry;
96: return -1;
97: }
98: return ret;
99: }
100:
101:
102: static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
103: {
104: gnutls_anon_server_credentials anon_cred;
105: int ret;
106:
107: if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
108: VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
109: return NULL;
110: }
111:
112: gnutls_anon_set_server_dh_params(anon_cred, dh_params);
113:
114: return anon_cred;
115: }
116:
117:
118: static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
119: {
120: gnutls_certificate_credentials_t x509_cred;
121: int ret;
122:
123: if (!vd->tls.x509cacert) {
124: VNC_DEBUG("No CA x509 certificate specified\n");
125: return NULL;
126: }
127: if (!vd->tls.x509cert) {
128: VNC_DEBUG("No server x509 certificate specified\n");
129: return NULL;
130: }
131: if (!vd->tls.x509key) {
132: VNC_DEBUG("No server private key specified\n");
133: return NULL;
134: }
135:
136: if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
137: VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
138: return NULL;
139: }
140: if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
141: vd->tls.x509cacert,
142: GNUTLS_X509_FMT_PEM)) < 0) {
143: VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
144: gnutls_certificate_free_credentials(x509_cred);
145: return NULL;
146: }
147:
148: if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
149: vd->tls.x509cert,
150: vd->tls.x509key,
151: GNUTLS_X509_FMT_PEM)) < 0) {
152: VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
153: gnutls_certificate_free_credentials(x509_cred);
154: return NULL;
155: }
156:
157: if (vd->tls.x509cacrl) {
158: if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
159: vd->tls.x509cacrl,
160: GNUTLS_X509_FMT_PEM)) < 0) {
161: VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
162: gnutls_certificate_free_credentials(x509_cred);
163: return NULL;
164: }
165: }
166:
167: gnutls_certificate_set_dh_params (x509_cred, dh_params);
168:
169: return x509_cred;
170: }
171:
172:
173: int vnc_tls_validate_certificate(struct VncState *vs)
174: {
175: int ret;
176: unsigned int status;
177: const gnutls_datum_t *certs;
178: unsigned int nCerts, i;
179: time_t now;
180:
181: VNC_DEBUG("Validating client certificate\n");
182: if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
183: VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
184: return -1;
185: }
186:
187: if ((now = time(NULL)) == ((time_t)-1)) {
188: return -1;
189: }
190:
191: if (status != 0) {
192: if (status & GNUTLS_CERT_INVALID)
193: VNC_DEBUG("The certificate is not trusted.\n");
194:
195: if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
196: VNC_DEBUG("The certificate hasn't got a known issuer.\n");
197:
198: if (status & GNUTLS_CERT_REVOKED)
199: VNC_DEBUG("The certificate has been revoked.\n");
200:
201: if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
202: VNC_DEBUG("The certificate uses an insecure algorithm\n");
203:
204: return -1;
205: } else {
206: VNC_DEBUG("Certificate is valid!\n");
207: }
208:
209: /* Only support x509 for now */
210: if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
211: return -1;
212:
213: if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
214: return -1;
215:
216: for (i = 0 ; i < nCerts ; i++) {
217: gnutls_x509_crt_t cert;
218: VNC_DEBUG ("Checking certificate chain %d\n", i);
219: if (gnutls_x509_crt_init (&cert) < 0)
220: return -1;
221:
222: if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
223: gnutls_x509_crt_deinit (cert);
224: return -1;
225: }
226:
227: if (gnutls_x509_crt_get_expiration_time (cert) < now) {
228: VNC_DEBUG("The certificate has expired\n");
229: gnutls_x509_crt_deinit (cert);
230: return -1;
231: }
232:
233: if (gnutls_x509_crt_get_activation_time (cert) > now) {
234: VNC_DEBUG("The certificate is not yet activated\n");
235: gnutls_x509_crt_deinit (cert);
236: return -1;
237: }
238:
239: if (gnutls_x509_crt_get_activation_time (cert) > now) {
240: VNC_DEBUG("The certificate is not yet activated\n");
241: gnutls_x509_crt_deinit (cert);
242: return -1;
243: }
244:
245: if (i == 0) {
246: size_t dnameSize = 1024;
247: vs->tls.dname = qemu_malloc(dnameSize);
248: requery:
249: if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
250: if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
251: vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize);
252: goto requery;
253: }
254: gnutls_x509_crt_deinit (cert);
255: VNC_DEBUG("Cannot get client distinguished name: %s",
256: gnutls_strerror (ret));
257: return -1;
258: }
259:
260: if (vs->vd->tls.x509verify) {
261: int allow;
262: if (!vs->vd->tls.acl) {
263: VNC_DEBUG("no ACL activated, allowing access");
264: gnutls_x509_crt_deinit (cert);
265: continue;
266: }
267:
268: allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
269: vs->tls.dname);
270:
271: VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
272: vs->tls.dname, allow ? "allowed" : "denied");
273: if (!allow) {
274: gnutls_x509_crt_deinit (cert);
275: return -1;
276: }
277: }
278: }
279:
280: gnutls_x509_crt_deinit (cert);
281: }
282:
283: return 0;
284: }
285:
286:
287: int vnc_tls_client_setup(struct VncState *vs,
288: int needX509Creds) {
289: static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
290: static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
291: static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
292: static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
293:
294: VNC_DEBUG("Do TLS setup\n");
295: if (vnc_tls_initialize() < 0) {
296: VNC_DEBUG("Failed to init TLS\n");
297: vnc_client_error(vs);
298: return -1;
299: }
300: if (vs->tls.session == NULL) {
301: if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
302: vnc_client_error(vs);
303: return -1;
304: }
305:
306: if (gnutls_set_default_priority(vs->tls.session) < 0) {
307: gnutls_deinit(vs->tls.session);
308: vs->tls.session = NULL;
309: vnc_client_error(vs);
310: return -1;
311: }
312:
313: if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
314: gnutls_deinit(vs->tls.session);
315: vs->tls.session = NULL;
316: vnc_client_error(vs);
317: return -1;
318: }
319:
320: if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
321: gnutls_deinit(vs->tls.session);
322: vs->tls.session = NULL;
323: vnc_client_error(vs);
324: return -1;
325: }
326:
327: if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) {
328: gnutls_deinit(vs->tls.session);
329: vs->tls.session = NULL;
330: vnc_client_error(vs);
331: return -1;
332: }
333:
334: if (needX509Creds) {
335: gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
336: if (!x509_cred) {
337: gnutls_deinit(vs->tls.session);
338: vs->tls.session = NULL;
339: vnc_client_error(vs);
340: return -1;
341: }
342: if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
343: gnutls_deinit(vs->tls.session);
344: vs->tls.session = NULL;
345: gnutls_certificate_free_credentials(x509_cred);
346: vnc_client_error(vs);
347: return -1;
348: }
349: if (vs->vd->tls.x509verify) {
350: VNC_DEBUG("Requesting a client certificate\n");
351: gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST);
352: }
353:
354: } else {
355: gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
356: if (!anon_cred) {
357: gnutls_deinit(vs->tls.session);
358: vs->tls.session = NULL;
359: vnc_client_error(vs);
360: return -1;
361: }
362: if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) {
363: gnutls_deinit(vs->tls.session);
364: vs->tls.session = NULL;
365: gnutls_anon_free_server_credentials(anon_cred);
366: vnc_client_error(vs);
367: return -1;
368: }
369: }
370:
371: gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
372: gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
373: gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
374: }
375: return 0;
376: }
377:
378:
379: void vnc_tls_client_cleanup(struct VncState *vs)
380: {
381: if (vs->tls.session) {
382: gnutls_deinit(vs->tls.session);
383: vs->tls.session = NULL;
384: }
385: vs->tls.wiremode = VNC_WIREMODE_CLEAR;
386: free(vs->tls.dname);
387: }
388:
389:
390:
391: static int vnc_set_x509_credential(VncDisplay *vd,
392: const char *certdir,
393: const char *filename,
394: char **cred,
395: int ignoreMissing)
396: {
397: struct stat sb;
398:
399: if (*cred) {
400: qemu_free(*cred);
401: *cred = NULL;
402: }
403:
404: *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
405:
406: strcpy(*cred, certdir);
407: strcat(*cred, "/");
408: strcat(*cred, filename);
409:
410: VNC_DEBUG("Check %s\n", *cred);
411: if (stat(*cred, &sb) < 0) {
412: qemu_free(*cred);
413: *cred = NULL;
414: if (ignoreMissing && errno == ENOENT)
415: return 0;
416: return -1;
417: }
418:
419: return 0;
420: }
421:
422:
423: int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
424: const char *certdir)
425: {
426: if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
427: goto cleanup;
428: if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
429: goto cleanup;
430: if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
431: goto cleanup;
432: if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
433: goto cleanup;
434:
435: return 0;
436:
437: cleanup:
438: qemu_free(vd->tls.x509cacert);
439: qemu_free(vd->tls.x509cacrl);
440: qemu_free(vd->tls.x509cert);
441: qemu_free(vd->tls.x509key);
442: vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
443: return -1;
444: }
445:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.