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 | + | ... | ... |