OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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 "remoting/base/constants.h" | |
12 #include "remoting/base/rsa_key_pair.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 namespace { | |
18 | |
19 const buzz::StaticQName kTokenUrlTag = { remoting::kChromotingXmlNamespace, | |
20 "third-party-token-url" }; | |
Sergey Ulanov
2013/03/05 04:09:11
nit: indent
rmsousa
2013/03/06 00:54:16
Done.
| |
21 const buzz::StaticQName kTokenScopeTag = { remoting::kChromotingXmlNamespace, | |
22 "third-party-token-scope" }; | |
23 const buzz::StaticQName kTokenTag = { remoting::kChromotingXmlNamespace, | |
24 "third-party-token" }; | |
25 } // namespace | |
26 | |
27 namespace remoting { | |
28 namespace protocol { | |
29 | |
30 class ClientThirdPartyAuthenticator : public ThirdPartyAuthenticator { | |
Sergey Ulanov
2013/03/05 04:09:11
Why does this need to be in .cc file? I suggest re
rmsousa
2013/03/06 00:54:16
Done.
| |
31 public: | |
32 ClientThirdPartyAuthenticator(const std::string& host_public_key, | |
33 scoped_ptr<TokenFetcher> token_fetcher, | |
34 Authenticator::State initial_state); | |
Wez
2013/03/05 22:55:53
nit: Need public virtual dtor.
rmsousa
2013/03/06 00:54:16
Done.
| |
35 | |
36 protected: | |
37 // ThirdPartyAuthenticator implementation. | |
38 virtual void ProcessMessageInternal( | |
39 const buzz::XmlElement* message, | |
40 const base::Closure& resume_callback) OVERRIDE; | |
41 virtual void GetNextMessageInternal(buzz::XmlElement* message) OVERRIDE; | |
42 | |
43 private: | |
44 void OnThirdPartyTokenFetched(const base::Closure& resume_callback, | |
45 const std::string& third_party_token, | |
46 const std::string& shared_secret); | |
47 | |
48 std::string host_public_key_; | |
49 std::string token_; | |
50 scoped_ptr<TokenFetcher> token_fetcher_; | |
51 }; | |
52 | |
53 class HostThirdPartyAuthenticator : public ThirdPartyAuthenticator { | |
Wez
2013/03/05 22:55:53
nit: I'd normally define one class and implement i
rmsousa
2013/03/06 00:54:16
separated into different files.
| |
54 public: | |
55 HostThirdPartyAuthenticator(const std::string& local_cert, | |
56 scoped_refptr<RsaKeyPair> key_pair, | |
57 scoped_ptr<TokenValidator> token_validator, | |
58 Authenticator::State initial_state); | |
Wez
2013/03/05 22:55:53
nit: Need public virtual dtor.
| |
59 | |
60 protected: | |
61 // ThirdPartyAuthenticator implementation. | |
62 virtual void ProcessMessageInternal( | |
63 const buzz::XmlElement* message, | |
64 const base::Closure& resume_callback) OVERRIDE; | |
65 virtual void GetNextMessageInternal(buzz::XmlElement* message) OVERRIDE; | |
66 | |
67 private: | |
68 void OnThirdPartyTokenValidated(const buzz::XmlElement* message, | |
69 const base::Closure& resume_callback, | |
70 const std::string& shared_secret); | |
Wez
2013/03/05 22:55:53
nit: Blank line after this.
| |
71 bool has_sent_urls_; | |
72 std::string local_cert_; | |
73 scoped_refptr<RsaKeyPair> key_pair_; | |
74 scoped_ptr<TokenValidator> token_validator_; | |
75 }; | |
76 | |
77 // static | |
78 scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForClient( | |
79 const std::string& host_public_key, | |
80 scoped_ptr<ThirdPartyAuthenticator::TokenFetcher> token_fetcher, | |
81 Authenticator::State initial_state) { | |
82 scoped_ptr<Authenticator> result(new ClientThirdPartyAuthenticator( | |
83 host_public_key, token_fetcher.Pass(), initial_state)); | |
84 return result.Pass(); | |
85 } | |
86 | |
87 // static | |
88 scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForHost( | |
89 const std::string& local_cert, | |
90 scoped_refptr<RsaKeyPair> key_pair, | |
91 scoped_ptr<HostThirdPartyAuthenticator::TokenValidator> token_validator, | |
92 Authenticator::State initial_state) { | |
93 scoped_ptr<Authenticator> result(new HostThirdPartyAuthenticator( | |
94 local_cert, key_pair, token_validator.Pass(), initial_state)); | |
95 return result.Pass(); | |
96 } | |
97 | |
98 ThirdPartyAuthenticator::ThirdPartyAuthenticator( | |
99 Authenticator::State initial_state) | |
100 : state_(initial_state), | |
101 rejection_reason_(INVALID_CREDENTIALS) { | |
102 } | |
103 | |
104 ThirdPartyAuthenticator::~ThirdPartyAuthenticator() { | |
105 } | |
106 | |
107 Authenticator::State ThirdPartyAuthenticator::state() const { | |
108 if (state_ == ACCEPTED) { | |
109 return underlying_->state(); | |
110 } | |
111 return state_; | |
112 } | |
113 | |
114 Authenticator::RejectionReason ThirdPartyAuthenticator::rejection_reason() | |
115 const { | |
116 DCHECK_EQ(state(), REJECTED); | |
117 | |
118 if (state_ == REJECTED) { | |
119 return rejection_reason_; | |
120 } else { | |
121 return underlying_->rejection_reason(); | |
122 } | |
123 } | |
124 | |
125 void ThirdPartyAuthenticator::ProcessMessage( | |
126 const buzz::XmlElement* message, | |
127 const base::Closure& resume_callback) { | |
128 DCHECK_EQ(state(), WAITING_MESSAGE); | |
129 | |
130 if (state_ == WAITING_MESSAGE) { | |
131 ProcessMessageInternal(message, resume_callback); | |
132 } else { | |
133 DCHECK(state_ == ACCEPTED); | |
134 DCHECK(underlying_); | |
135 DCHECK(underlying_->state() == WAITING_MESSAGE); | |
Wez
2013/03/05 22:55:53
If these are DCHECKs then will ProcessMessage() do
rmsousa
2013/03/06 00:54:16
The DCHECK means (and documents) that this code is
| |
136 underlying_->ProcessMessage(message, resume_callback); | |
137 } | |
138 } | |
139 | |
140 scoped_ptr<buzz::XmlElement> ThirdPartyAuthenticator::GetNextMessage() { | |
141 DCHECK_EQ(state(), MESSAGE_READY); | |
142 | |
143 scoped_ptr<buzz::XmlElement> message; | |
144 if (underlying_ && underlying_->state() == MESSAGE_READY) { | |
145 message = underlying_->GetNextMessage().Pass(); | |
146 } else { | |
147 message = CreateEmptyAuthenticatorMessage(); | |
Wez
2013/03/05 22:55:53
How can we be in the MESSAGE_READY state with no m
rmsousa
2013/03/06 00:54:16
There are two states, the state for the token nego
| |
148 } | |
149 GetNextMessageInternal(message.get()); | |
150 return message.Pass(); | |
151 } | |
152 | |
153 scoped_ptr<ChannelAuthenticator> | |
154 ThirdPartyAuthenticator::CreateChannelAuthenticator() const { | |
155 DCHECK_EQ(state(), ACCEPTED); | |
156 | |
157 return underlying_->CreateChannelAuthenticator(); | |
158 } | |
159 | |
160 ClientThirdPartyAuthenticator::ClientThirdPartyAuthenticator( | |
161 const std::string& host_public_key, | |
162 scoped_ptr<TokenFetcher> token_fetcher, | |
163 Authenticator::State initial_state) | |
164 : ThirdPartyAuthenticator(initial_state), | |
165 host_public_key_(host_public_key), | |
166 token_fetcher_(token_fetcher.Pass()) { | |
167 } | |
168 | |
169 void ClientThirdPartyAuthenticator::ProcessMessageInternal( | |
170 const buzz::XmlElement* message, | |
171 const base::Closure& resume_callback) { | |
172 std::string token_url = message->TextNamed(kTokenUrlTag); | |
Wez
2013/03/05 22:55:53
What happens if we manage to see two consecutive m
rmsousa
2013/03/06 00:54:16
JingleSession has code that explicitly rejects mes
| |
173 std::string token_scope = message->TextNamed(kTokenScopeTag); | |
174 if (!token_url.empty() && !token_scope.empty()) { | |
175 state_ = PROCESSING_MESSAGE; | |
176 // |token_fetcher_| is owned, so Unretained() is safe here. | |
Wez
2013/03/05 22:55:53
nit: Blank line before this comment.
| |
177 token_fetcher_->FetchThirdPartyToken( | |
178 token_url, host_public_key_, token_scope, base::Bind( | |
179 &ClientThirdPartyAuthenticator::OnThirdPartyTokenFetched, | |
180 base::Unretained(this), resume_callback)); | |
181 return; | |
182 } | |
183 | |
184 LOG(WARNING) << "Missing token issue URL/verification URL/scope."; | |
185 state_ = REJECTED; | |
186 rejection_reason_ = PROTOCOL_ERROR; | |
187 resume_callback.Run(); | |
188 return; | |
Wez
2013/03/05 22:55:53
nit: No need for return here.
| |
189 } | |
190 | |
191 void ClientThirdPartyAuthenticator::GetNextMessageInternal( | |
192 buzz::XmlElement* message) { | |
193 if (state_ == MESSAGE_READY) { | |
194 if (!token_.empty()) { | |
195 buzz::XmlElement* token_tag = new buzz::XmlElement(kTokenTag); | |
196 token_tag->SetBodyText(token_); | |
197 message->AddElement(token_tag); | |
198 state_ = ACCEPTED; | |
199 } else { | |
200 // The client doesn't really have anything to send yet, it's just | |
201 // waiting for the host to send the token_url. | |
202 state_ = WAITING_MESSAGE; | |
Wez
2013/03/05 22:55:53
Again, we shouldn't ever reach the MESSAGE_READY s
rmsousa
2013/03/06 00:54:16
Please read the previous comments and replies abou
| |
203 } | |
204 } | |
205 } | |
206 | |
207 void ClientThirdPartyAuthenticator::OnThirdPartyTokenFetched( | |
208 const base::Closure& resume_callback, const std::string& third_party_token, | |
209 const std::string& shared_secret) { | |
210 token_ = third_party_token; | |
211 if (!token_.empty() && !shared_secret.empty()) { | |
212 state_ = MESSAGE_READY; | |
213 underlying_ = V2Authenticator::CreateForClient( | |
214 shared_secret, MESSAGE_READY); | |
215 } else { | |
216 state_ = REJECTED; | |
217 rejection_reason_ = INVALID_CREDENTIALS; | |
218 } | |
219 resume_callback.Run(); | |
220 } | |
221 | |
222 HostThirdPartyAuthenticator::HostThirdPartyAuthenticator( | |
223 const std::string& local_cert, | |
224 scoped_refptr<RsaKeyPair> key_pair, | |
225 scoped_ptr<TokenValidator> token_validator, | |
226 Authenticator::State initial_state) | |
227 : ThirdPartyAuthenticator(initial_state), | |
228 has_sent_urls_(false), | |
229 local_cert_(local_cert), | |
230 key_pair_(key_pair), | |
231 token_validator_(token_validator.Pass()) { | |
232 } | |
233 | |
234 void HostThirdPartyAuthenticator::ProcessMessageInternal( | |
235 const buzz::XmlElement* message, | |
236 const base::Closure& resume_callback) { | |
237 if (!has_sent_urls_) { | |
238 // The host hasn't sent the token URLs to the client yet, so ignore the | |
239 // first message and send the URLs to the client. | |
Wez
2013/03/05 22:55:53
Why are we receiving a first message if we're not
rmsousa
2013/03/06 00:54:16
NegotiatingAuthenticator has baked in assumptions
| |
240 DCHECK(!underlying_); | |
241 | |
242 state_ = MESSAGE_READY; | |
243 resume_callback.Run(); | |
244 return; | |
245 } | |
246 // Host has already sent the URL and expects a token from the client. | |
Wez
2013/03/05 22:55:53
Blank line before this comment.
| |
247 std::string token = message->TextNamed(kTokenTag); | |
248 if (!token.empty()) { | |
249 state_ = PROCESSING_MESSAGE; | |
250 // This message also contains the client's first SPAKE message. Copy the | |
Wez
2013/03/05 22:55:53
nit: Blank line before this comment.
| |
251 // message into the callback, so that OnThirdPartyTokenValidated can give it | |
252 // to the underlying SPAKE authenticator that will be created. | |
253 // |token_validator_| is owned, so Unretained() is safe here. | |
Wez
2013/03/05 22:55:53
Is there ever a possibility that we'd want to have
rmsousa
2013/03/06 00:54:16
No.
There are two possibilities here:
(1) The cli
| |
254 token_validator_->ValidateThirdPartyToken(token, base::Bind( | |
255 &HostThirdPartyAuthenticator::OnThirdPartyTokenValidated, | |
256 base::Unretained(this), | |
257 base::Owned(new buzz::XmlElement(*message)), | |
258 resume_callback)); | |
259 return; | |
260 } | |
261 | |
262 LOG(WARNING) << "Missing token."; | |
263 state_ = REJECTED; | |
264 rejection_reason_ = PROTOCOL_ERROR; | |
265 resume_callback.Run(); | |
266 return; | |
Wez
2013/03/05 22:55:53
No need for return here.
| |
267 } | |
268 | |
269 void HostThirdPartyAuthenticator::GetNextMessageInternal( | |
270 buzz::XmlElement* message) { | |
271 if (state_ == MESSAGE_READY) { | |
272 DCHECK(token_validator_->token_url().is_valid()); | |
273 DCHECK(!token_validator_->token_scope().empty()); | |
274 | |
275 buzz::XmlElement* token_url_tag = new buzz::XmlElement( | |
276 kTokenUrlTag); | |
277 token_url_tag->SetBodyText(token_validator_->token_url().spec()); | |
278 message->AddElement(token_url_tag); | |
279 buzz::XmlElement* token_scope_tag = new buzz::XmlElement( | |
280 kTokenScopeTag); | |
281 token_scope_tag->SetBodyText(token_validator_->token_scope()); | |
282 message->AddElement(token_scope_tag); | |
283 has_sent_urls_ = true; | |
284 state_ = WAITING_MESSAGE; | |
285 } | |
286 } | |
287 | |
288 void HostThirdPartyAuthenticator::OnThirdPartyTokenValidated( | |
289 const buzz::XmlElement* message, | |
290 const base::Closure& resume_callback, | |
291 const std::string& shared_secret) { | |
292 if (!shared_secret.empty()) { | |
293 // The other side already started the SPAKE authentication. | |
Wez
2013/03/05 22:55:53
nit: Clarify that we know this because the message
rmsousa
2013/03/06 00:54:16
We aren't inspecting the message to see what it co
| |
294 state_ = ACCEPTED; | |
295 underlying_ = V2Authenticator::CreateForHost( | |
296 local_cert_, key_pair_, shared_secret, WAITING_MESSAGE); | |
297 underlying_->ProcessMessage(message, resume_callback); | |
298 } else { | |
299 state_ = REJECTED; | |
300 rejection_reason_ = INVALID_CREDENTIALS; | |
301 resume_callback.Run(); | |
302 } | |
303 } | |
304 | |
305 } // namespace protocol | |
306 } // namespace remoting | |
OLD | NEW |