OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/libjingle/xmpp/xmpplogintask.h" | |
12 | |
13 #include <string> | |
14 #include <vector> | |
15 | |
16 #include "webrtc/libjingle/xmllite/xmlelement.h" | |
17 #include "webrtc/libjingle/xmpp/constants.h" | |
18 #include "webrtc/libjingle/xmpp/jid.h" | |
19 #include "webrtc/libjingle/xmpp/saslmechanism.h" | |
20 #include "webrtc/libjingle/xmpp/xmppengineimpl.h" | |
21 #include "webrtc/base/base64.h" | |
22 #include "webrtc/base/common.h" | |
23 | |
24 using rtc::ConstantLabel; | |
25 | |
26 namespace buzz { | |
27 | |
28 #if !defined(NDEBUG) | |
29 const ConstantLabel XmppLoginTask::LOGINTASK_STATES[] = { | |
30 KLABEL(LOGINSTATE_INIT), | |
31 KLABEL(LOGINSTATE_STREAMSTART_SENT), | |
32 KLABEL(LOGINSTATE_STARTED_XMPP), | |
33 KLABEL(LOGINSTATE_TLS_INIT), | |
34 KLABEL(LOGINSTATE_AUTH_INIT), | |
35 KLABEL(LOGINSTATE_BIND_INIT), | |
36 KLABEL(LOGINSTATE_TLS_REQUESTED), | |
37 KLABEL(LOGINSTATE_SASL_RUNNING), | |
38 KLABEL(LOGINSTATE_BIND_REQUESTED), | |
39 KLABEL(LOGINSTATE_SESSION_REQUESTED), | |
40 KLABEL(LOGINSTATE_DONE), | |
41 LASTLABEL | |
42 }; | |
43 #endif | |
44 XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) : | |
45 pctx_(pctx), | |
46 authNeeded_(true), | |
47 allowNonGoogleLogin_(true), | |
48 state_(LOGINSTATE_INIT), | |
49 pelStanza_(NULL), | |
50 isStart_(false), | |
51 iqId_(STR_EMPTY), | |
52 pelFeatures_(), | |
53 fullJid_(STR_EMPTY), | |
54 streamId_(STR_EMPTY), | |
55 pvecQueuedStanzas_(new std::vector<XmlElement *>()), | |
56 sasl_mech_() { | |
57 } | |
58 | |
59 XmppLoginTask::~XmppLoginTask() { | |
60 for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) | |
61 delete (*pvecQueuedStanzas_)[i]; | |
62 } | |
63 | |
64 void | |
65 XmppLoginTask::IncomingStanza(const XmlElement *element, bool isStart) { | |
66 pelStanza_ = element; | |
67 isStart_ = isStart; | |
68 Advance(); | |
69 pelStanza_ = NULL; | |
70 isStart_ = false; | |
71 } | |
72 | |
73 const XmlElement * | |
74 XmppLoginTask::NextStanza() { | |
75 const XmlElement * result = pelStanza_; | |
76 pelStanza_ = NULL; | |
77 return result; | |
78 } | |
79 | |
80 bool | |
81 XmppLoginTask::Advance() { | |
82 | |
83 for (;;) { | |
84 | |
85 const XmlElement * element = NULL; | |
86 | |
87 #if !defined(NDEBUG) | |
88 LOG(LS_VERBOSE) << "XmppLoginTask::Advance - " | |
89 << rtc::ErrorName(state_, LOGINTASK_STATES); | |
90 #endif | |
91 | |
92 switch (state_) { | |
93 | |
94 case LOGINSTATE_INIT: { | |
95 pctx_->RaiseReset(); | |
96 pelFeatures_.reset(NULL); | |
97 | |
98 // The proper domain to verify against is the real underlying | |
99 // domain - i.e., the domain that owns the JID. Our XmppEngineImpl | |
100 // also allows matching against a proxy domain instead, if it is told | |
101 // to do so - see the implementation of XmppEngineImpl::StartTls and | |
102 // XmppEngine::SetTlsServerDomain to see how you can use that feature | |
103 pctx_->InternalSendStart(pctx_->user_jid_.domain()); | |
104 state_ = LOGINSTATE_STREAMSTART_SENT; | |
105 break; | |
106 } | |
107 | |
108 case LOGINSTATE_STREAMSTART_SENT: { | |
109 if (NULL == (element = NextStanza())) | |
110 return true; | |
111 | |
112 if (!isStart_ || !HandleStartStream(element)) | |
113 return Failure(XmppEngine::ERROR_VERSION); | |
114 | |
115 state_ = LOGINSTATE_STARTED_XMPP; | |
116 return true; | |
117 } | |
118 | |
119 case LOGINSTATE_STARTED_XMPP: { | |
120 if (NULL == (element = NextStanza())) | |
121 return true; | |
122 | |
123 if (!HandleFeatures(element)) | |
124 return Failure(XmppEngine::ERROR_VERSION); | |
125 | |
126 bool tls_present = (GetFeature(QN_TLS_STARTTLS) != NULL); | |
127 // Error if TLS required but not present. | |
128 if (pctx_->tls_option_ == buzz::TLS_REQUIRED && !tls_present) { | |
129 return Failure(XmppEngine::ERROR_TLS); | |
130 } | |
131 // Use TLS if required or enabled, and also available | |
132 if ((pctx_->tls_option_ == buzz::TLS_REQUIRED || | |
133 pctx_->tls_option_ == buzz::TLS_ENABLED) && tls_present) { | |
134 state_ = LOGINSTATE_TLS_INIT; | |
135 continue; | |
136 } | |
137 | |
138 if (authNeeded_) { | |
139 state_ = LOGINSTATE_AUTH_INIT; | |
140 continue; | |
141 } | |
142 | |
143 state_ = LOGINSTATE_BIND_INIT; | |
144 continue; | |
145 } | |
146 | |
147 case LOGINSTATE_TLS_INIT: { | |
148 const XmlElement * pelTls = GetFeature(QN_TLS_STARTTLS); | |
149 if (!pelTls) | |
150 return Failure(XmppEngine::ERROR_TLS); | |
151 | |
152 XmlElement el(QN_TLS_STARTTLS, true); | |
153 pctx_->InternalSendStanza(&el); | |
154 state_ = LOGINSTATE_TLS_REQUESTED; | |
155 continue; | |
156 } | |
157 | |
158 case LOGINSTATE_TLS_REQUESTED: { | |
159 if (NULL == (element = NextStanza())) | |
160 return true; | |
161 if (element->Name() != QN_TLS_PROCEED) | |
162 return Failure(XmppEngine::ERROR_TLS); | |
163 | |
164 // The proper domain to verify against is the real underlying | |
165 // domain - i.e., the domain that owns the JID. Our XmppEngineImpl | |
166 // also allows matching against a proxy domain instead, if it is told | |
167 // to do so - see the implementation of XmppEngineImpl::StartTls and | |
168 // XmppEngine::SetTlsServerDomain to see how you can use that feature | |
169 pctx_->StartTls(pctx_->user_jid_.domain()); | |
170 pctx_->tls_option_ = buzz::TLS_ENABLED; | |
171 state_ = LOGINSTATE_INIT; | |
172 continue; | |
173 } | |
174 | |
175 case LOGINSTATE_AUTH_INIT: { | |
176 const XmlElement * pelSaslAuth = GetFeature(QN_SASL_MECHANISMS); | |
177 if (!pelSaslAuth) { | |
178 return Failure(XmppEngine::ERROR_AUTH); | |
179 } | |
180 | |
181 // Collect together the SASL auth mechanisms presented by the server | |
182 std::vector<std::string> mechanisms; | |
183 for (const XmlElement * pelMech = | |
184 pelSaslAuth->FirstNamed(QN_SASL_MECHANISM); | |
185 pelMech; | |
186 pelMech = pelMech->NextNamed(QN_SASL_MECHANISM)) { | |
187 | |
188 mechanisms.push_back(pelMech->BodyText()); | |
189 } | |
190 | |
191 // Given all the mechanisms, choose the best | |
192 std::string choice(pctx_->ChooseBestSaslMechanism(mechanisms, pctx_->IsE
ncrypted())); | |
193 if (choice.empty()) { | |
194 return Failure(XmppEngine::ERROR_AUTH); | |
195 } | |
196 | |
197 // No recognized auth mechanism - that's an error | |
198 sasl_mech_.reset(pctx_->GetSaslMechanism(choice)); | |
199 if (!sasl_mech_) { | |
200 return Failure(XmppEngine::ERROR_AUTH); | |
201 } | |
202 | |
203 // OK, let's start it. | |
204 XmlElement * auth = sasl_mech_->StartSaslAuth(); | |
205 if (auth == NULL) { | |
206 return Failure(XmppEngine::ERROR_AUTH); | |
207 } | |
208 if (allowNonGoogleLogin_) { | |
209 // Setting the following two attributes is required to support | |
210 // non-google ids. | |
211 | |
212 // Allow login with non-google id accounts. | |
213 auth->SetAttr(QN_GOOGLE_ALLOW_NON_GOOGLE_ID_XMPP_LOGIN, "true"); | |
214 | |
215 // Allow login with either the non-google id or the friendly email. | |
216 auth->SetAttr(QN_GOOGLE_AUTH_CLIENT_USES_FULL_BIND_RESULT, "true"); | |
217 } | |
218 | |
219 pctx_->InternalSendStanza(auth); | |
220 delete auth; | |
221 state_ = LOGINSTATE_SASL_RUNNING; | |
222 continue; | |
223 } | |
224 | |
225 case LOGINSTATE_SASL_RUNNING: { | |
226 if (NULL == (element = NextStanza())) | |
227 return true; | |
228 if (element->Name().Namespace() != NS_SASL) | |
229 return Failure(XmppEngine::ERROR_AUTH); | |
230 if (element->Name() == QN_SASL_CHALLENGE) { | |
231 XmlElement * response = sasl_mech_->HandleSaslChallenge(element); | |
232 if (response == NULL) { | |
233 return Failure(XmppEngine::ERROR_AUTH); | |
234 } | |
235 pctx_->InternalSendStanza(response); | |
236 delete response; | |
237 state_ = LOGINSTATE_SASL_RUNNING; | |
238 continue; | |
239 } | |
240 if (element->Name() != QN_SASL_SUCCESS) { | |
241 return Failure(XmppEngine::ERROR_UNAUTHORIZED); | |
242 } | |
243 | |
244 // Authenticated! | |
245 authNeeded_ = false; | |
246 state_ = LOGINSTATE_INIT; | |
247 continue; | |
248 } | |
249 | |
250 case LOGINSTATE_BIND_INIT: { | |
251 const XmlElement * pelBindFeature = GetFeature(QN_BIND_BIND); | |
252 const XmlElement * pelSessionFeature = GetFeature(QN_SESSION_SESSION); | |
253 if (!pelBindFeature || !pelSessionFeature) | |
254 return Failure(XmppEngine::ERROR_BIND); | |
255 | |
256 XmlElement iq(QN_IQ); | |
257 iq.AddAttr(QN_TYPE, "set"); | |
258 | |
259 iqId_ = pctx_->NextId(); | |
260 iq.AddAttr(QN_ID, iqId_); | |
261 iq.AddElement(new XmlElement(QN_BIND_BIND, true)); | |
262 | |
263 if (pctx_->requested_resource_ != STR_EMPTY) { | |
264 iq.AddElement(new XmlElement(QN_BIND_RESOURCE), 1); | |
265 iq.AddText(pctx_->requested_resource_, 2); | |
266 } | |
267 pctx_->InternalSendStanza(&iq); | |
268 state_ = LOGINSTATE_BIND_REQUESTED; | |
269 continue; | |
270 } | |
271 | |
272 case LOGINSTATE_BIND_REQUESTED: { | |
273 if (NULL == (element = NextStanza())) | |
274 return true; | |
275 | |
276 if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ || | |
277 element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set") | |
278 return true; | |
279 | |
280 if (element->Attr(QN_TYPE) != "result" || element->FirstElement() == NUL
L || | |
281 element->FirstElement()->Name() != QN_BIND_BIND) | |
282 return Failure(XmppEngine::ERROR_BIND); | |
283 | |
284 fullJid_ = Jid(element->FirstElement()->TextNamed(QN_BIND_JID)); | |
285 if (!fullJid_.IsFull()) { | |
286 return Failure(XmppEngine::ERROR_BIND); | |
287 } | |
288 | |
289 // now request session | |
290 XmlElement iq(QN_IQ); | |
291 iq.AddAttr(QN_TYPE, "set"); | |
292 | |
293 iqId_ = pctx_->NextId(); | |
294 iq.AddAttr(QN_ID, iqId_); | |
295 iq.AddElement(new XmlElement(QN_SESSION_SESSION, true)); | |
296 pctx_->InternalSendStanza(&iq); | |
297 | |
298 state_ = LOGINSTATE_SESSION_REQUESTED; | |
299 continue; | |
300 } | |
301 | |
302 case LOGINSTATE_SESSION_REQUESTED: { | |
303 if (NULL == (element = NextStanza())) | |
304 return true; | |
305 if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ || | |
306 element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set") | |
307 return false; | |
308 | |
309 if (element->Attr(QN_TYPE) != "result") | |
310 return Failure(XmppEngine::ERROR_BIND); | |
311 | |
312 pctx_->SignalBound(fullJid_); | |
313 FlushQueuedStanzas(); | |
314 state_ = LOGINSTATE_DONE; | |
315 return true; | |
316 } | |
317 | |
318 case LOGINSTATE_DONE: | |
319 return false; | |
320 } | |
321 } | |
322 } | |
323 | |
324 bool | |
325 XmppLoginTask::HandleStartStream(const XmlElement *element) { | |
326 | |
327 if (element->Name() != QN_STREAM_STREAM) | |
328 return false; | |
329 | |
330 if (element->Attr(QN_XMLNS) != "jabber:client") | |
331 return false; | |
332 | |
333 if (element->Attr(QN_VERSION) != "1.0") | |
334 return false; | |
335 | |
336 if (!element->HasAttr(QN_ID)) | |
337 return false; | |
338 | |
339 streamId_ = element->Attr(QN_ID); | |
340 | |
341 return true; | |
342 } | |
343 | |
344 bool | |
345 XmppLoginTask::HandleFeatures(const XmlElement *element) { | |
346 if (element->Name() != QN_STREAM_FEATURES) | |
347 return false; | |
348 | |
349 pelFeatures_.reset(new XmlElement(*element)); | |
350 return true; | |
351 } | |
352 | |
353 const XmlElement * | |
354 XmppLoginTask::GetFeature(const QName & name) { | |
355 return pelFeatures_->FirstNamed(name); | |
356 } | |
357 | |
358 bool | |
359 XmppLoginTask::Failure(XmppEngine::Error reason) { | |
360 state_ = LOGINSTATE_DONE; | |
361 pctx_->SignalError(reason, 0); | |
362 return false; | |
363 } | |
364 | |
365 void | |
366 XmppLoginTask::OutgoingStanza(const XmlElement * element) { | |
367 XmlElement * pelCopy = new XmlElement(*element); | |
368 pvecQueuedStanzas_->push_back(pelCopy); | |
369 } | |
370 | |
371 void | |
372 XmppLoginTask::FlushQueuedStanzas() { | |
373 for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) { | |
374 pctx_->InternalSendStanza((*pvecQueuedStanzas_)[i]); | |
375 delete (*pvecQueuedStanzas_)[i]; | |
376 } | |
377 pvecQueuedStanzas_->clear(); | |
378 } | |
379 | |
380 } | |
OLD | NEW |