#include "vnc.h"
#include "vnc-jobs.h"
#include "trace.h"
+#include "hw/qdev-core.h"
#include "sysemu/sysemu.h"
#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/sockets.h"
#include "qemu/timer.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
#include "qemu/config-file.h"
-#include "qapi/qapi-events.h"
+#include "qapi/qapi-emit-events.h"
+#include "qapi/qapi-events-ui.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-ui.h"
#include "ui/input.h"
#include "crypto/hash.h"
#include "crypto/tlscredsanon.h"
#include "crypto/tlscredsx509.h"
+#include "crypto/random.h"
#include "qom/object_interfaces.h"
#include "qemu/cutils.h"
#include "io/dns-resolver.h"
}
QTAILQ_FOREACH(vs, &vd->clients, next) {
vnc_lock_output(vs);
+ if (vs->update == VNC_STATE_UPDATE_NONE &&
+ vs->job_update != VNC_STATE_UPDATE_NONE) {
+ /* job aborted before completion */
+ vs->update = vs->job_update;
+ vs->job_update = VNC_STATE_UPDATE_NONE;
+ }
vs->abort = false;
vnc_unlock_output(vs);
}
int bps;
switch (vs->as.fmt) {
default:
- case AUD_FMT_U8:
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_U8:
+ case AUDIO_FORMAT_S8:
bps = 1;
break;
- case AUD_FMT_U16:
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_U16:
+ case AUDIO_FORMAT_S16:
bps = 2;
break;
- case AUD_FMT_U32:
- case AUD_FMT_S32:
+ case AUDIO_FORMAT_U32:
+ case AUDIO_FORMAT_S32:
bps = 4;
break;
}
if (len == 4)
return 10;
switch (read_u8(data, 4)) {
- case 0: vs->as.fmt = AUD_FMT_U8; break;
- case 1: vs->as.fmt = AUD_FMT_S8; break;
- case 2: vs->as.fmt = AUD_FMT_U16; break;
- case 3: vs->as.fmt = AUD_FMT_S16; break;
- case 4: vs->as.fmt = AUD_FMT_U32; break;
- case 5: vs->as.fmt = AUD_FMT_S32; break;
+ case 0: vs->as.fmt = AUDIO_FORMAT_U8; break;
+ case 1: vs->as.fmt = AUDIO_FORMAT_S8; break;
+ case 2: vs->as.fmt = AUDIO_FORMAT_U16; break;
+ case 3: vs->as.fmt = AUDIO_FORMAT_S16; break;
+ case 4: vs->as.fmt = AUDIO_FORMAT_U32; break;
+ case 5: vs->as.fmt = AUDIO_FORMAT_S32; break;
default:
VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
vnc_client_error(vs);
vnc_read_when(vs, protocol_client_init, 1);
}
-static void make_challenge(VncState *vs)
+static void authentication_failed(VncState *vs)
{
- int i;
-
- srand(time(NULL)+getpid()+getpid()*987654+rand());
-
- for (i = 0 ; i < sizeof(vs->challenge) ; i++)
- vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
}
static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
return 0;
reject:
- vnc_write_u32(vs, 1); /* Reject auth */
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_flush(vs);
- vnc_client_error(vs);
+ authentication_failed(vs);
qcrypto_cipher_free(cipher);
return 0;
}
void start_auth_vnc(VncState *vs)
{
- make_challenge(vs);
+ Error *err = NULL;
+
+ if (qcrypto_random_bytes(vs->challenge, sizeof(vs->challenge), &err)) {
+ trace_vnc_auth_fail(vs, vs->auth, "cannot get random bytes",
+ error_get_pretty(err));
+ error_free(err);
+ authentication_failed(vs);
+ return;
+ }
+
/* Send client a 'random' challenge */
vnc_write(vs, vs->challenge, sizeof(vs->challenge));
vnc_flush(vs);
* must pick the one we sent. Verify this */
if (data[0] != vs->auth) { /* Reject auth */
trace_vnc_auth_reject(vs, vs->auth, (int)data[0]);
- vnc_write_u32(vs, 1);
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_client_error(vs);
+ authentication_failed(vs);
} else { /* Accept requested auth */
trace_vnc_auth_start(vs, vs->auth);
switch (vs->auth) {
default: /* Should not be possible, but just in case */
trace_vnc_auth_fail(vs, vs->auth, "Unhandled auth method", "");
- vnc_write_u8(vs, 1);
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_client_error(vs);
+ authentication_failed(vs);
}
}
return 0;
vs->as.freq = 44100;
vs->as.nchannels = 2;
- vs->as.fmt = AUD_FMT_S16;
+ vs->as.fmt = AUDIO_FORMAT_S16;
vs->as.endianness = 0;
qemu_mutex_init(&vs->output_mutex);
object_unparent(OBJECT(vd->tlscreds));
vd->tlscreds = NULL;
}
- g_free(vd->tlsaclname);
- vd->tlsaclname = NULL;
+ if (vd->tlsauthz) {
+ object_unparent(OBJECT(vd->tlsauthz));
+ vd->tlsauthz = NULL;
+ }
+ g_free(vd->tlsauthzid);
+ vd->tlsauthzid = NULL;
if (vd->lock_key_sync) {
qemu_remove_led_event_handler(vd->led);
vd->led = NULL;
}
+#ifdef CONFIG_VNC_SASL
+ if (vd->sasl.authz) {
+ object_unparent(OBJECT(vd->sasl.authz));
+ vd->sasl.authz = NULL;
+ }
+ g_free(vd->sasl.authzid);
+ vd->sasl.authzid = NULL;
+#endif
}
int vnc_display_password(const char *id, const char *password)
},{
.name = "acl",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "tls-authz",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "sasl-authz",
+ .type = QEMU_OPT_STRING,
},{
.name = "lossy",
.type = QEMU_OPT_BOOL,
const char *credid;
bool sasl = false;
int acl = 0;
+ const char *tlsauthz;
+ const char *saslauthz;
int lock_key_sync = 1;
int key_delay_ms;
goto fail;
}
}
+ if (qemu_opt_get(opts, "acl")) {
+ error_report("The 'acl' option to -vnc is deprecated. "
+ "Please use the 'tls-authz' and 'sasl-authz' "
+ "options instead");
+ }
acl = qemu_opt_get_bool(opts, "acl", false);
+ tlsauthz = qemu_opt_get(opts, "tls-authz");
+ if (acl && tlsauthz) {
+ error_setg(errp, "'acl' option is mutually exclusive with the "
+ "'tls-authz' option");
+ goto fail;
+ }
+ if (tlsauthz && !vd->tlscreds) {
+ error_setg(errp, "'tls-authz' provided but TLS is not enabled");
+ goto fail;
+ }
+
+ saslauthz = qemu_opt_get(opts, "sasl-authz");
+ if (acl && saslauthz) {
+ error_setg(errp, "'acl' option is mutually exclusive with the "
+ "'sasl-authz' option");
+ goto fail;
+ }
+ if (saslauthz && !sasl) {
+ error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled");
+ goto fail;
+ }
share = qemu_opt_get(opts, "share");
if (share) {
vd->non_adaptive = true;
}
- if (acl) {
+ if (tlsauthz) {
+ vd->tlsauthzid = g_strdup(tlsauthz);
+ } else if (acl) {
if (strcmp(vd->id, "default") == 0) {
- vd->tlsaclname = g_strdup("vnc.x509dname");
+ vd->tlsauthzid = g_strdup("vnc.x509dname");
} else {
- vd->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vd->id);
+ vd->tlsauthzid = g_strdup_printf("vnc.%s.x509dname", vd->id);
}
- qemu_acl_init(vd->tlsaclname);
+ vd->tlsauthz = QAUTHZ(qauthz_list_new(vd->tlsauthzid,
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort));
}
#ifdef CONFIG_VNC_SASL
- if (acl && sasl) {
- char *aclname;
-
- if (strcmp(vd->id, "default") == 0) {
- aclname = g_strdup("vnc.username");
- } else {
- aclname = g_strdup_printf("vnc.%s.username", vd->id);
+ if (sasl) {
+ if (saslauthz) {
+ vd->sasl.authzid = g_strdup(saslauthz);
+ } else if (acl) {
+ if (strcmp(vd->id, "default") == 0) {
+ vd->sasl.authzid = g_strdup("vnc.username");
+ } else {
+ vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id);
+ }
+ vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid,
+ QAUTHZ_LIST_POLICY_DENY,
+ &error_abort));
}
- vd->sasl.acl = qemu_acl_init(aclname);
- g_free(aclname);
}
#endif