version 3.0

This commit is contained in:
Bramfeld Team 2015-08-31 14:01:44 +02:00
commit d837490606
209 changed files with 19662 additions and 0 deletions

0
ssh/Makefile Normal file
View file

18
ssh/TODO Normal file
View file

@ -0,0 +1,18 @@
Everything has been sort-of hacked together to get something working. Lots to
do to make it not awful.
o) Make the send and receive processes more asynchronous. Really want to be
able to not have to do long, synchronous encryption operations, etc.
o) Make the Crypto* stuff less awful. It seems like the Method abstraction is
perhaps not worth it.
o) Do separate algorithm from instance in the SSH code. It's a mess right now,
what with the clone() methods. Yuck.
o) Make sure all those things created by new have appropriate deletes somewhere.
o) Likewise, go over all the RSA, DH, BIGNUM, etc., stuff and add frees.
o) Maybe don't ever generate random padding?
o) Some kind of server public key verification callback.
o) Figure out a naming scheme. Packet, payload, MACs, etc., all have lots of
different names. Hard because we sometimes need to have a Buffer and a
uint8_t array for the same data. But the code is hard to read due to all the
inconsistency and duplication.
o) Split up the key exchange input method. It's harder to check whether its
variables are used safely since they're in a big, combined lump.

12
ssh/lib.mk Normal file
View file

@ -0,0 +1,12 @@
VPATH+= ${TOPDIR}/ssh
SRCS+= ssh_algorithm_negotiation.cc
SRCS+= ssh_filter.cc
SRCS+= ssh_protocol.cc
SRCS+= ssh_session.cc
SRCS+= ssh_compression.cc
SRCS+= ssh_encryption.cc
SRCS+= ssh_key_exchange.cc
SRCS+= ssh_mac.cc
SRCS+= ssh_server_host_key.cc

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <common/buffer.h>
#include <common/endian.h>
#include <crypto/crypto_random.h>
#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_compression.h>
#include <ssh/ssh_encryption.h>
#include <ssh/ssh_key_exchange.h>
#include <ssh/ssh_language.h>
#include <ssh/ssh_mac.h>
#include <ssh/ssh_protocol.h>
#include <ssh/ssh_server_host_key.h>
#include <ssh/ssh_session.h>
#include <ssh/ssh_filter.h>
////////////////////////////////////////////////////////////////////////////////
// //
// File: ssh_algorithm_negotiation.cc //
// Description: SSH algorithm selection at session start //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
namespace {
template<typename T>
std::vector<Buffer> names(const std::list<T>& list)
{
typename std::list<T>::const_iterator it;
std::vector<Buffer> vec;
for (it = list.begin(); it != list.end(); ++it) {
const T alg = *it;
vec.push_back(alg->name());
}
return (vec);
}
template<typename T>
bool choose_algorithm(SSH::Role role,
T *chosenp,
std::list<T>& algorithm_list,
Buffer *in, const std::string& type)
{
std::vector<Buffer> local_algorithms = names(algorithm_list);
std::vector<Buffer> remote_algorithms;
if (!SSH::NameList::decode(remote_algorithms, in)) {
ERROR("/ssh/algorithm/negotiation") << "Failed to decode " << type << " name-list.";
return (false);
}
if (remote_algorithms.empty() && local_algorithms.empty()) {
DEBUG("/ssh/algorithm/negotiation") << "Neither client nor server has any preference in " << type << " algorithms.";
return (true);
}
const std::vector<Buffer> *client_algorithms;
const std::vector<Buffer> *server_algorithms;
if (role == SSH::ClientRole) {
client_algorithms = &local_algorithms;
server_algorithms = &remote_algorithms;
} else {
client_algorithms = &remote_algorithms;
server_algorithms = &local_algorithms;
}
std::vector<Buffer>::const_iterator it;
for (it = client_algorithms->begin();
it != client_algorithms->end(); ++it) {
std::vector<Buffer>::const_iterator it2;
for (it2 = server_algorithms->begin();
it2 != server_algorithms->end(); ++it2) {
const Buffer& server = *it2;
if (!it->equal(&server))
continue;
std::string algorithm;
it->extract(algorithm);
typename std::list<T>::const_iterator ait;
for (ait = algorithm_list.begin(); ait != algorithm_list.end(); ++ait) {
const T alg = *ait;
if (alg->name() != algorithm)
continue;
*chosenp = alg->clone();
DEBUG("/ssh/algorithm/negotiation") << "Selected " << type << " algorithm " << algorithm;
return (true);
}
NOTREACHED("/ssh/algorithm/negotiation");
}
}
ERROR("/ssh/algorithm/negotiation") << "Failed to choose " << type << " algorithm.";
return (false);
}
}
void
SSH::AlgorithmNegotiation::add_algorithms(void)
{
SSH::KeyExchange::add_algorithms(session_);
if (session_->role_ == ClientRole)
SSH::ServerHostKey::add_client_algorithms(session_);
SSH::Encryption::add_algorithms(session_);
SSH::MAC::add_algorithms(session_);
add_algorithm(SSH::Compression::none());
/* XXX Add languages? */
}
bool
SSH::AlgorithmNegotiation::input(Filter* sender, Buffer *in)
{
Buffer packet;
switch (in->peek()) {
case SSH::Message::KeyExchangeInitializationMessage:
session_->remote_kexinit(*in);
if (!choose_algorithms(in)) {
ERROR(log_) << "Unable to negotiate algorithms.";
return (false);
}
DEBUG(log_) << "Chose algorithms.";
if (session_->role_ == ClientRole && session_->chosen_algorithms_.key_exchange_ != NULL) {
Buffer out;
if (!session_->chosen_algorithms_.key_exchange_->init(&out)) {
ERROR(log_) << "Could not start new key exchange.";
return (false);
}
if (!out.empty())
sender->produce(out);
}
return (true);
case SSH::Message::NewKeysMessage:
packet.append(SSH::Message::NewKeysMessage);
sender->produce(packet);
session_->activate_chosen();
DEBUG(log_) << "Switched to new keys.";
return (true);
default:
DEBUG(log_) << "Unsupported algorithm negotiation message:" << std::endl << in->hexdump();
return (false);
}
}
bool
SSH::AlgorithmNegotiation::init(Buffer *out)
{
ASSERT(log_, out->empty());
Buffer cookie;
if (!CryptoRandomMethod::default_method->generate(CryptoTypeRNG, 16, &cookie))
return (false);
out->append(SSH::Message::KeyExchangeInitializationMessage);
out->append(cookie);
SSH::NameList::encode(out, names(algorithms_.key_exchange_list_));
SSH::NameList::encode(out, names(algorithms_.server_host_key_list_));
SSH::NameList::encode(out, names(algorithms_.encryption_client_to_server_list_));
SSH::NameList::encode(out, names(algorithms_.encryption_server_to_client_list_));
SSH::NameList::encode(out, names(algorithms_.mac_client_to_server_list_));
SSH::NameList::encode(out, names(algorithms_.mac_server_to_client_list_));
SSH::NameList::encode(out, names(algorithms_.compression_client_to_server_list_));
SSH::NameList::encode(out, names(algorithms_.compression_server_to_client_list_));
SSH::NameList::encode(out, names(algorithms_.language_client_to_server_list_));
SSH::NameList::encode(out, names(algorithms_.language_server_to_client_list_));
out->append(SSH::Boolean::False);
uint32_t reserved(0);
out->append(&reserved);
session_->local_kexinit(*out);
return (true);
}
bool
SSH::AlgorithmNegotiation::choose_algorithms(Buffer *in)
{
in->skip(17);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.key_exchange_, algorithms_.key_exchange_list_, in, "Key Exchange"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_host_key_, algorithms_.server_host_key_list_, in, "Server Host Key"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.encryption_, algorithms_.encryption_client_to_server_list_, in, "Encryption (Client->Server)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.encryption_, algorithms_.encryption_server_to_client_list_, in, "Encryption (Server->Client)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.mac_, algorithms_.mac_client_to_server_list_, in, "MAC (Client->Server)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.mac_, algorithms_.mac_server_to_client_list_, in, "MAC (Server->Client)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.compression_, algorithms_.compression_client_to_server_list_, in, "Compression (Client->Server)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.compression_, algorithms_.compression_server_to_client_list_, in, "Compression (Server->Client)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.language_, algorithms_.language_client_to_server_list_, in, "Language (Client->Server)"))
return (false);
if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.language_, algorithms_.language_server_to_client_list_, in, "Language (Server->Client)"))
return (false);
return (true);
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_ALGORITHM_NEGOTIATION_H
#define SSH_SSH_ALGORITHM_NEGOTIATION_H
#include <list>
////////////////////////////////////////////////////////////////////////////////
// //
// File: ssh_algorithm_negotiation.h //
// Description: SSH algorithm selection at session start //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
class Buffer;
class Filter;
namespace SSH {
class Compression;
class Encryption;
class KeyExchange;
class Language;
class MAC;
class ServerHostKey;
struct Session;
class AlgorithmNegotiation {
struct Algorithms {
std::list<KeyExchange *> key_exchange_list_;
std::list<ServerHostKey *> server_host_key_list_;
std::list<Encryption *> encryption_client_to_server_list_;
std::list<Encryption *> encryption_server_to_client_list_;
std::list<MAC *> mac_client_to_server_list_;
std::list<MAC *> mac_server_to_client_list_;
std::list<Compression *> compression_client_to_server_list_;
std::list<Compression *> compression_server_to_client_list_;
std::list<Language *> language_client_to_server_list_;
std::list<Language *> language_server_to_client_list_;
Algorithms(void)
: key_exchange_list_(),
server_host_key_list_(),
encryption_client_to_server_list_(),
encryption_server_to_client_list_(),
mac_client_to_server_list_(),
mac_server_to_client_list_(),
compression_client_to_server_list_(),
compression_server_to_client_list_(),
language_client_to_server_list_(),
language_server_to_client_list_()
{ }
};
LogHandle log_;
Session *session_;
Algorithms algorithms_;
public:
AlgorithmNegotiation(Session *session)
: log_("/ssh/algorithm/negotiation"),
session_(session),
algorithms_()
{ }
/* XXX Add a variant that takes only server_host_key_list and fills in suitable defaults. */
~AlgorithmNegotiation()
{ }
void add_algorithm(KeyExchange *key_exchange)
{
algorithms_.key_exchange_list_.push_back(key_exchange);
}
void add_algorithm(ServerHostKey *server_host_key)
{
algorithms_.server_host_key_list_.push_back(server_host_key);
}
void add_algorithm(Encryption *encryption)
{
algorithms_.encryption_client_to_server_list_.push_back(encryption);
algorithms_.encryption_server_to_client_list_.push_back(encryption);
}
void add_algorithm(MAC *mac)
{
algorithms_.mac_client_to_server_list_.push_back(mac);
algorithms_.mac_server_to_client_list_.push_back(mac);
}
void add_algorithm(Compression *compression)
{
algorithms_.compression_client_to_server_list_.push_back(compression);
algorithms_.compression_server_to_client_list_.push_back(compression);
}
void add_algorithm(Language *language)
{
algorithms_.language_client_to_server_list_.push_back(language);
algorithms_.language_server_to_client_list_.push_back(language);
}
void add_algorithms(void);
bool input(Filter *, Buffer *);
bool init(Buffer *);
private:
bool choose_algorithms(Buffer *);
};
}
#endif /* !SSH_SSH_ALGORITHM_NEGOTIATION_H */

