Commit 274b6fcc78401b6300bf86f9b656c13fd7e63619
1 parent
5f5aed22
Add more missing files
Major FAIL with my checkin scripts. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6731 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
734 additions
and
0 deletions
vnc-auth-vencrypt.c
0 → 100644
1 | +/* | ||
2 | + * QEMU VNC display driver: VeNCrypt authentication setup | ||
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 | + | ||
27 | +#include "vnc.h" | ||
28 | + | ||
29 | + | ||
30 | +static void start_auth_vencrypt_subauth(VncState *vs) | ||
31 | +{ | ||
32 | + switch (vs->vd->subauth) { | ||
33 | + case VNC_AUTH_VENCRYPT_TLSNONE: | ||
34 | + case VNC_AUTH_VENCRYPT_X509NONE: | ||
35 | + VNC_DEBUG("Accept TLS auth none\n"); | ||
36 | + vnc_write_u32(vs, 0); /* Accept auth completion */ | ||
37 | + start_client_init(vs); | ||
38 | + break; | ||
39 | + | ||
40 | + case VNC_AUTH_VENCRYPT_TLSVNC: | ||
41 | + case VNC_AUTH_VENCRYPT_X509VNC: | ||
42 | + VNC_DEBUG("Start TLS auth VNC\n"); | ||
43 | + start_auth_vnc(vs); | ||
44 | + break; | ||
45 | + | ||
46 | +#ifdef CONFIG_VNC_SASL | ||
47 | + case VNC_AUTH_VENCRYPT_TLSSASL: | ||
48 | + case VNC_AUTH_VENCRYPT_X509SASL: | ||
49 | + VNC_DEBUG("Start TLS auth SASL\n"); | ||
50 | + return start_auth_sasl(vs); | ||
51 | +#endif /* CONFIG_VNC_SASL */ | ||
52 | + | ||
53 | + default: /* Should not be possible, but just in case */ | ||
54 | + VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth); | ||
55 | + vnc_write_u8(vs, 1); | ||
56 | + if (vs->minor >= 8) { | ||
57 | + static const char err[] = "Unsupported authentication type"; | ||
58 | + vnc_write_u32(vs, sizeof(err)); | ||
59 | + vnc_write(vs, err, sizeof(err)); | ||
60 | + } | ||
61 | + vnc_client_error(vs); | ||
62 | + } | ||
63 | +} | ||
64 | + | ||
65 | +static void vnc_tls_handshake_io(void *opaque); | ||
66 | + | ||
67 | +static int vnc_start_vencrypt_handshake(struct VncState *vs) { | ||
68 | + int ret; | ||
69 | + | ||
70 | + if ((ret = gnutls_handshake(vs->tls.session)) < 0) { | ||
71 | + if (!gnutls_error_is_fatal(ret)) { | ||
72 | + VNC_DEBUG("Handshake interrupted (blocking)\n"); | ||
73 | + if (!gnutls_record_get_direction(vs->tls.session)) | ||
74 | + qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); | ||
75 | + else | ||
76 | + qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); | ||
77 | + return 0; | ||
78 | + } | ||
79 | + VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); | ||
80 | + vnc_client_error(vs); | ||
81 | + return -1; | ||
82 | + } | ||
83 | + | ||
84 | + if (vs->vd->tls.x509verify) { | ||
85 | + if (vnc_tls_validate_certificate(vs) < 0) { | ||
86 | + VNC_DEBUG("Client verification failed\n"); | ||
87 | + vnc_client_error(vs); | ||
88 | + return -1; | ||
89 | + } else { | ||
90 | + VNC_DEBUG("Client verification passed\n"); | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | + VNC_DEBUG("Handshake done, switching to TLS data mode\n"); | ||
95 | + vs->tls.wiremode = VNC_WIREMODE_TLS; | ||
96 | + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); | ||
97 | + | ||
98 | + start_auth_vencrypt_subauth(vs); | ||
99 | + | ||
100 | + return 0; | ||
101 | +} | ||
102 | + | ||
103 | +static void vnc_tls_handshake_io(void *opaque) { | ||
104 | + struct VncState *vs = (struct VncState *)opaque; | ||
105 | + | ||
106 | + VNC_DEBUG("Handshake IO continue\n"); | ||
107 | + vnc_start_vencrypt_handshake(vs); | ||
108 | +} | ||
109 | + | ||
110 | + | ||
111 | + | ||
112 | +#define NEED_X509_AUTH(vs) \ | ||
113 | + ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ | ||
114 | + (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ | ||
115 | + (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ | ||
116 | + (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) | ||
117 | + | ||
118 | + | ||
119 | +static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) | ||
120 | +{ | ||
121 | + int auth = read_u32(data, 0); | ||
122 | + | ||
123 | + if (auth != vs->vd->subauth) { | ||
124 | + VNC_DEBUG("Rejecting auth %d\n", auth); | ||
125 | + vnc_write_u8(vs, 0); /* Reject auth */ | ||
126 | + vnc_flush(vs); | ||
127 | + vnc_client_error(vs); | ||
128 | + } else { | ||
129 | + VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); | ||
130 | + vnc_write_u8(vs, 1); /* Accept auth */ | ||
131 | + vnc_flush(vs); | ||
132 | + | ||
133 | + if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { | ||
134 | + VNC_DEBUG("Failed to setup TLS\n"); | ||
135 | + return 0; | ||
136 | + } | ||
137 | + | ||
138 | + VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); | ||
139 | + if (vnc_start_vencrypt_handshake(vs) < 0) { | ||
140 | + VNC_DEBUG("Failed to start TLS handshake\n"); | ||
141 | + return 0; | ||
142 | + } | ||
143 | + } | ||
144 | + return 0; | ||
145 | +} | ||
146 | + | ||
147 | +static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) | ||
148 | +{ | ||
149 | + if (data[0] != 0 || | ||
150 | + data[1] != 2) { | ||
151 | + VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]); | ||
152 | + vnc_write_u8(vs, 1); /* Reject version */ | ||
153 | + vnc_flush(vs); | ||
154 | + vnc_client_error(vs); | ||
155 | + } else { | ||
156 | + VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); | ||
157 | + vnc_write_u8(vs, 0); /* Accept version */ | ||
158 | + vnc_write_u8(vs, 1); /* Number of sub-auths */ | ||
159 | + vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */ | ||
160 | + vnc_flush(vs); | ||
161 | + vnc_read_when(vs, protocol_client_vencrypt_auth, 4); | ||
162 | + } | ||
163 | + return 0; | ||
164 | +} | ||
165 | + | ||
166 | + | ||
167 | +void start_auth_vencrypt(VncState *vs) | ||
168 | +{ | ||
169 | + /* Send VeNCrypt version 0.2 */ | ||
170 | + vnc_write_u8(vs, 0); | ||
171 | + vnc_write_u8(vs, 2); | ||
172 | + | ||
173 | + vnc_read_when(vs, protocol_client_vencrypt_init, 2); | ||
174 | +} | ||
175 | + |
vnc-auth-vencrypt.h
0 → 100644
1 | +/* | ||
2 | + * QEMU VNC display driver | ||
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 | + | ||
27 | + | ||
28 | +#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__ | ||
29 | +#define __QEMU_VNC_AUTH_VENCRYPT_H__ | ||
30 | + | ||
31 | +void start_auth_vencrypt(VncState *vs); | ||
32 | + | ||
33 | +#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */ |
vnc-tls.c
0 → 100644
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 | + | ||
27 | +#include "vnc.h" | ||
28 | +#include "qemu_socket.h" | ||
29 | + | ||
30 | +#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 | ||
31 | +/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ | ||
32 | +static void vnc_debug_gnutls_log(int level, const char* str) { | ||
33 | + VNC_DEBUG("%d %s", level, str); | ||
34 | +} | ||
35 | +#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ | ||
36 | + | ||
37 | + | ||
38 | +#define DH_BITS 1024 | ||
39 | +static gnutls_dh_params_t dh_params; | ||
40 | + | ||
41 | +static int vnc_tls_initialize(void) | ||
42 | +{ | ||
43 | + static int tlsinitialized = 0; | ||
44 | + | ||
45 | + if (tlsinitialized) | ||
46 | + return 1; | ||
47 | + | ||
48 | + if (gnutls_global_init () < 0) | ||
49 | + return 0; | ||
50 | + | ||
51 | + /* XXX ought to re-generate diffie-hellmen params periodically */ | ||
52 | + if (gnutls_dh_params_init (&dh_params) < 0) | ||
53 | + return 0; | ||
54 | + if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) | ||
55 | + return 0; | ||
56 | + | ||
57 | +#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 | ||
58 | + gnutls_global_set_log_level(10); | ||
59 | + gnutls_global_set_log_function(vnc_debug_gnutls_log); | ||
60 | +#endif | ||
61 | + | ||
62 | + tlsinitialized = 1; | ||
63 | + | ||
64 | + return 1; | ||
65 | +} | ||
66 | + | ||
67 | +static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, | ||
68 | + const void *data, | ||
69 | + size_t len) { | ||
70 | + struct VncState *vs = (struct VncState *)transport; | ||
71 | + int ret; | ||
72 | + | ||
73 | + retry: | ||
74 | + ret = send(vs->csock, data, len, 0); | ||
75 | + if (ret < 0) { | ||
76 | + if (errno == EINTR) | ||
77 | + goto retry; | ||
78 | + return -1; | ||
79 | + } | ||
80 | + return ret; | ||
81 | +} | ||
82 | + | ||
83 | + | ||
84 | +static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, | ||
85 | + void *data, | ||
86 | + size_t len) { | ||
87 | + struct VncState *vs = (struct VncState *)transport; | ||
88 | + int ret; | ||
89 | + | ||
90 | + retry: | ||
91 | + ret = recv(vs->csock, data, len, 0); | ||
92 | + if (ret < 0) { | ||
93 | + if (errno == EINTR) | ||
94 | + goto retry; | ||
95 | + return -1; | ||
96 | + } | ||
97 | + return ret; | ||
98 | +} | ||
99 | + | ||
100 | + | ||
101 | +static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void) | ||
102 | +{ | ||
103 | + gnutls_anon_server_credentials anon_cred; | ||
104 | + int ret; | ||
105 | + | ||
106 | + if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { | ||
107 | + VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); | ||
108 | + return NULL; | ||
109 | + } | ||
110 | + | ||
111 | + gnutls_anon_set_server_dh_params(anon_cred, dh_params); | ||
112 | + | ||
113 | + return anon_cred; | ||
114 | +} | ||
115 | + | ||
116 | + | ||
117 | +static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) | ||
118 | +{ | ||
119 | + gnutls_certificate_credentials_t x509_cred; | ||
120 | + int ret; | ||
121 | + | ||
122 | + if (!vd->tls.x509cacert) { | ||
123 | + VNC_DEBUG("No CA x509 certificate specified\n"); | ||
124 | + return NULL; | ||
125 | + } | ||
126 | + if (!vd->tls.x509cert) { | ||
127 | + VNC_DEBUG("No server x509 certificate specified\n"); | ||
128 | + return NULL; | ||
129 | + } | ||
130 | + if (!vd->tls.x509key) { | ||
131 | + VNC_DEBUG("No server private key specified\n"); | ||
132 | + return NULL; | ||
133 | + } | ||
134 | + | ||
135 | + if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { | ||
136 | + VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); | ||
137 | + return NULL; | ||
138 | + } | ||
139 | + if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, | ||
140 | + vd->tls.x509cacert, | ||
141 | + GNUTLS_X509_FMT_PEM)) < 0) { | ||
142 | + VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); | ||
143 | + gnutls_certificate_free_credentials(x509_cred); | ||
144 | + return NULL; | ||
145 | + } | ||
146 | + | ||
147 | + if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, | ||
148 | + vd->tls.x509cert, | ||
149 | + vd->tls.x509key, | ||
150 | + GNUTLS_X509_FMT_PEM)) < 0) { | ||
151 | + VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); | ||
152 | + gnutls_certificate_free_credentials(x509_cred); | ||
153 | + return NULL; | ||
154 | + } | ||
155 | + | ||
156 | + if (vd->tls.x509cacrl) { | ||
157 | + if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, | ||
158 | + vd->tls.x509cacrl, | ||
159 | + GNUTLS_X509_FMT_PEM)) < 0) { | ||
160 | + VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); | ||
161 | + gnutls_certificate_free_credentials(x509_cred); | ||
162 | + return NULL; | ||
163 | + } | ||
164 | + } | ||
165 | + | ||
166 | + gnutls_certificate_set_dh_params (x509_cred, dh_params); | ||
167 | + | ||
168 | + return x509_cred; | ||
169 | +} | ||
170 | + | ||
171 | + | ||
172 | +int vnc_tls_validate_certificate(struct VncState *vs) | ||
173 | +{ | ||
174 | + int ret; | ||
175 | + unsigned int status; | ||
176 | + const gnutls_datum_t *certs; | ||
177 | + unsigned int nCerts, i; | ||
178 | + time_t now; | ||
179 | + | ||
180 | + VNC_DEBUG("Validating client certificate\n"); | ||
181 | + if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { | ||
182 | + VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); | ||
183 | + return -1; | ||
184 | + } | ||
185 | + | ||
186 | + if ((now = time(NULL)) == ((time_t)-1)) { | ||
187 | + return -1; | ||
188 | + } | ||
189 | + | ||
190 | + if (status != 0) { | ||
191 | + if (status & GNUTLS_CERT_INVALID) | ||
192 | + VNC_DEBUG("The certificate is not trusted.\n"); | ||
193 | + | ||
194 | + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) | ||
195 | + VNC_DEBUG("The certificate hasn't got a known issuer.\n"); | ||
196 | + | ||
197 | + if (status & GNUTLS_CERT_REVOKED) | ||
198 | + VNC_DEBUG("The certificate has been revoked.\n"); | ||
199 | + | ||
200 | + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) | ||
201 | + VNC_DEBUG("The certificate uses an insecure algorithm\n"); | ||
202 | + | ||
203 | + return -1; | ||
204 | + } else { | ||
205 | + VNC_DEBUG("Certificate is valid!\n"); | ||
206 | + } | ||
207 | + | ||
208 | + /* Only support x509 for now */ | ||
209 | + if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) | ||
210 | + return -1; | ||
211 | + | ||
212 | + if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) | ||
213 | + return -1; | ||
214 | + | ||
215 | + for (i = 0 ; i < nCerts ; i++) { | ||
216 | + gnutls_x509_crt_t cert; | ||
217 | + VNC_DEBUG ("Checking certificate chain %d\n", i); | ||
218 | + if (gnutls_x509_crt_init (&cert) < 0) | ||
219 | + return -1; | ||
220 | + | ||
221 | + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { | ||
222 | + gnutls_x509_crt_deinit (cert); | ||
223 | + return -1; | ||
224 | + } | ||
225 | + | ||
226 | + if (gnutls_x509_crt_get_expiration_time (cert) < now) { | ||
227 | + VNC_DEBUG("The certificate has expired\n"); | ||
228 | + gnutls_x509_crt_deinit (cert); | ||
229 | + return -1; | ||
230 | + } | ||
231 | + | ||
232 | + if (gnutls_x509_crt_get_activation_time (cert) > now) { | ||
233 | + VNC_DEBUG("The certificate is not yet activated\n"); | ||
234 | + gnutls_x509_crt_deinit (cert); | ||
235 | + return -1; | ||
236 | + } | ||
237 | + | ||
238 | + if (gnutls_x509_crt_get_activation_time (cert) > now) { | ||
239 | + VNC_DEBUG("The certificate is not yet activated\n"); | ||
240 | + gnutls_x509_crt_deinit (cert); | ||
241 | + return -1; | ||
242 | + } | ||
243 | + | ||
244 | + if (i == 0) { | ||
245 | + size_t dnameSize = 1024; | ||
246 | + vs->tls.dname = qemu_malloc(dnameSize); | ||
247 | + requery: | ||
248 | + if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) { | ||
249 | + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { | ||
250 | + vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize); | ||
251 | + goto requery; | ||
252 | + } | ||
253 | + gnutls_x509_crt_deinit (cert); | ||
254 | + VNC_DEBUG("Cannot get client distinguished name: %s", | ||
255 | + gnutls_strerror (ret)); | ||
256 | + return -1; | ||
257 | + } | ||
258 | + | ||
259 | + if (vs->vd->tls.x509verify) { | ||
260 | + int allow; | ||
261 | + if (!vs->vd->tls.acl) { | ||
262 | + VNC_DEBUG("no ACL activated, allowing access"); | ||
263 | + gnutls_x509_crt_deinit (cert); | ||
264 | + continue; | ||
265 | + } | ||
266 | + | ||
267 | + allow = qemu_acl_party_is_allowed(vs->vd->tls.acl, | ||
268 | + vs->tls.dname); | ||
269 | + | ||
270 | + VNC_DEBUG("TLS x509 ACL check for %s is %s\n", | ||
271 | + vs->tls.dname, allow ? "allowed" : "denied"); | ||
272 | + if (!allow) { | ||
273 | + gnutls_x509_crt_deinit (cert); | ||
274 | + return -1; | ||
275 | + } | ||
276 | + } | ||
277 | + } | ||
278 | + | ||
279 | + gnutls_x509_crt_deinit (cert); | ||
280 | + } | ||
281 | + | ||
282 | + return 0; | ||
283 | +} | ||
284 | + | ||
285 | + | ||
286 | +int vnc_tls_client_setup(struct VncState *vs, | ||
287 | + int needX509Creds) { | ||
288 | + static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; | ||
289 | + static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; | ||
290 | + static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0}; | ||
291 | + static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0}; | ||
292 | + | ||
293 | + VNC_DEBUG("Do TLS setup\n"); | ||
294 | + if (vnc_tls_initialize() < 0) { | ||
295 | + VNC_DEBUG("Failed to init TLS\n"); | ||
296 | + vnc_client_error(vs); | ||
297 | + return -1; | ||
298 | + } | ||
299 | + if (vs->tls.session == NULL) { | ||
300 | + if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { | ||
301 | + vnc_client_error(vs); | ||
302 | + return -1; | ||
303 | + } | ||
304 | + | ||
305 | + if (gnutls_set_default_priority(vs->tls.session) < 0) { | ||
306 | + gnutls_deinit(vs->tls.session); | ||
307 | + vs->tls.session = NULL; | ||
308 | + vnc_client_error(vs); | ||
309 | + return -1; | ||
310 | + } | ||
311 | + | ||
312 | + if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) { | ||
313 | + gnutls_deinit(vs->tls.session); | ||
314 | + vs->tls.session = NULL; | ||
315 | + vnc_client_error(vs); | ||
316 | + return -1; | ||
317 | + } | ||
318 | + | ||
319 | + if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) { | ||
320 | + gnutls_deinit(vs->tls.session); | ||
321 | + vs->tls.session = NULL; | ||
322 | + vnc_client_error(vs); | ||
323 | + return -1; | ||
324 | + } | ||
325 | + | ||
326 | + if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) { | ||
327 | + gnutls_deinit(vs->tls.session); | ||
328 | + vs->tls.session = NULL; | ||
329 | + vnc_client_error(vs); | ||
330 | + return -1; | ||
331 | + } | ||
332 | + | ||
333 | + if (needX509Creds) { | ||
334 | + gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); | ||
335 | + if (!x509_cred) { | ||
336 | + gnutls_deinit(vs->tls.session); | ||
337 | + vs->tls.session = NULL; | ||
338 | + vnc_client_error(vs); | ||
339 | + return -1; | ||
340 | + } | ||
341 | + if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { | ||
342 | + gnutls_deinit(vs->tls.session); | ||
343 | + vs->tls.session = NULL; | ||
344 | + gnutls_certificate_free_credentials(x509_cred); | ||
345 | + vnc_client_error(vs); | ||
346 | + return -1; | ||
347 | + } | ||
348 | + if (vs->vd->tls.x509verify) { | ||
349 | + VNC_DEBUG("Requesting a client certificate\n"); | ||
350 | + gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST); | ||
351 | + } | ||
352 | + | ||
353 | + } else { | ||
354 | + gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); | ||
355 | + if (!anon_cred) { | ||
356 | + gnutls_deinit(vs->tls.session); | ||
357 | + vs->tls.session = NULL; | ||
358 | + vnc_client_error(vs); | ||
359 | + return -1; | ||
360 | + } | ||
361 | + if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) { | ||
362 | + gnutls_deinit(vs->tls.session); | ||
363 | + vs->tls.session = NULL; | ||
364 | + gnutls_anon_free_server_credentials(anon_cred); | ||
365 | + vnc_client_error(vs); | ||
366 | + return -1; | ||
367 | + } | ||
368 | + } | ||
369 | + | ||
370 | + gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); | ||
371 | + gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); | ||
372 | + gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); | ||
373 | + } | ||
374 | + return 0; | ||
375 | +} | ||
376 | + | ||
377 | + | ||
378 | +void vnc_tls_client_cleanup(struct VncState *vs) | ||
379 | +{ | ||
380 | + if (vs->tls.session) { | ||
381 | + gnutls_deinit(vs->tls.session); | ||
382 | + vs->tls.session = NULL; | ||
383 | + } | ||
384 | + vs->tls.wiremode = VNC_WIREMODE_CLEAR; | ||
385 | + free(vs->tls.dname); | ||
386 | +} | ||
387 | + | ||
388 | + | ||
389 | + | ||
390 | +static int vnc_set_x509_credential(VncDisplay *vd, | ||
391 | + const char *certdir, | ||
392 | + const char *filename, | ||
393 | + char **cred, | ||
394 | + int ignoreMissing) | ||
395 | +{ | ||
396 | + struct stat sb; | ||
397 | + | ||
398 | + if (*cred) { | ||
399 | + qemu_free(*cred); | ||
400 | + *cred = NULL; | ||
401 | + } | ||
402 | + | ||
403 | + *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2); | ||
404 | + | ||
405 | + strcpy(*cred, certdir); | ||
406 | + strcat(*cred, "/"); | ||
407 | + strcat(*cred, filename); | ||
408 | + | ||
409 | + VNC_DEBUG("Check %s\n", *cred); | ||
410 | + if (stat(*cred, &sb) < 0) { | ||
411 | + qemu_free(*cred); | ||
412 | + *cred = NULL; | ||
413 | + if (ignoreMissing && errno == ENOENT) | ||
414 | + return 0; | ||
415 | + return -1; | ||
416 | + } | ||
417 | + | ||
418 | + return 0; | ||
419 | +} | ||
420 | + | ||
421 | + | ||
422 | +#define X509_CA_CERT_FILE "ca-cert.pem" | ||
423 | +#define X509_CA_CRL_FILE "ca-crl.pem" | ||
424 | +#define X509_SERVER_KEY_FILE "server-key.pem" | ||
425 | +#define X509_SERVER_CERT_FILE "server-cert.pem" | ||
426 | + | ||
427 | + | ||
428 | +int vnc_tls_set_x509_creds_dir(VncDisplay *vd, | ||
429 | + const char *certdir) | ||
430 | +{ | ||
431 | + if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) | ||
432 | + goto cleanup; | ||
433 | + if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) | ||
434 | + goto cleanup; | ||
435 | + if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) | ||
436 | + goto cleanup; | ||
437 | + if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) | ||
438 | + goto cleanup; | ||
439 | + | ||
440 | + return 0; | ||
441 | + | ||
442 | + cleanup: | ||
443 | + qemu_free(vd->tls.x509cacert); | ||
444 | + qemu_free(vd->tls.x509cacrl); | ||
445 | + qemu_free(vd->tls.x509cert); | ||
446 | + qemu_free(vd->tls.x509key); | ||
447 | + vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; | ||
448 | + return -1; | ||
449 | +} | ||
450 | + |
vnc-tls.h
0 → 100644
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 | + | ||
27 | + | ||
28 | +#ifndef __QEMU_VNC_TLS_H__ | ||
29 | +#define __QEMU_VNC_TLS_H__ | ||
30 | + | ||
31 | +#include <gnutls/gnutls.h> | ||
32 | +#include <gnutls/x509.h> | ||
33 | + | ||
34 | +#include "acl.h" | ||
35 | + | ||
36 | +enum { | ||
37 | + VNC_WIREMODE_CLEAR, | ||
38 | + VNC_WIREMODE_TLS, | ||
39 | +}; | ||
40 | + | ||
41 | +typedef struct VncDisplayTLS VncDisplayTLS; | ||
42 | +typedef struct VncStateTLS VncStateTLS; | ||
43 | + | ||
44 | +/* Server state */ | ||
45 | +struct VncDisplayTLS { | ||
46 | + int x509verify; /* Non-zero if server requests & validates client cert */ | ||
47 | + qemu_acl *acl; | ||
48 | + | ||
49 | + /* Paths to x509 certs/keys */ | ||
50 | + char *x509cacert; | ||
51 | + char *x509cacrl; | ||
52 | + char *x509cert; | ||
53 | + char *x509key; | ||
54 | +}; | ||
55 | + | ||
56 | +/* Per client state */ | ||
57 | +struct VncStateTLS { | ||
58 | + /* Whether data is being TLS encrypted yet */ | ||
59 | + int wiremode; | ||
60 | + gnutls_session_t session; | ||
61 | + | ||
62 | + /* Client's Distinguished Name from the x509 cert */ | ||
63 | + char *dname; | ||
64 | +}; | ||
65 | + | ||
66 | +int vnc_tls_client_setup(VncState *vs, int x509Creds); | ||
67 | +void vnc_tls_client_cleanup(VncState *vs); | ||
68 | + | ||
69 | +int vnc_tls_validate_certificate(VncState *vs); | ||
70 | + | ||
71 | +int vnc_tls_set_x509_creds_dir(VncDisplay *vd, | ||
72 | + const char *path); | ||
73 | + | ||
74 | + | ||
75 | +#endif /* __QEMU_VNC_TLS_H__ */ | ||
76 | + |