Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: chrome/browser/sync/notifier/gaia_auth/gaiaauth.cc

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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 <string>
6
7 #include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h"
8 #include "talk/base/asynchttprequest.h"
9 #include "talk/base/firewallsocketserver.h"
10 #include "talk/base/httpclient.h"
11 #include "talk/base/logging.h"
12 #include "talk/base/physicalsocketserver.h"
13 #include "talk/base/signalthread.h"
14 #include "talk/base/socketadapters.h"
15 #include "talk/base/socketpool.h"
16 #include "talk/base/stringutils.h"
17 #include "talk/base/urlencode.h"
18 #include "talk/xmpp/saslcookiemechanism.h"
19 #include "talk/xmpp/saslplainmechanism.h"
20
21 namespace buzz {
22
23 static const int kGaiaAuthTimeoutMs = 30 * 1000; // 30 sec
24
25 // Warning, this is externed.
26 GaiaServer buzz::g_gaia_server;
27
28 ///////////////////////////////////////////////////////////////////////////////
29 // GaiaAuth::WorkerThread
30 ///////////////////////////////////////////////////////////////////////////////
31
32 // GaiaAuth is NOT invoked during SASL authenticatioin, but
33 // it is invoked even before XMPP login begins. As a PreXmppAuth
34 // object, it is driven by XmppClient before the XMPP socket is
35 // opened. The job of GaiaAuth is to goes out using HTTPS
36 // POST to grab cookies from GAIA.
37
38 // It is used by XmppClient.
39 // It grabs a SaslAuthenticator which knows how to play the cookie login.
40
41 class GaiaAuth::WorkerThread : public talk_base::SignalThread {
42 public:
43 WorkerThread(const std::string& username,
44 const talk_base::CryptString& pass,
45 const std::string& token,
46 const std::string& service,
47 const std::string& user_agent,
48 const std::string& signature,
49 bool obtain_auth,
50 const std::string& token_service) :
51 username_(username),
52 pass_(pass),
53 service_(service),
54 firewall_(0),
55 done_(false),
56 success_(false),
57 error_(true),
58 error_code_(0),
59 proxy_auth_required_(false),
60 certificate_expired_(false),
61 auth_token_(token),
62 fresh_auth_token_(false),
63 obtain_auth_(obtain_auth),
64 agent_(user_agent),
65 signature_(signature),
66 token_service_(token_service) {}
67
68 void set_proxy(const talk_base::ProxyInfo& proxy) { proxy_ = proxy; }
69 void set_firewall(talk_base::FirewallManager * firewall) {
70 firewall_ = firewall;
71 }
72 void set_captcha_answer(const CaptchaAnswer& captcha_answer) {
73 captcha_answer_ = captcha_answer;
74 }
75
76 virtual void DoWork() {
77 LOG(INFO) << "GaiaAuth Begin";
78 // Maybe we already have an auth token, then there is nothing to do.
79 if (!auth_token_.empty()) {
80 LOG(INFO) << "Reusing auth token:" << auth_token_;
81 success_ = true;
82 error_ = false;
83 } else {
84 talk_base::PhysicalSocketServer physical;
85 talk_base::SocketServer * ss = &physical;
86 if (firewall_) {
87 ss = new talk_base::FirewallSocketServer(ss, firewall_);
88 }
89
90 talk_base::SslSocketFactory factory(ss, agent_);
91 factory.SetProxy(proxy_);
92 if (g_gaia_server.use_ssl()) {
93 factory.SetIgnoreBadCert(true);
94 factory.UseSSL(g_gaia_server.hostname().c_str());
95 }
96 factory.SetLogging(talk_base::LS_VERBOSE, "GaiaAuth");
97
98 talk_base::ReuseSocketPool pool(&factory);
99 talk_base::HttpClient http(agent_, &pool);
100
101 talk_base::HttpMonitor monitor(ss);
102 monitor.Connect(&http);
103
104 // If we do not already have a SID, let's get one using our password.
105 if (sid_.empty() || (auth_.empty() && obtain_auth_)) {
106 GaiaRequestSid(&http, username_, pass_, signature_,
107 obtain_auth_ ? service_ : "", captcha_answer_,
108 g_gaia_server);
109 ss->Wait(kGaiaAuthTimeoutMs, true);
110
111 error_code_ = monitor.error(); // save off the error code
112
113 if (!monitor.done()) {
114 LOG(INFO) << "GaiaAuth request timed out";
115 goto Cleanup;
116 } else if (monitor.error()) {
117 LOG(INFO) << "GaiaAuth request error: " << monitor.error();
118 if (monitor.error() == talk_base::HE_AUTH) {
119 success_ = false;
120 proxy_auth_required_ = true;
121 } else if (monitor.error() == talk_base::HE_CERTIFICATE_EXPIRED) {
122 success_ = false;
123 certificate_expired_ = true;
124 }
125 goto Cleanup;
126 } else {
127 std::string captcha_token, captcha_url;
128 switch (GaiaParseSidResponse(http, g_gaia_server,
129 &captcha_token, &captcha_url,
130 &sid_, &lsid_, &auth_)) {
131 case GR_ERROR:
132 goto Cleanup;
133
134 case GR_UNAUTHORIZED:
135 if (!captcha_url.empty()) {
136 captcha_challenge_ = buzz::CaptchaChallenge(captcha_token,
137 captcha_url);
138 }
139 // We had no "error" - we were just unauthorized
140 error_ = false;
141 error_code_ = 0;
142 goto Cleanup;
143
144 case GR_SUCCESS:
145 break;
146 }
147 }
148 }
149
150 // If all we need is a SID, then we are done now.
151 if (service_.empty() || obtain_auth_) {
152 success_ = true;
153 error_ = false;
154 error_code_ = 0;
155 goto Cleanup;
156 }
157
158 monitor.reset();
159 GaiaRequestAuthToken(&http, sid_, lsid_, service_, g_gaia_server);
160 ss->Wait(kGaiaAuthTimeoutMs, true);
161
162 error_code_ = monitor.error(); // save off the error code
163
164 if (!monitor.done()) {
165 LOG(INFO) << "GaiaAuth request timed out";
166 } else if (monitor.error()) {
167 LOG(INFO) << "GaiaAuth request error: " << monitor.error();
168 if (monitor.error() == talk_base::HE_AUTH) {
169 success_ = false;
170 proxy_auth_required_ = true;
171 } else if (monitor.error() == talk_base::HE_CERTIFICATE_EXPIRED) {
172 success_ = false;
173 certificate_expired_ = true;
174 }
175 } else {
176 if (GR_SUCCESS == GaiaParseAuthTokenResponse(http, &auth_token_)) {
177 fresh_auth_token_ = true;
178 success_ = true;
179 error_ = false;
180 error_code_ = 0;
181 }
182 }
183 }
184
185 // done authenticating
186
187 Cleanup:
188 done_ = true;
189 }
190
191 bool IsDone() const { return done_; }
192 bool Succeeded() const { return success_; }
193 bool HadError() const { return error_; }
194 int GetError() const { return error_code_; }
195 bool ProxyAuthRequired() const { return proxy_auth_required_; }
196 bool CertificateExpired() const { return certificate_expired_; }
197 const buzz::CaptchaChallenge& GetCaptchaChallenge() {
198 return captcha_challenge_;
199 }
200 bool fresh_auth_token() const { return fresh_auth_token_; }
201
202 talk_base::CryptString GetPassword() const { return pass_; }
203 std::string GetSID() const { return sid_; }
204 std::string GetAuth() const { return auth_; }
205 std::string GetToken() const { return auth_token_; }
206 std::string GetUsername() const { return username_; }
207 std::string GetTokenService() const { return token_service_; }
208
209 private:
210 std::string username_;
211 talk_base::CryptString pass_;
212 std::string service_;
213 talk_base::ProxyInfo proxy_;
214 talk_base::FirewallManager * firewall_;
215 bool done_;
216 bool success_;
217 bool error_;
218 int error_code_;
219 bool proxy_auth_required_;
220 bool certificate_expired_;
221 std::string sid_;
222 std::string lsid_;
223 std::string auth_;
224 std::string auth_token_;
225 buzz::CaptchaChallenge captcha_challenge_;
226 CaptchaAnswer captcha_answer_;
227 bool fresh_auth_token_;
228 bool obtain_auth_;
229 std::string agent_;
230 std::string signature_;
231 std::string token_service_;
232 };
233
234 ///////////////////////////////////////////////////////////////////////////////
235 // GaiaAuth
236 ///////////////////////////////////////////////////////////////////////////////
237
238 GaiaAuth::GaiaAuth(const std::string &user_agent, const std::string &sig)
239 : agent_(user_agent), signature_(sig),
240 firewall_(0), worker_(NULL), done_(false) {
241 }
242
243 GaiaAuth::~GaiaAuth() {
244 if (worker_) {
245 worker_->Release();
246 worker_ = NULL;
247 }
248 }
249
250 void GaiaAuth::StartPreXmppAuth(const buzz::Jid& jid,
251 const talk_base::SocketAddress& server,
252 const talk_base::CryptString& pass,
253 const std::string & auth_cookie) {
254 InternalStartGaiaAuth(jid, server, pass, auth_cookie, "mail", false);
255 }
256
257 void GaiaAuth::StartTokenAuth(const buzz::Jid& jid,
258 const talk_base::CryptString& pass,
259 const std::string& service) {
260 InternalStartGaiaAuth(jid, talk_base::SocketAddress(),
261 pass, "", service, false);
262 }
263
264 void GaiaAuth::StartAuth(const buzz::Jid& jid,
265 const talk_base::CryptString& pass,
266 const std::string & service) {
267 InternalStartGaiaAuth(jid, talk_base::SocketAddress(),
268 pass, "", service, true);
269 }
270
271 void GaiaAuth::StartAuthFromSid(const buzz::Jid& jid,
272 const std::string& sid,
273 const std::string& service) {
274 InternalStartGaiaAuth(jid, talk_base::SocketAddress(),
275 talk_base::CryptString(), sid, service, false);
276 }
277
278 void GaiaAuth::InternalStartGaiaAuth(const buzz::Jid& jid,
279 const talk_base::SocketAddress& server,
280 const talk_base::CryptString& pass,
281 const std::string& token,
282 const std::string& service,
283 bool obtain_auth) {
284 worker_ = new WorkerThread(jid.Str(), pass, token,
285 service, agent_, signature_,
286 obtain_auth, token_service_);
287 worker_->set_proxy(proxy_);
288 worker_->set_firewall(firewall_);
289 worker_->set_captcha_answer(captcha_answer_);
290 worker_->SignalWorkDone.connect(this, &GaiaAuth::OnAuthDone);
291 worker_->Start();
292 }
293
294 void GaiaAuth::OnAuthDone(talk_base::SignalThread* worker) {
295 if (!worker_->IsDone())
296 return;
297 done_ = true;
298
299 if (worker_->fresh_auth_token()) {
300 SignalFreshAuthCookie(worker_->GetToken());
301 }
302 if (worker_->ProxyAuthRequired()) {
303 SignalAuthenticationError();
304 }
305 if (worker_->CertificateExpired()) {
306 SignalCertificateExpired();
307 }
308 SignalAuthDone();
309 }
310
311 std::string GaiaAuth::ChooseBestSaslMechanism(
312 const std::vector<std::string> & mechanisms, bool encrypted) {
313 if (!done_)
314 return "";
315
316 std::vector<std::string>::const_iterator it;
317
318 // a token is the weakest auth - 15s, service-limited, so prefer it.
319 it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-TOKEN");
320 if (it != mechanisms.end())
321 return "X-GOOGLE-TOKEN";
322
323 // a cookie is the next weakest - 14 days
324 it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-COOKIE");
325 if (it != mechanisms.end())
326 return "X-GOOGLE-COOKIE";
327
328 // never pass @google.com passwords without encryption!!
329 if (!encrypted &&
330 buzz::Jid(worker_->GetUsername()).domain() == "google.com") {
331 return "";
332 }
333
334 // as a last resort, use plain authentication
335 if (buzz::Jid(worker_->GetUsername()).domain() != "google.com") {
336 it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN");
337 if (it != mechanisms.end())
338 return "PLAIN";
339 }
340
341 // No good mechanism found
342 return "";
343 }
344
345 buzz::SaslMechanism* GaiaAuth::CreateSaslMechanism(
346 const std::string& mechanism) {
347
348 if (!done_) {
349 return NULL;
350 }
351
352 if (mechanism == "X-GOOGLE-TOKEN") {
353 return new buzz::SaslCookieMechanism(
354 mechanism,
355 worker_->GetUsername(),
356 worker_->GetToken(),
357 worker_->GetTokenService());
358 }
359
360 if (mechanism == "X-GOOGLE-COOKIE") {
361 return new buzz::SaslCookieMechanism(
362 "X-GOOGLE-COOKIE",
363 worker_->GetUsername(),
364 worker_->GetSID(),
365 worker_->GetTokenService());
366 }
367
368 if (mechanism == "PLAIN") {
369 return new buzz::SaslPlainMechanism(buzz::Jid(worker_->GetUsername()),
370 worker_->GetPassword());
371 }
372
373 // oh well - none of the above
374 return NULL;
375 }
376
377 std::string GaiaAuth::CreateAuthenticatedUrl(
378 const std::string & continue_url, const std::string & service) {
379 if (!done_ || worker_->GetToken().empty())
380 return "";
381
382 std::string url;
383 // Note that http_prefix always ends with a "/"
384 url += g_gaia_server.http_prefix()
385 + "accounts/TokenAuth?auth="
386 + worker_->GetToken(); // Do not URL encode - GAIA doesn't like that
387 url += "&service=" + service;
388 url += "&continue=" + UrlEncodeString(continue_url);
389 url += "&source=" + signature_;
390 return url;
391 }
392
393 std::string GaiaAuth::GetAuthCookie() {
394 assert(IsAuthDone() && IsAuthorized());
395 if (!done_ || !worker_->Succeeded()) {
396 return "";
397 }
398 return worker_->GetToken();
399 }
400
401 std::string GaiaAuth::GetAuth() {
402 assert(IsAuthDone() && IsAuthorized());
403 if (!done_ || !worker_->Succeeded()) {
404 return "";
405 }
406 return worker_->GetAuth();
407 }
408
409 std::string GaiaAuth::GetSID() {
410 assert(IsAuthDone() && IsAuthorized());
411 if (!done_ || !worker_->Succeeded()) {
412 return "";
413 }
414 return worker_->GetSID();
415 }
416
417 bool GaiaAuth::IsAuthDone() {
418 return done_;
419 }
420
421 bool GaiaAuth::IsAuthorized() {
422 return done_ && worker_ != NULL && worker_->Succeeded();
423 }
424
425 bool GaiaAuth::HadError() {
426 return done_ && worker_ != NULL && worker_->HadError();
427 }
428
429 int GaiaAuth::GetError() {
430 if (done_ && worker_ != NULL) {
431 return worker_->GetError();
432 }
433 return 0;
434 }
435
436 buzz::CaptchaChallenge GaiaAuth::GetCaptchaChallenge() {
437 if (!done_ || worker_->Succeeded()) {
438 return buzz::CaptchaChallenge();
439 }
440 return worker_->GetCaptchaChallenge();
441 }
442 } // namespace buzz
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698