version 3.0
This commit is contained in:
commit
d837490606
209 changed files with 19662 additions and 0 deletions
0
ssh/Makefile
Normal file
0
ssh/Makefile
Normal file
18
ssh/TODO
Normal file
18
ssh/TODO
Normal 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
12
ssh/lib.mk
Normal 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
|
233
ssh/ssh_algorithm_negotiation.cc
Normal file
233
ssh/ssh_algorithm_negotiation.cc
Normal 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);
|
||||
}
|
139
ssh/ssh_algorithm_negotiation.h
Normal file
139
ssh/ssh_algorithm_negotiation.h
Normal 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
57
ssh/ssh_compression.cc
Normal 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
56
ssh/ssh_compression.h
Normal 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
128
ssh/ssh_encryption.cc
Normal 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
82
ssh/ssh_encryption.h
Normal 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
516
ssh/ssh_filter.cc
Normal 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
55
ssh/ssh_filter.h
Normal 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
357
ssh/ssh_key_exchange.cc
Normal 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
74
ssh/ssh_key_exchange.h
Normal 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
52
ssh/ssh_language.h
Normal 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
122
ssh/ssh_mac.cc
Normal 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
75
ssh/ssh_mac.h
Normal 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
154
ssh/ssh_protocol.cc
Normal 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
138
ssh/ssh_protocol.h
Normal 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
187
ssh/ssh_server_host_key.cc
Normal 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
63
ssh/ssh_server_host_key.h
Normal 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
112
ssh/ssh_session.cc
Normal 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
159
ssh/ssh_session.h
Normal 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 */
|
Loading…
Add table
Add a link
Reference in a new issue