]>
Commit | Line | Data |
---|---|---|
87ba737b | 1 | From d55b3d4bca482ded41c0c1489626e426007e786c Mon Sep 17 00:00:00 2001 |
ca0fe5f5 WB |
2 | From: Wolfgang Bumiller <w.bumiller@proxmox.com> |
3 | Date: Mon, 11 Jan 2016 10:40:31 +0100 | |
9c3bec39 | 4 | Subject: [PATCH 30/47] PVE VNC authentication |
ca0fe5f5 WB |
5 | |
6 | --- | |
b07d35a5 | 7 | crypto/tlscreds.c | 47 +++++++++++ |
ca0fe5f5 | 8 | crypto/tlscredspriv.h | 2 + |
1a91ab45 | 9 | crypto/tlscredsx509.c | 13 +-- |
ca0fe5f5 WB |
10 | crypto/tlssession.c | 1 + |
11 | include/crypto/tlscreds.h | 1 + | |
12 | include/ui/console.h | 1 + | |
13 | qemu-options.hx | 3 + | |
1a91ab45 | 14 | ui/vnc-auth-vencrypt.c | 196 ++++++++++++++++++++++++++++++++++++++-------- |
b07d35a5 TL |
15 | ui/vnc.c | 140 ++++++++++++++++++++++++++++++++- |
16 | ui/vnc.h | 4 + | |
ca0fe5f5 | 17 | vl.c | 9 +++ |
1a91ab45 | 18 | 11 files changed, 376 insertions(+), 41 deletions(-) |
ca0fe5f5 WB |
19 | |
20 | diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c | |
68a30562 | 21 | index a896553..e9ae13c 100644 |
ca0fe5f5 WB |
22 | --- a/crypto/tlscreds.c |
23 | +++ b/crypto/tlscreds.c | |
6fb04df7 | 24 | @@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj, |
ca0fe5f5 WB |
25 | |
26 | ||
27 | static void | |
28 | +qcrypto_tls_creds_prop_set_pve(Object *obj, | |
29 | + bool value, | |
30 | + Error **errp G_GNUC_UNUSED) | |
31 | +{ | |
32 | + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); | |
33 | + | |
34 | + creds->pve = value; | |
35 | +} | |
36 | + | |
37 | + | |
38 | +static bool | |
39 | +qcrypto_tls_creds_prop_get_pve(Object *obj, | |
40 | + Error **errp G_GNUC_UNUSED) | |
41 | +{ | |
42 | + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); | |
43 | + | |
44 | + return creds->pve; | |
45 | +} | |
46 | + | |
47 | +bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds) | |
48 | +{ | |
49 | + Error *errp = NULL; | |
50 | + return qcrypto_tls_creds_prop_get_pve((Object*)creds, &errp); | |
51 | +} | |
52 | + | |
53 | + | |
54 | +static void | |
55 | qcrypto_tls_creds_prop_set_dir(Object *obj, | |
56 | const char *value, | |
57 | Error **errp G_GNUC_UNUSED) | |
68a30562 | 58 | @@ -250,6 +277,26 @@ qcrypto_tls_creds_init(Object *obj) |
ca0fe5f5 WB |
59 | QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); |
60 | ||
61 | creds->verifyPeer = true; | |
62 | + creds->pve = false; | |
b07d35a5 TL |
63 | + |
64 | + object_property_add_bool(obj, "verify-peer", | |
65 | + qcrypto_tls_creds_prop_get_verify, | |
66 | + qcrypto_tls_creds_prop_set_verify, | |
67 | + NULL); | |
ca0fe5f5 WB |
68 | + object_property_add_bool(obj, "pve", |
69 | + qcrypto_tls_creds_prop_get_pve, | |
70 | + qcrypto_tls_creds_prop_set_pve, | |
71 | + NULL); | |
b07d35a5 TL |
72 | + object_property_add_str(obj, "dir", |
73 | + qcrypto_tls_creds_prop_get_dir, | |
74 | + qcrypto_tls_creds_prop_set_dir, | |
75 | + NULL); | |
76 | + object_property_add_enum(obj, "endpoint", | |
77 | + "QCryptoTLSCredsEndpoint", | |
78 | + QCryptoTLSCredsEndpoint_lookup, | |
79 | + qcrypto_tls_creds_prop_get_endpoint, | |
80 | + qcrypto_tls_creds_prop_set_endpoint, | |
81 | + NULL); | |
82 | } | |
83 | ||
84 | ||
ca0fe5f5 | 85 | diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h |
68a30562 | 86 | index 13e9b6c..0356acc 100644 |
ca0fe5f5 WB |
87 | --- a/crypto/tlscredspriv.h |
88 | +++ b/crypto/tlscredspriv.h | |
89 | @@ -36,6 +36,8 @@ int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, | |
90 | gnutls_dh_params_t *dh_params, | |
91 | Error **errp); | |
92 | ||
93 | +bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds); | |
94 | + | |
95 | #endif | |
96 | ||
68a30562 | 97 | #endif /* QCRYPTO_TLSCREDSPRIV_H */ |
ca0fe5f5 | 98 | diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c |
1a91ab45 | 99 | index 50eb54f..09f7364 100644 |
ca0fe5f5 WB |
100 | --- a/crypto/tlscredsx509.c |
101 | +++ b/crypto/tlscredsx509.c | |
68a30562 | 102 | @@ -555,22 +555,23 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, |
ca0fe5f5 WB |
103 | *key = NULL, *dhparams = NULL; |
104 | int ret; | |
105 | int rv = -1; | |
106 | + bool pve = qcrypto_tls_creds_is_pve(&creds->parent_obj); | |
107 | ||
108 | trace_qcrypto_tls_creds_x509_load(creds, | |
109 | creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); | |
110 | ||
111 | if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { | |
112 | if (qcrypto_tls_creds_get_path(&creds->parent_obj, | |
113 | - QCRYPTO_TLS_CREDS_X509_CA_CERT, | |
114 | + pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT, | |
115 | true, &cacert, errp) < 0 || | |
116 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
117 | QCRYPTO_TLS_CREDS_X509_CA_CRL, | |
118 | false, &cacrl, errp) < 0 || | |
119 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
120 | - QCRYPTO_TLS_CREDS_X509_SERVER_CERT, | |
121 | + pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_SERVER_CERT, | |
122 | true, &cert, errp) < 0 || | |
123 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
124 | - QCRYPTO_TLS_CREDS_X509_SERVER_KEY, | |
125 | + pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_SERVER_KEY, | |
126 | true, &key, errp) < 0 || | |
127 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
128 | QCRYPTO_TLS_CREDS_DH_PARAMS, | |
68a30562 | 129 | @@ -579,13 +580,13 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, |
ca0fe5f5 WB |
130 | } |
131 | } else { | |
132 | if (qcrypto_tls_creds_get_path(&creds->parent_obj, | |
133 | - QCRYPTO_TLS_CREDS_X509_CA_CERT, | |
134 | + pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT, | |
135 | true, &cacert, errp) < 0 || | |
136 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
137 | - QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, | |
138 | + pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, | |
139 | false, &cert, errp) < 0 || | |
140 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
141 | - QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, | |
142 | + pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, | |
143 | false, &key, errp) < 0) { | |
144 | goto cleanup; | |
145 | } | |
146 | diff --git a/crypto/tlssession.c b/crypto/tlssession.c | |
1a91ab45 | 147 | index 96a02de..c453e29 100644 |
ca0fe5f5 WB |
148 | --- a/crypto/tlssession.c |
149 | +++ b/crypto/tlssession.c | |
b07d35a5 | 150 | @@ -23,6 +23,7 @@ |
ca0fe5f5 WB |
151 | #include "crypto/tlscredsanon.h" |
152 | #include "crypto/tlscredsx509.h" | |
b07d35a5 | 153 | #include "qapi/error.h" |
ca0fe5f5 WB |
154 | +#include "crypto/tlscredspriv.h" |
155 | #include "qemu/acl.h" | |
156 | #include "trace.h" | |
157 | ||
158 | diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h | |
68a30562 | 159 | index ad47d88..f86d379 100644 |
ca0fe5f5 WB |
160 | --- a/include/crypto/tlscreds.h |
161 | +++ b/include/crypto/tlscreds.h | |
68a30562 | 162 | @@ -55,6 +55,7 @@ struct QCryptoTLSCreds { |
ca0fe5f5 WB |
163 | #endif |
164 | bool verifyPeer; | |
68a30562 | 165 | char *priority; |
ca0fe5f5 WB |
166 | + bool pve; |
167 | }; | |
168 | ||
169 | ||
170 | diff --git a/include/ui/console.h b/include/ui/console.h | |
1a91ab45 | 171 | index d759338..69f010e 100644 |
ca0fe5f5 WB |
172 | --- a/include/ui/console.h |
173 | +++ b/include/ui/console.h | |
1a91ab45 | 174 | @@ -462,6 +462,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen) |
68a30562 | 175 | #endif |
ca0fe5f5 WB |
176 | |
177 | /* vnc.c */ | |
178 | +void pve_auth_setup(int vmid); | |
179 | void vnc_display_init(const char *id); | |
180 | void vnc_display_open(const char *id, Error **errp); | |
181 | void vnc_display_add_client(const char *id, int csock, bool skipauth); | |
182 | diff --git a/qemu-options.hx b/qemu-options.hx | |
1a91ab45 | 183 | index 10f0e81..fbd1a1c 100644 |
ca0fe5f5 WB |
184 | --- a/qemu-options.hx |
185 | +++ b/qemu-options.hx | |
1a91ab45 | 186 | @@ -513,6 +513,9 @@ STEXI |
ca0fe5f5 WB |
187 | @table @option |
188 | ETEXI | |
189 | ||
190 | +DEF("id", HAS_ARG, QEMU_OPTION_id, | |
191 | + "-id n set the VMID\n", QEMU_ARCH_ALL) | |
192 | + | |
193 | DEF("fda", HAS_ARG, QEMU_OPTION_fda, | |
194 | "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) | |
195 | DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) | |
196 | diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c | |
1a91ab45 | 197 | index ffaab57..de1c194 100644 |
ca0fe5f5 WB |
198 | --- a/ui/vnc-auth-vencrypt.c |
199 | +++ b/ui/vnc-auth-vencrypt.c | |
b07d35a5 | 200 | @@ -28,6 +28,107 @@ |
ca0fe5f5 | 201 | #include "vnc.h" |
b07d35a5 | 202 | #include "qapi/error.h" |
ca0fe5f5 WB |
203 | #include "qemu/main-loop.h" |
204 | +#include "qemu/sockets.h" | |
205 | + | |
206 | +static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len) | |
207 | +{ | |
208 | + const char *err = NULL; | |
209 | + char username[256]; | |
210 | + char passwd[512]; | |
211 | + | |
212 | + char clientip[256]; | |
213 | + clientip[0] = 0; | |
214 | + struct sockaddr_in client; | |
215 | + socklen_t addrlen = sizeof(client); | |
216 | + if (getpeername(vs->csock, &client, &addrlen) == 0) { | |
217 | + inet_ntop(client.sin_family, &client.sin_addr, | |
218 | + clientip, sizeof(clientip)); | |
219 | + } | |
220 | + | |
221 | + if ((len != (vs->username_len + vs->password_len)) || | |
222 | + (vs->username_len >= (sizeof(username)-1)) || | |
223 | + (vs->password_len >= (sizeof(passwd)-1)) ) { | |
224 | + err = "Got unexpected data length"; | |
225 | + goto err; | |
226 | + } | |
227 | + | |
228 | + strncpy(username, (char *)data, vs->username_len); | |
229 | + username[vs->username_len] = 0; | |
230 | + strncpy(passwd, (char *)data + vs->username_len, vs->password_len); | |
231 | + passwd[vs->password_len] = 0; | |
232 | + | |
233 | + VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd); | |
234 | + | |
235 | + if (pve_auth_verify(clientip, username, passwd) == 0) { | |
236 | + vnc_write_u32(vs, 0); /* Accept auth completion */ | |
237 | + start_client_init(vs); | |
238 | + return 0; | |
239 | + } | |
240 | + | |
241 | + err = "Authentication failed"; | |
242 | +err: | |
243 | + if (err) { | |
244 | + VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err); | |
245 | + vnc_write_u32(vs, 1); /* Reject auth */ | |
246 | + if (vs->minor >= 8) { | |
247 | + int elen = strlen(err); | |
248 | + vnc_write_u32(vs, elen); | |
249 | + vnc_write(vs, err, elen); | |
250 | + } | |
251 | + } | |
252 | + vnc_flush(vs); | |
253 | + vnc_client_error(vs); | |
254 | + | |
255 | + return 0; | |
256 | + | |
257 | +} | |
258 | + | |
259 | +static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len) | |
260 | +{ | |
261 | + uint32_t ulen = read_u32(data, 0); | |
262 | + uint32_t pwlen = read_u32(data, 4); | |
263 | + const char *err = NULL; | |
264 | + | |
265 | + VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen); | |
266 | + | |
267 | + if (!ulen) { | |
268 | + err = "No User name."; | |
269 | + goto err; | |
270 | + } | |
271 | + if (ulen >= 255) { | |
272 | + err = "User name too long."; | |
273 | + goto err; | |
274 | + } | |
275 | + if (!pwlen) { | |
276 | + err = "Password too short"; | |
277 | + goto err; | |
278 | + } | |
279 | + if (pwlen >= 511) { | |
280 | + err = "Password too long."; | |
281 | + goto err; | |
282 | + } | |
283 | + | |
284 | + vs->username_len = ulen; | |
285 | + vs->password_len = pwlen; | |
286 | + | |
287 | + vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen); | |
288 | + | |
289 | + return 0; | |
290 | +err: | |
291 | + if (err) { | |
292 | + VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err); | |
293 | + vnc_write_u32(vs, 1); /* Reject auth */ | |
294 | + if (vs->minor >= 8) { | |
295 | + int elen = strlen(err); | |
296 | + vnc_write_u32(vs, elen); | |
297 | + vnc_write(vs, err, elen); | |
298 | + } | |
299 | + } | |
300 | + vnc_flush(vs); | |
301 | + vnc_client_error(vs); | |
302 | + | |
303 | + return 0; | |
304 | +} | |
305 | ||
306 | static void start_auth_vencrypt_subauth(VncState *vs) | |
307 | { | |
6fb04df7 | 308 | @@ -39,6 +140,17 @@ static void start_auth_vencrypt_subauth(VncState *vs) |
ca0fe5f5 WB |
309 | start_client_init(vs); |
310 | break; | |
311 | ||
312 | + case VNC_AUTH_VENCRYPT_TLSPLAIN: | |
313 | + case VNC_AUTH_VENCRYPT_X509PLAIN: | |
314 | + VNC_DEBUG("Start TLS auth PLAIN\n"); | |
315 | + vnc_read_when(vs, protocol_client_auth_plain_start, 8); | |
316 | + break; | |
317 | + | |
318 | + case VNC_AUTH_VENCRYPT_PLAIN: | |
319 | + VNC_DEBUG("Start auth PLAIN\n"); | |
320 | + vnc_read_when(vs, protocol_client_auth_plain_start, 8); | |
321 | + break; | |
322 | + | |
323 | case VNC_AUTH_VENCRYPT_TLSVNC: | |
324 | case VNC_AUTH_VENCRYPT_X509VNC: | |
325 | VNC_DEBUG("Start TLS auth VNC\n"); | |
1a91ab45 | 326 | @@ -88,45 +200,64 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len |
ca0fe5f5 WB |
327 | { |
328 | int auth = read_u32(data, 0); | |
329 | ||
330 | - if (auth != vs->subauth) { | |
331 | + if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) { | |
332 | VNC_DEBUG("Rejecting auth %d\n", auth); | |
333 | vnc_write_u8(vs, 0); /* Reject auth */ | |
334 | vnc_flush(vs); | |
335 | vnc_client_error(vs); | |
336 | } else { | |
337 | - Error *err = NULL; | |
b07d35a5 | 338 | - QIOChannelTLS *tls; |
ca0fe5f5 WB |
339 | - VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); |
340 | - vnc_write_u8(vs, 1); /* Accept auth */ | |
341 | - vnc_flush(vs); | |
b07d35a5 TL |
342 | - |
343 | - if (vs->ioc_tag) { | |
344 | - g_source_remove(vs->ioc_tag); | |
345 | - vs->ioc_tag = 0; | |
ca0fe5f5 WB |
346 | + if (auth == VNC_AUTH_VENCRYPT_PLAIN) { |
347 | + vs->subauth = auth; | |
348 | + start_auth_vencrypt_subauth(vs); | |
349 | } | |
350 | + else | |
351 | + { | |
352 | + Error *err = NULL; | |
b07d35a5 | 353 | + QIOChannelTLS *tls; |
ca0fe5f5 WB |
354 | + VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); |
355 | + vnc_write_u8(vs, 1); /* Accept auth */ | |
356 | + vnc_flush(vs); | |
b07d35a5 TL |
357 | |
358 | - tls = qio_channel_tls_new_server( | |
359 | - vs->ioc, | |
360 | - vs->vd->tlscreds, | |
361 | - vs->vd->tlsaclname, | |
362 | - &err); | |
363 | - if (!tls) { | |
364 | - VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); | |
365 | - error_free(err); | |
366 | - vnc_client_error(vs); | |
367 | - return 0; | |
368 | - } | |
369 | + if (vs->ioc_tag) { | |
370 | + g_source_remove(vs->ioc_tag); | |
371 | + vs->ioc_tag = 0; | |
372 | + } | |
373 | ||
1a91ab45 | 374 | - qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls"); |
b07d35a5 TL |
375 | - VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); |
376 | - object_unref(OBJECT(vs->ioc)); | |
377 | - vs->ioc = QIO_CHANNEL(tls); | |
378 | - vs->tls = qio_channel_tls_get_session(tls); | |
379 | + tls = qio_channel_tls_new_server( | |
380 | + vs->ioc, | |
381 | + vs->vd->tlscreds, | |
382 | + vs->vd->tlsaclname, | |
383 | + &err); | |
384 | + if (!tls) { | |
385 | + VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); | |
ca0fe5f5 WB |
386 | + error_free(err); |
387 | + vnc_client_error(vs); | |
388 | + return 0; | |
b07d35a5 TL |
389 | + vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds, |
390 | + NULL, | |
391 | + vs->vd->tlsaclname, | |
392 | + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, | |
393 | + &err); | |
394 | + if (!vs->tls) { | |
395 | + VNC_DEBUG("Failed to setup TLS %s\n", | |
396 | + error_get_pretty(err)); | |
397 | + error_free(err); | |
398 | + vnc_client_error(vs); | |
399 | + return 0; | |
400 | + } | |
ca0fe5f5 | 401 | + } |
1a91ab45 | 402 | + qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls"); |
ca0fe5f5 | 403 | |
b07d35a5 TL |
404 | - qio_channel_tls_handshake(tls, |
405 | - vnc_tls_handshake_done, | |
406 | - vs, | |
407 | - NULL); | |
ca0fe5f5 | 408 | + VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); |
b07d35a5 TL |
409 | + object_unref(OBJECT(vs->ioc)); |
410 | + vs->ioc = QIO_CHANNEL(tls); | |
411 | + vs->tls = qio_channel_tls_get_session(tls); | |
412 | + | |
413 | + qio_channel_tls_handshake(tls, | |
414 | + vnc_tls_handshake_done, | |
415 | + vs, | |
416 | + NULL); | |
417 | + } | |
ca0fe5f5 WB |
418 | } |
419 | return 0; | |
b07d35a5 | 420 | } |
1a91ab45 | 421 | @@ -140,10 +271,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len |
ca0fe5f5 WB |
422 | vnc_flush(vs); |
423 | vnc_client_error(vs); | |
424 | } else { | |
425 | - VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); | |
426 | + VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN); | |
427 | vnc_write_u8(vs, 0); /* Accept version */ | |
428 | - vnc_write_u8(vs, 1); /* Number of sub-auths */ | |
429 | + vnc_write_u8(vs, 2); /* Number of sub-auths */ | |
430 | vnc_write_u32(vs, vs->subauth); /* The supported auth */ | |
431 | + vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */ | |
432 | vnc_flush(vs); | |
433 | vnc_read_when(vs, protocol_client_vencrypt_auth, 4); | |
434 | } | |
435 | diff --git a/ui/vnc.c b/ui/vnc.c | |
1a91ab45 | 436 | index 039b3ed..a34ba08 100644 |
ca0fe5f5 WB |
437 | --- a/ui/vnc.c |
438 | +++ b/ui/vnc.c | |
1a91ab45 | 439 | @@ -56,6 +56,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; |
ca0fe5f5 WB |
440 | #include "vnc_keysym.h" |
441 | #include "crypto/cipher.h" | |
442 | ||
443 | +static int pve_vmid = 0; | |
444 | + | |
445 | +void pve_auth_setup(int vmid) { | |
446 | + pve_vmid = vmid; | |
447 | +} | |
448 | + | |
449 | +static char * | |
450 | +urlencode(char *buf, const char *value) | |
451 | +{ | |
452 | + static const char *hexchar = "0123456789abcdef"; | |
453 | + char *p = buf; | |
454 | + int i; | |
455 | + int l = strlen(value); | |
456 | + for (i = 0; i < l; i++) { | |
457 | + char c = value[i]; | |
458 | + if (('a' <= c && c <= 'z') || | |
459 | + ('A' <= c && c <= 'Z') || | |
460 | + ('0' <= c && c <= '9')) { | |
461 | + *p++ = c; | |
462 | + } else if (c == 32) { | |
463 | + *p++ = '+'; | |
464 | + } else { | |
465 | + *p++ = '%'; | |
466 | + *p++ = hexchar[c >> 4]; | |
467 | + *p++ = hexchar[c & 15]; | |
468 | + } | |
469 | + } | |
470 | + *p = 0; | |
471 | + | |
472 | + return p; | |
473 | +} | |
474 | + | |
475 | +int | |
476 | +pve_auth_verify(const char *clientip, const char *username, const char *passwd) | |
477 | +{ | |
478 | + struct sockaddr_in server; | |
479 | + | |
480 | + int sfd = socket(AF_INET, SOCK_STREAM, 0); | |
481 | + if (sfd == -1) { | |
482 | + perror("pve_auth_verify: socket failed"); | |
483 | + return -1; | |
484 | + } | |
485 | + | |
486 | + struct hostent *he; | |
487 | + if ((he = gethostbyname("localhost")) == NULL) { | |
488 | + fprintf(stderr, "pve_auth_verify: error resolving hostname\n"); | |
489 | + goto err; | |
490 | + } | |
491 | + | |
492 | + memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); | |
493 | + server.sin_family = AF_INET; | |
494 | + server.sin_port = htons(85); | |
495 | + | |
496 | + if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) { | |
497 | + perror("pve_auth_verify: error connecting to server"); | |
498 | + goto err; | |
499 | + } | |
500 | + | |
501 | + char buf[8192]; | |
502 | + char form[8192]; | |
503 | + | |
504 | + char *p = form; | |
505 | + p = urlencode(p, "username"); | |
506 | + *p++ = '='; | |
507 | + p = urlencode(p, username); | |
508 | + | |
509 | + *p++ = '&'; | |
510 | + p = urlencode(p, "password"); | |
511 | + *p++ = '='; | |
512 | + p = urlencode(p, passwd); | |
513 | + | |
514 | + *p++ = '&'; | |
515 | + p = urlencode(p, "path"); | |
516 | + *p++ = '='; | |
517 | + char authpath[256]; | |
518 | + sprintf(authpath, "/vms/%d", pve_vmid); | |
519 | + p = urlencode(p, authpath); | |
520 | + | |
521 | + *p++ = '&'; | |
522 | + p = urlencode(p, "privs"); | |
523 | + *p++ = '='; | |
524 | + p = urlencode(p, "VM.Console"); | |
525 | + | |
526 | + sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n" | |
527 | + "Host: localhost:85\n" | |
528 | + "Connection: close\n" | |
529 | + "PVEClientIP: %s\n" | |
530 | + "Content-Type: application/x-www-form-urlencoded\n" | |
531 | + "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form); | |
532 | + ssize_t len = strlen(buf); | |
533 | + ssize_t sb = send(sfd, buf, len, 0); | |
534 | + if (sb < 0) { | |
535 | + perror("pve_auth_verify: send failed"); | |
536 | + goto err; | |
537 | + } | |
538 | + if (sb != len) { | |
539 | + fprintf(stderr, "pve_auth_verify: partial send error\n"); | |
540 | + goto err; | |
541 | + } | |
542 | + | |
543 | + len = recv(sfd, buf, sizeof(buf) - 1, 0); | |
544 | + if (len < 0) { | |
545 | + perror("pve_auth_verify: recv failed"); | |
546 | + goto err; | |
547 | + } | |
548 | + | |
549 | + buf[len] = 0; | |
550 | + | |
551 | + //printf("DATA:%s\n", buf); | |
552 | + | |
553 | + shutdown(sfd, SHUT_RDWR); | |
554 | + | |
555 | + return strncmp(buf, "HTTP/1.1 200 OK", 15); | |
556 | + | |
557 | +err: | |
558 | + shutdown(sfd, SHUT_RDWR); | |
559 | + return -1; | |
560 | +} | |
561 | + | |
562 | static QTAILQ_HEAD(, VncDisplay) vnc_displays = | |
563 | QTAILQ_HEAD_INITIALIZER(vnc_displays); | |
564 | ||
1a91ab45 WB |
565 | @@ -3350,10 +3469,16 @@ vnc_display_setup_auth(int *auth, |
566 | if (password) { | |
567 | if (is_x509) { | |
ca0fe5f5 | 568 | VNC_DEBUG("Initializing VNC server with x509 password auth\n"); |
1a91ab45 WB |
569 | - *subauth = VNC_AUTH_VENCRYPT_X509VNC; |
570 | + if (tlscreds->pve) | |
571 | + *subauth = VNC_AUTH_VENCRYPT_X509PLAIN; | |
ca0fe5f5 | 572 | + else |
1a91ab45 WB |
573 | + *subauth = VNC_AUTH_VENCRYPT_X509VNC; |
574 | } else { | |
ca0fe5f5 | 575 | VNC_DEBUG("Initializing VNC server with TLS password auth\n"); |
1a91ab45 WB |
576 | - *subauth = VNC_AUTH_VENCRYPT_TLSVNC; |
577 | + if (tlscreds->pve) | |
578 | + *subauth = VNC_AUTH_VENCRYPT_TLSPLAIN; | |
ca0fe5f5 | 579 | + else |
1a91ab45 WB |
580 | + *subauth = VNC_AUTH_VENCRYPT_TLSVNC; |
581 | } | |
582 | ||
583 | } else if (sasl) { | |
584 | @@ -3387,6 +3512,7 @@ vnc_display_create_creds(bool x509, | |
ca0fe5f5 WB |
585 | bool x509verify, |
586 | const char *dir, | |
587 | const char *id, | |
588 | + bool pve, | |
589 | Error **errp) | |
590 | { | |
591 | gchar *credsid = g_strdup_printf("tlsvnc%s", id); | |
1a91ab45 | 592 | @@ -3402,6 +3528,7 @@ vnc_display_create_creds(bool x509, |
ca0fe5f5 WB |
593 | "endpoint", "server", |
594 | "dir", dir, | |
595 | "verify-peer", x509verify ? "yes" : "no", | |
596 | + "pve", pve ? "yes" : "no", | |
597 | NULL); | |
598 | } else { | |
599 | creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON, | |
1a91ab45 | 600 | @@ -3409,6 +3536,7 @@ vnc_display_create_creds(bool x509, |
ca0fe5f5 WB |
601 | credsid, |
602 | &err, | |
603 | "endpoint", "server", | |
604 | + "pve", pve ? "yes" : "no", | |
605 | NULL); | |
606 | } | |
607 | ||
1a91ab45 | 608 | @@ -3876,12 +4004,17 @@ void vnc_display_open(const char *id, Error **errp) |
ca0fe5f5 WB |
609 | } |
610 | } else { | |
611 | const char *path; | |
612 | - bool tls = false, x509 = false, x509verify = false; | |
613 | + bool tls = false, x509 = false, x509verify = false, pve = false; | |
614 | tls = qemu_opt_get_bool(opts, "tls", false); | |
615 | path = qemu_opt_get(opts, "x509"); | |
616 | if (tls || path) { | |
617 | if (path) { | |
618 | x509 = true; | |
619 | + if (!strcmp(path, "on")) { | |
620 | + /* magic to default to /etc/pve */ | |
621 | + path = "/etc/pve"; | |
622 | + pve = true; | |
623 | + } | |
624 | } else { | |
625 | path = qemu_opt_get(opts, "x509verify"); | |
626 | if (path) { | |
1a91ab45 | 627 | @@ -3893,6 +4026,7 @@ void vnc_display_open(const char *id, Error **errp) |
ca0fe5f5 WB |
628 | x509verify, |
629 | path, | |
1a91ab45 | 630 | vd->id, |
ca0fe5f5 WB |
631 | + pve, |
632 | errp); | |
1a91ab45 | 633 | if (!vd->tlscreds) { |
ca0fe5f5 WB |
634 | goto fail; |
635 | diff --git a/ui/vnc.h b/ui/vnc.h | |
1a91ab45 | 636 | index 694cf32..78d622a 100644 |
ca0fe5f5 WB |
637 | --- a/ui/vnc.h |
638 | +++ b/ui/vnc.h | |
1a91ab45 | 639 | @@ -284,6 +284,8 @@ struct VncState |
ca0fe5f5 WB |
640 | int auth; |
641 | int subauth; /* Used by VeNCrypt */ | |
642 | char challenge[VNC_AUTH_CHALLENGE_SIZE]; | |
643 | + int username_len; | |
644 | + int password_len; | |
b07d35a5 | 645 | QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */ |
ca0fe5f5 WB |
646 | #ifdef CONFIG_VNC_SASL |
647 | VncStateSASL sasl; | |
68a30562 | 648 | @@ -577,4 +579,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); |
ca0fe5f5 WB |
649 | int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); |
650 | void vnc_zrle_clear(VncState *vs); | |
651 | ||
652 | +int pve_auth_verify(const char *clientip, const char *username, const char *passwd); | |
653 | + | |
68a30562 | 654 | #endif /* QEMU_VNC_H */ |
ca0fe5f5 | 655 | diff --git a/vl.c b/vl.c |
1a91ab45 | 656 | index d0780a4..2496b06 100644 |
ca0fe5f5 WB |
657 | --- a/vl.c |
658 | +++ b/vl.c | |
1a91ab45 | 659 | @@ -2947,6 +2947,7 @@ static int qemu_read_default_config_file(void) |
ca0fe5f5 WB |
660 | int main(int argc, char **argv, char **envp) |
661 | { | |
662 | int i; | |
663 | + long int vm_id_long = 0; | |
664 | int snapshot, linux_boot; | |
665 | const char *initrd_filename; | |
666 | const char *kernel_filename, *kernel_cmdline; | |
1a91ab45 | 667 | @@ -3774,6 +3775,14 @@ int main(int argc, char **argv, char **envp) |
ca0fe5f5 WB |
668 | exit(1); |
669 | } | |
670 | break; | |
671 | + case QEMU_OPTION_id: | |
672 | + vm_id_long = strtol(optarg, (char **) &optarg, 10); | |
673 | + if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) { | |
674 | + fprintf(stderr, "Invalid ID\n"); | |
675 | + exit(1); | |
676 | + } | |
677 | + pve_auth_setup(vm_id_long); | |
678 | + break; | |
679 | case QEMU_OPTION_vnc: | |
68a30562 WB |
680 | vnc_parse(optarg, &error_fatal); |
681 | break; | |
ca0fe5f5 WB |
682 | -- |
683 | 2.1.4 | |
684 |