57
ssh/ssh_compression.cc Normal file
View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <ssh/ssh_compression.h>
namespace {
class NoneCompression : public SSH::Compression {
LogHandle log_;
public:
NoneCompression(void)
: SSH::Compression("none"),
log_("/ssh/compression/none")
{ }
~NoneCompression()
{ }
Compression *clone(void) const
{
return (new NoneCompression(*this));
}
bool input(Buffer *)
{
ERROR(log_) << "Not yet implemented.";
return (false);
}
};
}
SSH::Compression *
SSH::Compression::none(void)
{
return (new NoneCompression());
}

56
ssh/ssh_compression.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_COMPRESSION_H
#define SSH_SSH_COMPRESSION_H
class Buffer;
namespace SSH {
class Compression {
std::string name_;
protected:
Compression(const std::string& xname)
: name_(xname)
{ }
public:
virtual ~Compression()
{ }
std::string name(void) const
{
return (name_);
}
virtual Compression *clone(void) const = 0;
virtual bool input(Buffer *) = 0;
static Compression *none(void);
};
}
#endif /* !SSH_SSH_COMPRESSION_H */

128
ssh/ssh_encryption.cc Normal file
View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <common/buffer.h>
#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_encryption.h>
#include <ssh/ssh_session.h>
namespace {
struct ssh_encryption_algorithm {
const char *rfc4250_name_;
CryptoEncryption::Algorithm crypto_algorithm_;
CryptoEncryption::Mode crypto_mode_;
};
static const struct ssh_encryption_algorithm ssh_encryption_algorithms[] = {
{ "aes128-ctr", CryptoEncryption::AES128, CryptoEncryption::CTR },
{ "aes128-cbc", CryptoEncryption::AES128, CryptoEncryption::CBC },
{ "aes192-ctr", CryptoEncryption::AES192, CryptoEncryption::CTR },
{ "aes192-cbc", CryptoEncryption::AES192, CryptoEncryption::CBC },
{ "aes256-ctr", CryptoEncryption::AES256, CryptoEncryption::CTR },
{ "aes256-cbc", CryptoEncryption::AES256, CryptoEncryption::CBC },
{ "blowfish-ctr", CryptoEncryption::Blowfish, CryptoEncryption::CTR },
{ "blowfish-cbc", CryptoEncryption::Blowfish, CryptoEncryption::CBC },
{ "3des-ctr", CryptoEncryption::TripleDES, CryptoEncryption::CTR },
{ "3des-cbc", CryptoEncryption::TripleDES, CryptoEncryption::CBC },
{ "cast128-cbc", CryptoEncryption::CAST, CryptoEncryption::CBC },
{ "idea-cbc", CryptoEncryption::IDEA, CryptoEncryption::CBC },
{ "arcfour", CryptoEncryption::RC4, CryptoEncryption::Stream},
{ NULL, CryptoEncryption::AES128, CryptoEncryption::CBC }
};
class CryptoSSHEncryption : public SSH::Encryption {
LogHandle log_;
CryptoEncryption::Session *session_;
public:
CryptoSSHEncryption(const std::string& xname, CryptoEncryption::Session *session)
: SSH::Encryption(xname, session->block_size(), session->key_size(), session->iv_size()),
log_("/ssh/encryption/crypto/" + xname),
session_(session)
{ }
~CryptoSSHEncryption()
{ }
Encryption *clone(void) const
{
return (new CryptoSSHEncryption(name_, session_->clone()));
}
bool initialize(CryptoEncryption::Operation operation, const Buffer *key, const Buffer *iv)
{
return (session_->initialize(operation, key, iv));
}
bool cipher(Buffer *out, Buffer *in)
{
if (!session_->cipher(out, in)) {
in->clear();
return (false);
}
in->clear();
return (true);
}
};
}
void
SSH::Encryption::add_algorithms(Session *session)
{
const struct ssh_encryption_algorithm *alg;
for (alg = ssh_encryption_algorithms; alg->rfc4250_name_ != NULL; alg++) {
Encryption *encryption = cipher(CryptoEncryption::Cipher(alg->crypto_algorithm_, alg->crypto_mode_));
if (encryption == NULL)
continue;
session->algorithm_negotiation_->add_algorithm(encryption);
}
}
SSH::Encryption *
SSH::Encryption::cipher(CryptoEncryption::Cipher cipher)
{
const struct ssh_encryption_algorithm *alg;
for (alg = ssh_encryption_algorithms; alg->rfc4250_name_ != NULL; alg++) {
if (cipher.first != alg->crypto_algorithm_)
continue;
if (cipher.second != alg->crypto_mode_)
continue;
const CryptoEncryption::Method *method = CryptoEncryption::Method::method(cipher);
if (method == NULL) {
DEBUG("/ssh/encryption") << "Could not get method for cipher: " << cipher;
return (NULL);
}
CryptoEncryption::Session *session = method->session(cipher);
if (session == NULL) {
ERROR("/ssh/encryption") << "Could not get session for cipher: " << cipher;
return (NULL);
}
return (new CryptoSSHEncryption(alg->rfc4250_name_, session));
}
DEBUG("/ssh/encryption") << "No SSH encryption support is available for cipher: " << cipher;
return (NULL);
}

