root / ui / vnc-auth-sasl.c @ bd5c51ee
History | View | Annotate | Download (18.6 kB)
1 | 2f9606b3 | aliguori | /*
|
---|---|---|---|
2 | 2f9606b3 | aliguori | * QEMU VNC display driver: SASL auth protocol
|
3 | 2f9606b3 | aliguori | *
|
4 | 2f9606b3 | aliguori | * Copyright (C) 2009 Red Hat, Inc
|
5 | 2f9606b3 | aliguori | *
|
6 | 2f9606b3 | aliguori | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 2f9606b3 | aliguori | * of this software and associated documentation files (the "Software"), to deal
|
8 | 2f9606b3 | aliguori | * in the Software without restriction, including without limitation the rights
|
9 | 2f9606b3 | aliguori | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 2f9606b3 | aliguori | * copies of the Software, and to permit persons to whom the Software is
|
11 | 2f9606b3 | aliguori | * furnished to do so, subject to the following conditions:
|
12 | 2f9606b3 | aliguori | *
|
13 | 2f9606b3 | aliguori | * The above copyright notice and this permission notice shall be included in
|
14 | 2f9606b3 | aliguori | * all copies or substantial portions of the Software.
|
15 | 2f9606b3 | aliguori | *
|
16 | 2f9606b3 | aliguori | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 2f9606b3 | aliguori | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 2f9606b3 | aliguori | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 2f9606b3 | aliguori | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 2f9606b3 | aliguori | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 2f9606b3 | aliguori | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 2f9606b3 | aliguori | * THE SOFTWARE.
|
23 | 2f9606b3 | aliguori | */
|
24 | 2f9606b3 | aliguori | |
25 | 2f9606b3 | aliguori | #include "vnc.h" |
26 | 2f9606b3 | aliguori | |
27 | 2f9606b3 | aliguori | /* Max amount of data we send/recv for SASL steps to prevent DOS */
|
28 | 2f9606b3 | aliguori | #define SASL_DATA_MAX_LEN (1024 * 1024) |
29 | 2f9606b3 | aliguori | |
30 | 2f9606b3 | aliguori | |
31 | 2f9606b3 | aliguori | void vnc_sasl_client_cleanup(VncState *vs)
|
32 | 2f9606b3 | aliguori | { |
33 | 2f9606b3 | aliguori | if (vs->sasl.conn) {
|
34 | ee032ca1 | Stefan Weil | vs->sasl.runSSF = false;
|
35 | ee032ca1 | Stefan Weil | vs->sasl.wantSSF = false;
|
36 | ee032ca1 | Stefan Weil | vs->sasl.waitWriteSSF = 0;
|
37 | 28a76be8 | aliguori | vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
|
38 | 28a76be8 | aliguori | vs->sasl.encoded = NULL;
|
39 | ae878b17 | Stefan Weil | g_free(vs->sasl.username); |
40 | 302d9d6f | Markus Armbruster | g_free(vs->sasl.mechlist); |
41 | 28a76be8 | aliguori | vs->sasl.username = vs->sasl.mechlist = NULL;
|
42 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
43 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
44 | 2f9606b3 | aliguori | } |
45 | 2f9606b3 | aliguori | } |
46 | 2f9606b3 | aliguori | |
47 | 2f9606b3 | aliguori | |
48 | 2f9606b3 | aliguori | long vnc_client_write_sasl(VncState *vs)
|
49 | 2f9606b3 | aliguori | { |
50 | 2f9606b3 | aliguori | long ret;
|
51 | 2f9606b3 | aliguori | |
52 | bc47d201 | Corentin Chary | VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
|
53 | bc47d201 | Corentin Chary | "Encoded: %p size %d offset %d\n",
|
54 | 28a76be8 | aliguori | vs->output.buffer, vs->output.capacity, vs->output.offset, |
55 | 28a76be8 | aliguori | vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset); |
56 | 2f9606b3 | aliguori | |
57 | 2f9606b3 | aliguori | if (!vs->sasl.encoded) {
|
58 | 28a76be8 | aliguori | int err;
|
59 | 28a76be8 | aliguori | err = sasl_encode(vs->sasl.conn, |
60 | 28a76be8 | aliguori | (char *)vs->output.buffer,
|
61 | 28a76be8 | aliguori | vs->output.offset, |
62 | 28a76be8 | aliguori | (const char **)&vs->sasl.encoded, |
63 | 28a76be8 | aliguori | &vs->sasl.encodedLength); |
64 | 28a76be8 | aliguori | if (err != SASL_OK)
|
65 | 28a76be8 | aliguori | return vnc_client_io_error(vs, -1, EIO); |
66 | 28a76be8 | aliguori | |
67 | 28a76be8 | aliguori | vs->sasl.encodedOffset = 0;
|
68 | 2f9606b3 | aliguori | } |
69 | 2f9606b3 | aliguori | |
70 | 2f9606b3 | aliguori | ret = vnc_client_write_buf(vs, |
71 | 28a76be8 | aliguori | vs->sasl.encoded + vs->sasl.encodedOffset, |
72 | 28a76be8 | aliguori | vs->sasl.encodedLength - vs->sasl.encodedOffset); |
73 | 2f9606b3 | aliguori | if (!ret)
|
74 | 28a76be8 | aliguori | return 0; |
75 | 2f9606b3 | aliguori | |
76 | 2f9606b3 | aliguori | vs->sasl.encodedOffset += ret; |
77 | 2f9606b3 | aliguori | if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
|
78 | 28a76be8 | aliguori | vs->output.offset = 0;
|
79 | 28a76be8 | aliguori | vs->sasl.encoded = NULL;
|
80 | 28a76be8 | aliguori | vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
|
81 | 2f9606b3 | aliguori | } |
82 | 2f9606b3 | aliguori | |
83 | 2f9606b3 | aliguori | /* Can't merge this block with one above, because
|
84 | 2f9606b3 | aliguori | * someone might have written more unencrypted
|
85 | 2f9606b3 | aliguori | * data in vs->output while we were processing
|
86 | 2f9606b3 | aliguori | * SASL encoded output
|
87 | 2f9606b3 | aliguori | */
|
88 | 2f9606b3 | aliguori | if (vs->output.offset == 0) { |
89 | 28a76be8 | aliguori | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); |
90 | 2f9606b3 | aliguori | } |
91 | 2f9606b3 | aliguori | |
92 | 2f9606b3 | aliguori | return ret;
|
93 | 2f9606b3 | aliguori | } |
94 | 2f9606b3 | aliguori | |
95 | 2f9606b3 | aliguori | |
96 | 2f9606b3 | aliguori | long vnc_client_read_sasl(VncState *vs)
|
97 | 2f9606b3 | aliguori | { |
98 | 2f9606b3 | aliguori | long ret;
|
99 | 2f9606b3 | aliguori | uint8_t encoded[4096];
|
100 | 2f9606b3 | aliguori | const char *decoded; |
101 | 2f9606b3 | aliguori | unsigned int decodedLen; |
102 | 2f9606b3 | aliguori | int err;
|
103 | 2f9606b3 | aliguori | |
104 | 2f9606b3 | aliguori | ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
|
105 | 2f9606b3 | aliguori | if (!ret)
|
106 | 28a76be8 | aliguori | return 0; |
107 | 2f9606b3 | aliguori | |
108 | 2f9606b3 | aliguori | err = sasl_decode(vs->sasl.conn, |
109 | 28a76be8 | aliguori | (char *)encoded, ret,
|
110 | 28a76be8 | aliguori | &decoded, &decodedLen); |
111 | 2f9606b3 | aliguori | |
112 | 2f9606b3 | aliguori | if (err != SASL_OK)
|
113 | 28a76be8 | aliguori | return vnc_client_io_error(vs, -1, -EIO); |
114 | 2f9606b3 | aliguori | VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
|
115 | 28a76be8 | aliguori | encoded, ret, decoded, decodedLen); |
116 | 2f9606b3 | aliguori | buffer_reserve(&vs->input, decodedLen); |
117 | 2f9606b3 | aliguori | buffer_append(&vs->input, decoded, decodedLen); |
118 | 2f9606b3 | aliguori | return decodedLen;
|
119 | 2f9606b3 | aliguori | } |
120 | 2f9606b3 | aliguori | |
121 | 2f9606b3 | aliguori | |
122 | 2f9606b3 | aliguori | static int vnc_auth_sasl_check_access(VncState *vs) |
123 | 2f9606b3 | aliguori | { |
124 | 2f9606b3 | aliguori | const void *val; |
125 | 2f9606b3 | aliguori | int err;
|
126 | 76655d6d | aliguori | int allow;
|
127 | 2f9606b3 | aliguori | |
128 | 2f9606b3 | aliguori | err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); |
129 | 2f9606b3 | aliguori | if (err != SASL_OK) {
|
130 | 28a76be8 | aliguori | VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
|
131 | 28a76be8 | aliguori | err, sasl_errstring(err, NULL, NULL)); |
132 | 28a76be8 | aliguori | return -1; |
133 | 2f9606b3 | aliguori | } |
134 | 2f9606b3 | aliguori | if (val == NULL) { |
135 | 28a76be8 | aliguori | VNC_DEBUG("no client username was found, denying access\n");
|
136 | 28a76be8 | aliguori | return -1; |
137 | 2f9606b3 | aliguori | } |
138 | 2f9606b3 | aliguori | VNC_DEBUG("SASL client username %s\n", (const char *)val); |
139 | 2f9606b3 | aliguori | |
140 | 7267c094 | Anthony Liguori | vs->sasl.username = g_strdup((const char*)val); |
141 | 2f9606b3 | aliguori | |
142 | 76655d6d | aliguori | if (vs->vd->sasl.acl == NULL) { |
143 | 28a76be8 | aliguori | VNC_DEBUG("no ACL activated, allowing access\n");
|
144 | 28a76be8 | aliguori | return 0; |
145 | 76655d6d | aliguori | } |
146 | 76655d6d | aliguori | |
147 | 76655d6d | aliguori | allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username); |
148 | 76655d6d | aliguori | |
149 | 76655d6d | aliguori | VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
|
150 | 28a76be8 | aliguori | allow ? "allowed" : "denied"); |
151 | 76655d6d | aliguori | return allow ? 0 : -1; |
152 | 2f9606b3 | aliguori | } |
153 | 2f9606b3 | aliguori | |
154 | 2f9606b3 | aliguori | static int vnc_auth_sasl_check_ssf(VncState *vs) |
155 | 2f9606b3 | aliguori | { |
156 | 2f9606b3 | aliguori | const void *val; |
157 | 2f9606b3 | aliguori | int err, ssf;
|
158 | 2f9606b3 | aliguori | |
159 | 2f9606b3 | aliguori | if (!vs->sasl.wantSSF)
|
160 | 28a76be8 | aliguori | return 1; |
161 | 2f9606b3 | aliguori | |
162 | 2f9606b3 | aliguori | err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val); |
163 | 2f9606b3 | aliguori | if (err != SASL_OK)
|
164 | 28a76be8 | aliguori | return 0; |
165 | 2f9606b3 | aliguori | |
166 | 2f9606b3 | aliguori | ssf = *(const int *)val; |
167 | 2f9606b3 | aliguori | VNC_DEBUG("negotiated an SSF of %d\n", ssf);
|
168 | 2f9606b3 | aliguori | if (ssf < 56) |
169 | 28a76be8 | aliguori | return 0; /* 56 is good for Kerberos */ |
170 | 2f9606b3 | aliguori | |
171 | 2f9606b3 | aliguori | /* Only setup for read initially, because we're about to send an RPC
|
172 | 2f9606b3 | aliguori | * reply which must be in plain text. When the next incoming RPC
|
173 | 2f9606b3 | aliguori | * arrives, we'll switch on writes too
|
174 | 2f9606b3 | aliguori | *
|
175 | 2f9606b3 | aliguori | * cf qemudClientReadSASL in qemud.c
|
176 | 2f9606b3 | aliguori | */
|
177 | 2f9606b3 | aliguori | vs->sasl.runSSF = 1;
|
178 | 2f9606b3 | aliguori | |
179 | 2f9606b3 | aliguori | /* We have a SSF that's good enough */
|
180 | 2f9606b3 | aliguori | return 1; |
181 | 2f9606b3 | aliguori | } |
182 | 2f9606b3 | aliguori | |
183 | 2f9606b3 | aliguori | /*
|
184 | 2f9606b3 | aliguori | * Step Msg
|
185 | 2f9606b3 | aliguori | *
|
186 | 2f9606b3 | aliguori | * Input from client:
|
187 | 2f9606b3 | aliguori | *
|
188 | 2f9606b3 | aliguori | * u32 clientin-length
|
189 | 2f9606b3 | aliguori | * u8-array clientin-string
|
190 | 2f9606b3 | aliguori | *
|
191 | 2f9606b3 | aliguori | * Output to client:
|
192 | 2f9606b3 | aliguori | *
|
193 | 2f9606b3 | aliguori | * u32 serverout-length
|
194 | 2f9606b3 | aliguori | * u8-array serverout-strin
|
195 | 2f9606b3 | aliguori | * u8 continue
|
196 | 2f9606b3 | aliguori | */
|
197 | 2f9606b3 | aliguori | |
198 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len); |
199 | 2f9606b3 | aliguori | |
200 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len) |
201 | 2f9606b3 | aliguori | { |
202 | 2f9606b3 | aliguori | uint32_t datalen = len; |
203 | 2f9606b3 | aliguori | const char *serverout; |
204 | 2f9606b3 | aliguori | unsigned int serveroutlen; |
205 | 2f9606b3 | aliguori | int err;
|
206 | 2f9606b3 | aliguori | char *clientdata = NULL; |
207 | 2f9606b3 | aliguori | |
208 | 2f9606b3 | aliguori | /* NB, distinction of NULL vs "" is *critical* in SASL */
|
209 | 2f9606b3 | aliguori | if (datalen) {
|
210 | 28a76be8 | aliguori | clientdata = (char*)data;
|
211 | 28a76be8 | aliguori | clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */ |
212 | 28a76be8 | aliguori | datalen--; /* Don't count NULL byte when passing to _start() */
|
213 | 2f9606b3 | aliguori | } |
214 | 2f9606b3 | aliguori | |
215 | 2f9606b3 | aliguori | VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
|
216 | 28a76be8 | aliguori | clientdata, datalen); |
217 | 2f9606b3 | aliguori | err = sasl_server_step(vs->sasl.conn, |
218 | 28a76be8 | aliguori | clientdata, |
219 | 28a76be8 | aliguori | datalen, |
220 | 28a76be8 | aliguori | &serverout, |
221 | 28a76be8 | aliguori | &serveroutlen); |
222 | 2f9606b3 | aliguori | if (err != SASL_OK &&
|
223 | 28a76be8 | aliguori | err != SASL_CONTINUE) { |
224 | 28a76be8 | aliguori | VNC_DEBUG("sasl step failed %d (%s)\n",
|
225 | 28a76be8 | aliguori | err, sasl_errdetail(vs->sasl.conn)); |
226 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
227 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
228 | 28a76be8 | aliguori | goto authabort;
|
229 | 2f9606b3 | aliguori | } |
230 | 2f9606b3 | aliguori | |
231 | 2f9606b3 | aliguori | if (serveroutlen > SASL_DATA_MAX_LEN) {
|
232 | 28a76be8 | aliguori | VNC_DEBUG("sasl step reply data too long %d\n",
|
233 | 28a76be8 | aliguori | serveroutlen); |
234 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
235 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
236 | 28a76be8 | aliguori | goto authabort;
|
237 | 2f9606b3 | aliguori | } |
238 | 2f9606b3 | aliguori | |
239 | 2f9606b3 | aliguori | VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
|
240 | 28a76be8 | aliguori | serveroutlen, serverout ? 0 : 1); |
241 | 2f9606b3 | aliguori | |
242 | 2f9606b3 | aliguori | if (serveroutlen) {
|
243 | 28a76be8 | aliguori | vnc_write_u32(vs, serveroutlen + 1);
|
244 | 28a76be8 | aliguori | vnc_write(vs, serverout, serveroutlen + 1);
|
245 | 2f9606b3 | aliguori | } else {
|
246 | 28a76be8 | aliguori | vnc_write_u32(vs, 0);
|
247 | 2f9606b3 | aliguori | } |
248 | 2f9606b3 | aliguori | |
249 | 2f9606b3 | aliguori | /* Whether auth is complete */
|
250 | 2f9606b3 | aliguori | vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); |
251 | 2f9606b3 | aliguori | |
252 | 2f9606b3 | aliguori | if (err == SASL_CONTINUE) {
|
253 | 28a76be8 | aliguori | VNC_DEBUG("%s", "Authentication must continue\n"); |
254 | 28a76be8 | aliguori | /* Wait for step length */
|
255 | 28a76be8 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
|
256 | 2f9606b3 | aliguori | } else {
|
257 | 28a76be8 | aliguori | if (!vnc_auth_sasl_check_ssf(vs)) {
|
258 | 28a76be8 | aliguori | VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
|
259 | 28a76be8 | aliguori | goto authreject;
|
260 | 28a76be8 | aliguori | } |
261 | 28a76be8 | aliguori | |
262 | 28a76be8 | aliguori | /* Check username whitelist ACL */
|
263 | 28a76be8 | aliguori | if (vnc_auth_sasl_check_access(vs) < 0) { |
264 | 28a76be8 | aliguori | VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
|
265 | 28a76be8 | aliguori | goto authreject;
|
266 | 28a76be8 | aliguori | } |
267 | 28a76be8 | aliguori | |
268 | 28a76be8 | aliguori | VNC_DEBUG("Authentication successful %d\n", vs->csock);
|
269 | 28a76be8 | aliguori | vnc_write_u32(vs, 0); /* Accept auth */ |
270 | 28a76be8 | aliguori | /*
|
271 | 28a76be8 | aliguori | * Delay writing in SSF encoded mode until pending output
|
272 | 28a76be8 | aliguori | * buffer is written
|
273 | 28a76be8 | aliguori | */
|
274 | 28a76be8 | aliguori | if (vs->sasl.runSSF)
|
275 | 28a76be8 | aliguori | vs->sasl.waitWriteSSF = vs->output.offset; |
276 | 28a76be8 | aliguori | start_client_init(vs); |
277 | 2f9606b3 | aliguori | } |
278 | 2f9606b3 | aliguori | |
279 | 2f9606b3 | aliguori | return 0; |
280 | 2f9606b3 | aliguori | |
281 | 2f9606b3 | aliguori | authreject:
|
282 | 2f9606b3 | aliguori | vnc_write_u32(vs, 1); /* Reject auth */ |
283 | 2f9606b3 | aliguori | vnc_write_u32(vs, sizeof("Authentication failed")); |
284 | 2f9606b3 | aliguori | vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); |
285 | 2f9606b3 | aliguori | vnc_flush(vs); |
286 | 2f9606b3 | aliguori | vnc_client_error(vs); |
287 | 2f9606b3 | aliguori | return -1; |
288 | 2f9606b3 | aliguori | |
289 | 2f9606b3 | aliguori | authabort:
|
290 | 2f9606b3 | aliguori | vnc_client_error(vs); |
291 | 2f9606b3 | aliguori | return -1; |
292 | 2f9606b3 | aliguori | } |
293 | 2f9606b3 | aliguori | |
294 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len) |
295 | 2f9606b3 | aliguori | { |
296 | 2f9606b3 | aliguori | uint32_t steplen = read_u32(data, 0);
|
297 | 2f9606b3 | aliguori | VNC_DEBUG("Got client step len %d\n", steplen);
|
298 | 2f9606b3 | aliguori | if (steplen > SASL_DATA_MAX_LEN) {
|
299 | 28a76be8 | aliguori | VNC_DEBUG("Too much SASL data %d\n", steplen);
|
300 | 28a76be8 | aliguori | vnc_client_error(vs); |
301 | 28a76be8 | aliguori | return -1; |
302 | 2f9606b3 | aliguori | } |
303 | 2f9606b3 | aliguori | |
304 | 2f9606b3 | aliguori | if (steplen == 0) |
305 | 28a76be8 | aliguori | return protocol_client_auth_sasl_step(vs, NULL, 0); |
306 | 2f9606b3 | aliguori | else
|
307 | 28a76be8 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_step, steplen); |
308 | 2f9606b3 | aliguori | return 0; |
309 | 2f9606b3 | aliguori | } |
310 | 2f9606b3 | aliguori | |
311 | 2f9606b3 | aliguori | /*
|
312 | 2f9606b3 | aliguori | * Start Msg
|
313 | 2f9606b3 | aliguori | *
|
314 | 2f9606b3 | aliguori | * Input from client:
|
315 | 2f9606b3 | aliguori | *
|
316 | 2f9606b3 | aliguori | * u32 clientin-length
|
317 | 2f9606b3 | aliguori | * u8-array clientin-string
|
318 | 2f9606b3 | aliguori | *
|
319 | 2f9606b3 | aliguori | * Output to client:
|
320 | 2f9606b3 | aliguori | *
|
321 | 2f9606b3 | aliguori | * u32 serverout-length
|
322 | 2f9606b3 | aliguori | * u8-array serverout-strin
|
323 | 2f9606b3 | aliguori | * u8 continue
|
324 | 2f9606b3 | aliguori | */
|
325 | 2f9606b3 | aliguori | |
326 | 2f9606b3 | aliguori | #define SASL_DATA_MAX_LEN (1024 * 1024) |
327 | 2f9606b3 | aliguori | |
328 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len) |
329 | 2f9606b3 | aliguori | { |
330 | 2f9606b3 | aliguori | uint32_t datalen = len; |
331 | 2f9606b3 | aliguori | const char *serverout; |
332 | 2f9606b3 | aliguori | unsigned int serveroutlen; |
333 | 2f9606b3 | aliguori | int err;
|
334 | 2f9606b3 | aliguori | char *clientdata = NULL; |
335 | 2f9606b3 | aliguori | |
336 | 2f9606b3 | aliguori | /* NB, distinction of NULL vs "" is *critical* in SASL */
|
337 | 2f9606b3 | aliguori | if (datalen) {
|
338 | 28a76be8 | aliguori | clientdata = (char*)data;
|
339 | 28a76be8 | aliguori | clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ |
340 | 28a76be8 | aliguori | datalen--; /* Don't count NULL byte when passing to _start() */
|
341 | 2f9606b3 | aliguori | } |
342 | 2f9606b3 | aliguori | |
343 | 2f9606b3 | aliguori | VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
|
344 | 28a76be8 | aliguori | vs->sasl.mechlist, clientdata, datalen); |
345 | 2f9606b3 | aliguori | err = sasl_server_start(vs->sasl.conn, |
346 | 28a76be8 | aliguori | vs->sasl.mechlist, |
347 | 28a76be8 | aliguori | clientdata, |
348 | 28a76be8 | aliguori | datalen, |
349 | 28a76be8 | aliguori | &serverout, |
350 | 28a76be8 | aliguori | &serveroutlen); |
351 | 2f9606b3 | aliguori | if (err != SASL_OK &&
|
352 | 28a76be8 | aliguori | err != SASL_CONTINUE) { |
353 | 28a76be8 | aliguori | VNC_DEBUG("sasl start failed %d (%s)\n",
|
354 | 28a76be8 | aliguori | err, sasl_errdetail(vs->sasl.conn)); |
355 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
356 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
357 | 28a76be8 | aliguori | goto authabort;
|
358 | 2f9606b3 | aliguori | } |
359 | 2f9606b3 | aliguori | if (serveroutlen > SASL_DATA_MAX_LEN) {
|
360 | 28a76be8 | aliguori | VNC_DEBUG("sasl start reply data too long %d\n",
|
361 | 28a76be8 | aliguori | serveroutlen); |
362 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
363 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
364 | 28a76be8 | aliguori | goto authabort;
|
365 | 2f9606b3 | aliguori | } |
366 | 2f9606b3 | aliguori | |
367 | 2f9606b3 | aliguori | VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
|
368 | 28a76be8 | aliguori | serveroutlen, serverout ? 0 : 1); |
369 | 2f9606b3 | aliguori | |
370 | 2f9606b3 | aliguori | if (serveroutlen) {
|
371 | 28a76be8 | aliguori | vnc_write_u32(vs, serveroutlen + 1);
|
372 | 28a76be8 | aliguori | vnc_write(vs, serverout, serveroutlen + 1);
|
373 | 2f9606b3 | aliguori | } else {
|
374 | 28a76be8 | aliguori | vnc_write_u32(vs, 0);
|
375 | 2f9606b3 | aliguori | } |
376 | 2f9606b3 | aliguori | |
377 | 2f9606b3 | aliguori | /* Whether auth is complete */
|
378 | 2f9606b3 | aliguori | vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); |
379 | 2f9606b3 | aliguori | |
380 | 2f9606b3 | aliguori | if (err == SASL_CONTINUE) {
|
381 | 28a76be8 | aliguori | VNC_DEBUG("%s", "Authentication must continue\n"); |
382 | 28a76be8 | aliguori | /* Wait for step length */
|
383 | 28a76be8 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
|
384 | 2f9606b3 | aliguori | } else {
|
385 | 28a76be8 | aliguori | if (!vnc_auth_sasl_check_ssf(vs)) {
|
386 | 28a76be8 | aliguori | VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
|
387 | 28a76be8 | aliguori | goto authreject;
|
388 | 28a76be8 | aliguori | } |
389 | 28a76be8 | aliguori | |
390 | 28a76be8 | aliguori | /* Check username whitelist ACL */
|
391 | 28a76be8 | aliguori | if (vnc_auth_sasl_check_access(vs) < 0) { |
392 | 28a76be8 | aliguori | VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
|
393 | 28a76be8 | aliguori | goto authreject;
|
394 | 28a76be8 | aliguori | } |
395 | 28a76be8 | aliguori | |
396 | 28a76be8 | aliguori | VNC_DEBUG("Authentication successful %d\n", vs->csock);
|
397 | 28a76be8 | aliguori | vnc_write_u32(vs, 0); /* Accept auth */ |
398 | 28a76be8 | aliguori | start_client_init(vs); |
399 | 2f9606b3 | aliguori | } |
400 | 2f9606b3 | aliguori | |
401 | 2f9606b3 | aliguori | return 0; |
402 | 2f9606b3 | aliguori | |
403 | 2f9606b3 | aliguori | authreject:
|
404 | 2f9606b3 | aliguori | vnc_write_u32(vs, 1); /* Reject auth */ |
405 | 2f9606b3 | aliguori | vnc_write_u32(vs, sizeof("Authentication failed")); |
406 | 2f9606b3 | aliguori | vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); |
407 | 2f9606b3 | aliguori | vnc_flush(vs); |
408 | 2f9606b3 | aliguori | vnc_client_error(vs); |
409 | 2f9606b3 | aliguori | return -1; |
410 | 2f9606b3 | aliguori | |
411 | 2f9606b3 | aliguori | authabort:
|
412 | 2f9606b3 | aliguori | vnc_client_error(vs); |
413 | 2f9606b3 | aliguori | return -1; |
414 | 2f9606b3 | aliguori | } |
415 | 2f9606b3 | aliguori | |
416 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len) |
417 | 2f9606b3 | aliguori | { |
418 | 2f9606b3 | aliguori | uint32_t startlen = read_u32(data, 0);
|
419 | 2f9606b3 | aliguori | VNC_DEBUG("Got client start len %d\n", startlen);
|
420 | 2f9606b3 | aliguori | if (startlen > SASL_DATA_MAX_LEN) {
|
421 | 28a76be8 | aliguori | VNC_DEBUG("Too much SASL data %d\n", startlen);
|
422 | 28a76be8 | aliguori | vnc_client_error(vs); |
423 | 28a76be8 | aliguori | return -1; |
424 | 2f9606b3 | aliguori | } |
425 | 2f9606b3 | aliguori | |
426 | 2f9606b3 | aliguori | if (startlen == 0) |
427 | 28a76be8 | aliguori | return protocol_client_auth_sasl_start(vs, NULL, 0); |
428 | 2f9606b3 | aliguori | |
429 | 2f9606b3 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_start, startlen); |
430 | 2f9606b3 | aliguori | return 0; |
431 | 2f9606b3 | aliguori | } |
432 | 2f9606b3 | aliguori | |
433 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len) |
434 | 2f9606b3 | aliguori | { |
435 | 5847d9e1 | Jim Meyering | char *mechname = g_strndup((const char *) data, len); |
436 | 2f9606b3 | aliguori | VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
|
437 | 28a76be8 | aliguori | mechname, vs->sasl.mechlist); |
438 | 2f9606b3 | aliguori | |
439 | 2f9606b3 | aliguori | if (strncmp(vs->sasl.mechlist, mechname, len) == 0) { |
440 | 28a76be8 | aliguori | if (vs->sasl.mechlist[len] != '\0' && |
441 | 28a76be8 | aliguori | vs->sasl.mechlist[len] != ',') {
|
442 | 28a76be8 | aliguori | VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
|
443 | 8ce7d352 | Blue Swirl | goto fail;
|
444 | 28a76be8 | aliguori | } |
445 | 2f9606b3 | aliguori | } else {
|
446 | 28a76be8 | aliguori | char *offset = strstr(vs->sasl.mechlist, mechname);
|
447 | 28a76be8 | aliguori | VNC_DEBUG("Two %p\n", offset);
|
448 | 28a76be8 | aliguori | if (!offset) {
|
449 | 8ce7d352 | Blue Swirl | goto fail;
|
450 | 28a76be8 | aliguori | } |
451 | 28a76be8 | aliguori | VNC_DEBUG("Two '%s'\n", offset);
|
452 | 28a76be8 | aliguori | if (offset[-1] != ',' || |
453 | 28a76be8 | aliguori | (offset[len] != '\0'&&
|
454 | 28a76be8 | aliguori | offset[len] != ',')) {
|
455 | 8ce7d352 | Blue Swirl | goto fail;
|
456 | 28a76be8 | aliguori | } |
457 | 2f9606b3 | aliguori | } |
458 | 2f9606b3 | aliguori | |
459 | 302d9d6f | Markus Armbruster | g_free(vs->sasl.mechlist); |
460 | 2f9606b3 | aliguori | vs->sasl.mechlist = mechname; |
461 | 2f9606b3 | aliguori | |
462 | 2f9606b3 | aliguori | VNC_DEBUG("Validated mechname '%s'\n", mechname);
|
463 | 2f9606b3 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
|
464 | 2f9606b3 | aliguori | return 0; |
465 | 8ce7d352 | Blue Swirl | |
466 | 8ce7d352 | Blue Swirl | fail:
|
467 | 8ce7d352 | Blue Swirl | vnc_client_error(vs); |
468 | 302d9d6f | Markus Armbruster | g_free(mechname); |
469 | 8ce7d352 | Blue Swirl | return -1; |
470 | 2f9606b3 | aliguori | } |
471 | 2f9606b3 | aliguori | |
472 | 2f9606b3 | aliguori | static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len) |
473 | 2f9606b3 | aliguori | { |
474 | 2f9606b3 | aliguori | uint32_t mechlen = read_u32(data, 0);
|
475 | 2f9606b3 | aliguori | VNC_DEBUG("Got client mechname len %d\n", mechlen);
|
476 | 2f9606b3 | aliguori | if (mechlen > 100) { |
477 | 28a76be8 | aliguori | VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
|
478 | 28a76be8 | aliguori | vnc_client_error(vs); |
479 | 28a76be8 | aliguori | return -1; |
480 | 2f9606b3 | aliguori | } |
481 | 2f9606b3 | aliguori | if (mechlen < 1) { |
482 | 28a76be8 | aliguori | VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
|
483 | 28a76be8 | aliguori | vnc_client_error(vs); |
484 | 28a76be8 | aliguori | return -1; |
485 | 2f9606b3 | aliguori | } |
486 | 2f9606b3 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen); |
487 | 2f9606b3 | aliguori | return 0; |
488 | 2f9606b3 | aliguori | } |
489 | 2f9606b3 | aliguori | |
490 | 2f9606b3 | aliguori | void start_auth_sasl(VncState *vs)
|
491 | 2f9606b3 | aliguori | { |
492 | 2f9606b3 | aliguori | const char *mechlist = NULL; |
493 | 2f9606b3 | aliguori | sasl_security_properties_t secprops; |
494 | 2f9606b3 | aliguori | int err;
|
495 | 2f9606b3 | aliguori | char *localAddr, *remoteAddr;
|
496 | 2f9606b3 | aliguori | int mechlistlen;
|
497 | 2f9606b3 | aliguori | |
498 | 2f9606b3 | aliguori | VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
|
499 | 2f9606b3 | aliguori | |
500 | 2f9606b3 | aliguori | /* Get local & remote client addresses in form IPADDR;PORT */
|
501 | 2f9606b3 | aliguori | if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock))) |
502 | 28a76be8 | aliguori | goto authabort;
|
503 | 2f9606b3 | aliguori | |
504 | 2f9606b3 | aliguori | if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) { |
505 | ae878b17 | Stefan Weil | g_free(localAddr); |
506 | 28a76be8 | aliguori | goto authabort;
|
507 | 2f9606b3 | aliguori | } |
508 | 2f9606b3 | aliguori | |
509 | 2f9606b3 | aliguori | err = sasl_server_new("vnc",
|
510 | 28a76be8 | aliguori | NULL, /* FQDN - just delegates to gethostname */ |
511 | 28a76be8 | aliguori | NULL, /* User realm */ |
512 | 28a76be8 | aliguori | localAddr, |
513 | 28a76be8 | aliguori | remoteAddr, |
514 | 28a76be8 | aliguori | NULL, /* Callbacks, not needed */ |
515 | 28a76be8 | aliguori | SASL_SUCCESS_DATA, |
516 | 28a76be8 | aliguori | &vs->sasl.conn); |
517 | ae878b17 | Stefan Weil | g_free(localAddr); |
518 | ae878b17 | Stefan Weil | g_free(remoteAddr); |
519 | 2f9606b3 | aliguori | localAddr = remoteAddr = NULL;
|
520 | 2f9606b3 | aliguori | |
521 | 2f9606b3 | aliguori | if (err != SASL_OK) {
|
522 | 28a76be8 | aliguori | VNC_DEBUG("sasl context setup failed %d (%s)",
|
523 | 28a76be8 | aliguori | err, sasl_errstring(err, NULL, NULL)); |
524 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
525 | 28a76be8 | aliguori | goto authabort;
|
526 | 2f9606b3 | aliguori | } |
527 | 2f9606b3 | aliguori | |
528 | 2f9606b3 | aliguori | #ifdef CONFIG_VNC_TLS
|
529 | 2f9606b3 | aliguori | /* Inform SASL that we've got an external SSF layer from TLS/x509 */
|
530 | 7e7e2ebc | Daniel P. Berrange | if (vs->auth == VNC_AUTH_VENCRYPT &&
|
531 | 7e7e2ebc | Daniel P. Berrange | vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { |
532 | 28a76be8 | aliguori | gnutls_cipher_algorithm_t cipher; |
533 | 28a76be8 | aliguori | sasl_ssf_t ssf; |
534 | 28a76be8 | aliguori | |
535 | 28a76be8 | aliguori | cipher = gnutls_cipher_get(vs->tls.session); |
536 | 28a76be8 | aliguori | if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
|
537 | 28a76be8 | aliguori | VNC_DEBUG("%s", "cannot TLS get cipher size\n"); |
538 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
539 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
540 | 28a76be8 | aliguori | goto authabort;
|
541 | 28a76be8 | aliguori | } |
542 | 28a76be8 | aliguori | ssf *= 8; /* tls key size is bytes, sasl wants bits */ |
543 | 28a76be8 | aliguori | |
544 | 28a76be8 | aliguori | err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); |
545 | 28a76be8 | aliguori | if (err != SASL_OK) {
|
546 | 28a76be8 | aliguori | VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
|
547 | 28a76be8 | aliguori | err, sasl_errstring(err, NULL, NULL)); |
548 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
549 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
550 | 28a76be8 | aliguori | goto authabort;
|
551 | 28a76be8 | aliguori | } |
552 | 2f9606b3 | aliguori | } else
|
553 | 2f9606b3 | aliguori | #endif /* CONFIG_VNC_TLS */ |
554 | 28a76be8 | aliguori | vs->sasl.wantSSF = 1;
|
555 | 2f9606b3 | aliguori | |
556 | 2f9606b3 | aliguori | memset (&secprops, 0, sizeof secprops); |
557 | 2f9606b3 | aliguori | /* Inform SASL that we've got an external SSF layer from TLS */
|
558 | 2f9606b3 | aliguori | if (strncmp(vs->vd->display, "unix:", 5) == 0 |
559 | 2f9606b3 | aliguori | #ifdef CONFIG_VNC_TLS
|
560 | 28a76be8 | aliguori | /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
|
561 | 28a76be8 | aliguori | is not sufficiently strong */
|
562 | 7e7e2ebc | Daniel P. Berrange | || (vs->auth == VNC_AUTH_VENCRYPT && |
563 | 7e7e2ebc | Daniel P. Berrange | vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) |
564 | 2f9606b3 | aliguori | #endif /* CONFIG_VNC_TLS */ |
565 | 28a76be8 | aliguori | ) { |
566 | 28a76be8 | aliguori | /* If we've got TLS or UNIX domain sock, we don't care about SSF */
|
567 | 28a76be8 | aliguori | secprops.min_ssf = 0;
|
568 | 28a76be8 | aliguori | secprops.max_ssf = 0;
|
569 | 28a76be8 | aliguori | secprops.maxbufsize = 8192;
|
570 | 28a76be8 | aliguori | secprops.security_flags = 0;
|
571 | 2f9606b3 | aliguori | } else {
|
572 | 28a76be8 | aliguori | /* Plain TCP, better get an SSF layer */
|
573 | 28a76be8 | aliguori | secprops.min_ssf = 56; /* Good enough to require kerberos */ |
574 | 28a76be8 | aliguori | secprops.max_ssf = 100000; /* Arbitrary big number */ |
575 | 28a76be8 | aliguori | secprops.maxbufsize = 8192;
|
576 | 28a76be8 | aliguori | /* Forbid any anonymous or trivially crackable auth */
|
577 | 28a76be8 | aliguori | secprops.security_flags = |
578 | 28a76be8 | aliguori | SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; |
579 | 2f9606b3 | aliguori | } |
580 | 2f9606b3 | aliguori | |
581 | 2f9606b3 | aliguori | err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops); |
582 | 2f9606b3 | aliguori | if (err != SASL_OK) {
|
583 | 28a76be8 | aliguori | VNC_DEBUG("cannot set SASL security props %d (%s)\n",
|
584 | 28a76be8 | aliguori | err, sasl_errstring(err, NULL, NULL)); |
585 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
586 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
587 | 28a76be8 | aliguori | goto authabort;
|
588 | 2f9606b3 | aliguori | } |
589 | 2f9606b3 | aliguori | |
590 | 2f9606b3 | aliguori | err = sasl_listmech(vs->sasl.conn, |
591 | 28a76be8 | aliguori | NULL, /* Don't need to set user */ |
592 | 28a76be8 | aliguori | "", /* Prefix */ |
593 | 28a76be8 | aliguori | ",", /* Separator */ |
594 | 28a76be8 | aliguori | "", /* Suffix */ |
595 | 28a76be8 | aliguori | &mechlist, |
596 | 28a76be8 | aliguori | NULL,
|
597 | 28a76be8 | aliguori | NULL);
|
598 | 2f9606b3 | aliguori | if (err != SASL_OK) {
|
599 | 28a76be8 | aliguori | VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
|
600 | 28a76be8 | aliguori | err, sasl_errdetail(vs->sasl.conn)); |
601 | 28a76be8 | aliguori | sasl_dispose(&vs->sasl.conn); |
602 | 28a76be8 | aliguori | vs->sasl.conn = NULL;
|
603 | 28a76be8 | aliguori | goto authabort;
|
604 | 2f9606b3 | aliguori | } |
605 | 2f9606b3 | aliguori | VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
|
606 | 2f9606b3 | aliguori | |
607 | 302d9d6f | Markus Armbruster | vs->sasl.mechlist = g_strdup(mechlist); |
608 | 2f9606b3 | aliguori | mechlistlen = strlen(mechlist); |
609 | 2f9606b3 | aliguori | vnc_write_u32(vs, mechlistlen); |
610 | 2f9606b3 | aliguori | vnc_write(vs, mechlist, mechlistlen); |
611 | 2f9606b3 | aliguori | vnc_flush(vs); |
612 | 2f9606b3 | aliguori | |
613 | 2f9606b3 | aliguori | VNC_DEBUG("Wait for client mechname length\n");
|
614 | 2f9606b3 | aliguori | vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
|
615 | 2f9606b3 | aliguori | |
616 | 2f9606b3 | aliguori | return;
|
617 | 2f9606b3 | aliguori | |
618 | 2f9606b3 | aliguori | authabort:
|
619 | 2f9606b3 | aliguori | vnc_client_error(vs); |
620 | 2f9606b3 | aliguori | } |
621 | 2f9606b3 | aliguori |