686 lines
23 KiB
Diff
686 lines
23 KiB
Diff
From e9712692a0a336bf56025e1bc8056636cbd3931e Mon Sep 17 00:00:00 2001
|
|
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
|
|
Date: Mon, 11 Jan 2016 10:40:31 +0100
|
|
Subject: [PATCH 17/28] vnc: PVE VNC authentication
|
|
|
|
---
|
|
crypto/tlscreds.c | 47 +++++++++++
|
|
crypto/tlscredspriv.h | 2 +
|
|
crypto/tlscredsx509.c | 13 +--
|
|
crypto/tlssession.c | 1 +
|
|
include/crypto/tlscreds.h | 1 +
|
|
include/ui/console.h | 1 +
|
|
qemu-options.hx | 3 +
|
|
ui/vnc-auth-vencrypt.c | 197 ++++++++++++++++++++++++++++++++++++++--------
|
|
ui/vnc.c | 140 +++++++++++++++++++++++++++++++-
|
|
ui/vnc.h | 4 +
|
|
vl.c | 9 +++
|
|
11 files changed, 377 insertions(+), 41 deletions(-)
|
|
|
|
diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
|
|
index a8965531b6..e9ae13ce47 100644
|
|
--- a/crypto/tlscreds.c
|
|
+++ b/crypto/tlscreds.c
|
|
@@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj,
|
|
|
|
|
|
static void
|
|
+qcrypto_tls_creds_prop_set_pve(Object *obj,
|
|
+ bool value,
|
|
+ Error **errp G_GNUC_UNUSED)
|
|
+{
|
|
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
|
+
|
|
+ creds->pve = value;
|
|
+}
|
|
+
|
|
+
|
|
+static bool
|
|
+qcrypto_tls_creds_prop_get_pve(Object *obj,
|
|
+ Error **errp G_GNUC_UNUSED)
|
|
+{
|
|
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
|
+
|
|
+ return creds->pve;
|
|
+}
|
|
+
|
|
+bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds)
|
|
+{
|
|
+ Error *errp = NULL;
|
|
+ return qcrypto_tls_creds_prop_get_pve((Object*)creds, &errp);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
qcrypto_tls_creds_prop_set_dir(Object *obj,
|
|
const char *value,
|
|
Error **errp G_GNUC_UNUSED)
|
|
@@ -250,6 +277,26 @@ qcrypto_tls_creds_init(Object *obj)
|
|
QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
|
|
|
|
creds->verifyPeer = true;
|
|
+ creds->pve = false;
|
|
+
|
|
+ object_property_add_bool(obj, "verify-peer",
|
|
+ qcrypto_tls_creds_prop_get_verify,
|
|
+ qcrypto_tls_creds_prop_set_verify,
|
|
+ NULL);
|
|
+ object_property_add_bool(obj, "pve",
|
|
+ qcrypto_tls_creds_prop_get_pve,
|
|
+ qcrypto_tls_creds_prop_set_pve,
|
|
+ NULL);
|
|
+ object_property_add_str(obj, "dir",
|
|
+ qcrypto_tls_creds_prop_get_dir,
|
|
+ qcrypto_tls_creds_prop_set_dir,
|
|
+ NULL);
|
|
+ object_property_add_enum(obj, "endpoint",
|
|
+ "QCryptoTLSCredsEndpoint",
|
|
+ QCryptoTLSCredsEndpoint_lookup,
|
|
+ qcrypto_tls_creds_prop_get_endpoint,
|
|
+ qcrypto_tls_creds_prop_set_endpoint,
|
|
+ NULL);
|
|
}
|
|
|
|
|
|
diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
|
|
index 13e9b6c0b2..0356acc2c9 100644
|
|
--- a/crypto/tlscredspriv.h
|
|
+++ b/crypto/tlscredspriv.h
|
|
@@ -36,6 +36,8 @@ int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
|
|
gnutls_dh_params_t *dh_params,
|
|
Error **errp);
|
|
|
|
+bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds);
|
|
+
|
|
#endif
|
|
|
|
#endif /* QCRYPTO_TLSCREDSPRIV_H */
|
|
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
|
|
index 50eb54f6bb..09f7364001 100644
|
|
--- a/crypto/tlscredsx509.c
|
|
+++ b/crypto/tlscredsx509.c
|
|
@@ -555,22 +555,23 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
|
*key = NULL, *dhparams = NULL;
|
|
int ret;
|
|
int rv = -1;
|
|
+ bool pve = qcrypto_tls_creds_is_pve(&creds->parent_obj);
|
|
|
|
trace_qcrypto_tls_creds_x509_load(creds,
|
|
creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
|
|
|
|
if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
|
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
- QCRYPTO_TLS_CREDS_X509_CA_CERT,
|
|
+ pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT,
|
|
true, &cacert, errp) < 0 ||
|
|
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
QCRYPTO_TLS_CREDS_X509_CA_CRL,
|
|
false, &cacrl, errp) < 0 ||
|
|
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
- QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
|
|
+ pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
|
|
true, &cert, errp) < 0 ||
|
|
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
- QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
|
|
+ pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
|
|
true, &key, errp) < 0 ||
|
|
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
QCRYPTO_TLS_CREDS_DH_PARAMS,
|
|
@@ -579,13 +580,13 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
|
}
|
|
} else {
|
|
if (qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
- QCRYPTO_TLS_CREDS_X509_CA_CERT,
|
|
+ pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT,
|
|
true, &cacert, errp) < 0 ||
|
|
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
- QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
|
|
+ pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
|
|
false, &cert, errp) < 0 ||
|
|
qcrypto_tls_creds_get_path(&creds->parent_obj,
|
|
- QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
|
|
+ pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
|
|
false, &key, errp) < 0) {
|
|
goto cleanup;
|
|
}
|
|
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
|
|
index 96a02deb69..c453e29cad 100644
|
|
--- a/crypto/tlssession.c
|
|
+++ b/crypto/tlssession.c
|
|
@@ -23,6 +23,7 @@
|
|
#include "crypto/tlscredsanon.h"
|
|
#include "crypto/tlscredsx509.h"
|
|
#include "qapi/error.h"
|
|
+#include "crypto/tlscredspriv.h"
|
|
#include "qemu/acl.h"
|
|
#include "trace.h"
|
|
|
|
diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
|
|
index ad47d88be7..f86d379f26 100644
|
|
--- a/include/crypto/tlscreds.h
|
|
+++ b/include/crypto/tlscreds.h
|
|
@@ -55,6 +55,7 @@ struct QCryptoTLSCreds {
|
|
#endif
|
|
bool verifyPeer;
|
|
char *priority;
|
|
+ bool pve;
|
|
};
|
|
|
|
|
|
diff --git a/include/ui/console.h b/include/ui/console.h
|
|
index d759338816..69f010e1db 100644
|
|
--- a/include/ui/console.h
|
|
+++ b/include/ui/console.h
|
|
@@ -462,6 +462,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen)
|
|
#endif
|
|
|
|
/* vnc.c */
|
|
+void pve_auth_setup(int vmid);
|
|
void vnc_display_init(const char *id);
|
|
void vnc_display_open(const char *id, Error **errp);
|
|
void vnc_display_add_client(const char *id, int csock, bool skipauth);
|
|
diff --git a/qemu-options.hx b/qemu-options.hx
|
|
index 10f0e81f9b..fbd1a1cecf 100644
|
|
--- a/qemu-options.hx
|
|
+++ b/qemu-options.hx
|
|
@@ -513,6 +513,9 @@ STEXI
|
|
@table @option
|
|
ETEXI
|
|
|
|
+DEF("id", HAS_ARG, QEMU_OPTION_id,
|
|
+ "-id n set the VMID\n", QEMU_ARCH_ALL)
|
|
+
|
|
DEF("fda", HAS_ARG, QEMU_OPTION_fda,
|
|
"-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL)
|
|
DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL)
|
|
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
|
|
index ffaab57550..594ca737a9 100644
|
|
--- a/ui/vnc-auth-vencrypt.c
|
|
+++ b/ui/vnc-auth-vencrypt.c
|
|
@@ -28,6 +28,108 @@
|
|
#include "vnc.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/main-loop.h"
|
|
+#include "io/channel-socket.h"
|
|
+
|
|
+static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
|
|
+{
|
|
+ Error *err = NULL;
|
|
+ char username[256];
|
|
+ char passwd[512];
|
|
+
|
|
+ SocketAddress *clientip = qio_channel_socket_get_remote_address(vs->sioc, &err);
|
|
+ if (err) {
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if ((len != (vs->username_len + vs->password_len)) ||
|
|
+ (vs->username_len >= (sizeof(username)-1)) ||
|
|
+ (vs->password_len >= (sizeof(passwd)-1)) ) {
|
|
+ error_setg(&err, "Got unexpected data length");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ strncpy(username, (char *)data, vs->username_len);
|
|
+ username[vs->username_len] = 0;
|
|
+ strncpy(passwd, (char *)data + vs->username_len, vs->password_len);
|
|
+ passwd[vs->password_len] = 0;
|
|
+
|
|
+ VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
|
|
+
|
|
+ if (pve_auth_verify(clientip->u.inet.data->host, username, passwd) == 0) {
|
|
+ vnc_write_u32(vs, 0); /* Accept auth completion */
|
|
+ start_client_init(vs);
|
|
+ qapi_free_SocketAddress(clientip);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ error_setg(&err, "Authentication failed");
|
|
+err:
|
|
+ if (err) {
|
|
+ const char *err_msg = error_get_pretty(err);
|
|
+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err_msg);
|
|
+ vnc_write_u32(vs, 1); /* Reject auth */
|
|
+ if (vs->minor >= 8) {
|
|
+ int elen = strlen(err_msg);
|
|
+ vnc_write_u32(vs, elen);
|
|
+ vnc_write(vs, err_msg, elen);
|
|
+ }
|
|
+ error_free(err);
|
|
+ }
|
|
+ vnc_flush(vs);
|
|
+ vnc_client_error(vs);
|
|
+
|
|
+ qapi_free_SocketAddress(clientip);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len)
|
|
+{
|
|
+ uint32_t ulen = read_u32(data, 0);
|
|
+ uint32_t pwlen = read_u32(data, 4);
|
|
+ const char *err = NULL;
|
|
+
|
|
+ VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen);
|
|
+
|
|
+ if (!ulen) {
|
|
+ err = "No User name.";
|
|
+ goto err;
|
|
+ }
|
|
+ if (ulen >= 255) {
|
|
+ err = "User name too long.";
|
|
+ goto err;
|
|
+ }
|
|
+ if (!pwlen) {
|
|
+ err = "Password too short";
|
|
+ goto err;
|
|
+ }
|
|
+ if (pwlen >= 511) {
|
|
+ err = "Password too long.";
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ vs->username_len = ulen;
|
|
+ vs->password_len = pwlen;
|
|
+
|
|
+ vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen);
|
|
+
|
|
+ return 0;
|
|
+err:
|
|
+ if (err) {
|
|
+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
|
|
+ vnc_write_u32(vs, 1); /* Reject auth */
|
|
+ if (vs->minor >= 8) {
|
|
+ int elen = strlen(err);
|
|
+ vnc_write_u32(vs, elen);
|
|
+ vnc_write(vs, err, elen);
|
|
+ }
|
|
+ }
|
|
+ vnc_flush(vs);
|
|
+ vnc_client_error(vs);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
|
|
static void start_auth_vencrypt_subauth(VncState *vs)
|
|
{
|
|
@@ -39,6 +141,17 @@ static void start_auth_vencrypt_subauth(VncState *vs)
|
|
start_client_init(vs);
|
|
break;
|
|
|
|
+ case VNC_AUTH_VENCRYPT_TLSPLAIN:
|
|
+ case VNC_AUTH_VENCRYPT_X509PLAIN:
|
|
+ VNC_DEBUG("Start TLS auth PLAIN\n");
|
|
+ vnc_read_when(vs, protocol_client_auth_plain_start, 8);
|
|
+ break;
|
|
+
|
|
+ case VNC_AUTH_VENCRYPT_PLAIN:
|
|
+ VNC_DEBUG("Start auth PLAIN\n");
|
|
+ vnc_read_when(vs, protocol_client_auth_plain_start, 8);
|
|
+ break;
|
|
+
|
|
case VNC_AUTH_VENCRYPT_TLSVNC:
|
|
case VNC_AUTH_VENCRYPT_X509VNC:
|
|
VNC_DEBUG("Start TLS auth VNC\n");
|
|
@@ -88,45 +201,64 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
|
|
{
|
|
int auth = read_u32(data, 0);
|
|
|
|
- if (auth != vs->subauth) {
|
|
+ if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
|
|
VNC_DEBUG("Rejecting auth %d\n", auth);
|
|
vnc_write_u8(vs, 0); /* Reject auth */
|
|
vnc_flush(vs);
|
|
vnc_client_error(vs);
|
|
} else {
|
|
- Error *err = NULL;
|
|
- QIOChannelTLS *tls;
|
|
- VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
|
|
- vnc_write_u8(vs, 1); /* Accept auth */
|
|
- vnc_flush(vs);
|
|
-
|
|
- if (vs->ioc_tag) {
|
|
- g_source_remove(vs->ioc_tag);
|
|
- vs->ioc_tag = 0;
|
|
+ if (auth == VNC_AUTH_VENCRYPT_PLAIN) {
|
|
+ vs->subauth = auth;
|
|
+ start_auth_vencrypt_subauth(vs);
|
|
}
|
|
+ else
|
|
+ {
|
|
+ Error *err = NULL;
|
|
+ QIOChannelTLS *tls;
|
|
+ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
|
|
+ vnc_write_u8(vs, 1); /* Accept auth */
|
|
+ vnc_flush(vs);
|
|
|
|
- tls = qio_channel_tls_new_server(
|
|
- vs->ioc,
|
|
- vs->vd->tlscreds,
|
|
- vs->vd->tlsaclname,
|
|
- &err);
|
|
- if (!tls) {
|
|
- VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
|
|
- error_free(err);
|
|
- vnc_client_error(vs);
|
|
- return 0;
|
|
- }
|
|
+ if (vs->ioc_tag) {
|
|
+ g_source_remove(vs->ioc_tag);
|
|
+ vs->ioc_tag = 0;
|
|
+ }
|
|
|
|
- qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
|
|
- VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
|
|
- object_unref(OBJECT(vs->ioc));
|
|
- vs->ioc = QIO_CHANNEL(tls);
|
|
- vs->tls = qio_channel_tls_get_session(tls);
|
|
+ tls = qio_channel_tls_new_server(
|
|
+ vs->ioc,
|
|
+ vs->vd->tlscreds,
|
|
+ vs->vd->tlsaclname,
|
|
+ &err);
|
|
+ if (!tls) {
|
|
+ VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
|
|
+ error_free(err);
|
|
+ vnc_client_error(vs);
|
|
+ return 0;
|
|
+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
|
|
+ NULL,
|
|
+ vs->vd->tlsaclname,
|
|
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
|
|
+ &err);
|
|
+ if (!vs->tls) {
|
|
+ VNC_DEBUG("Failed to setup TLS %s\n",
|
|
+ error_get_pretty(err));
|
|
+ error_free(err);
|
|
+ vnc_client_error(vs);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
|
|
|
|
- qio_channel_tls_handshake(tls,
|
|
- vnc_tls_handshake_done,
|
|
- vs,
|
|
- NULL);
|
|
+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
|
|
+ object_unref(OBJECT(vs->ioc));
|
|
+ vs->ioc = QIO_CHANNEL(tls);
|
|
+ vs->tls = qio_channel_tls_get_session(tls);
|
|
+
|
|
+ qio_channel_tls_handshake(tls,
|
|
+ vnc_tls_handshake_done,
|
|
+ vs,
|
|
+ NULL);
|
|
+ }
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -140,10 +272,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
|
|
vnc_flush(vs);
|
|
vnc_client_error(vs);
|
|
} else {
|
|
- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
|
|
+ VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN);
|
|
vnc_write_u8(vs, 0); /* Accept version */
|
|
- vnc_write_u8(vs, 1); /* Number of sub-auths */
|
|
+ vnc_write_u8(vs, 2); /* Number of sub-auths */
|
|
vnc_write_u32(vs, vs->subauth); /* The supported auth */
|
|
+ vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */
|
|
vnc_flush(vs);
|
|
vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
|
|
}
|
|
diff --git a/ui/vnc.c b/ui/vnc.c
|
|
index eb9420e830..2710614a72 100644
|
|
--- a/ui/vnc.c
|
|
+++ b/ui/vnc.c
|
|
@@ -56,6 +56,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
|
|
#include "vnc_keysym.h"
|
|
#include "crypto/cipher.h"
|
|
|
|
+static int pve_vmid = 0;
|
|
+
|
|
+void pve_auth_setup(int vmid) {
|
|
+ pve_vmid = vmid;
|
|
+}
|
|
+
|
|
+static char *
|
|
+urlencode(char *buf, const char *value)
|
|
+{
|
|
+ static const char *hexchar = "0123456789abcdef";
|
|
+ char *p = buf;
|
|
+ int i;
|
|
+ int l = strlen(value);
|
|
+ for (i = 0; i < l; i++) {
|
|
+ char c = value[i];
|
|
+ if (('a' <= c && c <= 'z') ||
|
|
+ ('A' <= c && c <= 'Z') ||
|
|
+ ('0' <= c && c <= '9')) {
|
|
+ *p++ = c;
|
|
+ } else if (c == 32) {
|
|
+ *p++ = '+';
|
|
+ } else {
|
|
+ *p++ = '%';
|
|
+ *p++ = hexchar[c >> 4];
|
|
+ *p++ = hexchar[c & 15];
|
|
+ }
|
|
+ }
|
|
+ *p = 0;
|
|
+
|
|
+ return p;
|
|
+}
|
|
+
|
|
+int
|
|
+pve_auth_verify(const char *clientip, const char *username, const char *passwd)
|
|
+{
|
|
+ struct sockaddr_in server;
|
|
+
|
|
+ int sfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
+ if (sfd == -1) {
|
|
+ perror("pve_auth_verify: socket failed");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ struct hostent *he;
|
|
+ if ((he = gethostbyname("localhost")) == NULL) {
|
|
+ fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
|
|
+ server.sin_family = AF_INET;
|
|
+ server.sin_port = htons(85);
|
|
+
|
|
+ if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
|
|
+ perror("pve_auth_verify: error connecting to server");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ char buf[8192];
|
|
+ char form[8192];
|
|
+
|
|
+ char *p = form;
|
|
+ p = urlencode(p, "username");
|
|
+ *p++ = '=';
|
|
+ p = urlencode(p, username);
|
|
+
|
|
+ *p++ = '&';
|
|
+ p = urlencode(p, "password");
|
|
+ *p++ = '=';
|
|
+ p = urlencode(p, passwd);
|
|
+
|
|
+ *p++ = '&';
|
|
+ p = urlencode(p, "path");
|
|
+ *p++ = '=';
|
|
+ char authpath[256];
|
|
+ sprintf(authpath, "/vms/%d", pve_vmid);
|
|
+ p = urlencode(p, authpath);
|
|
+
|
|
+ *p++ = '&';
|
|
+ p = urlencode(p, "privs");
|
|
+ *p++ = '=';
|
|
+ p = urlencode(p, "VM.Console");
|
|
+
|
|
+ sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
|
|
+ "Host: localhost:85\n"
|
|
+ "Connection: close\n"
|
|
+ "PVEClientIP: %s\n"
|
|
+ "Content-Type: application/x-www-form-urlencoded\n"
|
|
+ "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
|
|
+ ssize_t len = strlen(buf);
|
|
+ ssize_t sb = send(sfd, buf, len, 0);
|
|
+ if (sb < 0) {
|
|
+ perror("pve_auth_verify: send failed");
|
|
+ goto err;
|
|
+ }
|
|
+ if (sb != len) {
|
|
+ fprintf(stderr, "pve_auth_verify: partial send error\n");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ len = recv(sfd, buf, sizeof(buf) - 1, 0);
|
|
+ if (len < 0) {
|
|
+ perror("pve_auth_verify: recv failed");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ buf[len] = 0;
|
|
+
|
|
+ //printf("DATA:%s\n", buf);
|
|
+
|
|
+ shutdown(sfd, SHUT_RDWR);
|
|
+
|
|
+ return strncmp(buf, "HTTP/1.1 200 OK", 15);
|
|
+
|
|
+err:
|
|
+ shutdown(sfd, SHUT_RDWR);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
static QTAILQ_HEAD(, VncDisplay) vnc_displays =
|
|
QTAILQ_HEAD_INITIALIZER(vnc_displays);
|
|
|
|
@@ -3356,10 +3475,16 @@ vnc_display_setup_auth(int *auth,
|
|
if (password) {
|
|
if (is_x509) {
|
|
VNC_DEBUG("Initializing VNC server with x509 password auth\n");
|
|
- *subauth = VNC_AUTH_VENCRYPT_X509VNC;
|
|
+ if (tlscreds->pve)
|
|
+ *subauth = VNC_AUTH_VENCRYPT_X509PLAIN;
|
|
+ else
|
|
+ *subauth = VNC_AUTH_VENCRYPT_X509VNC;
|
|
} else {
|
|
VNC_DEBUG("Initializing VNC server with TLS password auth\n");
|
|
- *subauth = VNC_AUTH_VENCRYPT_TLSVNC;
|
|
+ if (tlscreds->pve)
|
|
+ *subauth = VNC_AUTH_VENCRYPT_TLSPLAIN;
|
|
+ else
|
|
+ *subauth = VNC_AUTH_VENCRYPT_TLSVNC;
|
|
}
|
|
|
|
} else if (sasl) {
|
|
@@ -3393,6 +3518,7 @@ vnc_display_create_creds(bool x509,
|
|
bool x509verify,
|
|
const char *dir,
|
|
const char *id,
|
|
+ bool pve,
|
|
Error **errp)
|
|
{
|
|
gchar *credsid = g_strdup_printf("tlsvnc%s", id);
|
|
@@ -3408,6 +3534,7 @@ vnc_display_create_creds(bool x509,
|
|
"endpoint", "server",
|
|
"dir", dir,
|
|
"verify-peer", x509verify ? "yes" : "no",
|
|
+ "pve", pve ? "yes" : "no",
|
|
NULL);
|
|
} else {
|
|
creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
|
|
@@ -3415,6 +3542,7 @@ vnc_display_create_creds(bool x509,
|
|
credsid,
|
|
&err,
|
|
"endpoint", "server",
|
|
+ "pve", pve ? "yes" : "no",
|
|
NULL);
|
|
}
|
|
|
|
@@ -3879,12 +4007,17 @@ void vnc_display_open(const char *id, Error **errp)
|
|
}
|
|
} else {
|
|
const char *path;
|
|
- bool tls = false, x509 = false, x509verify = false;
|
|
+ bool tls = false, x509 = false, x509verify = false, pve = false;
|
|
tls = qemu_opt_get_bool(opts, "tls", false);
|
|
path = qemu_opt_get(opts, "x509");
|
|
if (tls || path) {
|
|
if (path) {
|
|
x509 = true;
|
|
+ if (!strcmp(path, "on")) {
|
|
+ /* magic to default to /etc/pve */
|
|
+ path = "/etc/pve";
|
|
+ pve = true;
|
|
+ }
|
|
} else {
|
|
path = qemu_opt_get(opts, "x509verify");
|
|
if (path) {
|
|
@@ -3896,6 +4029,7 @@ void vnc_display_open(const char *id, Error **errp)
|
|
x509verify,
|
|
path,
|
|
vd->id,
|
|
+ pve,
|
|
errp);
|
|
if (!vd->tlscreds) {
|
|
goto fail;
|
|
diff --git a/ui/vnc.h b/ui/vnc.h
|
|
index 694cf32ca9..78d622ab84 100644
|
|
--- a/ui/vnc.h
|
|
+++ b/ui/vnc.h
|
|
@@ -284,6 +284,8 @@ struct VncState
|
|
int auth;
|
|
int subauth; /* Used by VeNCrypt */
|
|
char challenge[VNC_AUTH_CHALLENGE_SIZE];
|
|
+ int username_len;
|
|
+ int password_len;
|
|
QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
|
|
#ifdef CONFIG_VNC_SASL
|
|
VncStateSASL sasl;
|
|
@@ -577,4 +579,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
|
int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
|
|
void vnc_zrle_clear(VncState *vs);
|
|
|
|
+int pve_auth_verify(const char *clientip, const char *username, const char *passwd);
|
|
+
|
|
#endif /* QEMU_VNC_H */
|
|
diff --git a/vl.c b/vl.c
|
|
index d0780a41b4..2496b066b7 100644
|
|
--- a/vl.c
|
|
+++ b/vl.c
|
|
@@ -2947,6 +2947,7 @@ static int qemu_read_default_config_file(void)
|
|
int main(int argc, char **argv, char **envp)
|
|
{
|
|
int i;
|
|
+ long int vm_id_long = 0;
|
|
int snapshot, linux_boot;
|
|
const char *initrd_filename;
|
|
const char *kernel_filename, *kernel_cmdline;
|
|
@@ -3774,6 +3775,14 @@ int main(int argc, char **argv, char **envp)
|
|
exit(1);
|
|
}
|
|
break;
|
|
+ case QEMU_OPTION_id:
|
|
+ vm_id_long = strtol(optarg, (char **) &optarg, 10);
|
|
+ if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) {
|
|
+ fprintf(stderr, "Invalid ID\n");
|
|
+ exit(1);
|
|
+ }
|
|
+ pve_auth_setup(vm_id_long);
|
|
+ break;
|
|
case QEMU_OPTION_vnc:
|
|
vnc_parse(optarg, &error_fatal);
|
|
break;
|
|
--
|
|
2.11.0
|
|
|