82
ssh/ssh_encryption.h Normal file
View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_ENCRYPTION_H
#define SSH_SSH_ENCRYPTION_H
#include <crypto/crypto_encryption.h>
namespace SSH {
struct Session;
class Encryption {
protected:
const std::string name_;
const unsigned block_size_;
const unsigned key_size_;
const unsigned iv_size_;
Encryption(const std::string& xname, unsigned xblock_size, unsigned xkey_size, unsigned xiv_size)
: name_(xname),
block_size_(xblock_size),
key_size_(xkey_size),
iv_size_(xiv_size)
{ }
public:
virtual ~Encryption()
{ }
unsigned block_size(void) const
{
return (block_size_);
}
unsigned key_size(void) const
{
return (key_size_);
}
unsigned iv_size(void) const
{
return (iv_size_);
}
std::string name(void) const
{
return (name_);
}
virtual Encryption *clone(void) const = 0;
virtual bool initialize(CryptoEncryption::Operation, const Buffer *, const Buffer *) = 0;
virtual bool cipher(Buffer *, Buffer *) = 0;
static void add_algorithms(Session *);
static Encryption *cipher(CryptoEncryption::Cipher);
};
}
#endif /* !SSH_SSH_ENCRYPTION_H */

516
ssh/ssh_filter.cc Normal file
View file

@ -0,0 +1,516 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <common/endian.h>
#include <event/event_callback.h>
#include <http/http_protocol.h>
#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_server_host_key.h>
#include <ssh/ssh_compression.h>
#include <ssh/ssh_encryption.h>
#include <ssh/ssh_key_exchange.h>
#include <ssh/ssh_mac.h>
#include <ssh/ssh_protocol.h>
#include "ssh_filter.h"
////////////////////////////////////////////////////////////////////////////////
// //
// File: ssh_filter.cc //
// Description: SSH encryption/decryption inside a data filter pair //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
namespace
{
static const uint8_t SSHStreamPacket = 0xff;
static uint8_t zero_padding[255];
}
// Encrypt
SSH::EncryptFilter::EncryptFilter (SSH::Role role, int flg) : BufferedFilter ("/ssh/encrypt"), session_ (role)
{
encoded_ = (flg & SOURCE_ENCODED) != 0;
negotiated_ = false;
session_.algorithm_negotiation_ = new SSH::AlgorithmNegotiation (&session_);
if (session_.role_ == SSH::ServerRole)
session_.algorithm_negotiation_->add_algorithm (SSH::ServerHostKey::server (&session_, "ssh-server1.pem"));
session_.algorithm_negotiation_->add_algorithms ();
Buffer str ("SSH-2.0-WANProxy " + (std::string)log_);
session_.local_version (str);
str.append ("\r\n");
Filter::produce (str);
}
bool SSH::EncryptFilter::consume (Buffer& buf)
{
buf.moveout (&pending_);
if (negotiated_)
{
/*
* If we're writing data that has been encoded, we need to tag it.
*/
if (encoded_)
{
Buffer packet;
packet.append (SSHStreamPacket);
pending_.moveout (&packet);
return produce (packet);
}
else
{
uint32_t length;
while (pending_.length () > sizeof length)
{
pending_.extract (&length);
length = BigEndian::decode (length);
if (pending_.length () < sizeof length + length)
{
DEBUG(log_) << "Waiting for more write data.";
return true;
}
Buffer packet;
pending_.moveout (&packet, sizeof length, length);
if (! produce (packet))
return false;
}
}
}
return true;
}
bool SSH::EncryptFilter::produce (Buffer& buf)
{
Encryption *encryption_algorithm;
MAC *mac_algorithm;
Buffer packet;
uint8_t padding_len;
uint32_t packet_len;
unsigned block_size;
Buffer mac;
encryption_algorithm = session_.active_algorithms_.local_to_remote_->encryption_;
if (encryption_algorithm)
{
block_size = encryption_algorithm->block_size();
if (block_size < 8)
block_size = 8;
}
else
block_size = 8;
mac_algorithm = session_.active_algorithms_.local_to_remote_->mac_;
packet_len = sizeof padding_len + buf.length();
padding_len = 4 + (block_size - ((sizeof packet_len + packet_len + 4) % block_size));
packet_len += padding_len;
BigEndian::append (&packet, packet_len);
packet.append (padding_len);
buf.moveout (&packet);
packet.append (zero_padding, padding_len);
if (mac_algorithm)
{
Buffer mac_input;
SSH::UInt32::encode (&mac_input, session_.local_sequence_number_);
mac_input.append (&packet);
if (! mac_algorithm->mac (&mac, &mac_input))
{
ERROR(log_) << "Could not compute outgoing MAC.";
return false;
}
}
if (encryption_algorithm)
{
Buffer ciphertext;
if (! encryption_algorithm->cipher (&ciphertext, &packet))
{
ERROR(log_) << "Could not encrypt outgoing packet.";
return false;
}
packet = ciphertext;
}
if (! mac.empty ())
packet.append (mac);
session_.local_sequence_number_++;
return Filter::produce (packet);
}
void SSH::EncryptFilter::flush (int flg)
{
if (flg == ALGORITHM_NEGOTIATED)
{
negotiated_ = true;
Buffer bfr;
if (! pending_.empty ())
consume (bfr);
}
else
{
flushing_ = true;
flush_flags_ |= flg;
}
if (flushing_ && negotiated_)
Filter::flush (flush_flags_);
}
// Decrypt
SSH::DecryptFilter::DecryptFilter (int flg) : LogisticFilter ("/ssh/decrypt")
{
session_ = 0;
encoded_ = (flg & SOURCE_ENCODED) != 0;
identified_ = false;
}
bool SSH::DecryptFilter::consume (Buffer& buf)
{
buf.moveout (&pending_);
if (! identified_)
{
HTTPProtocol::ParseStatus status;
while (! pending_.empty ())
{
Buffer line;
status = HTTPProtocol::ExtractLine (&line, &pending_);
switch (status)
{
case HTTPProtocol::ParseSuccess:
break;
case HTTPProtocol::ParseFailure:
ERROR(log_) << "Invalid line while waiting for identification string.";
return false;
case HTTPProtocol::ParseIncomplete:
/* Wait for more. */
return true;
}
if (! line.prefix ("SSH-"))
continue; /* Next line. */
if (! line.prefix ("SSH-2.0"))
{
ERROR(log_) << "Unsupported version.";
return false;
}
if (session_ && session_->algorithm_negotiation_ && upstream_)
{
session_->remote_version (line);
Buffer packet;
if (session_->algorithm_negotiation_->init (&packet))
{
upstream_->produce (packet);
identified_ = true;
break;
}
}
return false;
}
if (! identified_)
return true;
}
while (! pending_.empty ())
{
Encryption *encryption_algorithm;
MAC *mac_algorithm;
Buffer packet;
Buffer mac;
unsigned block_size;
unsigned mac_size;
uint32_t packet_len;
uint8_t padding_len;
uint8_t msg;
encryption_algorithm = session_->active_algorithms_.remote_to_local_->encryption_;
if (encryption_algorithm)
{
block_size = encryption_algorithm->block_size();
if (block_size < 8)
block_size = 8;
}
else
block_size = 8;
mac_algorithm = session_->active_algorithms_.remote_to_local_->mac_;
if (mac_algorithm)
mac_size = mac_algorithm->size();
else
mac_size = 0;
if (pending_.length() <= block_size)
{
DEBUG(log_) << "Waiting for first block of packet.";
return true;
}
if (encryption_algorithm)
{
if (first_block_.empty ())
{
Buffer block;
pending_.moveout (&block, block_size);
if (! encryption_algorithm->cipher (&first_block_, &block))
{
ERROR(log_) << "Decryption of first block failed.";
return false;
}
}
BigEndian::extract (&packet_len, &first_block_);
}
else
{
BigEndian::extract (&packet_len, &pending_);
}
if (packet_len == 0)
{
ERROR(log_) << "Need to handle 0-length packet.";
return false;
}
if (encryption_algorithm)
{
ASSERT(log_, !first_block_.empty());
if (block_size + pending_.length() < sizeof packet_len + packet_len + mac_size)
{
DEBUG(log_) << "Need " << sizeof packet_len + packet_len + mac_size << " bytes to decrypt encrypted packet; have " << (block_size + pending_.length()) << ".";
return true;
}
first_block_.moveout (&packet);
if (sizeof packet_len + packet_len > block_size)
{
Buffer ciphertext;
pending_.moveout (&ciphertext, sizeof packet_len + packet_len - block_size);
if (! encryption_algorithm->cipher (&packet, &ciphertext))
{
ERROR(log_) << "Decryption of packet failed.";
return false;
}
}
else
{
DEBUG(log_) << "Packet of exactly one block.";
}
ASSERT(log_, packet.length() == sizeof packet_len + packet_len);
}
else
{
if (pending_.length() < sizeof packet_len + packet_len + mac_size)
{
DEBUG(log_) << "Need " << sizeof packet_len + packet_len + mac_size << " bytes; have " << pending_.length() << ".";
return true;
}
pending_.moveout (&packet, sizeof packet_len + packet_len);
}
if (mac_algorithm)
{
Buffer expected_mac;
Buffer mac_input;
pending_.moveout (&mac, 0, mac_size);
SSH::UInt32::encode (&mac_input, session_->remote_sequence_number_);
mac_input.append (packet);
if (! mac_algorithm->mac (&expected_mac, &mac_input))
{
ERROR(log_) << "Could not compute expected MAC.";
return false;
}
if (! expected_mac.equal (&mac))
{
ERROR(log_) << "Received MAC does not match expected MAC.";
return false;
}
}
packet.skip (sizeof packet_len);
session_->remote_sequence_number_++;
padding_len = packet.pop();
if (padding_len != 0)
{
if (packet.length() < padding_len)
{
ERROR(log_) << "Padding too large for packet.";
return false;
}
packet.trim (padding_len);
}
if (packet.empty())
{
ERROR(log_) << "Need to handle empty packet.";
return false;
}
/*
* Pass by range to registered handlers for each range.
* Unhandled messages go to the receive_callback_, and
* the caller can register key exchange mechanisms,
* and handle (or discard) whatever they don't handle.
*
* NB: The caller could do all this, but it's assumed
* that they usually have better things to do. If
* they register no handlers, they can certainly do
* so by hand.
*
* XXX It seems like having a separate class which handles
* all these details and algorithm negotiation would be
* nice, and to have this one be a bit more oriented
* towards managing just the transport layer.
*
* At the very least, it needs to take responsibility
* for its failures and allow the handler functions
* here to mangle the packet buffer rather than trying
* to send it on to the receiver if decoding fails.
* A decoding failure should result in a disconnect,
* an error.
*/
msg = packet.peek();
if (msg >= SSH::Message::TransportRangeBegin &&
msg <= SSH::Message::TransportRangeEnd)
{
DEBUG(log_) << "Using default handler for transport message.";
}
else if (msg >= SSH::Message::AlgorithmNegotiationRangeBegin &&
msg <= SSH::Message::AlgorithmNegotiationRangeEnd)
{
if (session_->algorithm_negotiation_)
{
if (session_->algorithm_negotiation_->input (upstream_, &packet))
continue;
ERROR(log_) << "Algorithm negotiation message failed.";
return false;
}
DEBUG(log_) << "Using default handler for algorithm negotiation message.";
}
else if (msg >= SSH::Message::KeyExchangeMethodRangeBegin &&
msg <= SSH::Message::KeyExchangeMethodRangeEnd)
{
if (session_->chosen_algorithms_.key_exchange_)
{
if (session_->chosen_algorithms_.key_exchange_->input (upstream_, &packet))
continue;
ERROR(log_) << "Key exchange message failed.";
return false;
}
DEBUG(log_) << "Using default handler for key exchange method message.";
}
else if (msg >= SSH::Message::UserAuthenticationGenericRangeBegin &&
msg <= SSH::Message::UserAuthenticationGenericRangeEnd)
{
DEBUG(log_) << "Using default handler for generic user authentication message.";
}
else if (msg >= SSH::Message::UserAuthenticationMethodRangeBegin &&
msg <= SSH::Message::UserAuthenticationMethodRangeEnd)
{
DEBUG(log_) << "Using default handler for user authentication method message.";
}
else if (msg >= SSH::Message::ConnectionProtocolGlobalRangeBegin &&
msg <= SSH::Message::ConnectionProtocolGlobalRangeEnd)
{
DEBUG(log_) << "Using default handler for generic connection protocol message.";
}
else if (msg >= SSH::Message::ConnectionChannelRangeBegin &&
msg <= SSH::Message::ConnectionChannelRangeEnd)
{
DEBUG(log_) << "Using default handler for connection channel message.";
}
else if (msg >= SSH::Message::ClientProtocolReservedRangeBegin &&
msg <= SSH::Message::ClientProtocolReservedRangeEnd)
{
DEBUG(log_) << "Using default handler for client protocol message.";
}
else if (msg >= SSH::Message::LocalExtensionRangeBegin)
{
/* Because msg is a uint8_t, it will always be <= SSH::Message::LocalExtensionRangeEnd. */
DEBUG(log_) << "Using default handler for local extension message.";
}
else
{
ASSERT(log_, msg == 0);
ERROR(log_) << "Message outside of protocol range received. Passing to default handler, but not expecting much.";
}
/*
* If we're reading data that has been encoded, we need to untag it.
* Otherwise we need to frame it.
*/
if (encoded_)
{
if (packet.peek () != SSHStreamPacket || packet.length() == 1)
{
ERROR(log_) << "Got encoded packet with wrong message.";
return false;
}
packet.skip (1);
}
else
{
uint32_t length = packet.length ();
length = BigEndian::encode (length);
Buffer b;
b.append (&length);
packet.moveout (&b);
packet = b;
}
return produce (packet);
}
return true;
}
void SSH::DecryptFilter::flush (int flg)
{
Buffer bfr;
if (! pending_.empty ())
consume (bfr);
Filter::flush (flg);
}

