1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "remoting/protocol/v2_authenticator.h" 6 7 #include "base/base64.h" 8 #include "base/logging.h" 9 #include "remoting/base/constants.h" 10 #include "remoting/base/rsa_key_pair.h" 11 #include "remoting/protocol/ssl_hmac_channel_authenticator.h" 12 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" 13 14 using crypto::P224EncryptedKeyExchange; 15 16 #if defined(_WIN32) && defined(GetMessage) 17 #undef GetMessage 18 #endif 19 20 namespace remoting { 21 namespace protocol { 22 23 namespace { 24 25 const buzz::StaticQName kEkeTag = { kChromotingXmlNamespace, 26 "eke-message" }; 27 const buzz::StaticQName kCertificateTag = { kChromotingXmlNamespace, 28 "certificate" }; 29 30 } // namespace 31 32 // static 33 bool V2Authenticator::IsEkeMessage(const buzz::XmlElement* message) { 34 return message->FirstNamed(kEkeTag) != NULL; 35 } 36 37 // static 38 scoped_ptr<Authenticator> V2Authenticator::CreateForClient( 39 const std::string& shared_secret, 40 Authenticator::State initial_state) { 41 return scoped_ptr<Authenticator>(new V2Authenticator( 42 P224EncryptedKeyExchange::kPeerTypeClient, shared_secret, initial_state)); 43 } 44 45 // static 46 scoped_ptr<Authenticator> V2Authenticator::CreateForHost( 47 const std::string& local_cert, 48 scoped_refptr<RsaKeyPair> key_pair, 49 const std::string& shared_secret, 50 Authenticator::State initial_state) { 51 scoped_ptr<V2Authenticator> result(new V2Authenticator( 52 P224EncryptedKeyExchange::kPeerTypeServer, shared_secret, initial_state)); 53 result->local_cert_ = local_cert; 54 result->local_key_pair_ = key_pair; 55 return scoped_ptr<Authenticator>(result.Pass()); 56 } 57 58 V2Authenticator::V2Authenticator( 59 crypto::P224EncryptedKeyExchange::PeerType type, 60 const std::string& shared_secret, 61 Authenticator::State initial_state) 62 : certificate_sent_(false), 63 key_exchange_impl_(type, shared_secret), 64 state_(initial_state), 65 rejection_reason_(INVALID_CREDENTIALS) { 66 pending_messages_.push(key_exchange_impl_.GetMessage()); 67 } 68 69 V2Authenticator::~V2Authenticator() { 70 } 71 72 Authenticator::State V2Authenticator::state() const { 73 if (state_ == ACCEPTED && !pending_messages_.empty()) 74 return MESSAGE_READY; 75 return state_; 76 } 77 78 Authenticator::RejectionReason V2Authenticator::rejection_reason() const { 79 DCHECK_EQ(state(), REJECTED); 80 return rejection_reason_; 81 } 82 83 void V2Authenticator::ProcessMessage(const buzz::XmlElement* message, 84 const base::Closure& resume_callback) { 85 ProcessMessageInternal(message); 86 resume_callback.Run(); 87 } 88 89 void V2Authenticator::ProcessMessageInternal(const buzz::XmlElement* message) { 90 DCHECK_EQ(state(), WAITING_MESSAGE); 91 92 // Parse the certificate. 93 std::string base64_cert = message->TextNamed(kCertificateTag); 94 if (!base64_cert.empty()) { 95 if (!base::Base64Decode(base64_cert, &remote_cert_)) { 96 LOG(WARNING) << "Failed to decode certificate received from the peer."; 97 remote_cert_.clear(); 98 } 99 } 100 101 // Client always expect certificate in the first message. 102 if (!is_host_side() && remote_cert_.empty()) { 103 LOG(WARNING) << "No valid host certificate."; 104 state_ = REJECTED; 105 rejection_reason_ = PROTOCOL_ERROR; 106 return; 107 } 108 109 const buzz::XmlElement* eke_element = message->FirstNamed(kEkeTag); 110 if (!eke_element) { 111 LOG(WARNING) << "No eke-message found."; 112 state_ = REJECTED; 113 rejection_reason_ = PROTOCOL_ERROR; 114 return; 115 } 116 117 for (; eke_element; eke_element = eke_element->NextNamed(kEkeTag)) { 118 std::string base64_message = eke_element->BodyText(); 119 std::string spake_message; 120 if (base64_message.empty() || 121 !base::Base64Decode(base64_message, &spake_message)) { 122 LOG(WARNING) << "Failed to decode auth message received from the peer."; 123 state_ = REJECTED; 124 rejection_reason_ = PROTOCOL_ERROR; 125 return; 126 } 127 128 P224EncryptedKeyExchange::Result result = 129 key_exchange_impl_.ProcessMessage(spake_message); 130 switch (result) { 131 case P224EncryptedKeyExchange::kResultPending: 132 pending_messages_.push(key_exchange_impl_.GetMessage()); 133 break; 134 135 case P224EncryptedKeyExchange::kResultFailed: 136 state_ = REJECTED; 137 rejection_reason_ = INVALID_CREDENTIALS; 138 return; 139 140 case P224EncryptedKeyExchange::kResultSuccess: 141 auth_key_ = key_exchange_impl_.GetKey(); 142 state_ = ACCEPTED; 143 return; 144 } 145 } 146 147 state_ = MESSAGE_READY; 148 } 149 150 scoped_ptr<buzz::XmlElement> V2Authenticator::GetNextMessage() { 151 DCHECK_EQ(state(), MESSAGE_READY); 152 153 scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage(); 154 155 DCHECK(!pending_messages_.empty()); 156 while (!pending_messages_.empty()) { 157 const std::string& spake_message = pending_messages_.front(); 158 std::string base64_message; 159 if (!base::Base64Encode(spake_message, &base64_message)) { 160 LOG(DFATAL) << "Cannot perform base64 encode on certificate"; 161 continue; 162 } 163 164 buzz::XmlElement* eke_tag = new buzz::XmlElement(kEkeTag); 165 eke_tag->SetBodyText(base64_message); 166 message->AddElement(eke_tag); 167 168 pending_messages_.pop(); 169 } 170 171 if (!local_cert_.empty() && !certificate_sent_) { 172 buzz::XmlElement* certificate_tag = new buzz::XmlElement(kCertificateTag); 173 std::string base64_cert; 174 if (!base::Base64Encode(local_cert_, &base64_cert)) { 175 LOG(DFATAL) << "Cannot perform base64 encode on certificate"; 176 } 177 certificate_tag->SetBodyText(base64_cert); 178 message->AddElement(certificate_tag); 179 certificate_sent_ = true; 180 } 181 182 if (state_ != ACCEPTED) { 183 state_ = WAITING_MESSAGE; 184 } 185 return message.Pass(); 186 } 187 188 scoped_ptr<ChannelAuthenticator> 189 V2Authenticator::CreateChannelAuthenticator() const { 190 DCHECK_EQ(state(), ACCEPTED); 191 CHECK(!auth_key_.empty()); 192 193 if (is_host_side()) { 194 return scoped_ptr<ChannelAuthenticator>( 195 SslHmacChannelAuthenticator::CreateForHost( 196 local_cert_, local_key_pair_, auth_key_).Pass()); 197 } else { 198 return scoped_ptr<ChannelAuthenticator>( 199 SslHmacChannelAuthenticator::CreateForClient( 200 remote_cert_, auth_key_).Pass()); 201 } 202 } 203 204 bool V2Authenticator::is_host_side() const { 205 return local_key_pair_.get() != NULL; 206 } 207 208 } // namespace protocol 209 } // namespace remoting 210