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