55
ssh/ssh_filter.h Normal file
View file

@ -0,0 +1,55 @@
////////////////////////////////////////////////////////////////////////////////
// //
// File: ssh_filter.h //
// Description: SSH encryption/decryption inside a data filter pair //
// Project: WANProxy XTech //
// Author: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
#ifndef SSH_ENCRYPT_FILTER_H
#define SSH_ENCRYPT_FILTER_H
#include <common/filter.h>
#include <ssh/ssh_session.h>
namespace SSH
{
const int SOURCE_ENCODED = 0x01;
const int ALGORITHM_NEGOTIATED = 0x2C;
class EncryptFilter : public BufferedFilter
{
private:
Session session_;
bool encoded_, negotiated_;
public:
EncryptFilter (Role role, int flg = 0);
virtual bool consume (Buffer& buf);
virtual bool produce (Buffer& buf);
virtual void flush (int flg);
Session* current_session () { return &session_; }
};
class DecryptFilter : public LogisticFilter
{
private:
Buffer first_block_;
Session* session_;
bool encoded_, identified_;
public:
DecryptFilter (int flg = 0);
virtual bool consume (Buffer& buf);
virtual void flush (int flg);
void set_encrypter (EncryptFilter* f) { session_ = (f ? f->current_session () : 0); set_upstream (f); }
};
}
#endif /* !SSH_ENCRYPT_FILTER_H */

357
ssh/ssh_key_exchange.cc Normal file
View file

