"Fossies" - the Fresh Open Source Software archive

Member "tvnserver-2.0.4/rfb-sconn/RfbInitializer.cpp" of archive tvnserver-2.0.4-src.zip:


// Copyright (C) 2008, 2009, 2010 GlavSoft LLC.
// All rights reserved.
//
//-------------------------------------------------------------------------
// This file is part of the TightVNC software.  Please visit our Web site:
//
//                       http://www.tightvnc.com/
//
// This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//-------------------------------------------------------------------------
//

#include "RfbInitializer.h"
#include "thread/AutoLock.h"
#include "rfb/VendorDefs.h"
#include "rfb/AuthDefs.h"
#include "CapContainer.h"
#include "server-config-lib/Configurator.h"
#include "AuthException.h"
#include "util/VncPassCrypt.h"
#include "win-system/Environment.h"

#include <stdlib.h>
#include <time.h>

RfbInitializer::RfbInitializer(Channel *stream,
                               ClientAuthListener *extAuthListener,
                               RfbClient *client, bool authAllowed)
: m_shared(false),
  m_tightEnabled(false),
  m_minorVerNum(0),
  m_extAuthListener(extAuthListener),
  m_client(client),
  m_authAllowed(authAllowed),
  m_viewOnlyAuth(false)
{
  m_output = new DataOutputStream(stream);
  m_input = new DataInputStream(stream);
}

RfbInitializer::~RfbInitializer()
{
  delete m_output;
  delete m_input;
}

void RfbInitializer::authPhase()
{
  initVersion();
  initAuthenticate();
  readClientInit();
}

void RfbInitializer::afterAuthPhase(const CapContainer *srvToClCaps,
                                    const CapContainer *clToSrvCaps,
                                    const CapContainer *encCaps,
                                    const Dimension *dim,
                                    const PixelFormat *pf)
{
  sendServerInit(dim, pf);
  sendDesktopName();
  if (m_tightEnabled) {
    sendInteractionCaps(srvToClCaps, clToSrvCaps, encCaps);
  }
}

void RfbInitializer::initVersion()
{
  char initVersionMsg[] = "RFB 003.008\n";
  char clientVersionMsg[13];
  size_t msgLen = 12;
  m_output->writeFully(initVersionMsg, msgLen);
  m_input->readFully(clientVersionMsg, msgLen);
  clientVersionMsg[12] = 0;
  m_minorVerNum = getProtocolMinorVersion(clientVersionMsg);

  try {
    checkForLoopback();
    checkForBan();
  } catch (Exception &e) {
    if (m_minorVerNum == 3) {
      m_output->writeUInt32(0);
    } else {
      m_output->writeUInt8(0);
    }
    StringStorage errorMessage(e.getMessage());
    size_t reasonLen = errorMessage.getLength();
    char *reason = new char[reasonLen + 1];
    try {
      if (errorMessage.toAnsiString(reason, reasonLen + 1)) {
        m_output->writeUInt32(reasonLen);
        m_output->writeFully(reason, reasonLen);
      }
    } catch (...) {
    }
    delete reason;
    throw;
  }
}

void RfbInitializer::checkForLoopback()
{
  SocketAddressIPv4 sockAddr;
  m_client->getSocketAddr(&sockAddr);
  struct sockaddr_in addrIn = sockAddr.getSockAddr();

  bool isLoopback = (unsigned long)addrIn.sin_addr.S_un.S_addr == 16777343;

  ServerConfig *srvConf = Configurator::getInstance()->getServerConfig();
  if (isLoopback && !srvConf->isLoopbackConnectionsAllowed()) {
    throw Exception(_T("Sorry, loopback connections are not enabled"));
  }
  if (srvConf->isOnlyLoopbackConnectionsAllowed() && !isLoopback) {
    throw Exception(_T("Your connection has been rejected"));
  }
}

void RfbInitializer::doTightAuth()
{
  m_output->writeUInt32(0);
  if (Configurator::getInstance()->getServerConfig()->isUsingAuthentication()
      && m_authAllowed) {
    CapContainer authInfo;
    authInfo.addCap(AuthDefs::VNC, VendorDefs::STANDARD, AuthDefs::SIG_VNC);
    m_output->writeUInt32(authInfo.getCapCount());
    authInfo.sendCaps(m_output);
    UINT32 clientAuthValue = m_input->readUInt32();
    if (!authInfo.includes(clientAuthValue)) {
      throw Exception(_T(""));
    }
    doAuth(clientAuthValue);
  } else {
    m_output->writeUInt32(0);
    doAuth(AuthDefs::NONE);
  }
}

void RfbInitializer::doAuth(UINT32 authType)
{
  if (authType == AuthDefs::VNC) {
    doVncAuth();
  } else if (authType == AuthDefs::NONE) {
    doAuthNone();
  } else {
    throw Exception(_T(""));
  }
  m_extAuthListener->onCheckAccessControl(m_client);
  if (m_minorVerNum >= 8 || authType != AuthDefs::NONE) {
    m_output->writeUInt32(0); 
  }
}

