endif() | endif() | ||||
endif() | endif() | ||||
option(ENABLE_NETTLE "Enable RSA-AES security types" ON) | |||||
if (ENABLE_NETTLE) | |||||
find_package(Nettle) | |||||
if (NETTLE_FOUND) | |||||
add_definitions("-DHAVE_NETTLE") | |||||
endif() | |||||
endif() | |||||
# Check for PAM library | # Check for PAM library | ||||
if(UNIX AND NOT APPLE) | if(UNIX AND NOT APPLE) | ||||
check_include_files(security/pam_appl.h HAVE_PAM_H) | check_include_files(security/pam_appl.h HAVE_PAM_H) |
find_package(PkgConfig) | |||||
if (PKG_CONFIG_FOUND) | |||||
pkg_check_modules(NETTLE nettle>=3.0) | |||||
pkg_check_modules(HOGWEED hogweed) | |||||
pkg_check_modules(GMP gmp) | |||||
else() | |||||
find_path(NETTLE_INCLUDE_DIRS NAMES eax.h PATH_SUFFIXES nettle) | |||||
find_library(NETTLE_LIBRARIES NAMES nettle) | |||||
find_package_handle_standard_args(NETTLE DEFAULT_MSG NETTLE_LIBRARIES NETTLE_INCLUDE_DIRS) | |||||
find_path(GMP_INCLUDE_DIRS NAMES gmp.h PATH_SUFFIXES) | |||||
find_library(GMP_LIBRARIES NAMES gmp) | |||||
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARIES GMP_INCLUDE_DIRS) | |||||
find_library(HOGWEED_LIBRARIES NAMES hogweed) | |||||
find_package_handle_standard_args(HOGWEED DEFAULT_MSG HOGWEED_LIBRARIES) | |||||
endif() | |||||
if (NOT HOGWEED_FOUND OR NOT GMP_FOUND) | |||||
set(NETTLE_FOUND 0) | |||||
endif() |
string(STRIP ${GNUTLS_LIBRARIES} GNUTLS_LIBRARIES) | string(STRIP ${GNUTLS_LIBRARIES} GNUTLS_LIBRARIES) | ||||
endif() | endif() | ||||
if(NETTLE_FOUND) | |||||
set(NETTLE_LIBRARIES "-Wl,-Bstatic -lnettle -Wl,-Bdynamic") | |||||
set(HOGWEED_LIBRARIES "-Wl,-Bstatic -lhogweed -Wl,-Bdynamic") | |||||
set(GMP_LIBRARIES "-Wl,-Bstatic -lgmp -Wl,-Bdynamic") | |||||
endif() | |||||
if(DEFINED FLTK_LIBRARIES) | if(DEFINED FLTK_LIBRARIES) | ||||
set(FLTK_LIBRARIES "-Wl,-Bstatic -lfltk_images -lpng -ljpeg -lfltk -Wl,-Bdynamic") | set(FLTK_LIBRARIES "-Wl,-Bstatic -lfltk_images -lpng -ljpeg -lfltk -Wl,-Bdynamic") | ||||
/* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#include <assert.h> | |||||
#include <rdr/AESInStream.h> | |||||
#include <rdr/Exception.h> | |||||
#ifdef HAVE_NETTLE | |||||
using namespace rdr; | |||||
AESInStream::AESInStream(InStream* _in, const U8* key, int _keySize) | |||||
: keySize(_keySize), in(_in), counter() | |||||
{ | |||||
if (keySize == 128) | |||||
EAX_SET_KEY(&eaxCtx128, aes128_set_encrypt_key, aes128_encrypt, key); | |||||
else if (keySize == 256) | |||||
EAX_SET_KEY(&eaxCtx256, aes256_set_encrypt_key, aes256_encrypt, key); | |||||
else | |||||
assert(!"incorrect key size"); | |||||
} | |||||
AESInStream::~AESInStream() {} | |||||
bool AESInStream::fillBuffer() | |||||
{ | |||||
if (!in->hasData(2)) | |||||
return false; | |||||
const U8* ptr = in->getptr(2); | |||||
size_t length = ((int)ptr[0] << 8) | (int)ptr[1]; | |||||
if (!in->hasData(2 + length + 16)) | |||||
return false; | |||||
ensureSpace(length); | |||||
ptr = in->getptr(2 + length + 16); | |||||
const U8* ad = ptr; | |||||
const U8* data = ptr + 2; | |||||
const U8* mac = ptr + 2 + length; | |||||
U8 macComputed[16]; | |||||
if (keySize == 128) { | |||||
EAX_SET_NONCE(&eaxCtx128, aes128_encrypt, 16, counter); | |||||
EAX_UPDATE(&eaxCtx128, aes128_encrypt, 2, ad); | |||||
EAX_DECRYPT(&eaxCtx128, aes128_encrypt, length, (rdr::U8*)end, data); | |||||
EAX_DIGEST(&eaxCtx128, aes128_encrypt, 16, macComputed); | |||||
} else { | |||||
EAX_SET_NONCE(&eaxCtx256, aes256_encrypt, 16, counter); | |||||
EAX_UPDATE(&eaxCtx256, aes256_encrypt, 2, ad); | |||||
EAX_DECRYPT(&eaxCtx256, aes256_encrypt, length, (rdr::U8*)end, data); | |||||
EAX_DIGEST(&eaxCtx256, aes256_encrypt, 16, macComputed); | |||||
} | |||||
if (memcmp(mac, macComputed, 16) != 0) | |||||
throw Exception("AESInStream: failed to authenticate message"); | |||||
in->setptr(2 + length + 16); | |||||
end += length; | |||||
// Update nonce by incrementing the counter as a | |||||
// 128bit little endian unsigned integer | |||||
for (int i = 0; i < 16; ++i) { | |||||
// increment until there is no carry | |||||
if (++counter[i] != 0) { | |||||
break; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
#endif |
/* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifndef __RDR_AESINSTREAM_H__ | |||||
#define __RDR_AESINSTREAM_H__ | |||||
#ifdef HAVE_NETTLE | |||||
#include <nettle/eax.h> | |||||
#include <nettle/aes.h> | |||||
#include <rdr/BufferedInStream.h> | |||||
namespace rdr { | |||||
class AESInStream : public BufferedInStream { | |||||
public: | |||||
AESInStream(InStream* in, const U8* key, int keySize); | |||||
virtual ~AESInStream(); | |||||
private: | |||||
virtual bool fillBuffer(); | |||||
int keySize; | |||||
InStream* in; | |||||
union { | |||||
struct EAX_CTX(aes128_ctx) eaxCtx128; | |||||
struct EAX_CTX(aes256_ctx) eaxCtx256; | |||||
}; | |||||
U8 counter[16]; | |||||
}; | |||||
} | |||||
#endif | |||||
#endif |
/* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#include <assert.h> | |||||
#include <rdr/Exception.h> | |||||
#include <rdr/AESOutStream.h> | |||||
#ifdef HAVE_NETTLE | |||||
using namespace rdr; | |||||
const int MaxMessageSize = 8192; | |||||
AESOutStream::AESOutStream(OutStream* _out, const U8* key, int _keySize) | |||||
: keySize(_keySize), out(_out), counter() | |||||
{ | |||||
msg = new U8[MaxMessageSize + 16 + 2]; | |||||
if (keySize == 128) | |||||
EAX_SET_KEY(&eaxCtx128, aes128_set_encrypt_key, aes128_encrypt, key); | |||||
else if (keySize == 256) | |||||
EAX_SET_KEY(&eaxCtx256, aes256_set_encrypt_key, aes256_encrypt, key); | |||||
else | |||||
assert(!"incorrect key size"); | |||||
} | |||||
AESOutStream::~AESOutStream() | |||||
{ | |||||
delete[] msg; | |||||
} | |||||
void AESOutStream::flush() | |||||
{ | |||||
BufferedOutStream::flush(); | |||||
out->flush(); | |||||
} | |||||
void AESOutStream::cork(bool enable) | |||||
{ | |||||
BufferedOutStream::cork(enable); | |||||
out->cork(enable); | |||||
} | |||||
bool AESOutStream::flushBuffer() | |||||
{ | |||||
while (sentUpTo < ptr) { | |||||
size_t n = ptr - sentUpTo; | |||||
if (n > MaxMessageSize) | |||||
n = MaxMessageSize; | |||||
writeMessage(sentUpTo, n); | |||||
sentUpTo += n; | |||||
} | |||||
return true; | |||||
} | |||||
void AESOutStream::writeMessage(const U8* data, size_t length) | |||||
{ | |||||
msg[0] = (length & 0xff00) >> 8; | |||||
msg[1] = length & 0xff; | |||||
if (keySize == 128) { | |||||
EAX_SET_NONCE(&eaxCtx128, aes128_encrypt, 16, counter); | |||||
EAX_UPDATE(&eaxCtx128, aes128_encrypt, 2, msg); | |||||
EAX_ENCRYPT(&eaxCtx128, aes128_encrypt, length, msg + 2, data); | |||||
EAX_DIGEST(&eaxCtx128, aes128_encrypt, 16, msg + 2 + length); | |||||
} else { | |||||
EAX_SET_NONCE(&eaxCtx256, aes256_encrypt, 16, counter); | |||||
EAX_UPDATE(&eaxCtx256, aes256_encrypt, 2, msg); | |||||
EAX_ENCRYPT(&eaxCtx256, aes256_encrypt, length, msg + 2, data); | |||||
EAX_DIGEST(&eaxCtx256, aes256_encrypt, 16, msg + 2 + length); | |||||
} | |||||
out->writeBytes(msg, 2 + length + 16); | |||||
out->flush(); | |||||
// Update nonce by incrementing the counter as a | |||||
// 128bit little endian unsigned integer | |||||
for (int i = 0; i < 16; ++i) { | |||||
// increment until there is no carry | |||||
if (++counter[i] != 0) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
#endif |
/* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifndef __RDR_AESOUTSTREAM_H__ | |||||
#define __RDR_AESOUTSTREAM_H__ | |||||
#ifdef HAVE_NETTLE | |||||
#include <nettle/eax.h> | |||||
#include <nettle/aes.h> | |||||
#include <rdr/BufferedOutStream.h> | |||||
namespace rdr { | |||||
class AESOutStream : public BufferedOutStream { | |||||
public: | |||||
AESOutStream(OutStream* out, const U8* key, int keySize); | |||||
virtual ~AESOutStream(); | |||||
virtual void flush(); | |||||
virtual void cork(bool enable); | |||||
private: | |||||
virtual bool flushBuffer(); | |||||
void writeMessage(const U8* data, size_t length); | |||||
int keySize; | |||||
OutStream* out; | |||||
U8* msg; | |||||
union { | |||||
struct EAX_CTX(aes128_ctx) eaxCtx128; | |||||
struct EAX_CTX(aes256_ctx) eaxCtx256; | |||||
}; | |||||
U8 counter[16]; | |||||
}; | |||||
}; | |||||
#endif | |||||
#endif |
include_directories(${ZLIB_INCLUDE_DIRS}) | include_directories(${ZLIB_INCLUDE_DIRS}) | ||||
add_library(rdr STATIC | add_library(rdr STATIC | ||||
AESInStream.cxx | |||||
AESOutStream.cxx | |||||
BufferedInStream.cxx | BufferedInStream.cxx | ||||
BufferedOutStream.cxx | BufferedOutStream.cxx | ||||
Exception.cxx | Exception.cxx | ||||
include_directories(${GNUTLS_INCLUDE_DIR}) | include_directories(${GNUTLS_INCLUDE_DIR}) | ||||
target_link_libraries(rdr ${GNUTLS_LIBRARIES}) | target_link_libraries(rdr ${GNUTLS_LIBRARIES}) | ||||
endif() | endif() | ||||
if (NETTLE_FOUND) | |||||
include_directories(${NETTLE_INCLUDE_DIRS}) | |||||
target_link_libraries(rdr ${NETTLE_LINK_LIBRARIES}) | |||||
endif() | |||||
if(WIN32) | if(WIN32) | ||||
target_link_libraries(rdr ws2_32) | target_link_libraries(rdr ws2_32) | ||||
endif() | endif() |
target_link_libraries(rfb ${GNUTLS_LIBRARIES}) | target_link_libraries(rfb ${GNUTLS_LIBRARIES}) | ||||
endif() | endif() | ||||
if (NETTLE_FOUND) | |||||
target_sources(rfb PRIVATE CSecurityRSAAES.cxx SSecurityRSAAES.cxx) | |||||
include_directories(${NETTLE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS}) | |||||
target_link_libraries(rfb ${HOGWEED_LINK_LIBRARIES} ${NETTLE_LINK_LIBRARIES} ${GMP_LINK_LIBRARIES}) | |||||
endif() | |||||
if(UNIX) | if(UNIX) | ||||
libtool_create_control_file(rfb) | libtool_create_control_file(rfb) | ||||
endif() | endif() |
#define __RFB_CSECURITY_H__ | #define __RFB_CSECURITY_H__ | ||||
#include <rfb/UserPasswdGetter.h> | #include <rfb/UserPasswdGetter.h> | ||||
#include <rfb/UserMsgBox.h> | |||||
namespace rfb { | namespace rfb { | ||||
class CConnection; | class CConnection; | ||||
* It MUST be set by viewer. | * It MUST be set by viewer. | ||||
*/ | */ | ||||
static UserPasswdGetter *upg; | static UserPasswdGetter *upg; | ||||
static UserMsgBox *msg; | |||||
protected: | protected: | ||||
CConnection* cc; | CConnection* cc; |
/* | |||||
* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#ifndef HAVE_NETTLE | |||||
#error "This header should not be compiled without HAVE_NETTLE defined" | |||||
#endif | |||||
#include <stdlib.h> | |||||
#ifndef WIN32 | |||||
#include <unistd.h> | |||||
#endif | |||||
#include <assert.h> | |||||
#include <nettle/bignum.h> | |||||
#include <nettle/sha1.h> | |||||
#include <nettle/sha2.h> | |||||
#include <rfb/CSecurityRSAAES.h> | |||||
#include <rfb/CConnection.h> | |||||
#include <rfb/LogWriter.h> | |||||
#include <rfb/Exception.h> | |||||
#include <rfb/UserMsgBox.h> | |||||
#include <rdr/AESInStream.h> | |||||
#include <rdr/AESOutStream.h> | |||||
#include <os/os.h> | |||||
enum { | |||||
ReadPublicKey, | |||||
ReadRandom, | |||||
ReadHash, | |||||
ReadSubtype, | |||||
}; | |||||
const int MinKeyLength = 1024; | |||||
const int MaxKeyLength = 8192; | |||||
using namespace rfb; | |||||
CSecurityRSAAES::CSecurityRSAAES(CConnection* cc, rdr::U32 _secType, | |||||
int _keySize, bool _isAllEncrypted) | |||||
: CSecurity(cc), state(ReadPublicKey), | |||||
keySize(_keySize), isAllEncrypted(_isAllEncrypted), secType(_secType), | |||||
clientKey(), clientPublicKey(), serverKey(), | |||||
serverKeyN(NULL), serverKeyE(NULL), | |||||
clientKeyN(NULL), clientKeyE(NULL), | |||||
rais(NULL), raos(NULL), rawis(NULL), rawos(NULL) | |||||
{ | |||||
assert(keySize == 128 || keySize == 256); | |||||
} | |||||
CSecurityRSAAES::~CSecurityRSAAES() | |||||
{ | |||||
cleanup(); | |||||
} | |||||
void CSecurityRSAAES::cleanup() | |||||
{ | |||||
if (serverKeyN) | |||||
delete[] serverKeyN; | |||||
if (serverKeyE) | |||||
delete[] serverKeyE; | |||||
if (clientKeyN) | |||||
delete[] clientKeyN; | |||||
if (clientKeyE) | |||||
delete[] clientKeyE; | |||||
if (clientKey.size) | |||||
rsa_private_key_clear(&clientKey); | |||||
if (clientPublicKey.size) | |||||
rsa_public_key_clear(&clientPublicKey); | |||||
if (serverKey.size) | |||||
rsa_public_key_clear(&serverKey); | |||||
if (isAllEncrypted && rawis && rawos) | |||||
cc->setStreams(rawis, rawos); | |||||
if (rais) | |||||
delete rais; | |||||
if (raos) | |||||
delete raos; | |||||
} | |||||
bool CSecurityRSAAES::processMsg() | |||||
{ | |||||
switch (state) { | |||||
case ReadPublicKey: | |||||
if (readPublicKey()) { | |||||
verifyServer(); | |||||
writePublicKey(); | |||||
writeRandom(); | |||||
state = ReadRandom; | |||||
} | |||||
return false; | |||||
case ReadRandom: | |||||
if (readRandom()) { | |||||
setCipher(); | |||||
writeHash(); | |||||
state = ReadHash; | |||||
} | |||||
return false; | |||||
case ReadHash: | |||||
if (readHash()) { | |||||
clearSecrets(); | |||||
state = ReadSubtype; | |||||
} | |||||
case ReadSubtype: | |||||
if (readSubtype()) { | |||||
writeCredentials(); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
assert(!"unreachable"); | |||||
return false; | |||||
} | |||||
static void random_func(void* ctx, size_t length, uint8_t* dst) | |||||
{ | |||||
rdr::RandomStream* rs = (rdr::RandomStream*)ctx; | |||||
if (!rs->hasData(length)) | |||||
throw ConnFailedException("failed to generate random"); | |||||
rs->readBytes(dst, length); | |||||
} | |||||
void CSecurityRSAAES::writePublicKey() | |||||
{ | |||||
rdr::OutStream* os = cc->getOutStream(); | |||||
// generate client key | |||||
rsa_public_key_init(&clientPublicKey); | |||||
rsa_private_key_init(&clientKey); | |||||
// match the server key size | |||||
clientKeyLength = serverKeyLength; | |||||
int rsaKeySize = (clientKeyLength + 7) / 8; | |||||
// set key size to non-zero to allow clearing the keys when cleanup | |||||
clientPublicKey.size = rsaKeySize; | |||||
clientKey.size = rsaKeySize; | |||||
// set e = 65537 | |||||
mpz_set_ui(clientPublicKey.e, 65537); | |||||
if (!rsa_generate_keypair(&clientPublicKey, &clientKey, | |||||
&rs, random_func, NULL, NULL, clientKeyLength, 0)) | |||||
throw AuthFailureException("failed to generate key"); | |||||
clientKeyN = new rdr::U8[rsaKeySize]; | |||||
clientKeyE = new rdr::U8[rsaKeySize]; | |||||
nettle_mpz_get_str_256(rsaKeySize, clientKeyN, clientPublicKey.n); | |||||
nettle_mpz_get_str_256(rsaKeySize, clientKeyE, clientPublicKey.e); | |||||
os->writeU32(clientKeyLength); | |||||
os->writeBytes(clientKeyN, rsaKeySize); | |||||
os->writeBytes(clientKeyE, rsaKeySize); | |||||
os->flush(); | |||||
} | |||||
bool CSecurityRSAAES::readPublicKey() | |||||
{ | |||||
rdr::InStream* is = cc->getInStream(); | |||||
if (!is->hasData(4)) | |||||
return false; | |||||
is->setRestorePoint(); | |||||
serverKeyLength = is->readU32(); | |||||
if (serverKeyLength < MinKeyLength) | |||||
throw AuthFailureException("server key is too short"); | |||||
if (serverKeyLength > MaxKeyLength) | |||||
throw AuthFailureException("server key is too long"); | |||||
size_t size = (serverKeyLength + 7) / 8; | |||||
if (!is->hasDataOrRestore(size * 2)) | |||||
return false; | |||||
is->clearRestorePoint(); | |||||
serverKeyE = new rdr::U8[size]; | |||||
serverKeyN = new rdr::U8[size]; | |||||
is->readBytes(serverKeyN, size); | |||||
is->readBytes(serverKeyE, size); | |||||
rsa_public_key_init(&serverKey); | |||||
nettle_mpz_set_str_256_u(serverKey.n, size, serverKeyN); | |||||
nettle_mpz_set_str_256_u(serverKey.e, size, serverKeyE); | |||||
if (!rsa_public_key_prepare(&serverKey)) | |||||
throw AuthFailureException("server key is invalid"); | |||||
return true; | |||||
} | |||||
void CSecurityRSAAES::verifyServer() | |||||
{ | |||||
rdr::U8 lenServerKey[4] = { | |||||
(rdr::U8)((serverKeyLength & 0xff000000) >> 24), | |||||
(rdr::U8)((serverKeyLength & 0xff0000) >> 16), | |||||
(rdr::U8)((serverKeyLength & 0xff00) >> 8), | |||||
(rdr::U8)(serverKeyLength & 0xff) | |||||
}; | |||||
rdr::U8 f[8]; | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 4, lenServerKey); | |||||
sha1_update(&ctx, serverKey.size, serverKeyN); | |||||
sha1_update(&ctx, serverKey.size, serverKeyE); | |||||
sha1_digest(&ctx, sizeof(f), f); | |||||
const char *title = "Server key fingerprint"; | |||||
CharArray text; | |||||
text.format( | |||||
"The server has provided the following identifying information:\n" | |||||
"Fingerprint: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n" | |||||
"Please verify that the information is correct and press \"Yes\". " | |||||
"Otherwise press \"No\"", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7]); | |||||
if (!msg->showMsgBox(UserMsgBox::M_YESNO, title, text.buf)) | |||||
throw AuthFailureException("server key mismatch"); | |||||
} | |||||
void CSecurityRSAAES::writeRandom() | |||||
{ | |||||
rdr::OutStream* os = cc->getOutStream(); | |||||
if (!rs.hasData(keySize / 8)) | |||||
throw ConnFailedException("failed to generate random"); | |||||
rs.readBytes(clientRandom, keySize / 8); | |||||
mpz_t x; | |||||
mpz_init(x); | |||||
int res; | |||||
try { | |||||
res = rsa_encrypt(&serverKey, &rs, random_func, keySize / 8, | |||||
clientRandom, x); | |||||
} catch (...) { | |||||
mpz_clear(x); | |||||
throw; | |||||
} | |||||
if (!res) { | |||||
mpz_clear(x); | |||||
throw AuthFailureException("failed to encrypt random"); | |||||
} | |||||
rdr::U8* buffer = new rdr::U8[serverKey.size]; | |||||
nettle_mpz_get_str_256(serverKey.size, buffer, x); | |||||
mpz_clear(x); | |||||
os->writeU16(serverKey.size); | |||||
os->writeBytes(buffer, serverKey.size); | |||||
os->flush(); | |||||
delete[] buffer; | |||||
} | |||||
bool CSecurityRSAAES::readRandom() | |||||
{ | |||||
rdr::InStream* is = cc->getInStream(); | |||||
if (!is->hasData(2)) | |||||
return false; | |||||
is->setRestorePoint(); | |||||
size_t size = is->readU16(); | |||||
if (size != clientKey.size) | |||||
throw AuthFailureException("client key length doesn't match"); | |||||
if (!is->hasDataOrRestore(size)) | |||||
return false; | |||||
is->clearRestorePoint(); | |||||
rdr::U8* buffer = new rdr::U8[size]; | |||||
is->readBytes(buffer, size); | |||||
size_t randomSize = keySize / 8; | |||||
mpz_t x; | |||||
nettle_mpz_init_set_str_256_u(x, size, buffer); | |||||
delete[] buffer; | |||||
if (!rsa_decrypt(&clientKey, &randomSize, serverRandom, x) || | |||||
randomSize != (size_t)keySize / 8) { | |||||
mpz_clear(x); | |||||
throw AuthFailureException("failed to decrypt server random"); | |||||
} | |||||
mpz_clear(x); | |||||
return true; | |||||
} | |||||
void CSecurityRSAAES::setCipher() | |||||
{ | |||||
rawis = cc->getInStream(); | |||||
rawos = cc->getOutStream(); | |||||
rdr::U8 key[32]; | |||||
if (keySize == 128) { | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 16, clientRandom); | |||||
sha1_update(&ctx, 16, serverRandom); | |||||
sha1_digest(&ctx, 16, key); | |||||
rais = new rdr::AESInStream(rawis, key, 128); | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 16, serverRandom); | |||||
sha1_update(&ctx, 16, clientRandom); | |||||
sha1_digest(&ctx, 16, key); | |||||
raos = new rdr::AESOutStream(rawos, key, 128); | |||||
} else { | |||||
struct sha256_ctx ctx; | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 32, clientRandom); | |||||
sha256_update(&ctx, 32, serverRandom); | |||||
sha256_digest(&ctx, 32, key); | |||||
rais = new rdr::AESInStream(rawis, key, 256); | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 32, serverRandom); | |||||
sha256_update(&ctx, 32, clientRandom); | |||||
sha256_digest(&ctx, 32, key); | |||||
raos = new rdr::AESOutStream(rawos, key, 256); | |||||
} | |||||
if (isAllEncrypted) | |||||
cc->setStreams(rais, raos); | |||||
} | |||||
void CSecurityRSAAES::writeHash() | |||||
{ | |||||
rdr::U8 hash[32]; | |||||
size_t len = serverKeyLength; | |||||
rdr::U8 lenServerKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
len = clientKeyLength; | |||||
rdr::U8 lenClientKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
int hashSize; | |||||
if (keySize == 128) { | |||||
hashSize = 20; | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 4, lenClientKey); | |||||
sha1_update(&ctx, clientKey.size, clientKeyN); | |||||
sha1_update(&ctx, clientKey.size, clientKeyE); | |||||
sha1_update(&ctx, 4, lenServerKey); | |||||
sha1_update(&ctx, serverKey.size, serverKeyN); | |||||
sha1_update(&ctx, serverKey.size, serverKeyE); | |||||
sha1_digest(&ctx, hashSize, hash); | |||||
} else { | |||||
hashSize = 32; | |||||
struct sha256_ctx ctx; | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 4, lenClientKey); | |||||
sha256_update(&ctx, clientKey.size, clientKeyN); | |||||
sha256_update(&ctx, clientKey.size, clientKeyE); | |||||
sha256_update(&ctx, 4, lenServerKey); | |||||
sha256_update(&ctx, serverKey.size, serverKeyN); | |||||
sha256_update(&ctx, serverKey.size, serverKeyE); | |||||
sha256_digest(&ctx, hashSize, hash); | |||||
} | |||||
raos->writeBytes(hash, hashSize); | |||||
raos->flush(); | |||||
} | |||||
bool CSecurityRSAAES::readHash() | |||||
{ | |||||
rdr::U8 hash[32]; | |||||
rdr::U8 realHash[32]; | |||||
int hashSize = keySize == 128 ? 20 : 32; | |||||
if (!rais->hasData(hashSize)) | |||||
return false; | |||||
rais->readBytes(hash, hashSize); | |||||
size_t len = serverKeyLength; | |||||
rdr::U8 lenServerKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
len = clientKeyLength; | |||||
rdr::U8 lenClientKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
if (keySize == 128) { | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 4, lenServerKey); | |||||
sha1_update(&ctx, serverKey.size, serverKeyN); | |||||
sha1_update(&ctx, serverKey.size, serverKeyE); | |||||
sha1_update(&ctx, 4, lenClientKey); | |||||
sha1_update(&ctx, clientKey.size, clientKeyN); | |||||
sha1_update(&ctx, clientKey.size, clientKeyE); | |||||
sha1_digest(&ctx, hashSize, realHash); | |||||
} else { | |||||
struct sha256_ctx ctx; | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 4, lenServerKey); | |||||
sha256_update(&ctx, serverKey.size, serverKeyN); | |||||
sha256_update(&ctx, serverKey.size, serverKeyE); | |||||
sha256_update(&ctx, 4, lenClientKey); | |||||
sha256_update(&ctx, clientKey.size, clientKeyN); | |||||
sha256_update(&ctx, clientKey.size, clientKeyE); | |||||
sha256_digest(&ctx, hashSize, realHash); | |||||
} | |||||
if (memcmp(hash, realHash, hashSize) != 0) | |||||
throw AuthFailureException("hash doesn't match"); | |||||
return true; | |||||
} | |||||
void CSecurityRSAAES::clearSecrets() | |||||
{ | |||||
rsa_private_key_clear(&clientKey); | |||||
rsa_public_key_clear(&clientPublicKey); | |||||
rsa_public_key_clear(&serverKey); | |||||
clientKey.size = 0; | |||||
clientPublicKey.size = 0; | |||||
serverKey.size = 0; | |||||
delete[] serverKeyN; | |||||
delete[] serverKeyE; | |||||
delete[] clientKeyN; | |||||
delete[] clientKeyE; | |||||
serverKeyN = NULL; | |||||
serverKeyE = NULL; | |||||
clientKeyN = NULL; | |||||
clientKeyE = NULL; | |||||
memset(serverRandom, 0, sizeof(serverRandom)); | |||||
memset(clientRandom, 0, sizeof(clientRandom)); | |||||
} | |||||
bool CSecurityRSAAES::readSubtype() | |||||
{ | |||||
if (!rais->hasData(1)) | |||||
return false; | |||||
subtype = rais->readU8(); | |||||
if (subtype != secTypeRA2UserPass && subtype != secTypeRA2Pass) | |||||
throw AuthFailureException("unknown RSA-AES subtype"); | |||||
return true; | |||||
} | |||||
void CSecurityRSAAES::writeCredentials() | |||||
{ | |||||
CharArray username; | |||||
CharArray password; | |||||
(CSecurity::upg)->getUserPasswd( | |||||
isSecure(), | |||||
subtype == secTypeRA2UserPass ? &username.buf : NULL, &password.buf | |||||
); | |||||
size_t len; | |||||
if (username.buf) { | |||||
len = strlen(username.buf); | |||||
if (len > 255) | |||||
throw AuthFailureException("username is too long"); | |||||
raos->writeU8(len); | |||||
if (len) | |||||
raos->writeBytes(username.buf, len); | |||||
} else { | |||||
raos->writeU8(0); | |||||
} | |||||
len = strlen(password.buf); | |||||
if (len > 255) | |||||
throw AuthFailureException("password is too long"); | |||||
raos->writeU8(len); | |||||
if (len) | |||||
raos->writeBytes(password.buf, len); | |||||
raos->flush(); | |||||
} |
/* | |||||
* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifndef __C_SECURITY_RSAAES_H__ | |||||
#define __C_SECURITY_RSAAES_H__ | |||||
#ifndef HAVE_NETTLE | |||||
#error "This header should not be compiled without HAVE_NETTLE defined" | |||||
#endif | |||||
#include <nettle/rsa.h> | |||||
#include <rfb/CSecurity.h> | |||||
#include <rfb/Security.h> | |||||
#include <rfb/UserMsgBox.h> | |||||
#include <rdr/InStream.h> | |||||
#include <rdr/OutStream.h> | |||||
#include <rdr/RandomStream.h> | |||||
namespace rfb { | |||||
class UserMsgBox; | |||||
class CSecurityRSAAES : public CSecurity { | |||||
public: | |||||
CSecurityRSAAES(CConnection* cc, rdr::U32 secType, | |||||
int keySize, bool isAllEncrypted); | |||||
virtual ~CSecurityRSAAES(); | |||||
virtual bool processMsg(); | |||||
virtual int getType() const { return secType; } | |||||
virtual bool isSecure() const { return secType == secTypeRA256; } | |||||
static IntParameter RSAKeyLength; | |||||
private: | |||||
void cleanup(); | |||||
void writePublicKey(); | |||||
bool readPublicKey(); | |||||
void verifyServer(); | |||||
void writeRandom(); | |||||
bool readRandom(); | |||||
void setCipher(); | |||||
void writeHash(); | |||||
bool readHash(); | |||||
void clearSecrets(); | |||||
bool readSubtype(); | |||||
void writeCredentials(); | |||||
int state; | |||||
int keySize; | |||||
bool isAllEncrypted; | |||||
rdr::U32 secType; | |||||
rdr::U8 subtype; | |||||
struct rsa_private_key clientKey; | |||||
struct rsa_public_key clientPublicKey; | |||||
struct rsa_public_key serverKey; | |||||
rdr::U32 serverKeyLength; | |||||
rdr::U8* serverKeyN; | |||||
rdr::U8* serverKeyE; | |||||
rdr::U32 clientKeyLength; | |||||
rdr::U8* clientKeyN; | |||||
rdr::U8* clientKeyE; | |||||
rdr::U8 serverRandom[32]; | |||||
rdr::U8 clientRandom[32]; | |||||
rdr::InStream* rais; | |||||
rdr::OutStream* raos; | |||||
rdr::InStream* rawis; | |||||
rdr::OutStream* rawos; | |||||
rdr::RandomStream rs; | |||||
}; | |||||
} | |||||
#endif |
#include <gnutls/gnutls.h> | #include <gnutls/gnutls.h> | ||||
namespace rfb { | namespace rfb { | ||||
class UserMsgBox; | |||||
class CSecurityTLS : public CSecurity { | class CSecurityTLS : public CSecurity { | ||||
public: | public: | ||||
CSecurityTLS(CConnection* cc, bool _anon); | CSecurityTLS(CConnection* cc, bool _anon); | ||||
static StringParameter X509CA; | static StringParameter X509CA; | ||||
static StringParameter X509CRL; | static StringParameter X509CRL; | ||||
static UserMsgBox *msg; | |||||
protected: | protected: | ||||
void shutdown(); | void shutdown(); |
StringParameter PasswordValidator::plainUsers | StringParameter PasswordValidator::plainUsers | ||||
("PlainUsers", | ("PlainUsers", | ||||
"Users permitted to access via Plain security type (including TLSPlain, X509Plain etc.)", | |||||
"Users permitted to access via Plain security type (including TLSPlain, X509Plain etc.)" | |||||
#ifdef HAVE_NETTLE | |||||
" or RSA-AES security types (RA2, RA2ne, RA2_256, RA2ne_256)" | |||||
#endif | |||||
, | |||||
""); | ""); | ||||
bool PasswordValidator::validUser(const char* username) | bool PasswordValidator::validUser(const char* username) |
/* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#ifndef HAVE_NETTLE | |||||
#error "This source should not be compiled without HAVE_NETTLE defined" | |||||
#endif | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <assert.h> | |||||
#include <nettle/bignum.h> | |||||
#include <nettle/sha1.h> | |||||
#include <nettle/sha2.h> | |||||
#include <nettle/base64.h> | |||||
#include <nettle/asn1.h> | |||||
#include <rfb/SSecurityRSAAES.h> | |||||
#include <rfb/SConnection.h> | |||||
#include <rfb/LogWriter.h> | |||||
#include <rfb/Exception.h> | |||||
#include <rdr/AESInStream.h> | |||||
#include <rdr/AESOutStream.h> | |||||
#if !defined(WIN32) && !defined(__APPLE__) | |||||
#include <rfb/UnixPasswordValidator.h> | |||||
#endif | |||||
#ifdef WIN32 | |||||
#include <rfb/WinPasswdValidator.h> | |||||
#endif | |||||
#include <rfb/SSecurityVncAuth.h> | |||||
enum { | |||||
SendPublicKey, | |||||
ReadPublicKey, | |||||
ReadRandom, | |||||
ReadHash, | |||||
ReadCredentials, | |||||
}; | |||||
const int MinKeyLength = 1024; | |||||
const int MaxKeyLength = 8192; | |||||
const size_t MaxKeyFileSize = 32 * 1024; | |||||
using namespace rfb; | |||||
StringParameter SSecurityRSAAES::keyFile | |||||
("RSAKey", "Path to the RSA key for the RSA-AES security types in " | |||||
"PEM format", "", ConfServer); | |||||
BoolParameter SSecurityRSAAES::requireUsername | |||||
("RequireUsername", "Require username for the RSA-AES security types", | |||||
false, ConfServer); | |||||
SSecurityRSAAES::SSecurityRSAAES(SConnection* sc, rdr::U32 _secType, | |||||
int _keySize, bool _isAllEncrypted) | |||||
: SSecurity(sc), state(SendPublicKey), | |||||
keySize(_keySize), isAllEncrypted(_isAllEncrypted), secType(_secType), | |||||
serverKey(), clientKey(), | |||||
serverKeyN(NULL), serverKeyE(NULL), clientKeyN(NULL), clientKeyE(NULL), | |||||
accessRights(SConnection::AccessDefault), | |||||
rais(NULL), raos(NULL), rawis(NULL), rawos(NULL) | |||||
{ | |||||
assert(keySize == 128 || keySize == 256); | |||||
} | |||||
SSecurityRSAAES::~SSecurityRSAAES() | |||||
{ | |||||
cleanup(); | |||||
} | |||||
void SSecurityRSAAES::cleanup() | |||||
{ | |||||
if (serverKeyN) | |||||
delete[] serverKeyN; | |||||
if (serverKeyE) | |||||
delete[] serverKeyE; | |||||
if (clientKeyN) | |||||
delete[] clientKeyN; | |||||
if (clientKeyE) | |||||
delete[] clientKeyE; | |||||
if (serverKey.size) | |||||
rsa_private_key_clear(&serverKey); | |||||
if (clientKey.size) | |||||
rsa_public_key_clear(&clientKey); | |||||
if (isAllEncrypted && rawis && rawos) | |||||
sc->setStreams(rawis, rawos); | |||||
if (rais) | |||||
delete rais; | |||||
if (raos) | |||||
delete raos; | |||||
} | |||||
static inline ssize_t findSubstr(rdr::U8* data, size_t size, const char *pattern) | |||||
{ | |||||
size_t patternLength = strlen(pattern); | |||||
for (size_t i = 0; i + patternLength < size; ++i) { | |||||
for (size_t j = 0; j < patternLength; ++j) | |||||
if (data[i + j] != pattern[j]) | |||||
goto next; | |||||
return i; | |||||
next: | |||||
continue; | |||||
} | |||||
return -1; | |||||
} | |||||
static bool loadPEM(rdr::U8* data, size_t size, const char *begin, | |||||
const char *end, rdr::U8** der, size_t *derSize) | |||||
{ | |||||
ssize_t pos1 = findSubstr(data, size, begin); | |||||
if (pos1 == -1) | |||||
return false; | |||||
pos1 += strlen(begin); | |||||
ssize_t base64Size = findSubstr(data + pos1, size - pos1, end); | |||||
if (base64Size == -1) | |||||
return false; | |||||
char *derBase64 = (char *)data + pos1; | |||||
if (!base64Size) | |||||
return false; | |||||
*der = new rdr::U8[BASE64_DECODE_LENGTH(base64Size)]; | |||||
struct base64_decode_ctx ctx; | |||||
base64_decode_init(&ctx); | |||||
if (!base64_decode_update(&ctx, derSize, *der, base64Size, derBase64)) | |||||
return false; | |||||
if (!base64_decode_final(&ctx)) | |||||
return false; | |||||
return true; | |||||
} | |||||
void SSecurityRSAAES::loadPrivateKey() | |||||
{ | |||||
FILE* file = fopen(keyFile.getData(), "rb"); | |||||
if (!file) | |||||
throw ConnFailedException("failed to open key file"); | |||||
fseek(file, 0, SEEK_END); | |||||
size_t size = ftell(file); | |||||
if (size == 0 || size > MaxKeyFileSize) { | |||||
fclose(file); | |||||
throw ConnFailedException("size of key file is zero or too big"); | |||||
} | |||||
fseek(file, 0, SEEK_SET); | |||||
rdr::U8Array data(size); | |||||
if (fread(data.buf, 1, size, file) != size) { | |||||
fclose(file); | |||||
throw ConnFailedException("failed to read key"); | |||||
} | |||||
fclose(file); | |||||
rdr::U8Array der; | |||||
size_t derSize; | |||||
if (loadPEM(data.buf, size, "-----BEGIN RSA PRIVATE KEY-----\n", | |||||
"-----END RSA PRIVATE KEY-----", &der.buf, &derSize)) { | |||||
loadPKCS1Key(der.buf, derSize); | |||||
return; | |||||
} | |||||
if (der.buf) | |||||
delete[] der.takeBuf(); | |||||
if (loadPEM(data.buf, size, "-----BEGIN PRIVATE KEY-----\n", | |||||
"-----END PRIVATE KEY-----", &der.buf, &derSize)) { | |||||
loadPKCS8Key(der.buf, derSize); | |||||
return; | |||||
} | |||||
throw ConnFailedException("failed to import key"); | |||||
} | |||||
void SSecurityRSAAES::loadPKCS1Key(const rdr::U8* data, size_t size) | |||||
{ | |||||
struct rsa_public_key pub; | |||||
rsa_private_key_init(&serverKey); | |||||
rsa_public_key_init(&pub); | |||||
if (!rsa_keypair_from_der(&pub, &serverKey, 0, size, data)) { | |||||
rsa_private_key_clear(&serverKey); | |||||
rsa_public_key_clear(&pub); | |||||
throw ConnFailedException("failed to import key"); | |||||
} | |||||
serverKeyLength = serverKey.size * 8; | |||||
serverKeyN = new rdr::U8[serverKey.size]; | |||||
serverKeyE = new rdr::U8[serverKey.size]; | |||||
nettle_mpz_get_str_256(serverKey.size, serverKeyN, pub.n); | |||||
nettle_mpz_get_str_256(serverKey.size, serverKeyE, pub.e); | |||||
rsa_public_key_clear(&pub); | |||||
} | |||||
void SSecurityRSAAES::loadPKCS8Key(const rdr::U8* data, size_t size) | |||||
{ | |||||
struct asn1_der_iterator i, j; | |||||
uint32_t version; | |||||
const char* rsaIdentifier = "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01"; | |||||
const size_t rsaIdentifierLength = 9; | |||||
enum asn1_iterator_result res = asn1_der_iterator_first(&i, size, data); | |||||
if (res != ASN1_ITERATOR_CONSTRUCTED) | |||||
goto failed; | |||||
if (i.type != ASN1_SEQUENCE) | |||||
goto failed; | |||||
if (asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE) | |||||
goto failed; | |||||
if (!(i.type == ASN1_INTEGER && | |||||
asn1_der_get_uint32(&i, &version) && | |||||
version == 0)) | |||||
goto failed; | |||||
if (!(asn1_der_iterator_next(&i) == ASN1_ITERATOR_CONSTRUCTED && | |||||
i.type == ASN1_SEQUENCE && | |||||
asn1_der_decode_constructed(&i, &j) == ASN1_ITERATOR_PRIMITIVE && | |||||
j.type == ASN1_IDENTIFIER && | |||||
j.length == rsaIdentifierLength && | |||||
memcmp(j.data, rsaIdentifier, rsaIdentifierLength) == 0)) | |||||
goto failed; | |||||
if (!(asn1_der_iterator_next(&i) == ASN1_ITERATOR_PRIMITIVE && | |||||
i.type == ASN1_OCTETSTRING && i.length)) | |||||
goto failed; | |||||
loadPKCS1Key(i.data, i.length); | |||||
return; | |||||
failed: | |||||
throw ConnFailedException("failed to import key"); | |||||
} | |||||
bool SSecurityRSAAES::processMsg() | |||||
{ | |||||
switch (state) { | |||||
case SendPublicKey: | |||||
loadPrivateKey(); | |||||
writePublicKey(); | |||||
state = ReadPublicKey; | |||||
// fall through | |||||
case ReadPublicKey: | |||||
if (readPublicKey()) { | |||||
writeRandom(); | |||||
state = ReadRandom; | |||||
} | |||||
return false; | |||||
case ReadRandom: | |||||
if (readRandom()) { | |||||
setCipher(); | |||||
writeHash(); | |||||
state = ReadHash; | |||||
} | |||||
return false; | |||||
case ReadHash: | |||||
if (readHash()) { | |||||
clearSecrets(); | |||||
writeSubtype(); | |||||
state = ReadCredentials; | |||||
} | |||||
return false; | |||||
case ReadCredentials: | |||||
if (readCredentials()) { | |||||
if (requireUsername) | |||||
verifyUserPass(); | |||||
else | |||||
verifyPass(); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
assert(!"unreachable"); | |||||
return false; | |||||
} | |||||
void SSecurityRSAAES::writePublicKey() | |||||
{ | |||||
rdr::OutStream* os = sc->getOutStream(); | |||||
os->writeU32(serverKeyLength); | |||||
os->writeBytes(serverKeyN, serverKey.size); | |||||
os->writeBytes(serverKeyE, serverKey.size); | |||||
os->flush(); | |||||
} | |||||
bool SSecurityRSAAES::readPublicKey() | |||||
{ | |||||
rdr::InStream* is = sc->getInStream(); | |||||
if (!is->hasData(4)) | |||||
return false; | |||||
is->setRestorePoint(); | |||||
clientKeyLength = is->readU32(); | |||||
if (clientKeyLength < MinKeyLength) | |||||
throw ConnFailedException("client key is too short"); | |||||
if (clientKeyLength > MaxKeyLength) | |||||
throw ConnFailedException("client key is too long"); | |||||
size_t size = (clientKeyLength + 7) / 8; | |||||
if (!is->hasDataOrRestore(size * 2)) | |||||
return false; | |||||
is->clearRestorePoint(); | |||||
clientKeyE = new rdr::U8[size]; | |||||
clientKeyN = new rdr::U8[size]; | |||||
is->readBytes(clientKeyN, size); | |||||
is->readBytes(clientKeyE, size); | |||||
rsa_public_key_init(&clientKey); | |||||
nettle_mpz_set_str_256_u(clientKey.n, size, clientKeyN); | |||||
nettle_mpz_set_str_256_u(clientKey.e, size, clientKeyE); | |||||
if (!rsa_public_key_prepare(&clientKey)) | |||||
throw ConnFailedException("client key is invalid"); | |||||
return true; | |||||
} | |||||
static void random_func(void* ctx, size_t length, uint8_t* dst) | |||||
{ | |||||
rdr::RandomStream* rs = (rdr::RandomStream*)ctx; | |||||
if (!rs->hasData(length)) | |||||
throw ConnFailedException("failed to encrypt random"); | |||||
rs->readBytes(dst, length); | |||||
} | |||||
void SSecurityRSAAES::writeRandom() | |||||
{ | |||||
rdr::OutStream* os = sc->getOutStream(); | |||||
if (!rs.hasData(keySize / 8)) | |||||
throw ConnFailedException("failed to generate random"); | |||||
rs.readBytes(serverRandom, keySize / 8); | |||||
mpz_t x; | |||||
mpz_init(x); | |||||
int res; | |||||
try { | |||||
res = rsa_encrypt(&clientKey, &rs, random_func, keySize / 8, | |||||
serverRandom, x); | |||||
} catch (...) { | |||||
mpz_clear(x); | |||||
throw; | |||||
} | |||||
if (!res) { | |||||
mpz_clear(x); | |||||
throw ConnFailedException("failed to encrypt random"); | |||||
} | |||||
rdr::U8* buffer = new rdr::U8[clientKey.size]; | |||||
nettle_mpz_get_str_256(clientKey.size, buffer, x); | |||||
mpz_clear(x); | |||||
os->writeU16(clientKey.size); | |||||
os->writeBytes(buffer, clientKey.size); | |||||
os->flush(); | |||||
delete[] buffer; | |||||
} | |||||
bool SSecurityRSAAES::readRandom() | |||||
{ | |||||
rdr::InStream* is = sc->getInStream(); | |||||
if (!is->hasData(2)) | |||||
return false; | |||||
is->setRestorePoint(); | |||||
size_t size = is->readU16(); | |||||
if (size != serverKey.size) | |||||
throw ConnFailedException("server key length doesn't match"); | |||||
if (!is->hasDataOrRestore(size)) | |||||
return false; | |||||
is->clearRestorePoint(); | |||||
rdr::U8* buffer = new rdr::U8[size]; | |||||
is->readBytes(buffer, size); | |||||
size_t randomSize = keySize / 8; | |||||
mpz_t x; | |||||
nettle_mpz_init_set_str_256_u(x, size, buffer); | |||||
delete[] buffer; | |||||
if (!rsa_decrypt(&serverKey, &randomSize, clientRandom, x) || | |||||
randomSize != (size_t)keySize / 8) { | |||||
mpz_clear(x); | |||||
throw ConnFailedException("failed to decrypt client random"); | |||||
} | |||||
mpz_clear(x); | |||||
return true; | |||||
} | |||||
void SSecurityRSAAES::setCipher() | |||||
{ | |||||
rawis = sc->getInStream(); | |||||
rawos = sc->getOutStream(); | |||||
rdr::U8 key[32]; | |||||
if (keySize == 128) { | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 16, serverRandom); | |||||
sha1_update(&ctx, 16, clientRandom); | |||||
sha1_digest(&ctx, 16, key); | |||||
rais = new rdr::AESInStream(rawis, key, 128); | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 16, clientRandom); | |||||
sha1_update(&ctx, 16, serverRandom); | |||||
sha1_digest(&ctx, 16, key); | |||||
raos = new rdr::AESOutStream(rawos, key, 128); | |||||
} else { | |||||
struct sha256_ctx ctx; | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 32, serverRandom); | |||||
sha256_update(&ctx, 32, clientRandom); | |||||
sha256_digest(&ctx, 32, key); | |||||
rais = new rdr::AESInStream(rawis, key, 256); | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 32, clientRandom); | |||||
sha256_update(&ctx, 32, serverRandom); | |||||
sha256_digest(&ctx, 32, key); | |||||
raos = new rdr::AESOutStream(rawos, key, 256); | |||||
} | |||||
if (isAllEncrypted) | |||||
sc->setStreams(rais, raos); | |||||
} | |||||
void SSecurityRSAAES::writeHash() | |||||
{ | |||||
rdr::U8 hash[32]; | |||||
size_t len = serverKeyLength; | |||||
rdr::U8 lenServerKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
len = clientKeyLength; | |||||
rdr::U8 lenClientKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
int hashSize; | |||||
if (keySize == 128) { | |||||
hashSize = 20; | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 4, lenServerKey); | |||||
sha1_update(&ctx, serverKey.size, serverKeyN); | |||||
sha1_update(&ctx, serverKey.size, serverKeyE); | |||||
sha1_update(&ctx, 4, lenClientKey); | |||||
sha1_update(&ctx, clientKey.size, clientKeyN); | |||||
sha1_update(&ctx, clientKey.size, clientKeyE); | |||||
sha1_digest(&ctx, hashSize, hash); | |||||
} else { | |||||
hashSize = 32; | |||||
struct sha256_ctx ctx; | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 4, lenServerKey); | |||||
sha256_update(&ctx, serverKey.size, serverKeyN); | |||||
sha256_update(&ctx, serverKey.size, serverKeyE); | |||||
sha256_update(&ctx, 4, lenClientKey); | |||||
sha256_update(&ctx, clientKey.size, clientKeyN); | |||||
sha256_update(&ctx, clientKey.size, clientKeyE); | |||||
sha256_digest(&ctx, hashSize, hash); | |||||
} | |||||
raos->writeBytes(hash, hashSize); | |||||
raos->flush(); | |||||
} | |||||
bool SSecurityRSAAES::readHash() | |||||
{ | |||||
rdr::U8 hash[32]; | |||||
rdr::U8 realHash[32]; | |||||
int hashSize = keySize == 128 ? 20 : 32; | |||||
if (!rais->hasData(hashSize)) | |||||
return false; | |||||
rais->readBytes(hash, hashSize); | |||||
size_t len = serverKeyLength; | |||||
rdr::U8 lenServerKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
len = clientKeyLength; | |||||
rdr::U8 lenClientKey[4] = { | |||||
(rdr::U8)((len & 0xff000000) >> 24), | |||||
(rdr::U8)((len & 0xff0000) >> 16), | |||||
(rdr::U8)((len & 0xff00) >> 8), | |||||
(rdr::U8)(len & 0xff) | |||||
}; | |||||
if (keySize == 128) { | |||||
struct sha1_ctx ctx; | |||||
sha1_init(&ctx); | |||||
sha1_update(&ctx, 4, lenClientKey); | |||||
sha1_update(&ctx, clientKey.size, clientKeyN); | |||||
sha1_update(&ctx, clientKey.size, clientKeyE); | |||||
sha1_update(&ctx, 4, lenServerKey); | |||||
sha1_update(&ctx, serverKey.size, serverKeyN); | |||||
sha1_update(&ctx, serverKey.size, serverKeyE); | |||||
sha1_digest(&ctx, hashSize, realHash); | |||||
} else { | |||||
struct sha256_ctx ctx; | |||||
sha256_init(&ctx); | |||||
sha256_update(&ctx, 4, lenClientKey); | |||||
sha256_update(&ctx, clientKey.size, clientKeyN); | |||||
sha256_update(&ctx, clientKey.size, clientKeyE); | |||||
sha256_update(&ctx, 4, lenServerKey); | |||||
sha256_update(&ctx, serverKey.size, serverKeyN); | |||||
sha256_update(&ctx, serverKey.size, serverKeyE); | |||||
sha256_digest(&ctx, hashSize, realHash); | |||||
} | |||||
if (memcmp(hash, realHash, hashSize) != 0) | |||||
throw ConnFailedException("hash doesn't match"); | |||||
return true; | |||||
} | |||||
void SSecurityRSAAES::clearSecrets() | |||||
{ | |||||
rsa_private_key_clear(&serverKey); | |||||
rsa_public_key_clear(&clientKey); | |||||
serverKey.size = 0; | |||||
clientKey.size = 0; | |||||
delete[] serverKeyN; | |||||
delete[] serverKeyE; | |||||
delete[] clientKeyN; | |||||
delete[] clientKeyE; | |||||
serverKeyN = NULL; | |||||
serverKeyE = NULL; | |||||
clientKeyN = NULL; | |||||
clientKeyE = NULL; | |||||
memset(serverRandom, 0, sizeof(serverRandom)); | |||||
memset(clientRandom, 0, sizeof(clientRandom)); | |||||
} | |||||
void SSecurityRSAAES::writeSubtype() | |||||
{ | |||||
if (requireUsername) | |||||
raos->writeU8(secTypeRA2UserPass); | |||||
else | |||||
raos->writeU8(secTypeRA2Pass); | |||||
raos->flush(); | |||||
} | |||||
bool SSecurityRSAAES::readCredentials() | |||||
{ | |||||
rais->setRestorePoint(); | |||||
if (!rais->hasData(1)) | |||||
return false; | |||||
rdr::U8 lenUsername = rais->readU8(); | |||||
if (!rais->hasDataOrRestore(lenUsername + 1)) | |||||
return false; | |||||
if (!username.buf) { | |||||
username.replaceBuf(new char[lenUsername + 1]); | |||||
rais->readBytes(username.buf, lenUsername); | |||||
username.buf[lenUsername] = 0; | |||||
} else { | |||||
rais->skip(lenUsername); | |||||
} | |||||
rdr::U8 lenPassword = rais->readU8(); | |||||
if (!rais->hasDataOrRestore(lenPassword)) | |||||
return false; | |||||
password.replaceBuf(new char[lenPassword + 1]); | |||||
rais->readBytes(password.buf, lenPassword); | |||||
password.buf[lenPassword] = 0; | |||||
rais->clearRestorePoint(); | |||||
return true; | |||||
} | |||||
void SSecurityRSAAES::verifyUserPass() | |||||
{ | |||||
#ifndef __APPLE__ | |||||
#ifdef WIN32 | |||||
WinPasswdValidator* valid = new WinPasswdValidator(); | |||||
#elif !defined(__APPLE__) | |||||
UnixPasswordValidator *valid = new UnixPasswordValidator(); | |||||
#endif | |||||
if (!valid->validate(sc, username.buf, password.buf)) { | |||||
delete valid; | |||||
throw AuthFailureException("invalid password or username"); | |||||
} | |||||
delete valid; | |||||
#else | |||||
throw AuthFailureException("No password validator configured"); | |||||
#endif | |||||
} | |||||
void SSecurityRSAAES::verifyPass() | |||||
{ | |||||
VncAuthPasswdGetter* pg = &SSecurityVncAuth::vncAuthPasswd; | |||||
PlainPasswd passwd, passwdReadOnly; | |||||
pg->getVncAuthPasswd(&passwd, &passwdReadOnly); | |||||
if (!passwd.buf) | |||||
throw AuthFailureException("No password configured for VNC Auth"); | |||||
if (strcmp(password.buf, passwd.buf) == 0) { | |||||
accessRights = SConnection::AccessDefault; | |||||
return; | |||||
} | |||||
if (passwdReadOnly.buf && strcmp(password.buf, passwdReadOnly.buf) == 0) { | |||||
accessRights = SConnection::AccessView; | |||||
return; | |||||
} | |||||
throw AuthFailureException(); | |||||
} | |||||
const char* SSecurityRSAAES::getUserName() const | |||||
{ | |||||
return username.buf; | |||||
} |
/* Copyright (C) 2022 Dinglan Peng | |||||
* | |||||
* This is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifndef __S_SECURITY_RSAAES_H__ | |||||
#define __S_SECURITY_RSAAES_H__ | |||||
#ifndef HAVE_NETTLE | |||||
#error "This header should not be included without HAVE_NETTLE defined" | |||||
#endif | |||||
#include <nettle/rsa.h> | |||||
#include <rfb/SSecurity.h> | |||||
#include <rdr/InStream.h> | |||||
#include <rdr/OutStream.h> | |||||
#include <rdr/RandomStream.h> | |||||
namespace rfb { | |||||
class SSecurityRSAAES : public SSecurity { | |||||
public: | |||||
SSecurityRSAAES(SConnection* sc, rdr::U32 secType, | |||||
int keySize, bool isAllEncrypted); | |||||
virtual ~SSecurityRSAAES(); | |||||
virtual bool processMsg(); | |||||
virtual const char* getUserName() const; | |||||
virtual int getType() const { return secType; } | |||||
virtual SConnection::AccessRights getAccessRights() const | |||||
{ | |||||
return accessRights; | |||||
} | |||||
static StringParameter keyFile; | |||||
static BoolParameter requireUsername; | |||||
private: | |||||
void cleanup(); | |||||
void loadPrivateKey(); | |||||
void loadPKCS1Key(const rdr::U8* data, size_t size); | |||||
void loadPKCS8Key(const rdr::U8* data, size_t size); | |||||
void writePublicKey(); | |||||
bool readPublicKey(); | |||||
void writeRandom(); | |||||
bool readRandom(); | |||||
void setCipher(); | |||||
void writeHash(); | |||||
bool readHash(); | |||||
void clearSecrets(); | |||||
void writeSubtype(); | |||||
bool readCredentials(); | |||||
void verifyUserPass(); | |||||
void verifyPass(); | |||||
int state; | |||||
int keySize; | |||||
bool isAllEncrypted; | |||||
rdr::U32 secType; | |||||
struct rsa_private_key serverKey; | |||||
struct rsa_public_key clientKey; | |||||
rdr::U32 serverKeyLength; | |||||
rdr::U8* serverKeyN; | |||||
rdr::U8* serverKeyE; | |||||
rdr::U32 clientKeyLength; | |||||
rdr::U8* clientKeyN; | |||||
rdr::U8* clientKeyE; | |||||
rdr::U8 serverRandom[32]; | |||||
rdr::U8 clientRandom[32]; | |||||
CharArray username; | |||||
CharArray password; | |||||
SConnection::AccessRights accessRights; | |||||
rdr::InStream* rais; | |||||
rdr::OutStream* raos; | |||||
rdr::InStream* rawis; | |||||
rdr::OutStream* rawos; | |||||
rdr::RandomStream rs; | |||||
}; | |||||
} | |||||
#endif |
if (strcasecmp(name, "Tight") == 0) return secTypeTight; | if (strcasecmp(name, "Tight") == 0) return secTypeTight; | ||||
if (strcasecmp(name, "RA2") == 0) return secTypeRA2; | if (strcasecmp(name, "RA2") == 0) return secTypeRA2; | ||||
if (strcasecmp(name, "RA2ne") == 0) return secTypeRA2ne; | if (strcasecmp(name, "RA2ne") == 0) return secTypeRA2ne; | ||||
if (strcasecmp(name, "RA2_256") == 0) return secTypeRA256; | |||||
if (strcasecmp(name, "RA2ne_256") == 0) return secTypeRAne256; | |||||
if (strcasecmp(name, "SSPI") == 0) return secTypeSSPI; | if (strcasecmp(name, "SSPI") == 0) return secTypeSSPI; | ||||
if (strcasecmp(name, "SSPIne") == 0) return secTypeSSPIne; | if (strcasecmp(name, "SSPIne") == 0) return secTypeSSPIne; | ||||
if (strcasecmp(name, "VeNCrypt") == 0) return secTypeVeNCrypt; | if (strcasecmp(name, "VeNCrypt") == 0) return secTypeVeNCrypt; | ||||
case secTypeTight: return "Tight"; | case secTypeTight: return "Tight"; | ||||
case secTypeRA2: return "RA2"; | case secTypeRA2: return "RA2"; | ||||
case secTypeRA2ne: return "RA2ne"; | case secTypeRA2ne: return "RA2ne"; | ||||
case secTypeRA256: return "RA2_256"; | |||||
case secTypeRAne256: return "RA2ne_256"; | |||||
case secTypeSSPI: return "SSPI"; | case secTypeSSPI: return "SSPI"; | ||||
case secTypeSSPIne: return "SSPIne"; | case secTypeSSPIne: return "SSPIne"; | ||||
case secTypeVeNCrypt: return "VeNCrypt"; | case secTypeVeNCrypt: return "VeNCrypt"; |
const rdr::U8 secTypeTLS = 18; | const rdr::U8 secTypeTLS = 18; | ||||
const rdr::U8 secTypeVeNCrypt= 19; | const rdr::U8 secTypeVeNCrypt= 19; | ||||
const rdr::U8 secTypeRA256 = 129; | |||||
const rdr::U8 secTypeRAne256 = 130; | |||||
/* VeNCrypt subtypes */ | /* VeNCrypt subtypes */ | ||||
const int secTypePlain = 256; | const int secTypePlain = 256; | ||||
const int secTypeTLSNone = 257; | const int secTypeTLSNone = 257; | ||||
const int secTypeX509Vnc = 261; | const int secTypeX509Vnc = 261; | ||||
const int secTypeX509Plain = 262; | const int secTypeX509Plain = 262; | ||||
/* RSA-AES subtypes */ | |||||
const int secTypeRA2UserPass = 1; | |||||
const int secTypeRA2Pass = 2; | |||||
// result types | // result types | ||||
const rdr::U32 secResultOK = 0; | const rdr::U32 secResultOK = 0; |
#ifdef HAVE_GNUTLS | #ifdef HAVE_GNUTLS | ||||
#include <rfb/CSecurityTLS.h> | #include <rfb/CSecurityTLS.h> | ||||
#endif | #endif | ||||
#ifdef HAVE_NETTLE | |||||
#include <rfb/CSecurityRSAAES.h> | |||||
#endif | |||||
using namespace rdr; | using namespace rdr; | ||||
using namespace rfb; | using namespace rfb; | ||||
UserPasswdGetter *CSecurity::upg = NULL; | UserPasswdGetter *CSecurity::upg = NULL; | ||||
#ifdef HAVE_GNUTLS | |||||
UserMsgBox *CSecurityTLS::msg = NULL; | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
UserMsgBox *CSecurity::msg = NULL; | |||||
#endif | #endif | ||||
StringParameter SecurityClient::secTypes | StringParameter SecurityClient::secTypes | ||||
"Specify which security scheme to use (None, VncAuth, Plain" | "Specify which security scheme to use (None, VncAuth, Plain" | ||||
#ifdef HAVE_GNUTLS | #ifdef HAVE_GNUTLS | ||||
", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain" | ", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain" | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
", RA2, RA2ne, RA2_256, RA2ne_256" | |||||
#endif | #endif | ||||
")", | ")", | ||||
#ifdef HAVE_GNUTLS | #ifdef HAVE_GNUTLS | ||||
"X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None", | |||||
#else | |||||
"VncAuth,None", | |||||
"X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone," | |||||
#endif | #endif | ||||
#ifdef HAVE_NETTLE | |||||
"RA2,RA2_256,RA2ne,RA2ne_256," | |||||
#endif | |||||
"VncAuth,None", | |||||
ConfViewer); | ConfViewer); | ||||
CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType) | CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType) | ||||
{ | { | ||||
assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */ | assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */ | ||||
#ifdef HAVE_GNUTLS | |||||
assert (CSecurityTLS::msg != NULL); | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
assert (CSecurity::msg != NULL); | |||||
#endif | #endif | ||||
if (!IsSupported(secType)) | if (!IsSupported(secType)) | ||||
return new CSecurityStack(cc, secTypeX509Plain, | return new CSecurityStack(cc, secTypeX509Plain, | ||||
new CSecurityTLS(cc, false), | new CSecurityTLS(cc, false), | ||||
new CSecurityPlain(cc)); | new CSecurityPlain(cc)); | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
case secTypeRA2: | |||||
return new CSecurityRSAAES(cc, secTypeRA2, 128, true); | |||||
case secTypeRA2ne: | |||||
return new CSecurityRSAAES(cc, secTypeRA2ne, 128, false); | |||||
case secTypeRA256: | |||||
return new CSecurityRSAAES(cc, secTypeRA256, 256, true); | |||||
case secTypeRAne256: | |||||
return new CSecurityRSAAES(cc, secTypeRAne256, 256, false); | |||||
#endif | #endif | ||||
} | } | ||||
#ifdef HAVE_GNUTLS | #ifdef HAVE_GNUTLS | ||||
#include <rfb/SSecurityTLS.h> | #include <rfb/SSecurityTLS.h> | ||||
#endif | #endif | ||||
#ifdef HAVE_NETTLE | |||||
#include <rfb/SSecurityRSAAES.h> | |||||
#endif | |||||
using namespace rdr; | using namespace rdr; | ||||
using namespace rfb; | using namespace rfb; | ||||
"Specify which security scheme to use (None, VncAuth, Plain" | "Specify which security scheme to use (None, VncAuth, Plain" | ||||
#ifdef HAVE_GNUTLS | #ifdef HAVE_GNUTLS | ||||
", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain" | ", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain" | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
", RA2, RA2ne, RA2_256, RA2ne_256" | |||||
#endif | #endif | ||||
")", | ")", | ||||
#ifdef HAVE_GNUTLS | #ifdef HAVE_GNUTLS | ||||
"TLSVnc,VncAuth", | |||||
#else | |||||
"VncAuth", | |||||
"TLSVnc," | |||||
#endif | #endif | ||||
#ifdef HAVE_NETTLE | |||||
"RA2_256,RA2,RA2ne_256,RA2ne," | |||||
#endif | |||||
"VncAuth", | |||||
ConfServer); | ConfServer); | ||||
SSecurity* SecurityServer::GetSSecurity(SConnection* sc, U32 secType) | SSecurity* SecurityServer::GetSSecurity(SConnection* sc, U32 secType) | ||||
return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false), new SSecurityVncAuth(sc)); | return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false), new SSecurityVncAuth(sc)); | ||||
case secTypeX509Plain: | case secTypeX509Plain: | ||||
return new SSecurityStack(sc, secTypeX509Plain, new SSecurityTLS(sc, false), new SSecurityPlain(sc)); | return new SSecurityStack(sc, secTypeX509Plain, new SSecurityTLS(sc, false), new SSecurityPlain(sc)); | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
case secTypeRA2: | |||||
return new SSecurityRSAAES(sc, secTypeRA2, 128, true); | |||||
case secTypeRA2ne: | |||||
return new SSecurityRSAAES(sc, secTypeRA2ne, 128, false); | |||||
case secTypeRA256: | |||||
return new SSecurityRSAAES(sc, secTypeRA256, 256, true); | |||||
case secTypeRAne256: | |||||
return new SSecurityRSAAES(sc, secTypeRAne256, 256, false); | |||||
#endif | #endif | ||||
} | } | ||||
.B \-SecurityTypes \fIsec-types\fP | .B \-SecurityTypes \fIsec-types\fP | ||||
Specify which security scheme to use for incoming connections. Valid values | Specify which security scheme to use for incoming connections. Valid values | ||||
are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP, | are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP, | ||||
\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP | |||||
and \fBX509Plain\fP. Default is \fBVncAuth,TLSVnc\fP. | |||||
\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP, | |||||
\fBX509Plain\fP, \fBRA2\fP, \fBRA2ne\fP, \fBRA2_256\fP and \fBRA2ne_256\fP. | |||||
Default is \fBVncAuth,RA2_256,RA2,RA2ne_256,RA2ne,TLSVnc\fP. | |||||
. | . | ||||
.TP | .TP | ||||
.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP | .B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP | ||||
See the GnuTLS manual for possible values. Default is \fBNORMAL\fP. | See the GnuTLS manual for possible values. Default is \fBNORMAL\fP. | ||||
. | . | ||||
.TP | .TP | ||||
.B \-RSAKey \fIpath\fP | |||||
Path to the RSA key for the RSA-AES security types (\fBRA2\fP, \fBRA2ne\fP, | |||||
\fBRA2_256\fP and \fBRA2ne_256\fP) in PEM format. | |||||
. | |||||
.TP | |||||
.B \-RequireUsername | |||||
Require username for the RSA-AES security types. Default is off. | |||||
. | |||||
.TP | |||||
.B \-UseBlacklist | .B \-UseBlacklist | ||||
Temporarily reject connections from a host if it repeatedly fails to | Temporarily reject connections from a host if it repeatedly fails to | ||||
authenticate. Default is on. | authenticate. Default is on. |
.B \-SecurityTypes \fIsec-types\fP | .B \-SecurityTypes \fIsec-types\fP | ||||
Specify which security scheme to use for incoming connections. Valid values | Specify which security scheme to use for incoming connections. Valid values | ||||
are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP, | are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP, | ||||
\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP | |||||
and \fBX509Plain\fP. Default is \fBVncAuth,TLSVnc\fP. | |||||
\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP, | |||||
\fBX509Plain\fP, \fBRA2\fP, \fBRA2ne\fP, \fBRA2_256\fP and \fBRA2ne_256\fP. | |||||
Default is \fBVncAuth,RA2_256,RA2,RA2ne_256,RA2ne,TLSVnc\fP. | |||||
. | . | ||||
.TP | .TP | ||||
.B \-Password \fIpassword\fP | .B \-Password \fIpassword\fP | ||||
of GnuTLS system-wide crypto policy will be used. | of GnuTLS system-wide crypto policy will be used. | ||||
. | . | ||||
.TP | .TP | ||||
.B \-RSAKey \fIpath\fP | |||||
Path to the RSA key for the RSA-AES security types (\fBRA2\fP, \fBRA2ne\fP, | |||||
\fBRA2_256\fP and \fBRA2ne_256\fP) in PEM format. | |||||
. | |||||
.TP | |||||
.B \-RequireUsername | |||||
Require username for the RSA-AES security types. Default is off. | |||||
. | |||||
.TP | |||||
.B \-UseBlacklist | .B \-UseBlacklist | ||||
Temporarily reject connections from a host if it repeatedly fails to | Temporarily reject connections from a host if it repeatedly fails to | ||||
authenticate. Default is on. | authenticate. Default is on. |
#include <rdr/types.h> | #include <rdr/types.h> | ||||
#include <rfb/encodings.h> | #include <rfb/encodings.h> | ||||
#ifdef HAVE_GNUTLS | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
#include <rfb/Security.h> | #include <rfb/Security.h> | ||||
#include <rfb/SecurityClient.h> | #include <rfb/SecurityClient.h> | ||||
#ifdef HAVE_GNUTLS | |||||
#include <rfb/CSecurityTLS.h> | #include <rfb/CSecurityTLS.h> | ||||
#endif | #endif | ||||
#endif | |||||
#include "OptionsDialog.h" | #include "OptionsDialog.h" | ||||
#include "fltk_layout.h" | #include "fltk_layout.h" | ||||
handleCompression(compressionCheckbox, this); | handleCompression(compressionCheckbox, this); | ||||
handleJpeg(jpegCheckbox, this); | handleJpeg(jpegCheckbox, this); | ||||
#ifdef HAVE_GNUTLS | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
/* Security */ | /* Security */ | ||||
Security security(SecurityClient::secTypes); | Security security(SecurityClient::secTypes); | ||||
list<U32>::iterator iterExt; | list<U32>::iterator iterExt; | ||||
encNoneCheckbox->value(false); | encNoneCheckbox->value(false); | ||||
#ifdef HAVE_GNUTLS | |||||
encTLSCheckbox->value(false); | encTLSCheckbox->value(false); | ||||
encX509Checkbox->value(false); | encX509Checkbox->value(false); | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
encRSAAESCheckbox->value(false); | |||||
#endif | |||||
authNoneCheckbox->value(false); | authNoneCheckbox->value(false); | ||||
authVncCheckbox->value(false); | authVncCheckbox->value(false); | ||||
encNoneCheckbox->value(true); | encNoneCheckbox->value(true); | ||||
authPlainCheckbox->value(true); | authPlainCheckbox->value(true); | ||||
break; | break; | ||||
#ifdef HAVE_GNUTLS | |||||
case secTypeTLSNone: | case secTypeTLSNone: | ||||
encTLSCheckbox->value(true); | encTLSCheckbox->value(true); | ||||
authNoneCheckbox->value(true); | authNoneCheckbox->value(true); | ||||
encX509Checkbox->value(true); | encX509Checkbox->value(true); | ||||
authPlainCheckbox->value(true); | authPlainCheckbox->value(true); | ||||
break; | break; | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
case secTypeRA2: | |||||
case secTypeRA256: | |||||
encRSAAESCheckbox->value(true); | |||||
case secTypeRA2ne: | |||||
case secTypeRAne256: | |||||
authVncCheckbox->value(true); | |||||
authPlainCheckbox->value(true); | |||||
break; | |||||
#endif | |||||
} | } | ||||
} | } | ||||
#ifdef HAVE_GNUTLS | |||||
caInput->value(CSecurityTLS::X509CA); | caInput->value(CSecurityTLS::X509CA); | ||||
crlInput->value(CSecurityTLS::X509CRL); | crlInput->value(CSecurityTLS::X509CRL); | ||||
handleX509(encX509Checkbox, this); | handleX509(encX509Checkbox, this); | ||||
#endif | |||||
#endif | #endif | ||||
/* Input */ | /* Input */ | ||||
compressLevel.setParam(atoi(compressionInput->value())); | compressLevel.setParam(atoi(compressionInput->value())); | ||||
qualityLevel.setParam(atoi(jpegInput->value())); | qualityLevel.setParam(atoi(jpegInput->value())); | ||||
#ifdef HAVE_GNUTLS | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
/* Security */ | /* Security */ | ||||
Security security; | Security security; | ||||
if (encNoneCheckbox->value()) { | if (encNoneCheckbox->value()) { | ||||
if (authNoneCheckbox->value()) | if (authNoneCheckbox->value()) | ||||
security.EnableSecType(secTypeNone); | security.EnableSecType(secTypeNone); | ||||
if (authVncCheckbox->value()) | |||||
if (authVncCheckbox->value()) { | |||||
security.EnableSecType(secTypeVncAuth); | security.EnableSecType(secTypeVncAuth); | ||||
if (authPlainCheckbox->value()) | |||||
#ifdef HAVE_NETTLE | |||||
security.EnableSecType(secTypeRAne256); | |||||
#endif | |||||
} | |||||
if (authPlainCheckbox->value()) { | |||||
security.EnableSecType(secTypePlain); | security.EnableSecType(secTypePlain); | ||||
#ifdef HAVE_NETTLE | |||||
security.EnableSecType(secTypeRA2ne); | |||||
security.EnableSecType(secTypeRAne256); | |||||
#endif | |||||
} | |||||
} | } | ||||
#ifdef HAVE_GNUTLS | |||||
/* Process security types which use TLS encryption */ | /* Process security types which use TLS encryption */ | ||||
if (encTLSCheckbox->value()) { | if (encTLSCheckbox->value()) { | ||||
if (authNoneCheckbox->value()) | if (authNoneCheckbox->value()) | ||||
security.EnableSecType(secTypeX509Plain); | security.EnableSecType(secTypeX509Plain); | ||||
} | } | ||||
SecurityClient::secTypes.setParam(security.ToString()); | |||||
CSecurityTLS::X509CA.setParam(caInput->value()); | CSecurityTLS::X509CA.setParam(caInput->value()); | ||||
CSecurityTLS::X509CRL.setParam(crlInput->value()); | CSecurityTLS::X509CRL.setParam(crlInput->value()); | ||||
#endif | #endif | ||||
#ifdef HAVE_NETTLE | |||||
if (encRSAAESCheckbox->value()) { | |||||
security.EnableSecType(secTypeRA2); | |||||
security.EnableSecType(secTypeRA256); | |||||
} | |||||
#endif | |||||
SecurityClient::secTypes.setParam(security.ToString()); | |||||
#endif | |||||
/* Input */ | /* Input */ | ||||
viewOnly.setParam(viewOnlyCheckbox->value()); | viewOnly.setParam(viewOnlyCheckbox->value()); | ||||
emulateMiddleButton.setParam(emulateMBCheckbox->value()); | emulateMiddleButton.setParam(emulateMBCheckbox->value()); | ||||
void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th) | void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th) | ||||
{ | { | ||||
#ifdef HAVE_GNUTLS | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Security")); | Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Security")); | ||||
int orig_tx; | int orig_tx; | ||||
/* Encryption */ | /* Encryption */ | ||||
ty += GROUP_LABEL_OFFSET; | ty += GROUP_LABEL_OFFSET; | ||||
#if defined(HAVE_GNUTLS) && defined(HAVE_NETTLE) | |||||
height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 5 + CHECK_HEIGHT * 4 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2; | |||||
#elif defined(HAVE_GNUTLS) | |||||
height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 4 + CHECK_HEIGHT * 3 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2; | height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 4 + CHECK_HEIGHT * 3 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2; | ||||
#elif defined(HAVE_NETTLE) | |||||
height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 1 + CHECK_HEIGHT * 2; | |||||
#endif | |||||
encryptionGroup = new Fl_Group(tx, ty, width, height, _("Encryption")); | encryptionGroup = new Fl_Group(tx, ty, width, height, _("Encryption")); | ||||
encryptionGroup->box(FL_ENGRAVED_BOX); | encryptionGroup->box(FL_ENGRAVED_BOX); | ||||
encryptionGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP); | encryptionGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP); | ||||
_("None"))); | _("None"))); | ||||
ty += CHECK_HEIGHT + TIGHT_MARGIN; | ty += CHECK_HEIGHT + TIGHT_MARGIN; | ||||
#ifdef HAVE_GNUTLS | |||||
encTLSCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, | encTLSCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, | ||||
CHECK_MIN_WIDTH, | CHECK_MIN_WIDTH, | ||||
CHECK_HEIGHT, | CHECK_HEIGHT, | ||||
_("Path to X509 CRL file")); | _("Path to X509 CRL file")); | ||||
crlInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP); | crlInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP); | ||||
ty += INPUT_HEIGHT + TIGHT_MARGIN; | ty += INPUT_HEIGHT + TIGHT_MARGIN; | ||||
#endif | |||||
#ifdef HAVE_NETTLE | |||||
encRSAAESCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, | |||||
CHECK_MIN_WIDTH, | |||||
CHECK_HEIGHT, | |||||
_("RSA-AES"))); | |||||
encRSAAESCheckbox->callback(handleRSAAES, this); | |||||
ty += CHECK_HEIGHT + TIGHT_MARGIN; | |||||
#endif | |||||
} | } | ||||
ty += GROUP_MARGIN - TIGHT_MARGIN; | ty += GROUP_MARGIN - TIGHT_MARGIN; | ||||
} | } | ||||
void OptionsDialog::handleRSAAES(Fl_Widget *widget, void *data) | |||||
{ | |||||
OptionsDialog *dialog = (OptionsDialog*)data; | |||||
if (dialog->encRSAAESCheckbox->value()) { | |||||
dialog->authVncCheckbox->value(true); | |||||
dialog->authPlainCheckbox->value(true); | |||||
} | |||||
} | |||||
void OptionsDialog::handleClipboard(Fl_Widget *widget, void *data) | void OptionsDialog::handleClipboard(Fl_Widget *widget, void *data) | ||||
{ | { | ||||
#if !defined(WIN32) && !defined(__APPLE__) | #if !defined(WIN32) && !defined(__APPLE__) |
static void handleJpeg(Fl_Widget *widget, void *data); | static void handleJpeg(Fl_Widget *widget, void *data); | ||||
static void handleX509(Fl_Widget *widget, void *data); | static void handleX509(Fl_Widget *widget, void *data); | ||||
static void handleRSAAES(Fl_Widget *widget, void *data); | |||||
static void handleClipboard(Fl_Widget *widget, void *data); | static void handleClipboard(Fl_Widget *widget, void *data); | ||||
Fl_Check_Button *encNoneCheckbox; | Fl_Check_Button *encNoneCheckbox; | ||||
Fl_Check_Button *encTLSCheckbox; | Fl_Check_Button *encTLSCheckbox; | ||||
Fl_Check_Button *encX509Checkbox; | Fl_Check_Button *encX509Checkbox; | ||||
Fl_Check_Button *encRSAAESCheckbox; | |||||
Fl_Input *caInput; | Fl_Input *caInput; | ||||
Fl_Input *crlInput; | Fl_Input *crlInput; | ||||
mkvnchomedir(); | mkvnchomedir(); | ||||
CSecurity::upg = &dlg; | CSecurity::upg = &dlg; | ||||
#ifdef HAVE_GNUTLS | |||||
CSecurityTLS::msg = &dlg; | |||||
#if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) | |||||
CSecurity::msg = &dlg; | |||||
#endif | #endif | ||||
Socket *sock = NULL; | Socket *sock = NULL; |
Specify which security schemes to attempt to use when authenticating with | Specify which security schemes to attempt to use when authenticating with | ||||
the server. Valid values are a comma separated list of \fBNone\fP, | the server. Valid values are a comma separated list of \fBNone\fP, | ||||
\fBVncAuth\fP, \fBPlain\fP, \fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, | \fBVncAuth\fP, \fBPlain\fP, \fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, | ||||
\fBX509None\fP, \fBX509Vnc\fP and \fBX509Plain\fP. Default is to attempt | |||||
\fBX509None\fP, \fBX509Vnc\fP, \fBX509Plain\fP, \fBRA2\fP, \fBRA2ne\fP, | |||||
\fBRA2_256\fP and \fBRA2ne_256\fP. Default is to attempt | |||||
every supported scheme. | every supported scheme. | ||||
. | . | ||||
.TP | .TP |