@ -0,0 +1,357 @@
/*
* Copyright (c) 2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <common/buffer.h>
#include <crypto/crypto_hash.h>
#include <event/event_callback.h>
#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_key_exchange.h>
#include <ssh/ssh_protocol.h>
#include <ssh/ssh_server_host_key.h>
#include <ssh/ssh_session.h>
#include <ssh/ssh_filter.h>
////////////////////////////////////////////////////////////////////////////////
// //
// File: ssh_key_exchange.cc //
// Description: SSH key check performed on filter session start //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
#define DH_GROUP_MIN 1024
#define DH_GROUP_MAX 8192
#define USE_TEST_GROUP
namespace {
#ifdef USE_TEST_GROUP
static uint8_t test_prime_and_generator[] = {
0x00, 0x00, 0x00, 0x81, 0x00, 0xe3, 0x1d, 0xfe, 0x85, 0x59, 0x9b, 0xcb, 0x5c, 0x2b, 0xbe, 0xcf,
0x20, 0x1f, 0x5f, 0x49, 0xf1, 0xea, 0x31, 0x07, 0x7d, 0xa9, 0x26, 0xcb, 0x31, 0x03, 0x9d, 0x82,
0x33, 0x2f, 0xed, 0x67, 0xa3, 0xa9, 0xb1, 0xc9, 0xe6, 0x34, 0x6c, 0xd7, 0xb5, 0x1a, 0x0a, 0x94,
0x11, 0xa7, 0xd9, 0x26, 0xff, 0x0e, 0x8d, 0x72, 0xc1, 0x7b, 0x53, 0x9a, 0x13, 0x78, 0x7e, 0x16,
0x38, 0x74, 0x7c, 0xb2, 0xdc, 0x60, 0x2c, 0x8c, 0xe8, 0x31, 0xf8, 0xd9, 0x7b, 0xac, 0xa6, 0x71,
0xee, 0x61, 0x0c, 0x1a, 0xa4, 0x2f, 0x47, 0x2f, 0xe2, 0x22, 0xbd, 0x01, 0xe5, 0x25, 0xb6, 0x95,
0xda, 0x3f, 0xf7, 0x03, 0xf4, 0x0e, 0xd6, 0x8c, 0xbb, 0x69, 0x1d, 0xcb, 0xd1, 0xe2, 0x60, 0xdb,
0xf5, 0x0b, 0x85, 0x98, 0xe6, 0x17, 0xbe, 0x29, 0x4e, 0xa7, 0x90, 0x11, 0xac, 0xbc, 0xa5, 0x3e,
0x05, 0xfe, 0xe9, 0x56, 0x93, 0x00, 0x00, 0x00, 0x01, 0x02
};
#endif
static const uint8_t
DiffieHellmanGroupExchangeRequest = 34,
DiffieHellmanGroupExchangeGroup = 31,
DiffieHellmanGroupExchangeInitialize = 32,
DiffieHellmanGroupExchangeReply = 33;
/*
* XXX
* Like a non-trivial amount of other code, this has been
* written a bit fast-and-loose. The usage of the dh_ and
* k_ in particularly are a bit dodgy and need to be freed
* in the destructor.
*
* Need to add assertions and frees.
*/
template<CryptoHash::Algorithm hash_algorithm>
class DiffieHellmanGroupExchange : public SSH::KeyExchange {
LogHandle log_;
SSH::Session *session_;
DH *dh_;
Buffer key_exchange_;
BIGNUM *k_;
public:
DiffieHellmanGroupExchange(SSH::Session *session, const std::string& key_exchange_name)
: SSH::KeyExchange(key_exchange_name),
log_("/ssh/key_exchange/" + key_exchange_name),
session_(session),
dh_(NULL),
key_exchange_(),
k_()
{ }
~DiffieHellmanGroupExchange()
{ }
KeyExchange *clone(void) const
{
return (new DiffieHellmanGroupExchange(session_, name_));
}
bool hash(Buffer *out, const Buffer *in) const
{
return (CryptoHash::hash(hash_algorithm, out, in));
}
bool input(Filter* sender, Buffer *in)
{
SSH::ServerHostKey *key;
uint32_t max, min, n;
BIGNUM *e, *f;
Buffer server_public_key;
Buffer signature;
Buffer packet;
Buffer group;
Buffer exchange_hash;
Buffer data;
Buffer initialize;
switch (in->peek()) {
case DiffieHellmanGroupExchangeRequest:
if (session_->role_ != SSH::ServerRole) {
ERROR(log_) << "Received group exchange request as client.";
return (false);
}
in->skip(1);
key_exchange_ = *in;
if (!SSH::UInt32::decode(&min, in))
return (false);
if (!SSH::UInt32::decode(&n, in))
return (false);
if (!SSH::UInt32::decode(&max, in))
return (false);
if (min < DH_GROUP_MIN)
min = DH_GROUP_MIN;
if (max > DH_GROUP_MAX)
max = DH_GROUP_MAX;
if (min > max)
return (false);
if (n < min)
n = min;
else if (n > max)
n = max;
#ifdef USE_TEST_GROUP
group.append(test_prime_and_generator, sizeof test_prime_and_generator);
dh_ = DH_new();
SSH::MPInt::decode(&dh_->p, &group);
SSH::MPInt::decode(&dh_->g, &group);
ASSERT(log_, group.empty());
#else
DEBUG(log_) << "Doing DH_generate_parameters for " << n << " bits.";
ASSERT(log_, dh_ == NULL);
dh_ = DH_generate_parameters(n, 2, NULL, NULL);
if (dh_ == NULL) {
ERROR(log_) << "DH_generate_parameters failed.";
return (false);
}
#endif
SSH::MPInt::encode(&group, dh_->p);
SSH::MPInt::encode(&group, dh_->g);
key_exchange_.append(group);
packet.append(DiffieHellmanGroupExchangeGroup);
packet.append(group);
sender->produce(packet);
return (true);
case DiffieHellmanGroupExchangeGroup:
if (session_->role_ != SSH::ClientRole) {
ERROR(log_) << "Received DH group as server.";
return (false);
}
in->skip(1);
key_exchange_.append(in);
dh_ = DH_new();
if (dh_ == NULL) {
ERROR(log_) << "DH_new failed.";
return (false);
}
if (!SSH::MPInt::decode(&dh_->p, in))
return (false);
if (!SSH::MPInt::decode(&dh_->g, in))
return (false);
if (!DH_generate_key(dh_)) {
ERROR(log_) << "DH_generate_key failed.";
return (false);
}
e = dh_->pub_key;
SSH::MPInt::encode(&initialize, e);
key_exchange_.append(initialize);
packet.append(DiffieHellmanGroupExchangeInitialize);
packet.append(initialize);
sender->produce(packet);
return (true);
case DiffieHellmanGroupExchangeInitialize:
if (session_->role_ != SSH::ServerRole) {
ERROR(log_) << "Received group exchange initialization as client.";
return (false);
}
in->skip(1);
key_exchange_.append(in);
if (!SSH::MPInt::decode(&e, in))
return (false);
if (!DH_generate_key(dh_))
return (false);
f = dh_->pub_key;
SSH::MPInt::encode(&key_exchange_, f);
if (!exchange_finish(e)) {
ERROR(log_) << "Server key exchange finish failed.";
return (false);
}
key = session_->chosen_algorithms_.server_host_key_;
if (!key->sign(&signature, &session_->exchange_hash_))
return (false);
key->encode_public_key(&server_public_key);
packet.append(DiffieHellmanGroupExchangeReply);
SSH::String::encode(&packet, server_public_key);
SSH::MPInt::encode(&packet, f);
SSH::String::encode(&packet, &signature);
sender->produce(packet);
sender->flush(SSH::ALGORITHM_NEGOTIATED);
/*
* XXX
* Should send NEWKEYS.
*/
return (true);
case DiffieHellmanGroupExchangeReply:
if (session_->role_ != SSH::ClientRole) {
ERROR(log_) << "Received group exchange reply as client.";
return (false);
}
in->skip(1);
if (!SSH::String::decode(&server_public_key, in))
return (false);
if (!SSH::MPInt::decode(&f, in))
return (false);
if (!SSH::String::decode(&signature, in))
return (false);
key = session_->chosen_algorithms_.server_host_key_;
if (!key->decode_public_key(&server_public_key)) {
ERROR(log_) << "Could not decode server public key:" << std::endl << server_public_key.hexdump();
return (false);
}
SSH::MPInt::encode(&key_exchange_, f);
if (!exchange_finish(f)) {
ERROR(log_) << "Client key exchange finish failed.";
return (false);
}
if (!key->verify(&signature, &session_->exchange_hash_)) {
ERROR(log_) << "Failed to verify exchange hash.";
return (false);
}
sender->flush(SSH::ALGORITHM_NEGOTIATED);
/*
* XXX
* Should send NEWKEYS, but we're not ready for that yet.
* For now we just assume the peer will do it. How lazy,
* no?
*/
return (true);
default:
ERROR(log_) << "Not yet implemented.";
return (false);
}
}
bool init(Buffer *out)
{
ASSERT(log_, out->empty());
ASSERT(log_, session_->role_ == SSH::ClientRole);
Buffer request;
SSH::UInt32::encode(&request, DH_GROUP_MIN);
SSH::UInt32::encode(&request, DH_GROUP_MIN);
SSH::UInt32::encode(&request, DH_GROUP_MAX);
key_exchange_ = request;
out->append(DiffieHellmanGroupExchangeRequest);
out->append(request);
return (true);
}
private:
bool exchange_finish(BIGNUM *remote_pubkey)
{
SSH::ServerHostKey *key;
Buffer server_public_key;
Buffer exchange_hash;
Buffer data;
ASSERT(log_, dh_ != NULL);
uint8_t secret[DH_size(dh_)];
int secretlen = DH_compute_key(secret, remote_pubkey, dh_);
if (secretlen == -1)
return (false);
k_ = BN_bin2bn(secret, secretlen, NULL);
if (k_ == NULL)
return (false);
key = session_->chosen_algorithms_.server_host_key_;
key->encode_public_key(&server_public_key);
SSH::String::encode(&data, session_->client_version_);
SSH::String::encode(&data, session_->server_version_);
SSH::String::encode(&data, session_->client_kexinit_);
SSH::String::encode(&data, session_->server_kexinit_);
SSH::String::encode(&data, server_public_key);
data.append(key_exchange_);
SSH::MPInt::encode(&data, k_);
if (!CryptoHash::hash(hash_algorithm, &exchange_hash, &data))
return (false);
session_->exchange_hash_ = exchange_hash;
SSH::MPInt::encode(&session_->shared_secret_, k_);
if (session_->session_id_.empty())
session_->session_id_ = exchange_hash;
return (true);
}
};
}
void
SSH::KeyExchange::add_algorithms(SSH::Session *session)
{
session->algorithm_negotiation_->add_algorithm(new DiffieHellmanGroupExchange<CryptoHash::SHA256>(session, "diffie-hellman-group-exchange-sha256"));
session->algorithm_negotiation_->add_algorithm(new DiffieHellmanGroupExchange<CryptoHash::SHA1>(session, "diffie-hellman-group-exchange-sha1"));
}

