• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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