1 From 981ee6783da6e4d5905fcc972950296187a55c0d Mon Sep 17 00:00:00 2001
2 From: Wolfgang Bumiller <w.bumiller@proxmox.com>
3 Date: Mon, 11 Jan 2016 10:40:31 +0100
4 Subject: [PATCH 17/28] vnc: PVE VNC authentication
7 crypto/tlscreds.c | 47 +++++++++++
8 crypto/tlscredspriv.h | 2 +
9 crypto/tlscredsx509.c | 13 +--
10 crypto/tlssession.c | 1 +
11 include/crypto/tlscreds.h | 1 +
12 include/ui/console.h | 1 +
14 ui/vnc-auth-vencrypt.c | 197 ++++++++++++++++++++++++++++++++++++++--------
15 ui/vnc.c | 140 +++++++++++++++++++++++++++++++-
18 11 files changed, 377 insertions(+), 41 deletions(-)
20 diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
21 index a8965531b6..e9ae13ce47 100644
22 --- a/crypto/tlscreds.c
23 +++ b/crypto/tlscreds.c
24 @@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj,
28 +qcrypto_tls_creds_prop_set_pve(Object *obj,
30 + Error **errp G_GNUC_UNUSED)
32 + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
39 +qcrypto_tls_creds_prop_get_pve(Object *obj,
40 + Error **errp G_GNUC_UNUSED)
42 + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
47 +bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds)
50 + return qcrypto_tls_creds_prop_get_pve((Object*)creds, &errp);
55 qcrypto_tls_creds_prop_set_dir(Object *obj,
57 Error **errp G_GNUC_UNUSED)
58 @@ -250,6 +277,26 @@ qcrypto_tls_creds_init(Object *obj)
59 QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
61 creds->verifyPeer = true;
64 + object_property_add_bool(obj, "verify-peer",
65 + qcrypto_tls_creds_prop_get_verify,
66 + qcrypto_tls_creds_prop_set_verify,
68 + object_property_add_bool(obj, "pve",
69 + qcrypto_tls_creds_prop_get_pve,
70 + qcrypto_tls_creds_prop_set_pve,
72 + object_property_add_str(obj, "dir",
73 + qcrypto_tls_creds_prop_get_dir,
74 + qcrypto_tls_creds_prop_set_dir,
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,
85 diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
86 index 13e9b6c0b2..0356acc2c9 100644
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,
93 +bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds);
97 #endif /* QCRYPTO_TLSCREDSPRIV_H */
98 diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
99 index 50eb54f6bb..09f7364001 100644
100 --- a/crypto/tlscredsx509.c
101 +++ b/crypto/tlscredsx509.c
102 @@ -555,22 +555,23 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
103 *key = NULL, *dhparams = NULL;
106 + bool pve = qcrypto_tls_creds_is_pve(&creds->parent_obj);
108 trace_qcrypto_tls_creds_x509_load(creds,
109 creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
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,
129 @@ -579,13 +580,13 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
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) {
146 diff --git a/crypto/tlssession.c b/crypto/tlssession.c
147 index 96a02deb69..c453e29cad 100644
148 --- a/crypto/tlssession.c
149 +++ b/crypto/tlssession.c
151 #include "crypto/tlscredsanon.h"
152 #include "crypto/tlscredsx509.h"
153 #include "qapi/error.h"
154 +#include "crypto/tlscredspriv.h"
155 #include "qemu/acl.h"
158 diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
159 index ad47d88be7..f86d379f26 100644
160 --- a/include/crypto/tlscreds.h
161 +++ b/include/crypto/tlscreds.h
162 @@ -55,6 +55,7 @@ struct QCryptoTLSCreds {
170 diff --git a/include/ui/console.h b/include/ui/console.h
171 index d759338816..69f010e1db 100644
172 --- a/include/ui/console.h
173 +++ b/include/ui/console.h
174 @@ -462,6 +462,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen)
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
183 index cbcb27da9a..0b1957c034 100644
184 --- a/qemu-options.hx
185 +++ b/qemu-options.hx
186 @@ -513,6 +513,9 @@ STEXI
190 +DEF("id", HAS_ARG, QEMU_OPTION_id,
191 + "-id n set the VMID\n", QEMU_ARCH_ALL)
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
197 index ffaab57550..594ca737a9 100644
198 --- a/ui/vnc-auth-vencrypt.c
199 +++ b/ui/vnc-auth-vencrypt.c
202 #include "qapi/error.h"
203 #include "qemu/main-loop.h"
204 +#include "io/channel-socket.h"
206 +static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
209 + char username[256];
212 + SocketAddress *clientip = qio_channel_socket_get_remote_address(vs->sioc, &err);
217 + if ((len != (vs->username_len + vs->password_len)) ||
218 + (vs->username_len >= (sizeof(username)-1)) ||
219 + (vs->password_len >= (sizeof(passwd)-1)) ) {
220 + error_setg(&err, "Got unexpected data length");
224 + strncpy(username, (char *)data, vs->username_len);
225 + username[vs->username_len] = 0;
226 + strncpy(passwd, (char *)data + vs->username_len, vs->password_len);
227 + passwd[vs->password_len] = 0;
229 + VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
231 + if (pve_auth_verify(clientip->u.inet.data->host, username, passwd) == 0) {
232 + vnc_write_u32(vs, 0); /* Accept auth completion */
233 + start_client_init(vs);
234 + qapi_free_SocketAddress(clientip);
238 + error_setg(&err, "Authentication failed");
241 + const char *err_msg = error_get_pretty(err);
242 + VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err_msg);
243 + vnc_write_u32(vs, 1); /* Reject auth */
244 + if (vs->minor >= 8) {
245 + int elen = strlen(err_msg);
246 + vnc_write_u32(vs, elen);
247 + vnc_write(vs, err_msg, elen);
252 + vnc_client_error(vs);
254 + qapi_free_SocketAddress(clientip);
260 +static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len)
262 + uint32_t ulen = read_u32(data, 0);
263 + uint32_t pwlen = read_u32(data, 4);
264 + const char *err = NULL;
266 + VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen);
269 + err = "No User name.";
273 + err = "User name too long.";
277 + err = "Password too short";
280 + if (pwlen >= 511) {
281 + err = "Password too long.";
285 + vs->username_len = ulen;
286 + vs->password_len = pwlen;
288 + vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen);
293 + VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
294 + vnc_write_u32(vs, 1); /* Reject auth */
295 + if (vs->minor >= 8) {
296 + int elen = strlen(err);
297 + vnc_write_u32(vs, elen);
298 + vnc_write(vs, err, elen);
302 + vnc_client_error(vs);
307 static void start_auth_vencrypt_subauth(VncState *vs)
309 @@ -39,6 +141,17 @@ static void start_auth_vencrypt_subauth(VncState *vs)
310 start_client_init(vs);
313 + case VNC_AUTH_VENCRYPT_TLSPLAIN:
314 + case VNC_AUTH_VENCRYPT_X509PLAIN:
315 + VNC_DEBUG("Start TLS auth PLAIN\n");
316 + vnc_read_when(vs, protocol_client_auth_plain_start, 8);
319 + case VNC_AUTH_VENCRYPT_PLAIN:
320 + VNC_DEBUG("Start auth PLAIN\n");
321 + vnc_read_when(vs, protocol_client_auth_plain_start, 8);
324 case VNC_AUTH_VENCRYPT_TLSVNC:
325 case VNC_AUTH_VENCRYPT_X509VNC:
326 VNC_DEBUG("Start TLS auth VNC\n");
327 @@ -88,45 +201,64 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
329 int auth = read_u32(data, 0);
331 - if (auth != vs->subauth) {
332 + if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
333 VNC_DEBUG("Rejecting auth %d\n", auth);
334 vnc_write_u8(vs, 0); /* Reject auth */
336 vnc_client_error(vs);
339 - QIOChannelTLS *tls;
340 - VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
341 - vnc_write_u8(vs, 1); /* Accept auth */
345 - g_source_remove(vs->ioc_tag);
347 + if (auth == VNC_AUTH_VENCRYPT_PLAIN) {
348 + vs->subauth = auth;
349 + start_auth_vencrypt_subauth(vs);
354 + QIOChannelTLS *tls;
355 + VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
356 + vnc_write_u8(vs, 1); /* Accept auth */
359 - tls = qio_channel_tls_new_server(
362 - vs->vd->tlsaclname,
365 - VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
367 - vnc_client_error(vs);
371 + g_source_remove(vs->ioc_tag);
375 - qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
376 - VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
377 - object_unref(OBJECT(vs->ioc));
378 - vs->ioc = QIO_CHANNEL(tls);
379 - vs->tls = qio_channel_tls_get_session(tls);
380 + tls = qio_channel_tls_new_server(
383 + vs->vd->tlsaclname,
386 + VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
388 + vnc_client_error(vs);
390 + vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
392 + vs->vd->tlsaclname,
393 + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
396 + VNC_DEBUG("Failed to setup TLS %s\n",
397 + error_get_pretty(err));
399 + vnc_client_error(vs);
403 + qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
405 - qio_channel_tls_handshake(tls,
406 - vnc_tls_handshake_done,
409 + VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
410 + object_unref(OBJECT(vs->ioc));
411 + vs->ioc = QIO_CHANNEL(tls);
412 + vs->tls = qio_channel_tls_get_session(tls);
414 + qio_channel_tls_handshake(tls,
415 + vnc_tls_handshake_done,
422 @@ -140,10 +272,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
424 vnc_client_error(vs);
426 - VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
427 + VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN);
428 vnc_write_u8(vs, 0); /* Accept version */
429 - vnc_write_u8(vs, 1); /* Number of sub-auths */
430 + vnc_write_u8(vs, 2); /* Number of sub-auths */
431 vnc_write_u32(vs, vs->subauth); /* The supported auth */
432 + vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */
434 vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
436 diff --git a/ui/vnc.c b/ui/vnc.c
437 index a345bf0d78..42db7e386b 100644
440 @@ -56,6 +56,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
441 #include "vnc_keysym.h"
442 #include "crypto/cipher.h"
444 +static int pve_vmid = 0;
446 +void pve_auth_setup(int vmid) {
451 +urlencode(char *buf, const char *value)
453 + static const char *hexchar = "0123456789abcdef";
456 + int l = strlen(value);
457 + for (i = 0; i < l; i++) {
459 + if (('a' <= c && c <= 'z') ||
460 + ('A' <= c && c <= 'Z') ||
461 + ('0' <= c && c <= '9')) {
463 + } else if (c == 32) {
467 + *p++ = hexchar[c >> 4];
468 + *p++ = hexchar[c & 15];
477 +pve_auth_verify(const char *clientip, const char *username, const char *passwd)
479 + struct sockaddr_in server;
481 + int sfd = socket(AF_INET, SOCK_STREAM, 0);
483 + perror("pve_auth_verify: socket failed");
487 + struct hostent *he;
488 + if ((he = gethostbyname("localhost")) == NULL) {
489 + fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
493 + memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
494 + server.sin_family = AF_INET;
495 + server.sin_port = htons(85);
497 + if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
498 + perror("pve_auth_verify: error connecting to server");
506 + p = urlencode(p, "username");
508 + p = urlencode(p, username);
511 + p = urlencode(p, "password");
513 + p = urlencode(p, passwd);
516 + p = urlencode(p, "path");
518 + char authpath[256];
519 + sprintf(authpath, "/vms/%d", pve_vmid);
520 + p = urlencode(p, authpath);
523 + p = urlencode(p, "privs");
525 + p = urlencode(p, "VM.Console");
527 + sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
528 + "Host: localhost:85\n"
529 + "Connection: close\n"
530 + "PVEClientIP: %s\n"
531 + "Content-Type: application/x-www-form-urlencoded\n"
532 + "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
533 + ssize_t len = strlen(buf);
534 + ssize_t sb = send(sfd, buf, len, 0);
536 + perror("pve_auth_verify: send failed");
540 + fprintf(stderr, "pve_auth_verify: partial send error\n");
544 + len = recv(sfd, buf, sizeof(buf) - 1, 0);
546 + perror("pve_auth_verify: recv failed");
552 + //printf("DATA:%s\n", buf);
554 + shutdown(sfd, SHUT_RDWR);
556 + return strncmp(buf, "HTTP/1.1 200 OK", 15);
559 + shutdown(sfd, SHUT_RDWR);
563 static QTAILQ_HEAD(, VncDisplay) vnc_displays =
564 QTAILQ_HEAD_INITIALIZER(vnc_displays);
566 @@ -3356,10 +3475,16 @@ vnc_display_setup_auth(int *auth,
569 VNC_DEBUG("Initializing VNC server with x509 password auth\n");
570 - *subauth = VNC_AUTH_VENCRYPT_X509VNC;
572 + *subauth = VNC_AUTH_VENCRYPT_X509PLAIN;
574 + *subauth = VNC_AUTH_VENCRYPT_X509VNC;
576 VNC_DEBUG("Initializing VNC server with TLS password auth\n");
577 - *subauth = VNC_AUTH_VENCRYPT_TLSVNC;
579 + *subauth = VNC_AUTH_VENCRYPT_TLSPLAIN;
581 + *subauth = VNC_AUTH_VENCRYPT_TLSVNC;
585 @@ -3393,6 +3518,7 @@ vnc_display_create_creds(bool x509,
592 gchar *credsid = g_strdup_printf("tlsvnc%s", id);
593 @@ -3408,6 +3534,7 @@ vnc_display_create_creds(bool x509,
594 "endpoint", "server",
596 "verify-peer", x509verify ? "yes" : "no",
597 + "pve", pve ? "yes" : "no",
600 creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
601 @@ -3415,6 +3542,7 @@ vnc_display_create_creds(bool x509,
604 "endpoint", "server",
605 + "pve", pve ? "yes" : "no",
609 @@ -3879,12 +4007,17 @@ void vnc_display_open(const char *id, Error **errp)
613 - bool tls = false, x509 = false, x509verify = false;
614 + bool tls = false, x509 = false, x509verify = false, pve = false;
615 tls = qemu_opt_get_bool(opts, "tls", false);
616 path = qemu_opt_get(opts, "x509");
620 + if (!strcmp(path, "on")) {
621 + /* magic to default to /etc/pve */
626 path = qemu_opt_get(opts, "x509verify");
628 @@ -3896,6 +4029,7 @@ void vnc_display_open(const char *id, Error **errp)
636 diff --git a/ui/vnc.h b/ui/vnc.h
637 index 694cf32ca9..78d622ab84 100644
640 @@ -284,6 +284,8 @@ struct VncState
642 int subauth; /* Used by VeNCrypt */
643 char challenge[VNC_AUTH_CHALLENGE_SIZE];
646 QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
647 #ifdef CONFIG_VNC_SASL
649 @@ -577,4 +579,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
650 int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
651 void vnc_zrle_clear(VncState *vs);
653 +int pve_auth_verify(const char *clientip, const char *username, const char *passwd);
655 #endif /* QEMU_VNC_H */
656 diff --git a/vl.c b/vl.c
657 index 5d888cd179..1000a4a259 100644
660 @@ -2947,6 +2947,7 @@ static int qemu_read_default_config_file(void)
661 int main(int argc, char **argv, char **envp)
664 + long int vm_id_long = 0;
665 int snapshot, linux_boot;
666 const char *initrd_filename;
667 const char *kernel_filename, *kernel_cmdline;
668 @@ -3778,6 +3779,14 @@ int main(int argc, char **argv, char **envp)
672 + case QEMU_OPTION_id:
673 + vm_id_long = strtol(optarg, (char **) &optarg, 10);
674 + if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) {
675 + fprintf(stderr, "Invalid ID\n");
678 + pve_auth_setup(vm_id_long);
680 case QEMU_OPTION_vnc:
681 vnc_parse(optarg, &error_fatal);