74
ssh/ssh_key_exchange.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_KEY_EXCHANGE_H
#define SSH_SSH_KEY_EXCHANGE_H
////////////////////////////////////////////////////////////////////////////////
// //
// File: ssh_key_exchange.h //
// Description: SSH key check performed on filter session start //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
class Buffer;
class Filter;
namespace SSH {
struct Session;
class KeyExchange {
protected:
const std::string name_;
KeyExchange(const std::string& xname)
: name_(xname)
{ }
public:
virtual ~KeyExchange()
{ }
std::string name(void) const
{
return (name_);
}
virtual KeyExchange *clone(void) const = 0;
virtual bool hash(Buffer *, const Buffer *) const = 0;
virtual bool input(Filter *, Buffer *) = 0;
virtual bool init(Buffer *) = 0;
static void add_algorithms(Session *);
};
}
#endif /* !SSH_SSH_KEY_EXCHANGE_H */

52
ssh/ssh_language.h Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_LANGUAGE_H
#define SSH_SSH_LANGUAGE_H
namespace SSH {
class Language {
std::string name_;
protected:
Language(const std::string& xname)
: name_(xname)
{ }
public:
virtual ~Language()
{ }
std::string name(void) const
{
return (name_);
}
virtual Language *clone(void) const = 0;
virtual bool input(Buffer *) = 0;
};
}
#endif /* !SSH_SSH_LANGUAGE_H */

122
ssh/ssh_mac.cc Normal file
View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <common/buffer.h>
#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_mac.h>
#include <ssh/ssh_session.h>
namespace {
struct ssh_mac_algorithm {
const char *rfc4250_name_;
CryptoMAC::Algorithm crypto_algorithm_;
unsigned size_;
};
static const struct ssh_mac_algorithm ssh_mac_algorithms[] = {
{ "hmac-sha1", CryptoMAC::SHA1, 0 },
{ "hmac-sha2-256", CryptoMAC::SHA256, 0 },
{ "hmac-sha2-512", CryptoMAC::SHA512, 0 },
{ "hmac-ripemd160", CryptoMAC::RIPEMD160, 0 },
{ "hmac-md5", CryptoMAC::MD5, 0 },
{ "hmac-sha1-96", CryptoMAC::SHA1, 12 },
{ "hmac-md5-96", CryptoMAC::MD5, 12 },
{ NULL, CryptoMAC::MD5, 0 }
};
class CryptoSSHMAC : public SSH::MAC {
LogHandle log_;
CryptoMAC::Instance *instance_;
public:
CryptoSSHMAC(const std::string& xname, CryptoMAC::Instance *instance, unsigned xsize)
: SSH::MAC(xname, xsize == 0 ? instance->size() : xsize, instance->size()),
log_("/ssh/mac/crypto/" + xname),
instance_(instance)
{ }
~CryptoSSHMAC()
{ }
MAC *clone(void) const
{
return (new CryptoSSHMAC(name_, instance_->clone(), key_size_));
}
bool initialize(const Buffer *key)
{
return (instance_->initialize(key));
}
bool mac(Buffer *out, const Buffer *in)
{
return (instance_->mac(out, in));
}
};
}
void
SSH::MAC::add_algorithms(Session *session)
{
const struct ssh_mac_algorithm *alg;
for (alg = ssh_mac_algorithms; alg->rfc4250_name_ != NULL; alg++) {
const CryptoMAC::Method *method = CryptoMAC::Method::method(alg->crypto_algorithm_);
if (method == NULL) {
DEBUG("/ssh/mac") << "Could not get method for algorithm: " << alg->crypto_algorithm_;
continue;
}
CryptoMAC::Instance *instance = method->instance(alg->crypto_algorithm_);
if (instance == NULL) {
DEBUG("/ssh/mac") << "Could not get instance for algorithm: " << alg->crypto_algorithm_;
continue;
}
session->algorithm_negotiation_->add_algorithm(new CryptoSSHMAC(alg->rfc4250_name_, instance, alg->size_));
}
}
SSH::MAC *
SSH::MAC::algorithm(CryptoMAC::Algorithm xalgorithm)
{
const struct ssh_mac_algorithm *alg;
for (alg = ssh_mac_algorithms; alg->rfc4250_name_ != NULL; alg++) {
if (xalgorithm != alg->crypto_algorithm_)
continue;
const CryptoMAC::Method *method = CryptoMAC::Method::method(xalgorithm);
if (method == NULL) {
ERROR("/ssh/mac") << "Could not get method for algorithm: " << xalgorithm;
return (NULL);
}
CryptoMAC::Instance *instance = method->instance(xalgorithm);
if (instance == NULL) {
ERROR("/ssh/mac") << "Could not get instance for algorithm: " << xalgorithm;
return (NULL);
}
return (new CryptoSSHMAC(alg->rfc4250_name_, instance, alg->size_));
}
ERROR("/ssh/mac") << "No SSH MAC support is available for algorithm: " << xalgorithm;
return (NULL);
}

75
ssh/ssh_mac.h Normal file
View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_MAC_H
#define SSH_SSH_MAC_H
#include <crypto/crypto_mac.h>
namespace SSH {
struct Session;
class MAC {
protected:
const std::string name_;
const unsigned size_;
const unsigned key_size_;
MAC(const std::string& xname, unsigned xsize, unsigned xkey_size)
: name_(xname),
size_(xsize),
key_size_(xkey_size)
{ }
public:
virtual ~MAC()
{ }
std::string name(void) const
{
return (name_);
}
unsigned size(void) const
{
return (size_);
}
unsigned key_size(void) const
{
return (key_size_);
}
virtual MAC *clone(void) const = 0;
virtual bool initialize(const Buffer *) = 0;
virtual bool mac(Buffer *, const Buffer *) = 0;
static void add_algorithms(Session *);
static MAC *algorithm(CryptoMAC::Algorithm);
};
}
#endif /* !SSH_SSH_MAC_H */

154
ssh/ssh_protocol.cc Normal file
View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2012-2013 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <openssl/bn.h>
#include <common/buffer.h>
#include <common/endian.h>
#include <ssh/ssh_protocol.h>
void
SSH::String::encode(Buffer *out, const Buffer& in)
{
SSH::UInt32::encode(out, in.length());
if (!in.empty())
out->append(in);
}
void
SSH::String::encode(Buffer *out, Buffer *in)
{
SSH::String::encode(out, *in);
if (!in->empty())
in->clear();
}
bool
SSH::String::decode(Buffer *out, Buffer *in)
{
uint32_t len;
if (in->length() < sizeof len)
return (false);
BigEndian::extract(&len, in);
if (len == 0) {
in->skip(sizeof len);
return (true);
}
if (in->length() < sizeof len + len)
return (false);
in->moveout(out, sizeof len, len);
return (true);
}
void
SSH::UInt32::encode(Buffer *out, uint32_t in)
{
BigEndian::append(out, in);
}
bool
SSH::UInt32::decode(uint32_t *outp, Buffer *in)
{
if (in->length() < sizeof *outp)
return (false);
BigEndian::extract(outp, in);
in->skip(sizeof *outp);
return (true);
}
void
SSH::MPInt::encode(Buffer *out, const BIGNUM *in)
{
if (BN_is_negative(in)) {
HALT("/ssh/mpint/encode") << "Negative numbers not yet implemented.";
}
uint8_t buf[BN_num_bytes(in)];
BN_bn2bin(in, buf);
if ((buf[0] & 0x80) == 0x80) {
SSH::UInt32::encode(out, sizeof buf + 1);
out->append((uint8_t)0x00);
out->append(buf, sizeof buf);
} else {
SSH::UInt32::encode(out, sizeof buf);
out->append(buf, sizeof buf);
}
}
bool
SSH::MPInt::decode(BIGNUM **outp, Buffer *in)
{
uint32_t len;
BIGNUM *out;
if (in->length() < sizeof len)
return (false);
BigEndian::extract(&len, in);
if (len == 0) {
out = BN_new();
if (out == NULL)
return (false);
in->skip(sizeof len);
BN_zero(out);
*outp = out;
return (true);
}
if (in->length() < sizeof len + len)
return (false);
uint8_t buf[len];
in->copyout(buf, sizeof len, len);
out = BN_bin2bn(buf, sizeof buf, NULL);
if (out == NULL)
return (false);
in->skip(sizeof len + len);
*outp = out;
return (true);
}
void
SSH::NameList::encode(Buffer *out, const std::vector<Buffer>& in)
{
Buffer merged;
merged = Buffer::join(in, ",");
SSH::String::encode(out, &merged);
}
bool
SSH::NameList::decode(std::vector<Buffer>& out, Buffer *in)
{
Buffer merged;
if (!SSH::String::decode(&merged, in))
return (false);
out = merged.split(',');
return (true);
}

