233 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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);
 | |
| }
 |