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