LibVNCServer/LibVNCClient
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tls_gnutls.c
Go to the documentation of this file.
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
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
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
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
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
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
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
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
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 ||
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
274 FreeX509Credential(rfbCredential *cred)
275 {
280  free(cred);
281 }
282 
283 static gnutls_certificate_credentials_t
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  }
308  {
309  if ((ret = gnutls_certificate_set_x509_key_file(x509_cred,
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
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
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
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
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 
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 }