summaryrefslogtreecommitdiffstats
path: root/libvncclient
diff options
context:
space:
mode:
Diffstat (limited to 'libvncclient')
-rw-r--r--libvncclient/rfbproto.c75
-rw-r--r--libvncclient/tls.c227
2 files changed, 298 insertions, 4 deletions
diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
index 1a71f4f..497facb 100644
--- a/libvncclient/rfbproto.c
+++ b/libvncclient/rfbproto.c
@@ -504,7 +504,7 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth)
rfbClientLog("%d) Received security type %d\n", loop, tAuth[loop]);
if (flag) continue;
if (tAuth[loop]==rfbVncAuth || tAuth[loop]==rfbNoAuth ||
- (!subAuth && tAuth[loop]==rfbTLS))
+ (!subAuth && (tAuth[loop]==rfbTLS || tAuth[loop]==rfbVeNCrypt)))
{
flag++;
authScheme=tAuth[loop];
@@ -569,6 +569,50 @@ HandleVncAuth(rfbClient *client)
return TRUE;
}
+static rfbBool
+HandlePlainAuth(rfbClient *client)
+{
+ uint32_t ulen, ulensw;
+ uint32_t plen, plensw;
+ rfbCredential *cred;
+
+ if (!client->GetCredential)
+ {
+ rfbClientLog("GetCredential callback is not set.\n");
+ return FALSE;
+ }
+ cred = client->GetCredential(client, rfbCredentialTypeUser);
+ if (!cred)
+ {
+ rfbClientLog("Reading credential failed\n");
+ return FALSE;
+ }
+
+ ulen = (cred->userCredential.username ? strlen(cred->userCredential.username) : 0);
+ ulensw = rfbClientSwap32IfLE(ulen);
+ plen = (cred->userCredential.password ? strlen(cred->userCredential.password) : 0);
+ plensw = rfbClientSwap32IfLE(plen);
+ if (!WriteToRFBServer(client, (char *)&ulensw, 4)) return FALSE;
+ if (!WriteToRFBServer(client, (char *)&plensw, 4)) return FALSE;
+ if (ulen > 0)
+ {
+ if (!WriteToRFBServer(client, cred->userCredential.username, ulen)) return FALSE;
+ }
+ if (plen > 0)
+ {
+ if (!WriteToRFBServer(client, cred->userCredential.password, plen)) return FALSE;
+ }
+
+ if (cred->userCredential.username) free(cred->userCredential.username);
+ if (cred->userCredential.password) free(cred->userCredential.password);
+ free(cred);
+
+ /* Handle the SecurityResult message */
+ if (!rfbHandleAuthResult(client)) return FALSE;
+
+ return TRUE;
+}
+
/*
* InitialiseRFBConnection.
*/
@@ -700,6 +744,35 @@ InitialiseRFBConnection(rfbClient* client)
break;
+ case rfbVeNCrypt:
+ if (!HandleVeNCryptAuth(client)) return FALSE;
+
+ switch (client->subAuthScheme) {
+
+ case rfbVeNCryptTLSNone:
+ case rfbVeNCryptX509None:
+ rfbClientLog("No sub authentication needed\n");
+ if (!rfbHandleAuthResult(client)) return FALSE;
+ break;
+
+ case rfbVeNCryptTLSVNC:
+ case rfbVeNCryptX509VNC:
+ if (!HandleVncAuth(client)) return FALSE;
+ break;
+
+ case rfbVeNCryptTLSPlain:
+ case rfbVeNCryptX509Plain:
+ if (!HandlePlainAuth(client)) return FALSE;
+ break;
+
+ default:
+ rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
+ client->subAuthScheme);
+ return FALSE;
+ }
+
+ break;
+
default:
rfbClientLog("Unknown authentication scheme from VNC server: %d\n",
(int)authScheme);
diff --git a/libvncclient/tls.c b/libvncclient/tls.c
index b8d2d1f..206dbda 100644
--- a/libvncclient/tls.c
+++ b/libvncclient/tls.c
@@ -168,6 +168,136 @@ HandshakeTLS(rfbClient* client)
return TRUE;
}
+/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
+static rfbBool
+ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
+{
+ uint8_t count=0;
+ uint8_t loop=0;
+ uint8_t flag=0;
+ uint32_t tAuth[256], t;
+ char buf1[500],buf2[10];
+ uint32_t authScheme;
+
+ if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
+
+ if (count==0)
+ {
+ rfbClientLog("List of security types is ZERO. Giving up.\n");
+ return FALSE;
+ }
+ if (count>sizeof(tAuth))
+ {
+ rfbClientLog("%d security types are too many; maximum is %d\n", count, sizeof(tAuth));
+ return FALSE;
+ }
+
+ rfbClientLog("We have %d security types to read\n", count);
+ authScheme=0;
+ /* now, we have a list of available security types to read ( uint8_t[] ) */
+ for (loop=0;loop<count;loop++)
+ {
+ if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
+ t=rfbClientSwap32IfLE(tAuth[loop]);
+ rfbClientLog("%d) Received security type %d\n", loop, t);
+ if (flag) continue;
+ if (t==rfbVeNCryptTLSNone ||
+ t==rfbVeNCryptTLSVNC ||
+ t==rfbVeNCryptTLSPlain ||
+ t==rfbVeNCryptX509None ||
+ t==rfbVeNCryptX509VNC ||
+ t==rfbVeNCryptX509Plain)
+ {
+ flag++;
+ authScheme=t;
+ rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
+ /* send back 4 bytes (in original byte order!) indicating which security type to use */
+ if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
+ }
+ tAuth[loop]=t;
+ }
+ if (authScheme==0)
+ {
+ memset(buf1, 0, sizeof(buf1));
+ for (loop=0;loop<count;loop++)
+ {
+ if (strlen(buf1)>=sizeof(buf1)-1) break;
+ snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
+ strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
+ }
+ rfbClientLog("Unknown VeNCrypt authentication scheme from VNC server: %s\n",
+ buf1);
+ return FALSE;
+ }
+ *result = authScheme;
+ return TRUE;
+}
+
+static void
+FreeX509Credential(rfbCredential *cred)
+{
+ if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
+ if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
+ if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
+ if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
+ free(cred);
+}
+
+static gnutls_certificate_credentials_t
+CreateX509CertCredential(rfbCredential *cred)
+{
+ gnutls_certificate_credentials_t x509_cred;
+ int ret;
+
+ if (!cred->x509Credential.x509CACertFile)
+ {
+ rfbClientLog("No CA certificate provided.\n");
+ return NULL;
+ }
+
+ if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
+ {
+ rfbClientLog("Cannot allocate credentials: %s.\n", gnutls_strerror(ret));
+ return NULL;
+ }
+ if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
+ cred->x509Credential.x509CACertFile, GNUTLS_X509_FMT_PEM)) < 0)
+ {
+ rfbClientLog("Cannot load CA credentials: %s.\n", gnutls_strerror(ret));
+ gnutls_certificate_free_credentials (x509_cred);
+ return NULL;
+ }
+ if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
+ {
+ if ((ret = gnutls_certificate_set_x509_key_file(x509_cred,
+ cred->x509Credential.x509ClientCertFile, cred->x509Credential.x509ClientKeyFile,
+ GNUTLS_X509_FMT_PEM)) < 0)
+ {
+ rfbClientLog("Cannot load client certificate or key: %s.\n", gnutls_strerror(ret));
+ gnutls_certificate_free_credentials (x509_cred);
+ return NULL;
+ }
+ } else
+ {
+ rfbClientLog("No client certificate or key provided.\n");
+ }
+ if (cred->x509Credential.x509CACrlFile)
+ {
+ if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
+ cred->x509Credential.x509CACrlFile, GNUTLS_X509_FMT_PEM)) < 0)
+ {
+ rfbClientLog("Cannot load CRL: %s.\n", gnutls_strerror(ret));
+ gnutls_certificate_free_credentials (x509_cred);
+ return NULL;
+ }
+ } else
+ {
+ rfbClientLog("No CRL provided.\n");
+ }
+ gnutls_certificate_set_dh_params (x509_cred, rfbDHParams);
+ return x509_cred;
+}
+
#endif
rfbBool
@@ -193,17 +323,108 @@ rfbBool
HandleVeNCryptAuth(rfbClient* client)
{
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
+ uint8_t major, minor, status;
+ uint32_t authScheme;
+ rfbBool anonTLS;
+ gnutls_certificate_credentials_t x509_cred = NULL;
int ret;
- if (!InitializeTLS() || !InitializeTLSSession(client, FALSE)) return FALSE;
+ if (!InitializeTLS()) return FALSE;
+
+ /* Read VeNCrypt version */
+ if (!ReadFromRFBServer(client, (char *)&major, 1) ||
+ !ReadFromRFBServer(client, (char *)&minor, 1))
+ {
+ return FALSE;
+ }
+ rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
+
+ if (major != 0 && minor != 2)
+ {
+ rfbClientLog("Unsupported VeNCrypt version.\n");
+ return FALSE;
+ }
+
+ if (!WriteToRFBServer(client, (char *)&major, 1) ||
+ !WriteToRFBServer(client, (char *)&minor, 1) ||
+ !ReadFromRFBServer(client, (char *)&status, 1))
+ {
+ return FALSE;
+ }
+
+ if (status != 0)
+ {
+ rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
+ return FALSE;
+ }
+
+ if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
+ if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
+ {
+ rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
+ return FALSE;
+ }
+ client->subAuthScheme = authScheme;
+
+ /* Some VeNCrypt security types are anonymous TLS, others are X509 */
+ switch (authScheme)
+ {
+ case rfbVeNCryptTLSNone:
+ case rfbVeNCryptTLSVNC:
+ case rfbVeNCryptTLSPlain:
+ anonTLS = TRUE;
+ break;
+ default:
+ anonTLS = FALSE;
+ break;
+ }
+
+ /* Get X509 Credentials if it's not anonymous */
+ if (!anonTLS)
+ {
+ rfbCredential *cred;
+
+ if (!client->GetCredential)
+ {
+ rfbClientLog("GetCredential callback is not set.\n");
+ return FALSE;
+ }
+ cred = client->GetCredential(client, rfbCredentialTypeX509);
+ if (!cred)
+ {
+ rfbClientLog("Reading credential failed\n");
+ return FALSE;
+ }
+
+ x509_cred = CreateX509CertCredential(cred);
+ FreeX509Credential(cred);
+ if (!x509_cred) return FALSE;
+ }
- /* TODO: read VeNCrypt version, etc */
- /* TODO: call GetCredential and set to TLS session */
+ /* Start up the TLS session */
+ if (!InitializeTLSSession(client, anonTLS)) return FALSE;
+
+ if (anonTLS)
+ {
+ if (!SetTLSAnonCredential(client)) return FALSE;
+ }
+ else
+ {
+ if ((ret = gnutls_credentials_set(client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0)
+ {
+ rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret));
+ FreeTLS(client);
+ return FALSE;
+ }
+ }
if (!HandshakeTLS(client)) return FALSE;
/* TODO: validate certificate */
+ /* We are done here. The caller should continue with client->subAuthScheme
+ * to do actual sub authentication.
+ */
return TRUE;
#else