Home | History | Annotate | Download | only in libvncclient
      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 ||
    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
    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
    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
    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
    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
    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 }
    506