1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
# Copyright (c) 2014-2016 Red Hat, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import enum
import ssl
import struct
from oslo_config import cfg
from oslo_log import log as logging
from nova.console.rfb import auth
from nova import exception
from nova.i18n import _
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class AuthVeNCryptSubtype(enum.IntEnum):
"""Possible VeNCrypt subtypes.
From https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst
"""
PLAIN = 256
TLSNONE = 257
TLSVNC = 258
TLSPLAIN = 259
X509NONE = 260
X509VNC = 261
X509PLAIN = 262
X509SASL = 263
TLSSASL = 264
class RFBAuthSchemeVeNCrypt(auth.RFBAuthScheme):
"""A security proxy helper which uses VeNCrypt.
This security proxy helper uses the VeNCrypt security
type to achieve SSL/TLS-secured VNC. It supports both
standard SSL/TLS encryption and SSL/TLS encryption with
x509 authentication.
Refer to https://www.berrange.com/~dan/vencrypt.txt for
a brief overview of the protocol.
"""
def security_type(self):
return auth.AuthType.VENCRYPT
def security_handshake(self, compute_sock):
def recv(num):
b = compute_sock.recv(num)
if len(b) != num:
reason = _("Short read from compute socket, wanted "
"%(wanted)d bytes but got %(got)d") % {
'wanted': num, 'got': len(b)}
raise exception.RFBAuthHandshakeFailed(reason=reason)
return b
# get the VeNCrypt version from the server
maj_ver = ord(recv(1))
min_ver = ord(recv(1))
LOG.debug("Server sent VeNCrypt version "
"%(maj)s.%(min)s", {'maj': maj_ver, 'min': min_ver})
if maj_ver != 0 or min_ver != 2:
reason = _("Only VeNCrypt version 0.2 is supported by this "
"proxy, but the server wanted to use version "
"%(maj)s.%(min)s") % {'maj': maj_ver, 'min': min_ver}
raise exception.RFBAuthHandshakeFailed(reason=reason)
# use version 0.2
compute_sock.sendall(b"\x00\x02")
can_use_version = ord(recv(1))
if can_use_version > 0:
reason = _("Server could not use VeNCrypt version 0.2")
raise exception.RFBAuthHandshakeFailed(reason=reason)
# get the supported auth subtypes
sub_types_cnt = ord(recv(1))
sub_types_raw = recv(sub_types_cnt * auth.SUBTYPE_LENGTH)
sub_types = struct.unpack('!' + str(sub_types_cnt) + 'I',
sub_types_raw)
LOG.debug(
"Server supports VeNCrypt subtypes: %s",
', '.join(
'%d (%s)' % (
AuthVeNCryptSubtype(t).value, AuthVeNCryptSubtype(t).name,
) for t in sub_types
))
# We use X509None as we're only seeking to encrypt the channel (ruling
# out PLAIN) and prevent MITM (ruling out TLS*, which uses trivially
# MITM'd Anonymous Diffie Hellmann (DH) cyphers)
if AuthVeNCryptSubtype.X509NONE not in sub_types:
reason = _(
"Server does not support the %d (%s) VeNCrypt auth subtype"
) % (
AuthVeNCryptSubtype.X509NONE.value,
AuthVeNCryptSubtype.X509NONE.name)
raise exception.RFBAuthHandshakeFailed(reason=reason)
LOG.debug(
"Attempting to use the %d (%s) VeNCrypt auth subtype",
AuthVeNCryptSubtype.X509NONE.value,
AuthVeNCryptSubtype.X509NONE.name)
compute_sock.sendall(struct.pack(
'!I', AuthVeNCryptSubtype.X509NONE))
# NB(sross): the spec is missing a U8 here that's used in
# multiple implementations (e.g. QEMU, GTK-VNC). 1 means
# acceptance, 0 means failure (unlike the rest of RFB)
auth_accepted = ord(recv(1))
if auth_accepted == 0:
reason = _(
"Server didn't accept the requested VeNCrypt auth subtype")
raise exception.RFBAuthHandshakeFailed(reason=reason)
LOG.debug("Server accepted the requested VeNCrypt auth subtype")
if CONF.vnc.vencrypt_client_key and CONF.vnc.vencrypt_client_cert:
client_key = CONF.vnc.vencrypt_client_key
client_cert = CONF.vnc.vencrypt_client_cert
else:
client_key = None
client_cert = None
try:
wrapped_sock = ssl.wrap_socket(
compute_sock,
keyfile=client_key,
certfile=client_cert,
server_side=False,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs=CONF.vnc.vencrypt_ca_certs)
LOG.info("VeNCrypt security handshake accepted")
return wrapped_sock
except ssl.SSLError as e:
reason = _("Error establishing TLS connection to server: %s")
raise exception.RFBAuthHandshakeFailed(reason=reason % str(e))
|