OLD | NEW |
---|---|
(Empty) | |
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/third_party_authenticator.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/bind.h" | |
9 #include "base/callback.h" | |
10 #include "base/logging.h" | |
11 #include "crypto/rsa_private_key.h" | |
12 #include "remoting/base/constants.h" | |
13 #include "remoting/protocol/channel_authenticator.h" | |
14 #include "remoting/protocol/v2_authenticator.h" | |
15 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" | |
16 | |
17 #if defined(_WIN32) && defined(GetMessage) | |
Sergey Ulanov
2013/02/26 01:14:50
Do you need this here?
rmsousa
2013/03/05 03:30:24
Done.
| |
18 #undef GetMessage | |
19 #endif | |
20 | |
21 namespace { | |
22 | |
23 const buzz::StaticQName kTokenUrlTag = { remoting::kChromotingXmlNamespace, | |
24 "third-party-token-url" }; | |
25 const buzz::StaticQName kTokenScopeTag = { remoting::kChromotingXmlNamespace, | |
26 "third-party-token-scope" }; | |
27 const buzz::StaticQName kTokenTag = { remoting::kChromotingXmlNamespace, | |
28 "third-party-token" }; | |
29 } // namespace | |
30 | |
Sergey Ulanov
2013/02/26 01:14:50
remove extra empty line.
rmsousa
2013/03/05 03:30:24
Done.
| |
31 | |
32 namespace remoting { | |
33 namespace protocol { | |
34 | |
35 // static | |
36 scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForClient( | |
37 const std::string& host_public_key, | |
38 ThirdPartyAuthenticator::TokenFetcher* token_fetcher, | |
39 Authenticator::State initial_state) { | |
40 scoped_ptr<ThirdPartyAuthenticator> result(new ThirdPartyAuthenticator( | |
41 initial_state)); | |
42 result->host_public_key_ = host_public_key; | |
43 result->token_fetcher_ = token_fetcher; | |
44 return scoped_ptr<Authenticator>(result.Pass()); | |
45 } | |
46 // static | |
Wez
2013/02/27 07:05:30
nit: Blank line, plz
rmsousa
2013/03/05 03:30:24
Done.
| |
47 scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForHost( | |
48 const std::string& local_cert, | |
49 scoped_ptr<KeyPair> key_pair, | |
50 const std::string& token_url, | |
51 const std::string& token_validation_url, | |
52 const std::string& token_scope, | |
53 scoped_ptr<ThirdPartyAuthenticator::TokenValidator> token_validator, | |
54 Authenticator::State initial_state) { | |
55 scoped_ptr<ThirdPartyAuthenticator> result(new ThirdPartyAuthenticator( | |
56 initial_state)); | |
57 result->local_cert_ = local_cert; | |
58 result->key_pair_ = key_pair.Pass(); | |
59 result->host_public_key_ = result->key_pair_->GetPublicKey(); | |
60 result->token_url_ = token_url; | |
61 result->token_validation_url_ = token_validation_url; | |
62 result->token_scope_ = token_scope; | |
63 result->token_validator_ = token_validator.Pass(); | |
64 result->expecting_token_ = false; | |
Sergey Ulanov
2013/02/26 01:14:50
don't need this - it should be already initialized
rmsousa
2013/03/05 03:30:24
Done.
| |
65 return scoped_ptr<Authenticator>(result.Pass()); | |
66 } | |
67 | |
68 ThirdPartyAuthenticator::ThirdPartyAuthenticator( | |
Sergey Ulanov
2013/02/26 01:14:50
Would it make sense two have separate classes - on
Wez
2013/02/27 07:05:30
Agreed.
You don't really need a public class for
rmsousa
2013/03/05 03:30:24
Done.
rmsousa
2013/03/05 03:30:24
I separated in one public class plus two local sub
| |
69 Authenticator::State initial_state) | |
70 : expecting_token_(false), | |
71 token_validator_(NULL), | |
72 token_fetcher_(NULL), | |
73 state_(initial_state), | |
74 rejection_reason_(INVALID_CREDENTIALS), | |
75 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
76 } | |
77 | |
78 ThirdPartyAuthenticator::~ThirdPartyAuthenticator() { | |
79 } | |
80 | |
81 Authenticator::State ThirdPartyAuthenticator::state() const { | |
82 if (state_ == ACCEPTED) { | |
Wez
2013/02/27 07:05:30
nit: No need for {} for simple single-line if.
| |
83 return underlying_->state(); | |
84 } | |
85 return state_; | |
86 } | |
87 | |
88 Authenticator::RejectionReason ThirdPartyAuthenticator::rejection_reason() | |
89 const { | |
90 DCHECK_EQ(state(), REJECTED); | |
91 if (state_ == REJECTED) { | |
92 return rejection_reason_; | |
93 } else { | |
94 return underlying_->rejection_reason(); | |
95 } | |
96 } | |
97 | |
98 void ThirdPartyAuthenticator::ProcessMessage(const buzz::XmlElement* message) { | |
99 DCHECK_EQ(state(), WAITING_MESSAGE); | |
100 | |
101 // The other side may have started the SPAKE negotiation. | |
102 // Keep a copy of the message until the V2 authenticator is ready. | |
103 const buzz::XmlElement* underlying_message = | |
104 Authenticator::FindAuthenticatorMessage(message); | |
105 if (underlying_message) { | |
106 if (underlying_) { | |
107 // Pass the message through the underlying authenticator. | |
108 DCHECK_EQ(underlying_->state(), WAITING_MESSAGE); | |
109 underlying_->ProcessMessage(underlying_message); | |
110 } else { | |
111 pending_message_.reset(new buzz::XmlElement(*underlying_message)); | |
112 } | |
113 } | |
114 | |
115 if (state_ == WAITING_MESSAGE) { | |
Sergey Ulanov
2013/02/26 01:14:50
there is a DCHECK for this condition at the top, s
Wez
2013/02/27 07:05:30
Is the caller responsible for never calling Proces
rmsousa
2013/03/05 03:30:24
the DCHECK is for state(), which is the authentica
rmsousa
2013/03/05 03:30:24
The caller is responsible for not calling if state
| |
116 if (!is_host_side()) { | |
117 if (token_url_.empty()) { | |
118 // First message from the host, token URL and scope. | |
Wez
2013/02/27 07:05:30
nit: Make the above a sentence. Also, please move
| |
119 DCHECK(token_scope_.empty()); | |
120 token_url_ = message->TextNamed(kTokenUrlTag); | |
121 token_scope_ = message->TextNamed(kTokenScopeTag); | |
122 if (!token_url_.empty() && !token_scope_.empty()) { | |
123 state_ = WAITING_EXTERNAL; | |
124 return; | |
125 } | |
126 } | |
127 LOG(WARNING) << "Missing token issue URL/verification URL/scope."; | |
128 } else { | |
129 if (!expecting_token_) { | |
Wez
2013/02/27 07:05:30
Should this be |has_sent_urls| instead?
rmsousa
2013/03/05 03:30:24
Done.
| |
130 // The host hasn't send the token URLs to the client yet, so ignore the | |
Wez
2013/02/27 07:05:30
typo: send -> sent
rmsousa
2013/03/05 03:30:24
Done.
| |
131 // first message and send the URLs to the client. | |
132 state_ = MESSAGE_READY; | |
133 return; | |
134 } | |
135 DCHECK(token_.empty()); | |
136 DCHECK(token_signature_.empty()); | |
137 // Host has already sent the URL and expects a token from the client. | |
138 token_ = message->TextNamed(kTokenTag); | |
139 token_signature_ = key_pair_->GetSignature(token_); | |
140 if (!token_.empty() && !token_signature_.empty()) { | |
141 state_ = WAITING_EXTERNAL; | |
142 return; | |
143 } | |
144 LOG(WARNING) << "Missing token."; | |
145 } | |
146 state_ = REJECTED; | |
147 rejection_reason_ = PROTOCOL_ERROR; | |
148 return; | |
149 } | |
150 } | |
151 | |
152 scoped_ptr<buzz::XmlElement> ThirdPartyAuthenticator::GetNextMessage() { | |
153 DCHECK_EQ(state(), MESSAGE_READY); | |
154 | |
155 scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage(); | |
156 if (state_ == MESSAGE_READY) { | |
Sergey Ulanov
2013/02/26 01:14:50
same here - there is a DCHECK for this condition.
| |
157 if (!is_host_side()) { | |
158 if (!token_.empty()) { | |
159 buzz::XmlElement* token_tag = new buzz::XmlElement(kTokenTag); | |
160 token_tag->SetBodyText(token_); | |
161 message->AddElement(token_tag); | |
162 } else { | |
163 // The client doesn't really have anything to send yet, it's just | |
164 // waiting for the host to send the token_url. | |
Wez
2013/02/27 07:05:30
Surely the client should never be in the MESSAGE_R
rmsousa
2013/03/05 03:30:24
We expect an authentication message on the session
| |
165 } | |
166 } else { | |
167 if (token_.empty()) { | |
168 DCHECK(!token_url_.empty()); | |
169 DCHECK(!token_scope_.empty()); | |
170 buzz::XmlElement* token_url_tag = new buzz::XmlElement( | |
171 kTokenUrlTag); | |
172 token_url_tag->SetBodyText(token_url_); | |
173 message->AddElement(token_url_tag); | |
174 buzz::XmlElement* token_scope_tag = new buzz::XmlElement( | |
175 kTokenScopeTag); | |
176 token_scope_tag->SetBodyText(token_scope_); | |
177 message->AddElement(token_scope_tag); | |
178 expecting_token_ = true; | |
179 } | |
180 } | |
181 } | |
182 if (underlying_ && underlying_->state() == MESSAGE_READY) { | |
Wez
2013/02/27 07:05:30
nit: Blank lines before & after this if...else...
rmsousa
2013/03/05 03:30:24
Done.
| |
183 DCHECK(!shared_secret_.empty()); | |
184 // This side already has the shared secret and can start the SPAKE | |
185 // negotiation in parallel with the remaining token messages. | |
186 message->AddElement(underlying_->GetNextMessage().release()); | |
187 state_ = ACCEPTED; | |
188 } else { | |
189 state_ = WAITING_MESSAGE; | |
190 } | |
191 return message.Pass(); | |
192 } | |
193 | |
194 scoped_ptr<ChannelAuthenticator> | |
195 ThirdPartyAuthenticator::CreateChannelAuthenticator() const { | |
196 DCHECK_EQ(state(), ACCEPTED); | |
197 return underlying_->CreateChannelAuthenticator(); | |
198 } | |
199 | |
200 bool ThirdPartyAuthenticator::is_host_side() const { | |
201 return !token_fetcher_; | |
202 } | |
203 | |
204 void ThirdPartyAuthenticator::PerformExternalAction( | |
205 const base::Closure& resume_callback) { | |
206 DCHECK_EQ(state(), WAITING_EXTERNAL); | |
207 if (is_host_side()) { | |
Sergey Ulanov
2013/02/26 01:14:50
Here the order of client and host parts is reverse
rmsousa
2013/03/05 03:30:24
Done.
| |
208 DCHECK(!token_signature_.empty()); | |
209 DCHECK(!token_validation_url_.empty()); | |
210 DCHECK(!token_.empty()); | |
211 DCHECK(!host_public_key_.empty()); | |
212 DCHECK(!token_scope_.empty()); | |
213 token_validator_->ValidateThirdPartyToken( | |
214 token_validation_url_, token_, host_public_key_, token_signature_, | |
215 token_scope_, base::Bind( | |
216 &ThirdPartyAuthenticator::OnThirdPartyTokenValidated, | |
217 weak_factory_.GetWeakPtr(), | |
218 resume_callback)); | |
219 } else { | |
220 DCHECK(token_.empty()); | |
221 token_fetcher_->FetchThirdPartyToken( | |
222 token_url_, host_public_key_, token_scope_, base::Bind( | |
223 &ThirdPartyAuthenticator::OnThirdPartyTokenFetched, | |
224 weak_factory_.GetWeakPtr(), | |
225 resume_callback)); | |
226 } | |
227 } | |
228 | |
229 void ThirdPartyAuthenticator::OnThirdPartyTokenFetched( | |
230 const base::Closure& resume_callback, const std::string& third_party_token, | |
Sergey Ulanov
2013/02/26 01:14:50
nit: one argument per line please when whole funct
rmsousa
2013/03/05 03:30:24
Done.
| |
231 const std::string& shared_secret) { | |
232 DCHECK_EQ(state_, WAITING_EXTERNAL); | |
233 DCHECK(!is_host_side()); | |
234 token_ = third_party_token; | |
235 if (!token_.empty() && !shared_secret.empty()) { | |
236 this->OnThirdPartyTokenValidated(resume_callback, shared_secret); | |
237 return; | |
238 } | |
239 state_ = REJECTED; | |
240 rejection_reason_ = INVALID_CREDENTIALS; | |
241 resume_callback.Run(); | |
242 } | |
243 | |
244 void ThirdPartyAuthenticator::OnThirdPartyTokenValidated( | |
245 const base::Closure& resume_callback, const std::string& shared_secret) { | |
Sergey Ulanov
2013/02/26 01:14:50
one argument per line please
rmsousa
2013/03/05 03:30:24
Done.
| |
246 DCHECK_EQ(state_, WAITING_EXTERNAL); | |
247 shared_secret_ = shared_secret; | |
248 Authenticator::State initial_state; | |
249 if (!shared_secret_.empty()) { | |
250 if (pending_message_) { | |
251 // The other side already started the SPAKE authentication. | |
252 state_ = ACCEPTED; | |
253 initial_state = WAITING_MESSAGE; | |
254 } else { | |
255 // The other side still needs data from this side to start SPAKE. | |
256 state_ = MESSAGE_READY; | |
257 initial_state = MESSAGE_READY; | |
258 } | |
259 if (is_host_side()) { | |
260 underlying_ = V2Authenticator::CreateForHost( | |
261 local_cert_, key_pair_->Copy(), shared_secret_, | |
262 initial_state); | |
263 } else { | |
264 underlying_ = V2Authenticator::CreateForClient( | |
265 shared_secret_, initial_state); | |
266 } | |
267 if (pending_message_) { | |
268 underlying_->ProcessMessage(pending_message_.release()); | |
269 DCHECK_NE(underlying_->state(), WAITING_MESSAGE); | |
270 } | |
271 } else { | |
272 state_ = REJECTED; | |
273 rejection_reason_ = INVALID_CREDENTIALS; | |
274 } | |
275 resume_callback.Run(); | |
276 } | |
277 | |
278 } // namespace protocol | |
279 } // namespace remoting | |
OLD | NEW |