summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVic Lee <llyzs@163.com>2011-01-21 13:03:40 +0800
committerChristian Beier <dontmind@freeshell.org>2011-01-31 17:47:44 +0100
commit030ccf673d96016733ffb3bef3feede20dba19a7 (patch)
treec1f9fbb7c509244881ff4a7b8b33f8981d7b9fe2
parentffe30366d63fd318f0ee3b55dd9a60642bfb5d88 (diff)
downloadlibtdevnc-030ccf67.tar.gz
libtdevnc-030ccf67.zip
Add ARD (Apple Remote Desktop) security type support
Signed-off-by: Vic Lee <llyzs@163.com> Signed-off-by: Christian Beier <dontmind@freeshell.org>
-rw-r--r--acinclude.m4109
-rw-r--r--configure.ac16
-rw-r--r--libvncclient/rfbproto.c216
-rw-r--r--rfb/rfbproto.h1
4 files changed, 342 insertions, 0 deletions
diff --git a/acinclude.m4 b/acinclude.m4
index c69a38d..fc947ac 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -6999,3 +6999,112 @@ done
SED=$lt_cv_path_SED
AC_MSG_RESULT([$SED])
])
+
+dnl Autoconf macros for libgcrypt
+dnl Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+dnl
+dnl This file is free software; as a special exception the author gives
+dnl unlimited permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+dnl This file is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
+dnl AM_PATH_LIBGCRYPT([MINIMUM-VERSION,
+dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgcrypt and define LIBGCRYPT_CFLAGS and LIBGCRYPT_LIBS.
+dnl MINIMUN-VERSION is a string with the version number optionalliy prefixed
+dnl with the API version to also check the API compatibility. Example:
+dnl a MINIMUN-VERSION of 1:1.2.5 won't pass the test unless the installed
+dnl version of libgcrypt is at least 1.2.5 *and* the API number is 1. Using
+dnl this features allows to prevent build against newer versions of libgcrypt
+dnl with a changed API.
+dnl
+AC_DEFUN([AM_PATH_LIBGCRYPT],
+[ AC_ARG_WITH(libgcrypt-prefix,
+ AC_HELP_STRING([--with-libgcrypt-prefix=PFX],
+ [prefix where LIBGCRYPT is installed (optional)]),
+ libgcrypt_config_prefix="$withval", libgcrypt_config_prefix="")
+ if test x$libgcrypt_config_prefix != x ; then
+ if test x${LIBGCRYPT_CONFIG+set} != xset ; then
+ LIBGCRYPT_CONFIG=$libgcrypt_config_prefix/bin/libgcrypt-config
+ fi
+ fi
+
+ AC_PATH_PROG(LIBGCRYPT_CONFIG, libgcrypt-config, no)
+ tmp=ifelse([$1], ,1:1.2.0,$1)
+ if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+ req_libgcrypt_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'`
+ min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+ else
+ req_libgcrypt_api=0
+ min_libgcrypt_version="$tmp"
+ fi
+
+ AC_MSG_CHECKING(for LIBGCRYPT - version >= $min_libgcrypt_version)
+ ok=no
+ if test "$LIBGCRYPT_CONFIG" != "no" ; then
+ req_major=`echo $min_libgcrypt_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+ req_minor=`echo $min_libgcrypt_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+ req_micro=`echo $min_libgcrypt_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+ libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version`
+ major=`echo $libgcrypt_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+ minor=`echo $libgcrypt_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+ micro=`echo $libgcrypt_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+ if test "$major" -gt "$req_major"; then
+ ok=yes
+ else
+ if test "$major" -eq "$req_major"; then
+ if test "$minor" -gt "$req_minor"; then
+ ok=yes
+ else
+ if test "$minor" -eq "$req_minor"; then
+ if test "$micro" -ge "$req_micro"; then
+ ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ AC_MSG_RESULT([yes ($libgcrypt_config_version)])
+ else
+ AC_MSG_RESULT(no)
+ fi
+ if test $ok = yes; then
+ # If we have a recent libgcrypt, we should also check that the
+ # API is compatible
+ if test "$req_libgcrypt_api" -gt 0 ; then
+ tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0`
+ if test "$tmp" -gt 0 ; then
+ AC_MSG_CHECKING([LIBGCRYPT API version])
+ if test "$req_libgcrypt_api" -eq "$tmp" ; then
+ AC_MSG_RESULT([okay])
+ else
+ ok=no
+ AC_MSG_RESULT([does not match. want=$req_libgcrypt_api got=$tmp])
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags`
+ LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs`
+ ifelse([$2], , :, [$2])
+ else
+ LIBGCRYPT_CFLAGS=""
+ LIBGCRYPT_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(LIBGCRYPT_CFLAGS)
+ AC_SUBST(LIBGCRYPT_LIBS)
+])
diff --git a/configure.ac b/configure.ac
index 4ad489b..bf760dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -689,6 +689,22 @@ if test ! -z "$MINGW"; then
fi
AC_SUBST(WSOCKLIB)
+# Check for libgcrypt
+AH_TEMPLATE(WITH_CLIENT_GCRYPT, [Enable support for libgcrypt in libvncclient])
+AC_ARG_WITH(gcrypt,
+[ --without-gcrypt disable support for gcrypt],,)
+AC_ARG_WITH(client-gcrypt,
+[ --without-client-gcrypt disable support for gcrypt in libvncclient],,)
+
+if test "x$with_gcrypt" != "xno"; then
+ AM_PATH_LIBGCRYPT(1.4.0, , with_client_gcrypt=no)
+ CFLAGS="$CFLAGS $LIBGCRYPT_CFLAGS"
+ LIBS="$LIBS $LIBGCRYPT_LIBS"
+ if test "x$with_client_gcrypt" != "xno"; then
+ AC_DEFINE(WITH_CLIENT_GCRYPT)
+ fi
+fi
+
# Checks for GnuTLS
AH_TEMPLATE(WITH_CLIENT_TLS, [Enable support for gnutls in libvncclient])
AC_ARG_WITH(gnutls,
diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
index 010d08d..2de9891 100644
--- a/libvncclient/rfbproto.c
+++ b/libvncclient/rfbproto.c
@@ -51,6 +51,10 @@
#include <stdarg.h>
#include <time.h>
+#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
+#include <gcrypt.h>
+#endif
+
#include "minilzo.h"
#include "tls.h"
@@ -566,6 +570,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 || tAuth[loop]==rfbMSLogon ||
+ tAuth[loop]==rfbARD ||
(!subAuth && (tAuth[loop]==rfbTLS || tAuth[loop]==rfbVeNCrypt)))
{
if (!subAuth && client->clientAuthSchemes)
@@ -795,6 +800,208 @@ HandleMSLogonAuth(rfbClient *client)
return TRUE;
}
+#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
+static rfbBool
+rfbMpiToBytes(const gcry_mpi_t value, uint8_t *result, size_t size)
+{
+ gcry_error_t error;
+ size_t len;
+ int i;
+
+ error = gcry_mpi_print(GCRYMPI_FMT_USG, result, size, &len, value);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_mpi_print error: %s\n", gcry_strerror(error));
+ return FALSE;
+ }
+ for (i=size-1;i>(int)size-1-(int)len;--i)
+ result[i] = result[i-size+len];
+ for (;i>=0;--i)
+ result[i] = 0;
+ return TRUE;
+}
+
+static rfbBool
+HandleARDAuth(rfbClient *client)
+{
+ uint8_t gen[2], len[2];
+ size_t keylen;
+ uint8_t *mod = NULL, *resp, *pub, *key, *shared;
+ gcry_mpi_t genmpi = NULL, modmpi = NULL, respmpi = NULL;
+ gcry_mpi_t privmpi = NULL, pubmpi = NULL, keympi = NULL;
+ gcry_md_hd_t md5 = NULL;
+ gcry_cipher_hd_t aes = NULL;
+ gcry_error_t error;
+ uint8_t userpass[128], ciphertext[128];
+ int passwordLen, usernameLen;
+ rfbCredential *cred = NULL;
+ rfbBool result = FALSE;
+
+ while (1)
+ {
+ if (!ReadFromRFBServer(client, (char *)gen, 2))
+ break;
+ if (!ReadFromRFBServer(client, (char *)len, 2))
+ break;
+
+ if (!client->GetCredential)
+ {
+ rfbClientLog("GetCredential callback is not set.\n");
+ break;
+ }
+ cred = client->GetCredential(client, rfbCredentialTypeUser);
+ if (!cred)
+ {
+ rfbClientLog("Reading credential failed\n");
+ break;
+ }
+
+ keylen = 256*len[0]+len[1];
+ mod = (uint8_t*)malloc(keylen*4);
+ if (!mod)
+ {
+ rfbClientLog("malloc out of memory\n");
+ break;
+ }
+ resp = mod+keylen;
+ pub = resp+keylen;
+ key = pub+keylen;
+
+ if (!ReadFromRFBServer(client, (char *)mod, keylen))
+ break;
+ if (!ReadFromRFBServer(client, (char *)resp, keylen))
+ break;
+
+ error = gcry_mpi_scan(&genmpi, GCRYMPI_FMT_USG, gen, 2, NULL);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
+ break;
+ }
+ error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, mod, keylen, NULL);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
+ break;
+ }
+ error = gcry_mpi_scan(&respmpi, GCRYMPI_FMT_USG, resp, keylen, NULL);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
+ break;
+ }
+
+ privmpi = gcry_mpi_new(keylen);
+ if (!privmpi)
+ {
+ rfbClientLog("gcry_mpi_new out of memory\n");
+ break;
+ }
+ gcry_mpi_randomize(privmpi, (keylen/8)*8, GCRY_STRONG_RANDOM);
+
+ pubmpi = gcry_mpi_new(keylen);
+ if (!pubmpi)
+ {
+ rfbClientLog("gcry_mpi_new out of memory\n");
+ break;
+ }
+ gcry_mpi_powm(pubmpi, genmpi, privmpi, modmpi);
+
+ keympi = gcry_mpi_new(keylen);
+ if (!keympi)
+ {
+ rfbClientLog("gcry_mpi_new out of memory\n");
+ break;
+ }
+ gcry_mpi_powm(keympi, respmpi, privmpi, modmpi);
+
+ if (!rfbMpiToBytes(pubmpi, pub, keylen))
+ break;
+ if (!rfbMpiToBytes(keympi, key, keylen))
+ break;
+
+ error = gcry_md_open(&md5, GCRY_MD_MD5, 0);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_md_open error: %s\n", gcry_strerror(error));
+ break;
+ }
+ gcry_md_write(md5, key, keylen);
+ error = gcry_md_final(md5);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_md_final error: %s\n", gcry_strerror(error));
+ break;
+ }
+ shared = gcry_md_read(md5, GCRY_MD_MD5);
+
+ passwordLen = strlen(cred->userCredential.password)+1;
+ usernameLen = strlen(cred->userCredential.username)+1;
+ if (passwordLen > sizeof(userpass)/2)
+ passwordLen = sizeof(userpass)/2;
+ if (usernameLen > sizeof(userpass)/2)
+ usernameLen = sizeof(userpass)/2;
+
+ gcry_randomize(userpass, sizeof(userpass), GCRY_STRONG_RANDOM);
+ memcpy(userpass, cred->userCredential.username, usernameLen);
+ memcpy(userpass+sizeof(userpass)/2, cred->userCredential.password, passwordLen);
+
+ error = gcry_cipher_open(&aes, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_cipher_open error: %s\n", gcry_strerror(error));
+ break;
+ }
+ error = gcry_cipher_setkey(aes, shared, 16);
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_cipher_setkey error: %s\n", gcry_strerror(error));
+ break;
+ }
+ error = gcry_cipher_encrypt(aes, ciphertext, sizeof(ciphertext), userpass, sizeof(userpass));
+ if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
+ {
+ rfbClientLog("gcry_cipher_encrypt error: %s\n", gcry_strerror(error));
+ break;
+ }
+
+ if (!WriteToRFBServer(client, (char *)ciphertext, sizeof(ciphertext)))
+ break;
+ if (!WriteToRFBServer(client, (char *)pub, keylen))
+ break;
+
+ /* Handle the SecurityResult message */
+ if (!rfbHandleAuthResult(client))
+ break;
+
+ result = TRUE;
+ break;
+ }
+
+ if (cred)
+ FreeUserCredential(cred);
+ if (mod)
+ free(mod);
+ if (genmpi)
+ gcry_mpi_release(genmpi);
+ if (modmpi)
+ gcry_mpi_release(modmpi);
+ if (respmpi)
+ gcry_mpi_release(respmpi);
+ if (privmpi)
+ gcry_mpi_release(privmpi);
+ if (pubmpi)
+ gcry_mpi_release(pubmpi);
+ if (keympi)
+ gcry_mpi_release(keympi);
+ if (md5)
+ gcry_md_close(md5);
+ if (aes)
+ gcry_cipher_close(aes);
+ return result;
+}
+#endif
+
/*
* SetClientAuthSchemes.
*/
@@ -928,6 +1135,15 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandleMSLogonAuth(client)) return FALSE;
break;
+ case rfbARD:
+#ifndef LIBVNCSERVER_WITH_CLIENT_GCRYPT
+ rfbClientLog("GCrypt support was not compiled in\n");
+ return FALSE;
+#else
+ if (!HandleARDAuth(client)) return FALSE;
+#endif
+ break;
+
case rfbTLS:
#ifndef LIBVNCSERVER_WITH_CLIENT_TLS
rfbClientLog("TLS support was not compiled in\n");
diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h
index 023528b..73d200a 100644
--- a/rfb/rfbproto.h
+++ b/rfb/rfbproto.h
@@ -286,6 +286,7 @@ typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
#define rfbUltra 17
#define rfbTLS 18
#define rfbVeNCrypt 19
+#define rfbARD 30
#define rfbMSLogon 0xfffffffa
#define rfbVeNCryptPlain 256