blob: 3daa4163ba09e5eaf4a173d83ed4fa181acdb90e [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/*
2 * Copyright (C) 2009 Vic Lee.
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20#include <gnutls/gnutls.h>
21#include <rfb/rfbclient.h>
22#include <errno.h>
23#ifdef WIN32
24#undef SOCKET
25#include <windows.h> /* for Sleep() */
26#define sleep(X) Sleep(1000*X) /* MinGW32 has no sleep() */
27#include <winsock2.h>
28#define read(sock,buf,len) recv(sock,buf,len,0)
29#define write(sock,buf,len) send(sock,buf,len,0)
30#endif
31#include "tls.h"
32
33
34static const char *rfbTLSPriority = "NORMAL:+DHE-DSS:+RSA:+DHE-RSA:+SRP";
35static const char *rfbAnonTLSPriority= "NORMAL:+ANON-DH";
36
37#define DH_BITS 1024
38static gnutls_dh_params_t rfbDHParams;
39
40static rfbBool rfbTLSInitialized = FALSE;
41
42static rfbBool
43InitializeTLS(void)
44{
45 int ret;
46
47 if (rfbTLSInitialized) return TRUE;
48 if ((ret = gnutls_global_init()) < 0 ||
49 (ret = gnutls_dh_params_init(&rfbDHParams)) < 0 ||
50 (ret = gnutls_dh_params_generate2(rfbDHParams, DH_BITS)) < 0)
51 {
52 rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret));
53 return FALSE;
54 }
55 rfbClientLog("GnuTLS initialized.\n");
56 rfbTLSInitialized = TRUE;
57 return TRUE;
58}
59
60/*
61 * On Windows, translate WSAGetLastError() to errno values as GNU TLS does it
62 * internally too. This is necessary because send() and recv() on Windows
63 * don't set errno when they fail but GNUTLS expects a proper errno value.
64 *
65 * Use gnutls_transport_set_global_errno() like the GNU TLS documentation
66 * suggests to avoid problems with different errno variables when GNU TLS and
67 * libvncclient are linked to different versions of msvcrt.dll.
68 */
69#ifdef WIN32
70static void WSAtoTLSErrno()
71{
72 switch(WSAGetLastError()) {
73 case WSAEWOULDBLOCK:
74 gnutls_transport_set_global_errno(EAGAIN);
75 break;
76 case WSAEINTR:
77 gnutls_transport_set_global_errno(EINTR);
78 break;
79 default:
80 gnutls_transport_set_global_errno(EIO);
81 break;
82 }
83}
84#endif
85
86
87static ssize_t
88PushTLS(gnutls_transport_ptr_t transport, const void *data, size_t len)
89{
90 rfbClient *client = (rfbClient*)transport;
91 int ret;
92
93 while (1)
94 {
95 ret = write(client->sock, data, len);
96 if (ret < 0)
97 {
98#ifdef WIN32
99 WSAtoTLSErrno();
100#endif
101 if (errno == EINTR) continue;
102 return -1;
103 }
104 return ret;
105 }
106}
107
108
109static ssize_t
110PullTLS(gnutls_transport_ptr_t transport, void *data, size_t len)
111{
112 rfbClient *client = (rfbClient*)transport;
113 int ret;
114
115 while (1)
116 {
117 ret = read(client->sock, data, len);
118 if (ret < 0)
119 {
120#ifdef WIN32
121 WSAtoTLSErrno();
122#endif
123 if (errno == EINTR) continue;
124 return -1;
125 }
126 return ret;
127 }
128}
129
130static rfbBool
131InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
132{
133 int ret;
134 const char *p;
135
136 if (client->tlsSession) return TRUE;
137
138 if ((ret = gnutls_init((gnutls_session_t*)&client->tlsSession, GNUTLS_CLIENT)) < 0)
139 {
140 rfbClientLog("Failed to initialized TLS session: %s.\n", gnutls_strerror(ret));
141 return FALSE;
142 }
143
144 if ((ret = gnutls_priority_set_direct((gnutls_session_t)client->tlsSession,
145 anonTLS ? rfbAnonTLSPriority : rfbTLSPriority, &p)) < 0)
146 {
147 rfbClientLog("Warning: Failed to set TLS priority: %s (%s).\n", gnutls_strerror(ret), p);
148 }
149
150 gnutls_transport_set_ptr((gnutls_session_t)client->tlsSession, (gnutls_transport_ptr_t)client);
151 gnutls_transport_set_push_function((gnutls_session_t)client->tlsSession, PushTLS);
152 gnutls_transport_set_pull_function((gnutls_session_t)client->tlsSession, PullTLS);
153
154 rfbClientLog("TLS session initialized.\n");
155
156 return TRUE;
157}
158
159static rfbBool
160SetTLSAnonCredential(rfbClient* client)
161{
162 gnutls_anon_client_credentials anonCred;
163 int ret;
164
165 if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 ||
166 (ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_ANON, anonCred)) < 0)
167 {
168 FreeTLS(client);
169 rfbClientLog("Failed to create anonymous credentials: %s", gnutls_strerror(ret));
170 return FALSE;
171 }
172 rfbClientLog("TLS anonymous credential created.\n");
173 return TRUE;
174}
175
176static rfbBool
177HandshakeTLS(rfbClient* client)
178{
179 int timeout = 15;
180 int ret;
181
182 while (timeout > 0 && (ret = gnutls_handshake((gnutls_session_t)client->tlsSession)) < 0)
183 {
184 if (!gnutls_error_is_fatal(ret))
185 {
186 rfbClientLog("TLS handshake blocking.\n");
187 sleep(1);
188 timeout--;
189 continue;
190 }
191 rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret));
192 FreeTLS(client);
193 return FALSE;
194 }
195
196 if (timeout <= 0)
197 {
198 rfbClientLog("TLS handshake timeout.\n");
199 FreeTLS(client);
200 return FALSE;
201 }
202
203 rfbClientLog("TLS handshake done.\n");
204 return TRUE;
205}
206
207/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
208static rfbBool
209ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
210{
211 uint8_t count=0;
212 uint8_t loop=0;
213 uint8_t flag=0;
214 uint32_t tAuth[256], t;
215 char buf1[500],buf2[10];
216 uint32_t authScheme;
217
218 if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
219
220 if (count==0)
221 {
222 rfbClientLog("List of security types is ZERO. Giving up.\n");
223 return FALSE;
224 }
225
226 if (count>sizeof(tAuth))
227 {
228 rfbClientLog("%d security types are too many; maximum is %d\n", count, sizeof(tAuth));
229 return FALSE;
230 }
231
232 rfbClientLog("We have %d security types to read\n", count);
233 authScheme=0;
234 /* now, we have a list of available security types to read ( uint8_t[] ) */
235 for (loop=0;loop<count;loop++)
236 {
237 if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
238 t=rfbClientSwap32IfLE(tAuth[loop]);
239 rfbClientLog("%d) Received security type %d\n", loop, t);
240 if (flag) continue;
241 if (t==rfbVeNCryptTLSNone ||
242 t==rfbVeNCryptTLSVNC ||
243 t==rfbVeNCryptTLSPlain ||
244 t==rfbVeNCryptX509None ||
245 t==rfbVeNCryptX509VNC ||
246 t==rfbVeNCryptX509Plain)
247 {
248 flag++;
249 authScheme=t;
250 rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
251 /* send back 4 bytes (in original byte order!) indicating which security type to use */
252 if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
253 }
254 tAuth[loop]=t;
255 }
256 if (authScheme==0)
257 {
258 memset(buf1, 0, sizeof(buf1));
259 for (loop=0;loop<count;loop++)
260 {
261 if (strlen(buf1)>=sizeof(buf1)-1) break;
262 snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
263 strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
264 }
265 rfbClientLog("Unknown VeNCrypt authentication scheme from VNC server: %s\n",
266 buf1);
267 return FALSE;
268 }
269 *result = authScheme;
270 return TRUE;
271}
272
273static void
274FreeX509Credential(rfbCredential *cred)
275{
276 if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
277 if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
278 if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
279 if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
280 free(cred);
281}
282
283static gnutls_certificate_credentials_t
284CreateX509CertCredential(rfbCredential *cred)
285{
286 gnutls_certificate_credentials_t x509_cred;
287 int ret;
288
289 if (!cred->x509Credential.x509CACertFile)
290 {
291 rfbClientLog("No CA certificate provided.\n");
292 return NULL;
293 }
294
295 if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
296 {
297 rfbClientLog("Cannot allocate credentials: %s.\n", gnutls_strerror(ret));
298 return NULL;
299 }
300 if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
301 cred->x509Credential.x509CACertFile, GNUTLS_X509_FMT_PEM)) < 0)
302 {
303 rfbClientLog("Cannot load CA credentials: %s.\n", gnutls_strerror(ret));
304 gnutls_certificate_free_credentials (x509_cred);
305 return NULL;
306 }
307 if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
308 {
309 if ((ret = gnutls_certificate_set_x509_key_file(x509_cred,
310 cred->x509Credential.x509ClientCertFile, cred->x509Credential.x509ClientKeyFile,
311 GNUTLS_X509_FMT_PEM)) < 0)
312 {
313 rfbClientLog("Cannot load client certificate or key: %s.\n", gnutls_strerror(ret));
314 gnutls_certificate_free_credentials (x509_cred);
315 return NULL;
316 }
317 } else
318 {
319 rfbClientLog("No client certificate or key provided.\n");
320 }
321 if (cred->x509Credential.x509CACrlFile)
322 {
323 if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
324 cred->x509Credential.x509CACrlFile, GNUTLS_X509_FMT_PEM)) < 0)
325 {
326 rfbClientLog("Cannot load CRL: %s.\n", gnutls_strerror(ret));
327 gnutls_certificate_free_credentials (x509_cred);
328 return NULL;
329 }
330 } else
331 {
332 rfbClientLog("No CRL provided.\n");
333 }
334 gnutls_certificate_set_dh_params (x509_cred, rfbDHParams);
335 return x509_cred;
336}
337
338
339rfbBool
340HandleAnonTLSAuth(rfbClient* client)
341{
342 if (!InitializeTLS() || !InitializeTLSSession(client, TRUE)) return FALSE;
343
344 if (!SetTLSAnonCredential(client)) return FALSE;
345
346 if (!HandshakeTLS(client)) return FALSE;
347
348 return TRUE;
349}
350
351rfbBool
352HandleVeNCryptAuth(rfbClient* client)
353{
354 uint8_t major, minor, status;
355 uint32_t authScheme;
356 rfbBool anonTLS;
357 gnutls_certificate_credentials_t x509_cred = NULL;
358 int ret;
359
360 if (!InitializeTLS()) return FALSE;
361
362 /* Read VeNCrypt version */
363 if (!ReadFromRFBServer(client, (char *)&major, 1) ||
364 !ReadFromRFBServer(client, (char *)&minor, 1))
365 {
366 return FALSE;
367 }
368 rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
369
370 if (major != 0 && minor != 2)
371 {
372 rfbClientLog("Unsupported VeNCrypt version.\n");
373 return FALSE;
374 }
375
376 if (!WriteToRFBServer(client, (char *)&major, 1) ||
377 !WriteToRFBServer(client, (char *)&minor, 1) ||
378 !ReadFromRFBServer(client, (char *)&status, 1))
379 {
380 return FALSE;
381 }
382
383 if (status != 0)
384 {
385 rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
386 return FALSE;
387 }
388
389 if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
390 if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
391 {
392 rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
393 return FALSE;
394 }
395 client->subAuthScheme = authScheme;
396
397 /* Some VeNCrypt security types are anonymous TLS, others are X509 */
398 switch (authScheme)
399 {
400 case rfbVeNCryptTLSNone:
401 case rfbVeNCryptTLSVNC:
402 case rfbVeNCryptTLSPlain:
403 anonTLS = TRUE;
404 break;
405 default:
406 anonTLS = FALSE;
407 break;
408 }
409
410 /* Get X509 Credentials if it's not anonymous */
411 if (!anonTLS)
412 {
413 rfbCredential *cred;
414
415 if (!client->GetCredential)
416 {
417 rfbClientLog("GetCredential callback is not set.\n");
418 return FALSE;
419 }
420 cred = client->GetCredential(client, rfbCredentialTypeX509);
421 if (!cred)
422 {
423 rfbClientLog("Reading credential failed\n");
424 return FALSE;
425 }
426
427 x509_cred = CreateX509CertCredential(cred);
428 FreeX509Credential(cred);
429 if (!x509_cred) return FALSE;
430 }
431
432 /* Start up the TLS session */
433 if (!InitializeTLSSession(client, anonTLS)) return FALSE;
434
435 if (anonTLS)
436 {
437 if (!SetTLSAnonCredential(client)) return FALSE;
438 }
439 else
440 {
441 if ((ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0)
442 {
443 rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret));
444 FreeTLS(client);
445 return FALSE;
446 }
447 }
448
449 if (!HandshakeTLS(client)) return FALSE;
450
451 /* TODO: validate certificate */
452
453 /* We are done here. The caller should continue with client->subAuthScheme
454 * to do actual sub authentication.
455 */
456 return TRUE;
457}
458
459int
460ReadFromTLS(rfbClient* client, char *out, unsigned int n)
461{
462 ssize_t ret;
463
464 ret = gnutls_record_recv((gnutls_session_t)client->tlsSession, out, n);
465 if (ret >= 0) return ret;
466 if (ret == GNUTLS_E_REHANDSHAKE || ret == GNUTLS_E_AGAIN)
467 {
468 errno = EAGAIN;
469 } else
470 {
471 rfbClientLog("Error reading from TLS: %s.\n", gnutls_strerror(ret));
472 errno = EINTR;
473 }
474 return -1;
475}
476
477int
478WriteToTLS(rfbClient* client, char *buf, unsigned int n)
479{
480 unsigned int offset = 0;
481 ssize_t ret;
482
483 while (offset < n)
484 {
485 ret = gnutls_record_send((gnutls_session_t)client->tlsSession, buf+offset, (size_t)(n-offset));
486 if (ret == 0) continue;
487 if (ret < 0)
488 {
489 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) continue;
490 rfbClientLog("Error writing to TLS: %s.\n", gnutls_strerror(ret));
491 return -1;
492 }
493 offset += (unsigned int)ret;
494 }
495 return offset;
496}
497
498void FreeTLS(rfbClient* client)
499{
500 if (client->tlsSession)
501 {
502 gnutls_deinit((gnutls_session_t)client->tlsSession);
503 client->tlsSession = NULL;
504 }
505}