138
ssh/ssh_protocol.h Normal file
View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_PROTOCOL_H
#define SSH_SSH_PROTOCOL_H
#include <common/endian.h>
/* XXX OpenSSL dependency. */
typedef struct bignum_st BIGNUM;
namespace SSH {
namespace Message {
static const uint8_t
TransportRangeBegin = 1,
TransportDisconnectMessage = 1,
TransportIgnoreMessage = 2,
TransportUnimplementedMessage = 3,
TransportDebugMessage = 4,
TransportServiceRequestMessage = 5,
TransportServiceAcceptMessage = 6,
TransportRangeEnd = 19,
AlgorithmNegotiationRangeBegin = 20,
KeyExchangeInitializationMessage = 20,
NewKeysMessage = 21,
AlgorithmNegotiationRangeEnd = 29,
KeyExchangeMethodRangeBegin = 30,
KeyExchangeMethodRangeEnd = 49,
UserAuthenticationGenericRangeBegin = 50,
UserAuthenticationRequestMessage = 50,
UserAuthenticationFailureMessage = 51,
UserAuthenticationSuccessMessage = 52,
UserAuthenticationBannerMessage = 53,
UserAuthenticationGenericRangeEnd = 59,
UserAuthenticationMethodRangeBegin = 60,
UserAuthenticationMethodRangeEnd = 79,
ConnectionProtocolGlobalRangeBegin = 80,
ConnectionProtocolGlobalRequestMessage = 80,
ConnectionProtocolGlobalRequestSuccessMessage = 81,
ConnectionProtocolGlobalRequestFailureMessage = 82,
ConnectionProtocolGlobalRangeEnd = 89,
ConnectionChannelRangeBegin = 90,
ConnectionChannelOpen = 90,
ConnectionChannelOpenConfirmation = 91,
ConnectionChannelOpenFailure = 92,
ConnectionChannelWindowAdjust = 93,
ConnectionChannelData = 94,
ConnectionChannelExtendedData = 95,
ConnectionChannelEndOfFile = 96,
ConnectionChannelClose = 97,
ConnectionChannelRequest = 98,
ConnectionChannelRequestSuccess = 99,
ConnectionChannelRequestFailure = 100,
ConnectionChannelRangeEnd = 127,
ClientProtocolReservedRangeBegin = 128,
ClientProtocolReservedRangeEnd = 191,
LocalExtensionRangeBegin = 192,
LocalExtensionRangeEnd = 255;
}
namespace Boolean {
static const uint8_t
True = 1,
False = 0;
}
namespace Disconnect {
static const uint8_t
HostNotallowedToConnect = 1,
ProtocolError = 2,
KeyExchangeFailed = 3,
Reserved = 4,
MACError = 5,
CompressionError = 6,
ServiceNotAvailable = 7,
ProtocolVersionNotSupported = 8,
HostKeyNotVerifiable = 9,
ConnectionLost = 10,
ByApplication = 11,
TooManyConnections = 12,
AuthenticationCancelledByUser = 13,
NoMoreAuthenticationMethodsAvailable = 14,
IllegalUserName = 15;
}
namespace String {
void encode(Buffer *, const Buffer&);
void encode(Buffer *, Buffer *);
bool decode(Buffer *, Buffer *);
}
namespace UInt32 {
void encode(Buffer *, uint32_t);
bool decode(uint32_t *, Buffer *);
}
namespace MPInt {
void encode(Buffer *, const BIGNUM *);
bool decode(BIGNUM **, Buffer *);
}
namespace NameList {
void encode(Buffer *, const std::vector<Buffer>&);
bool decode(std::vector<Buffer>&, Buffer *);
}
}
#endif /* !SSH_SSH_PROTOCOL_H */

187
ssh/ssh_server_host_key.cc Normal file
View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <common/buffer.h>
#include <crypto/crypto_hash.h>
#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_protocol.h>
#include <ssh/ssh_server_host_key.h>
#include <ssh/ssh_session.h>
namespace {
class RSAServerHostKey : public SSH::ServerHostKey {
LogHandle log_;
SSH::Session *session_;
RSA *rsa_;
public:
RSAServerHostKey(SSH::Session *session, RSA *rsa)
: SSH::ServerHostKey("ssh-rsa"),
log_("/ssh/serverhostkey/rsa"),
session_(session),
rsa_(rsa)
{
ASSERT(log_, rsa != NULL || session->role_ == SSH::ClientRole);
}
~RSAServerHostKey()
{ }
SSH::ServerHostKey *clone(void) const
{
if (session_->role_ == SSH::ClientRole) {
if (rsa_ != NULL)
return (new RSAServerHostKey(session_, RSAPublicKey_dup(rsa_)));
else
return (new RSAServerHostKey(session_, NULL));
} else {
return (new RSAServerHostKey(session_, RSAPrivateKey_dup(rsa_)));
}
}
bool decode_public_key(Buffer *in)
{
ASSERT(log_, session_->role_ == SSH::ClientRole);
ASSERT(log_, rsa_ == NULL);
Buffer tag;
if (!SSH::String::decode(&tag, in))
return (false);
if (!tag.equal("ssh-rsa"))
return (false);
rsa_ = RSA_new();
if (rsa_ == NULL)
return (false);
if (!SSH::MPInt::decode(&rsa_->e, in))
return (false);
if (!SSH::MPInt::decode(&rsa_->n, in))
return (false);
return (true);
}
void encode_public_key(Buffer *out) const
{
SSH::String::encode(out, Buffer("ssh-rsa"));
SSH::MPInt::encode(out, rsa_->e);
SSH::MPInt::encode(out, rsa_->n);
}
bool sign(Buffer *out, const Buffer *in) const
{
ASSERT(log_, session_->role_ == SSH::ServerRole);
Buffer hash;
if (!CryptoHash::hash(CryptoHash::SHA1, &hash, in))
return (false);
uint8_t m[hash.length()];
hash.moveout(m, sizeof m);
uint8_t signature[RSA_size(rsa_)];
unsigned signature_length = sizeof signature;
int rv = RSA_sign(NID_sha1, m, sizeof m, signature, &signature_length, rsa_);
if (rv == 0)
return (false);
SSH::String::encode(out, Buffer("ssh-rsa"));
SSH::String::encode(out, Buffer(signature, signature_length));
return (true);
}
bool verify(const Buffer *signature, const Buffer *message) const
{
ASSERT(log_, session_->role_ == SSH::ClientRole);
Buffer hash;
if (!CryptoHash::hash(CryptoHash::SHA1, &hash, message))
return (false);
Buffer in;
in.append(signature);
Buffer tag;
if (!SSH::String::decode(&tag, &in))
return (false);
if (!tag.equal("ssh-rsa"))
return (false);
Buffer sig;
if (!SSH::String::decode(&sig, &in))
return (false);
uint8_t m[hash.length()];
hash.moveout(m, sizeof m);
uint8_t sigbuf[sig.length()];
sig.moveout(sigbuf, sig.length());
int rv = RSA_verify(NID_sha1, m, sizeof m, sigbuf, sizeof sigbuf, rsa_);
if (rv == 0)
return (false);
return (true);
}
static RSAServerHostKey *open(SSH::Session *session, FILE *file)
{
RSA *rsa;
ASSERT("/ssh/serverhostkey/rsa/open", session->role_ == SSH::ServerRole);
rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
if (rsa == NULL)
return (NULL);
return (new RSAServerHostKey(session, rsa));
}
};
}
void
SSH::ServerHostKey::add_client_algorithms(SSH::Session *session)
{
ASSERT("/ssh/serverhostkey/client", session->role_ == SSH::ClientRole);
session->algorithm_negotiation_->add_algorithm(new RSAServerHostKey(session, NULL));
}
SSH::ServerHostKey *
SSH::ServerHostKey::server(SSH::Session *session, const std::string& keyfile)
{
SSH::ServerHostKey *key;
FILE *file;
ASSERT("/ssh/serverhostkey/server", session->role_ == SSH::ServerRole);
file = fopen(keyfile.c_str(), "r");
if (file == NULL)
return (NULL);
key = RSAServerHostKey::open(session, file);
if (key == NULL) {
fclose(file);
return (NULL);
}
fclose(file);
return (key);
}

