From 40e24436e339bfe0790b82cd8581d77c0536633a Mon Sep 17 00:00:00 2001 From: Dinglan Peng Date: Tue, 27 Dec 2022 04:06:08 -0500 Subject: [PATCH] Add client-side support for the MSLogonII security type. --- common/rfb/CMakeLists.txt | 2 +- common/rfb/CSecurityMSLogonII.cxx | 151 ++++++++++++++++++++++++++++++ common/rfb/CSecurityMSLogonII.h | 48 ++++++++++ common/rfb/Security.cxx | 2 + common/rfb/Security.h | 48 +++++----- common/rfb/SecurityClient.cxx | 7 +- vncviewer/OptionsDialog.cxx | 2 + 7 files changed, 234 insertions(+), 26 deletions(-) create mode 100644 common/rfb/CSecurityMSLogonII.cxx create mode 100644 common/rfb/CSecurityMSLogonII.h diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 4a7a1b61..b7ac1145 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -103,7 +103,7 @@ if(GNUTLS_FOUND) endif() if (NETTLE_FOUND) - target_sources(rfb PRIVATE CSecurityDH.cxx + target_sources(rfb PRIVATE CSecurityDH.cxx CSecurityMSLogonII.cxx CSecurityRSAAES.cxx SSecurityRSAAES.cxx) include_directories(${NETTLE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS}) target_link_libraries(rfb ${HOGWEED_LIBRARIES} diff --git a/common/rfb/CSecurityMSLogonII.cxx b/common/rfb/CSecurityMSLogonII.cxx new file mode 100644 index 00000000..e9b7d621 --- /dev/null +++ b/common/rfb/CSecurityMSLogonII.cxx @@ -0,0 +1,151 @@ +/* + * 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 +#endif + +#ifndef HAVE_NETTLE +#error "This header should not be compiled without HAVE_NETTLE defined" +#endif + +#include +#ifndef WIN32 +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; + +CSecurityMSLogonII::CSecurityMSLogonII(CConnection* cc) + : CSecurity(cc) +{ + mpz_init(g); + mpz_init(p); + mpz_init(A); + mpz_init(b); + mpz_init(B); + mpz_init(k); +} + +CSecurityMSLogonII::~CSecurityMSLogonII() +{ + mpz_clear(g); + mpz_clear(p); + mpz_clear(A); + mpz_clear(b); + mpz_clear(B); + mpz_clear(k); +} + +bool CSecurityMSLogonII::processMsg() +{ + if (readKey()) { + writeCredentials(); + return true; + } + return false; +} + +bool CSecurityMSLogonII::readKey() +{ + rdr::InStream* is = cc->getInStream(); + if (!is->hasData(24)) + return false; + rdr::U8 gBytes[8]; + rdr::U8 pBytes[8]; + rdr::U8 ABytes[8]; + is->readBytes(gBytes, 8); + is->readBytes(pBytes, 8); + is->readBytes(ABytes, 8); + nettle_mpz_set_str_256_u(g, 8, gBytes); + nettle_mpz_set_str_256_u(p, 8, pBytes); + nettle_mpz_set_str_256_u(A, 8, ABytes); + return true; +} + +void CSecurityMSLogonII::writeCredentials() +{ + CharArray username; + CharArray password; + rdr::RandomStream rs; + + (CSecurity::upg)->getUserPasswd(isSecure(), &username.buf, &password.buf); + rdr::U8Array bBytes(8); + if (!rs.hasData(8)) + throw ConnFailedException("failed to generate DH private key"); + rs.readBytes(bBytes.buf, 8); + nettle_mpz_set_str_256_u(b, 8, bBytes.buf); + mpz_powm(k, A, b, p); + mpz_powm(B, g, b, p); + + rdr::U8 key[8]; + rdr::U8 reversedKey[8]; + rdr::U8 BBytes[8]; + rdr::U8 user[256]; + rdr::U8 pass[64]; + nettle_mpz_get_str_256(8, key, k); + nettle_mpz_get_str_256(8, BBytes, B); + for (int i = 0; i < 8; ++i) { + rdr::U8 x = 0; + for (int j = 0; j < 8; ++j) { + x |= ((key[i] >> j) & 1) << (7 - j); + } + reversedKey[i] = x; + } + + if (!rs.hasData(256 + 64)) + throw ConnFailedException("failed to generate random padding"); + rs.readBytes(user, 256); + rs.readBytes(pass, 64); + size_t len = strlen(username.buf); + if (len >= 256) + throw AuthFailureException("username is too long"); + memcpy(user, username.buf, len + 1); + len = strlen(password.buf); + if (len >= 64) + throw AuthFailureException("password is too long"); + memcpy(pass, password.buf, len + 1); + + // DES-CBC with the original key as IV, and the reversed one as the DES key + struct CBC_CTX(struct des_ctx, DES_BLOCK_SIZE) ctx; + des_fix_parity(8, reversedKey, reversedKey); + des_set_key(&ctx.ctx, reversedKey); + CBC_SET_IV(&ctx, key); + CBC_ENCRYPT(&ctx, des_encrypt, 256, user, user); + CBC_SET_IV(&ctx, key); + CBC_ENCRYPT(&ctx, des_encrypt, 64, pass, pass); + + rdr::OutStream* os = cc->getOutStream(); + os->writeBytes(BBytes, 8); + os->writeBytes(user, 256); + os->writeBytes(pass, 64); + os->flush(); +} diff --git a/common/rfb/CSecurityMSLogonII.h b/common/rfb/CSecurityMSLogonII.h new file mode 100644 index 00000000..28f0c752 --- /dev/null +++ b/common/rfb/CSecurityMSLogonII.h @@ -0,0 +1,48 @@ +/* + * 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_MSLOGONII_H__ +#define __C_SECURITY_MSLOGONII_H__ + +#ifndef HAVE_NETTLE +#error "This header should not be compiled without HAVE_NETTLE defined" +#endif + +#include +#include +#include + +namespace rfb { + class CSecurityMSLogonII : public CSecurity { + public: + CSecurityMSLogonII(CConnection* cc); + virtual ~CSecurityMSLogonII(); + virtual bool processMsg(); + virtual int getType() const { return secTypeMSLogonII; } + virtual bool isSecure() const { return false; } + + private: + bool readKey(); + void writeCredentials(); + + mpz_t g, p, A, b, B, k; + }; +} + +#endif diff --git a/common/rfb/Security.cxx b/common/rfb/Security.cxx index 3a51be80..efed0cd0 100644 --- a/common/rfb/Security.cxx +++ b/common/rfb/Security.cxx @@ -166,6 +166,7 @@ rdr::U32 rfb::secTypeNum(const char* name) if (strcasecmp(name, "SSPIne") == 0) return secTypeSSPIne; if (strcasecmp(name, "VeNCrypt") == 0) return secTypeVeNCrypt; if (strcasecmp(name, "DH") == 0) return secTypeDH; + if (strcasecmp(name, "MSLogonII") == 0) return secTypeMSLogonII; /* VeNCrypt subtypes */ if (strcasecmp(name, "Plain") == 0) return secTypePlain; @@ -193,6 +194,7 @@ const char* rfb::secTypeName(rdr::U32 num) case secTypeSSPIne: return "SSPIne"; case secTypeVeNCrypt: return "VeNCrypt"; case secTypeDH: return "DH"; + case secTypeMSLogonII: return "MSLogonII"; /* VeNCrypt subtypes */ case secTypePlain: return "Plain"; diff --git a/common/rfb/Security.h b/common/rfb/Security.h index 860d9a58..57800ffd 100644 --- a/common/rfb/Security.h +++ b/common/rfb/Security.h @@ -28,38 +28,40 @@ #include namespace rfb { - const rdr::U8 secTypeInvalid = 0; - const rdr::U8 secTypeNone = 1; - const rdr::U8 secTypeVncAuth = 2; + const rdr::U8 secTypeInvalid = 0; + const rdr::U8 secTypeNone = 1; + const rdr::U8 secTypeVncAuth = 2; - const rdr::U8 secTypeRA2 = 5; - const rdr::U8 secTypeRA2ne = 6; + const rdr::U8 secTypeRA2 = 5; + const rdr::U8 secTypeRA2ne = 6; - const rdr::U8 secTypeSSPI = 7; - const rdr::U8 secTypeSSPIne = 8; + const rdr::U8 secTypeSSPI = 7; + const rdr::U8 secTypeSSPIne = 8; - const rdr::U8 secTypeTight = 16; - const rdr::U8 secTypeUltra = 17; - const rdr::U8 secTypeTLS = 18; - const rdr::U8 secTypeVeNCrypt= 19; + const rdr::U8 secTypeTight = 16; + const rdr::U8 secTypeUltra = 17; + const rdr::U8 secTypeTLS = 18; + const rdr::U8 secTypeVeNCrypt = 19; - const rdr::U8 secTypeDH = 30; + const rdr::U8 secTypeDH = 30; - const rdr::U8 secTypeRA256 = 129; - const rdr::U8 secTypeRAne256 = 130; + const rdr::U8 secTypeMSLogonII = 113; + + const rdr::U8 secTypeRA256 = 129; + const rdr::U8 secTypeRAne256 = 130; /* VeNCrypt subtypes */ - const int secTypePlain = 256; - const int secTypeTLSNone = 257; - const int secTypeTLSVnc = 258; - const int secTypeTLSPlain = 259; - const int secTypeX509None = 260; - const int secTypeX509Vnc = 261; - const int secTypeX509Plain = 262; + const int secTypePlain = 256; + const int secTypeTLSNone = 257; + const int secTypeTLSVnc = 258; + const int secTypeTLSPlain = 259; + const int secTypeX509None = 260; + const int secTypeX509Vnc = 261; + const int secTypeX509Plain = 262; /* RSA-AES subtypes */ - const int secTypeRA2UserPass = 1; - const int secTypeRA2Pass = 2; + const int secTypeRA2UserPass = 1; + const int secTypeRA2Pass = 2; // result types diff --git a/common/rfb/SecurityClient.cxx b/common/rfb/SecurityClient.cxx index 9c9218ee..80165f57 100644 --- a/common/rfb/SecurityClient.cxx +++ b/common/rfb/SecurityClient.cxx @@ -35,6 +35,7 @@ #ifdef HAVE_NETTLE #include #include +#include #endif using namespace rdr; @@ -52,14 +53,14 @@ StringParameter SecurityClient::secTypes ", TLSNone, TLSVnc, TLSPlain, X509None, X509Vnc, X509Plain" #endif #ifdef HAVE_NETTLE - ", RA2, RA2ne, RA2_256, RA2ne_256, DH" + ", RA2, RA2ne, RA2_256, RA2ne_256, DH, MSLogonII" #endif ")", #ifdef HAVE_GNUTLS "X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone," #endif #ifdef HAVE_NETTLE - "RA2,RA2_256,RA2ne,RA2ne_256,DH" + "RA2,RA2_256,RA2ne,RA2ne_256,DH,MSLogonII" #endif "VncAuth,None", ConfViewer); @@ -114,6 +115,8 @@ CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType) return new CSecurityRSAAES(cc, secTypeRAne256, 256, false); case secTypeDH: return new CSecurityDH(cc); + case secTypeMSLogonII: + return new CSecurityMSLogonII(cc); #endif } diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx index 7a9cd6ae..f4736a64 100644 --- a/vncviewer/OptionsDialog.cxx +++ b/vncviewer/OptionsDialog.cxx @@ -296,6 +296,7 @@ void OptionsDialog::loadOptions(void) case secTypeRAne256: authVncCheckbox->value(true); case secTypeDH: + case secTypeMSLogonII: encNoneCheckbox->value(true); authPlainCheckbox->value(true); break; @@ -410,6 +411,7 @@ void OptionsDialog::storeOptions(void) security.EnableSecType(secTypeRA2ne); security.EnableSecType(secTypeRAne256); security.EnableSecType(secTypeDH); + security.EnableSecType(secTypeMSLogonII); #endif } } -- 2.39.5