pve-qemu-qoup/debian/patches/pve/0017-vnc-PVE-VNC-authentication.patch
Wolfgang Bumiller 6838f03890 bump version to 2.11.1-1
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2018-02-22 12:40:28 +01:00

683 lines
22 KiB
Diff

From 0000000000000000000000000000000000000000 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] 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 +
qapi-schema.json | 1 +
qemu-options.hx | 3 +
ui/vnc-auth-vencrypt.c | 182 ++++++++++++++++++++++++++++++++++++++--------
ui/vnc.c | 140 ++++++++++++++++++++++++++++++++++-
ui/vnc.h | 4 +
vl.c | 9 +++
12 files changed, 364 insertions(+), 40 deletions(-)
diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index 3cd41035bb..e982da3451 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 580dfc57ee..383e5c88bd 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -466,6 +466,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/qapi-schema.json b/qapi-schema.json
index 348b527681..d2155cb00f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -56,6 +56,7 @@
{ 'pragma': {
# Commands allowed to return a non-dictionary:
'returns-whitelist': [
+ 'get_link_status',
'human-monitor-command',
'qom-get',
'query-migrate-cache-size',
diff --git a/qemu-options.hx b/qemu-options.hx
index 7c054af8f9..07129d55bc 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -583,6 +583,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 7833631275..c42acd3714 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -29,6 +29,108 @@
#include "qapi/error.h"
#include "qemu/main-loop.h"
#include "trace.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.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:
start_auth_vnc(vs);
@@ -90,45 +203,51 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
int auth = read_u32(data, 0);
trace_vnc_auth_vencrypt_subauth(vs, auth);
- if (auth != vs->subauth) {
+ if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
trace_vnc_auth_fail(vs, vs->auth, "Unsupported sub-auth version", "");
vnc_write_u8(vs, 0); /* Reject auth */
vnc_flush(vs);
vnc_client_error(vs);
} else {
- Error *err = NULL;
- QIOChannelTLS *tls;
- 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_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) {
- trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed",
- 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;
+ }
+ tls = qio_channel_tls_new_server(
+ vs->ioc,
+ vs->vd->tlscreds,
+ vs->vd->tlsaclname,
+ &err);
+ if (!tls) {
+ trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed",
+ error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return 0;
+ }
- qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
- object_unref(OBJECT(vs->ioc));
- vs->ioc = QIO_CHANNEL(tls);
- trace_vnc_client_io_wrap(vs, vs->ioc, "tls");
- vs->tls = qio_channel_tls_get_session(tls);
+ qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
+ object_unref(OBJECT(vs->ioc));
+ vs->ioc = QIO_CHANNEL(tls);
+ trace_vnc_client_io_wrap(vs, vs->ioc, "tls");
+ vs->tls = qio_channel_tls_get_session(tls);
- qio_channel_tls_handshake(tls,
- vnc_tls_handshake_done,
- vs,
- NULL);
+ qio_channel_tls_handshake(tls,
+ vnc_tls_handshake_done,
+ vs,
+ NULL);
+ }
}
return 0;
}
@@ -144,8 +263,9 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
vnc_client_error(vs);
} else {
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 4494cb1dd4..1589cbe1b3 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -55,6 +55,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);
@@ -3507,10 +3626,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) {
@@ -3544,6 +3669,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);
@@ -3559,6 +3685,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,
@@ -3566,6 +3693,7 @@ vnc_display_create_creds(bool x509,
credsid,
&err,
"endpoint", "server",
+ "pve", pve ? "yes" : "no",
NULL);
}
@@ -4032,12 +4160,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) {
@@ -4049,6 +4182,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 bbda0540a7..8cc6367ed3 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -290,6 +290,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;
@@ -595,4 +597,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 75fde82180..255d989009 100644
--- a/vl.c
+++ b/vl.c
@@ -3096,6 +3096,7 @@ static void register_global_properties(MachineState *ms)
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;
@@ -3922,6 +3923,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