63
ssh/ssh_server_host_key.h Normal file
View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2011-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_SERVER_HOST_KEY_H
#define SSH_SSH_SERVER_HOST_KEY_H
class Buffer;
namespace SSH {
struct Session;
class ServerHostKey {
std::string name_;
protected:
ServerHostKey(const std::string& xname)
: name_(xname)
{ }
public:
virtual ~ServerHostKey()
{ }
std::string name(void) const
{
return (name_);
}
virtual ServerHostKey *clone(void) const = 0;
virtual bool decode_public_key(Buffer *) = 0;
virtual void encode_public_key(Buffer *) const = 0;
virtual bool sign(Buffer *, const Buffer *) const = 0;
virtual bool verify(const Buffer *, const Buffer *) const = 0;
static void add_client_algorithms(Session *);
static ServerHostKey *server(Session *, const std::string&);
};
}
#endif /* !SSH_SSH_SERVER_HOST_KEY_H */

112
ssh/ssh_session.cc Normal file
View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <common/buffer.h>
#include <ssh/ssh_encryption.h>
#include <ssh/ssh_key_exchange.h>
#include <ssh/ssh_mac.h>
#include <ssh/ssh_session.h>
void
SSH::Session::activate_chosen(void)
{
const Buffer *local_to_remote_iv_;
const Buffer *remote_to_local_iv_;
const Buffer *local_to_remote_key_;
const Buffer *remote_to_local_key_;
/*
* XXX
* Need to free instances in active_algorithms_.
*/
active_algorithms_ = chosen_algorithms_;
if (active_algorithms_.client_to_server_.encryption_ != NULL) {
client_to_server_iv_ = generate_key("A", active_algorithms_.client_to_server_.encryption_->iv_size());
client_to_server_key_ = generate_key("C", active_algorithms_.client_to_server_.encryption_->key_size());
}
if (active_algorithms_.server_to_client_.encryption_ != NULL) {
server_to_client_iv_ = generate_key("B", active_algorithms_.server_to_client_.encryption_->iv_size());
server_to_client_key_ = generate_key("D", active_algorithms_.server_to_client_.encryption_->key_size());
}
if (active_algorithms_.client_to_server_.mac_ != NULL) {
client_to_server_integrity_key_ = generate_key("E", active_algorithms_.client_to_server_.mac_->key_size());
if (!active_algorithms_.client_to_server_.mac_->initialize(&client_to_server_integrity_key_))
HALT("/ssh/session") << "Failed to activate client-to-server MAC.";
}
if (active_algorithms_.server_to_client_.mac_ != NULL) {
server_to_client_integrity_key_ = generate_key("F", active_algorithms_.server_to_client_.mac_->key_size());
if (!active_algorithms_.server_to_client_.mac_->initialize(&server_to_client_integrity_key_))
HALT("/ssh/session") << "Failed to activate server-to-client MAC.";
}
if (active_algorithms_.local_to_remote_->encryption_ != NULL) {
if (role_ == ClientRole) {
local_to_remote_iv_ = &client_to_server_iv_;
local_to_remote_key_ = &client_to_server_key_;
} else {
local_to_remote_iv_ = &server_to_client_iv_;
local_to_remote_key_ = &server_to_client_key_;
}
if (!active_algorithms_.local_to_remote_->encryption_->initialize(CryptoEncryption::Encrypt, local_to_remote_key_, local_to_remote_iv_))
HALT("/ssh/session") << "Failed to initialize local-to-remote encryption.";
}
if (active_algorithms_.remote_to_local_->encryption_ != NULL) {
if (role_ == ClientRole) {
remote_to_local_iv_ = &server_to_client_iv_;
remote_to_local_key_ = &server_to_client_key_;
} else {
remote_to_local_iv_ = &client_to_server_iv_;
remote_to_local_key_ = &client_to_server_key_;
}
if (!active_algorithms_.remote_to_local_->encryption_->initialize(CryptoEncryption::Decrypt, remote_to_local_key_, remote_to_local_iv_))
HALT("/ssh/session") << "Failed to initialize local-to-remote encryption.";
}
}
Buffer
SSH::Session::generate_key(const std::string& x, unsigned key_size)
{
Buffer key;
while (key.length() < key_size) {
Buffer input;
input.append(shared_secret_);
input.append(exchange_hash_);
if (key.empty()) {
input.append(x);
input.append(session_id_);
} else {
input.append(key);
}
if (!active_algorithms_.key_exchange_->hash(&key, &input))
HALT("/ssh/session") << "Hash failed in generating key.";
}
if (key.length() > key_size)
key.truncate(key_size);
return (key);
}

159
ssh/ssh_session.h Normal file
View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2012-2013 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SSH_SSH_SESSION_H
#define SSH_SSH_SESSION_H
namespace SSH {
class AlgorithmNegotiation;
class Compression;
class Encryption;
class KeyExchange;
class Language;
class MAC;
class ServerHostKey;
enum Role {
ClientRole,
ServerRole,
};
struct UnidirectionalAlgorithms {
Encryption *encryption_;
MAC *mac_;
Compression *compression_;
Language *language_;
UnidirectionalAlgorithms(void)
: encryption_(NULL),
mac_(NULL),
compression_(NULL),
language_(NULL)
{ }
};
struct Algorithms {
KeyExchange *key_exchange_;
ServerHostKey *server_host_key_;
UnidirectionalAlgorithms client_to_server_;
UnidirectionalAlgorithms server_to_client_;
UnidirectionalAlgorithms *local_to_remote_;
UnidirectionalAlgorithms *remote_to_local_;
Algorithms(Role role)
: key_exchange_(NULL),
server_host_key_(NULL),
client_to_server_(),
server_to_client_(),
local_to_remote_(role == ClientRole ? &client_to_server_ : &server_to_client_),
remote_to_local_(role == ClientRole ? &server_to_client_ : &client_to_server_)
{ }
};
struct Session {
Role role_;
AlgorithmNegotiation *algorithm_negotiation_;
Algorithms chosen_algorithms_;
Algorithms active_algorithms_;
Buffer client_version_; /* Client's version string. */
Buffer server_version_; /* Server's version string. */
Buffer client_kexinit_; /* Client's first key exchange packet. */
Buffer server_kexinit_; /* Server's first key exchange packet. */
Buffer shared_secret_; /* Shared secret from key exchange. */
Buffer session_id_; /* First exchange hash. */
Buffer exchange_hash_; /* Most recent exchange hash. */
private:
Buffer client_to_server_iv_; /* Initial client-to-server IV. */
Buffer server_to_client_iv_; /* Initial server-to-client IV. */
Buffer client_to_server_key_; /* Client-to-server encryption key. */
Buffer server_to_client_key_; /* Server-to-client encryption key. */
Buffer client_to_server_integrity_key_; /* Client-to-server integrity key. */
Buffer server_to_client_integrity_key_; /* Server-to-client integrity key. */
public:
uint32_t local_sequence_number_; /* Our packet sequence number. */
uint32_t remote_sequence_number_; /* Our peer's packet sequence number. */
Session(Role role)
: role_(role),
algorithm_negotiation_(NULL),
chosen_algorithms_(role),
active_algorithms_(role),
client_version_(),
server_version_(),
client_kexinit_(),
server_kexinit_(),
shared_secret_(),
session_id_(),
exchange_hash_(),
client_to_server_iv_(),
server_to_client_iv_(),
client_to_server_key_(),
server_to_client_key_(),
client_to_server_integrity_key_(),
server_to_client_integrity_key_(),
local_sequence_number_(0),
remote_sequence_number_(0)
{ }
void local_version(const Buffer& version)
{
if (role_ == ClientRole)
client_version_ = version;
else
server_version_ = version;
}
void remote_version(const Buffer& version)
{
if (role_ == ClientRole)
server_version_ = version;
else
client_version_ = version;
}
void local_kexinit(const Buffer& kexinit)
{
if (role_ == ClientRole)
client_kexinit_ = kexinit;
else
server_kexinit_ = kexinit;
}
void remote_kexinit(const Buffer& kexinit)
{
if (role_ == ClientRole)
server_kexinit_ = kexinit;
else
client_kexinit_ = kexinit;
}
void activate_chosen(void);
private:
Buffer generate_key(const std::string& x, unsigned key_size);
};
}
#endif /* !SSH_SSH_SESSION_H */