void RfbInitializer::doVncAuth()
{
  UINT8 challenge[16];
  srand((unsigned)time(0));
  for (int i = 0; i < sizeof(challenge); i++) {
    challenge[i] = rand() & 0xff;
  }

  m_output->writeFully(challenge, sizeof(challenge));
  UINT8 response[16];
  m_input->readFully(response, sizeof(response));
  checkForBan();

  ServerConfig *srvConf = Configurator::getInstance()->getServerConfig();
  bool hasPrim = srvConf->hasPrimaryPassword();
  bool hasRdly = srvConf->hasReadOnlyPassword();

  if (!hasPrim && !hasRdly) {
    throw AuthException(_T("Server is not configured properly"));
  }

  if (hasPrim) {
    UINT8 crypPrimPass[8];
    srvConf->getPrimaryPassword(crypPrimPass);
    VncPassCrypt passCrypt;
    passCrypt.updatePlain(crypPrimPass);
    if (passCrypt.challengeAndResponseIsValid(challenge, response)) {
      return;
    }
  }
  if (hasRdly) {
    UINT8 crypReadOnlyPass[8];
    srvConf->getReadOnlyPassword(crypReadOnlyPass);
    VncPassCrypt passCrypt;
    passCrypt.updatePlain(crypReadOnlyPass);
    if (passCrypt.challengeAndResponseIsValid(challenge, response)) {
      m_viewOnlyAuth = true;
      return;
    }
  }
  m_extAuthListener->onAuthFailed(m_client);
  throw AuthException(_T("Authentication failed"));
}

void RfbInitializer::doAuthNone()
{
}

void RfbInitializer::initAuthenticate()
{
  try {
    UINT32 primSecType = SecurityDefs::VNC;
    if (!Configurator::getInstance()->getServerConfig()->isUsingAuthentication()
        || !m_authAllowed) {
      primSecType = SecurityDefs::NONE;
    }
    if (m_minorVerNum >= 7) {
      m_output->writeUInt8(2);
      m_output->writeUInt8(primSecType);
      m_output->writeUInt8(SecurityDefs::TIGHT);
      UINT8 clientSecType = m_input->readUInt8();
      if (clientSecType == SecurityDefs::TIGHT) {
        m_tightEnabled = true;
        doTightAuth();
      } else {
        if (clientSecType != primSecType) {
          throw Exception(_T("Security types do not match"));
        }
        doAuth(AuthDefs::convertFromSecurityType(clientSecType));
      }
    } else {
      m_output->writeUInt32(primSecType);
      doAuth(AuthDefs::convertFromSecurityType(primSecType));
    }
  } catch (AuthException &e) {
    if (m_minorVerNum >= 8) {
      StringStorage errorMessage(e.getMessage());
      size_t reasonLen = errorMessage.getLength();
      char *reason = new char[reasonLen + 1];
      try {
        if (errorMessage.toAnsiString(reason, reasonLen + 1)) {
          m_output->writeUInt32(1); 
          m_output->writeUInt32(reasonLen);
          m_output->writeFully(reason, reasonLen);
        }
      } catch (...) {
        delete reason;
        throw;
      }
      delete reason;
    }
    throw;
  }
}

void RfbInitializer::readClientInit()
{
  m_shared = m_input->readUInt8() != 0;
}

void RfbInitializer::sendServerInit(const Dimension *dim,
                                    const PixelFormat *pf)
{
  m_output->writeUInt16((UINT16)dim->width);
  m_output->writeUInt16((UINT16)dim->height);
  m_output->writeUInt8((UINT8)pf->bitsPerPixel);
  m_output->writeUInt8((UINT8)pf->colorDepth);
  m_output->writeUInt8((UINT8)pf->bigEndian);
  m_output->writeUInt8(1);
  m_output->writeUInt16((UINT16)pf->redMax);
  m_output->writeUInt16((UINT16)pf->greenMax);
  m_output->writeUInt16((UINT16)pf->blueMax);
  m_output->writeUInt8((UINT8)pf->redShift);
  m_output->writeUInt8((UINT8)pf->greenShift);
  m_output->writeUInt8((UINT8)pf->blueShift);
  m_output->writeUInt8(0);
  m_output->writeUInt16(0);
}

void RfbInitializer::sendDesktopName()
{
  StringStorage deskName;
  if (!Environment::getComputerName(&deskName)) {
    deskName.setString(_T("TightVNC Server"));
  }

  size_t dnLen = deskName.getLength();
  char *ansiName = new char[dnLen + 1];
  deskName.toAnsiString(ansiName, dnLen + 1);

  m_output->writeUInt32(dnLen);
  m_output->writeFully(ansiName, dnLen);
}

void RfbInitializer::sendInteractionCaps(const CapContainer *srvToClCaps,
                                         const CapContainer *clToSrvCaps,
                                         const CapContainer *encCaps)
{
  m_output->writeUInt16(srvToClCaps->getCapCount());
  m_output->writeUInt16(clToSrvCaps->getCapCount());
  m_output->writeUInt16(encCaps->getCapCount());
  m_output->writeUInt16(0); 

  srvToClCaps->sendCaps(m_output);
  clToSrvCaps->sendCaps(m_output);
  encCaps->sendCaps(m_output);
}

unsigned int RfbInitializer::getProtocolMinorVersion(const char str[12])
{
  if ( str[0] != 'R' || str[1] != 'F' || str[2] != 'B' || str[3] != ' ' ||
       !isdigit(str[4]) || !isdigit(str[5]) || !isdigit(str[6]) ||
       str[7] != '.' ||
       !isdigit(str[8]) || !isdigit(str[9]) || !isdigit(str[10]) ||
       str[11] != '\n' ) {
    throw Exception(_T("Invalid format of the RFB version message"));
  }

  unsigned int majorVersion =
    (str[4] - '0') * 100 + (str[5] - '0') * 10 + (str[6] - '0');
  if (majorVersion != 3) {
    throw Exception(_T("Unsupported RFB protocol version requested"));
  }

  unsigned int minorVersion =
    (str[8] - '0') * 100 + (str[9] - '0') * 10 + (str[10] - '0');
  return minorVersion;
}

void RfbInitializer::checkForBan()
{
  if (m_extAuthListener->onCheckForBan(m_client)) {
    throw AuthException(_T("Your connection has been rejected"));
  }
}