1 /*
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
34 static const char *rfbTLSPriority = "NORMAL:+DHE-DSS:+RSA:+DHE-RSA:+SRP";
35 static const char *rfbAnonTLSPriority= "NORMAL:+ANON-DH";
36
37 #define DH_BITS 1024
38 static gnutls_dh_params_t rfbDHParams;
39
40 static rfbBool rfbTLSInitialized = FALSE;
41
42 static rfbBool
InitializeTLS(void)43 InitializeTLS(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
WSAtoTLSErrno()70 static 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
87 static ssize_t
PushTLS(gnutls_transport_ptr_t transport,const void * data,size_t len)88 PushTLS(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
109 static ssize_t
PullTLS(gnutls_transport_ptr_t transport,void * data,size_t len)110 PullTLS(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
130 static rfbBool
InitializeTLSSession(rfbClient * client,rfbBool anonTLS)131 InitializeTLSSession(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
159 static rfbBool
SetTLSAnonCredential(rfbClient * client)160 SetTLSAnonCredential(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
176 static rfbBool
HandshakeTLS(rfbClient * client)177 HandshakeTLS(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 */
208 static rfbBool
ReadVeNCryptSecurityType(rfbClient * client,uint32_t * result)209 ReadVeNCryptSecurityType(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
273 static void
FreeX509Credential(rfbCredential * cred)274 FreeX509Credential(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
283 static gnutls_certificate_credentials_t
CreateX509CertCredential(rfbCredential * cred)284 CreateX509CertCredential(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
339 rfbBool
HandleAnonTLSAuth(rfbClient * client)340 HandleAnonTLSAuth(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
351 rfbBool
HandleVeNCryptAuth(rfbClient * client)352 HandleVeNCryptAuth(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
459 int
ReadFromTLS(rfbClient * client,char * out,unsigned int n)460 ReadFromTLS(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
477 int
WriteToTLS(rfbClient * client,char * buf,unsigned int n)478 WriteToTLS(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
FreeTLS(rfbClient * client)498 void FreeTLS(rfbClient* client)
499 {
500 if (client->tlsSession)
501 {
502 gnutls_deinit((gnutls_session_t)client->tlsSession);
503 client->tlsSession = NULL;
504 }
505 }
506