Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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/signaling/xmpp_login_handler.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/base64.h" | |
| 10 #include "base/bind.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "remoting/signaling/xmpp_stream_parser.h" | |
| 13 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h" | |
| 14 | |
| 15 namespace remoting { | |
| 16 | |
| 17 buzz::StaticQName kXmppIqName = {"jabber:client", "iq"}; | |
| 18 | |
| 19 char kXmppBindNs[] = "urn:ietf:params:xml:ns:xmpp-bind"; | |
| 20 buzz::StaticQName kXmppBindName = {kXmppBindNs, "bind"}; | |
| 21 buzz::StaticQName kXmppJidName = {kXmppBindNs, "jid"}; | |
| 22 | |
| 23 buzz::StaticQName kJabberFeaturesName = {"http://etherx.jabber.org/streams", | |
| 24 "features"}; | |
| 25 | |
| 26 char kXmppTlsNs[] = "urn:ietf:params:xml:ns:xmpp-tls"; | |
| 27 buzz::StaticQName kStartTlsName = {kXmppTlsNs, "starttls"}; | |
| 28 buzz::StaticQName kTlsProceedName = {kXmppTlsNs, "proceed"}; | |
| 29 | |
| 30 char kXmppSaslNs[] = "urn:ietf:params:xml:ns:xmpp-sasl"; | |
| 31 buzz::StaticQName kSaslMechanismsName = {kXmppSaslNs, "mechanisms"}; | |
| 32 buzz::StaticQName kSaslMechanismName = {kXmppSaslNs, "mechanism"}; | |
| 33 buzz::StaticQName kSaslSuccessName = {kXmppSaslNs, "success"}; | |
| 34 | |
| 35 XmppLoginHandler::XmppLoginHandler(const std::string& server, | |
| 36 const std::string& username, | |
| 37 const std::string& auth_token, | |
| 38 const std::string& auth_service, | |
| 39 bool need_handshake_before_tls, | |
| 40 Delegate* delegate) | |
| 41 : server_(server), | |
| 42 username_(username), | |
| 43 auth_token_(auth_token), | |
| 44 auth_service_(auth_service), | |
| 45 auth_mechanism_((auth_service == "oauth2") ? "X-OAUTH2" | |
|
rmsousa
2015/02/28 06:33:29
nit: if this is for client login, it can be remove
Sergey Ulanov
2015/03/02 18:11:09
Done.
| |
| 46 : "X-GOOGLE-TOKEN"), | |
| 47 need_handshake_before_tls_(need_handshake_before_tls), | |
| 48 delegate_(delegate), | |
| 49 state_(State::INIT) { | |
| 50 } | |
| 51 | |
| 52 XmppLoginHandler::~XmppLoginHandler() { | |
| 53 } | |
| 54 | |
| 55 void XmppLoginHandler::Start() { | |
| 56 if (need_handshake_before_tls_) { | |
| 57 state_ = State::WAIT_STREAM_HEADER; | |
| 58 StartStream("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>"); | |
| 59 } else { | |
| 60 // If <starttls> handshake is not required then start TLS right away. | |
| 61 state_ = State::STARTING_TLS; | |
| 62 delegate_->StartTls(); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 void XmppLoginHandler::OnDataReceived(const std::string& data) { | |
| 67 DCHECK(state_ != State::INIT && state_ != State::DONE && | |
| 68 state_ != State::ERROR); | |
| 69 stream_parser_->AppendData(data); | |
| 70 } | |
| 71 | |
| 72 void XmppLoginHandler::OnStanza(scoped_ptr<buzz::XmlElement> stanza) { | |
| 73 switch (state_) { | |
| 74 case State::WAIT_STREAM_HEADER: { | |
| 75 if (stanza->Name() == kJabberFeaturesName && | |
| 76 stanza->FirstNamed(kStartTlsName) != nullptr) { | |
| 77 state_ = State::WAIT_STARTTLS_RESPONSE; | |
| 78 } else { | |
| 79 LOG(ERROR) << "Server doesn't support TLS."; | |
| 80 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 81 } | |
| 82 break; | |
| 83 } | |
| 84 | |
| 85 case State::WAIT_STARTTLS_RESPONSE: { | |
| 86 if (stanza->Name() == kTlsProceedName) { | |
| 87 state_ = State::STARTING_TLS; | |
| 88 delegate_->StartTls(); | |
| 89 } else { | |
| 90 LOG(ERROR) << "Failed to start TLS: " << stanza->Str(); | |
| 91 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 92 } | |
| 93 break; | |
| 94 } | |
| 95 | |
| 96 case State::WAIT_STREAM_HEADER_AFTER_TLS: { | |
| 97 buzz::XmlElement* mechanisms_element = | |
| 98 stanza->FirstNamed(kSaslMechanismsName); | |
| 99 std::set<std::string> mechanisms; | |
| 100 if (mechanisms_element) { | |
| 101 for (buzz::XmlElement* element = | |
| 102 mechanisms_element->FirstNamed(kSaslMechanismName); | |
| 103 element; element = element->NextNamed(kSaslMechanismName)) { | |
| 104 mechanisms.insert(element->BodyText()); | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 if (mechanisms.find(auth_mechanism_) == mechanisms.end()) { | |
| 109 LOG(ERROR) << auth_mechanism_ | |
| 110 << " auth mechanism is not supported by the server."; | |
| 111 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 112 return; | |
| 113 } | |
| 114 | |
| 115 state_ = State::WAIT_AUTH_RESULT; | |
| 116 break; | |
| 117 } | |
| 118 | |
| 119 case State::WAIT_AUTH_RESULT: { | |
| 120 if (stanza->Name() == kSaslSuccessName) { | |
| 121 state_ = State::WAIT_STREAM_HEADER_AFTER_AUTH; | |
| 122 StartStream( | |
| 123 "<iq type=\"set\" id=\"0\">" | |
| 124 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">" | |
| 125 "<resource>chromoting</resource>" | |
| 126 "</bind>" | |
| 127 "</iq>" | |
| 128 "<iq type=\"set\" id=\"1\">" | |
| 129 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>" | |
| 130 "</iq>"); | |
| 131 } else { | |
| 132 OnError(SignalStrategy::AUTHENTICATION_FAILED); | |
| 133 } | |
| 134 break; | |
| 135 } | |
| 136 | |
| 137 case State::WAIT_STREAM_HEADER_AFTER_AUTH: | |
| 138 if (stanza->Name() == kJabberFeaturesName && | |
| 139 stanza->FirstNamed(kXmppBindName) != nullptr) { | |
| 140 state_ = State::WAIT_BIND_RESULT; | |
| 141 } else { | |
| 142 LOG(ERROR) << "Server doesn't support bind after authentication."; | |
| 143 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 144 } | |
| 145 break; | |
| 146 | |
| 147 case State::WAIT_BIND_RESULT: { | |
| 148 buzz::XmlElement* bind = stanza->FirstNamed(kXmppBindName); | |
| 149 buzz::XmlElement* jid = bind ? bind->FirstNamed(kXmppJidName) : nullptr; | |
| 150 if (stanza->Attr(buzz::QName("", "id")) != "0" || | |
| 151 stanza->Attr(buzz::QName("", "type")) != "result" || !jid) { | |
| 152 LOG(ERROR) << "Received unexpected response to bind: " << stanza->Str(); | |
| 153 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 154 return; | |
| 155 } | |
| 156 jid_ = jid->BodyText(); | |
| 157 state_ = State::WAIT_SESSION_IQ_RESULT; | |
| 158 break; | |
| 159 } | |
| 160 | |
| 161 case State::WAIT_SESSION_IQ_RESULT: | |
| 162 if (stanza->Name() != kXmppIqName || | |
| 163 stanza->Attr(buzz::QName("", "id")) != "1" || | |
| 164 stanza->Attr(buzz::QName("", "type")) != "result") { | |
| 165 LOG(ERROR) << "Failed to start session: " << stanza->Str(); | |
| 166 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 167 return; | |
| 168 } | |
| 169 state_ = State::DONE; | |
| 170 delegate_->OnHandshakeDone(jid_, stream_parser_.Pass()); | |
| 171 break; | |
| 172 | |
| 173 default: | |
| 174 NOTREACHED(); | |
| 175 break; | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 void XmppLoginHandler::OnTlsStarted() { | |
| 180 DCHECK(state_ == State::STARTING_TLS); | |
| 181 state_ = State::WAIT_STREAM_HEADER_AFTER_TLS; | |
| 182 | |
| 183 std::string cookie; | |
| 184 base::Base64Encode( | |
| 185 std::string("\0", 1) + username_ + std::string("\0", 1) + auth_token_, | |
| 186 &cookie); | |
| 187 StartStream( | |
| 188 "<auth xmlns=\"" + std::string(kXmppSaslNs) + "\" " | |
| 189 "mechanism=\"" + auth_mechanism_+ "\" " | |
| 190 "auth:service=\"" + auth_service_ + "\" " | |
| 191 "auth:allow-generated-jid=\"true\" " | |
| 192 "auth:client-uses-full-bind-result=\"true\" " | |
| 193 "auth:allow-non-google-login=\"true\" " | |
| 194 "xmlns:auth=\"http://www.google.com/talk/protocol/auth\">" + | |
| 195 cookie + | |
| 196 "</auth>"); | |
| 197 }; | |
| 198 | |
| 199 void XmppLoginHandler::OnParserError() { | |
| 200 OnError(SignalStrategy::PROTOCOL_ERROR); | |
| 201 } | |
| 202 | |
| 203 void XmppLoginHandler::StartStream(const std::string& first_message) { | |
| 204 delegate_->SendMessage("<stream:stream to=\"" + server_ + | |
| 205 "\" version=\"1.0\" xmlns=\"jabber:client\" " | |
| 206 "xmlns:stream=\"http://etherx.jabber.org/streams\">" + | |
| 207 first_message); | |
| 208 stream_parser_.reset(new XmppStreamParser()); | |
| 209 stream_parser_->SetCallbacks( | |
| 210 base::Bind(&XmppLoginHandler::OnStanza, base::Unretained(this)), | |
| 211 base::Bind(&XmppLoginHandler::OnParserError, base::Unretained(this))); | |
| 212 } | |
| 213 | |
| 214 void XmppLoginHandler::OnError(SignalStrategy::Error error) { | |
| 215 if (state_ != State::ERROR) { | |
| 216 state_ = State::ERROR; | |
| 217 delegate_->OnLoginHandlerError(error); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 } // namespace remoting | |
| OLD | NEW |