~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/googletalk/libjingle/talk/xmpp/xmpplogintask.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * libjingle
3
 
 * Copyright 2004--2005, Google Inc.
4
 
 *
5
 
 * Redistribution and use in source and binary forms, with or without
6
 
 * modification, are permitted provided that the following conditions are met:
7
 
 *
8
 
 *  1. Redistributions of source code must retain the above copyright notice,
9
 
 *     this list of conditions and the following disclaimer.
10
 
 *  2. Redistributions in binary form must reproduce the above copyright notice,
11
 
 *     this list of conditions and the following disclaimer in the documentation
12
 
 *     and/or other materials provided with the distribution.
13
 
 *  3. The name of the author may not be used to endorse or promote products
14
 
 *     derived from this software without specific prior written permission.
15
 
 *
16
 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23
 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24
 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25
 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 
 */
27
 
 
28
 
#include "talk/xmpp/xmpplogintask.h"
29
 
 
30
 
#include <string>
31
 
#include <vector>
32
 
 
33
 
#include "talk/base/base64.h"
34
 
#include "talk/base/common.h"
35
 
#include "talk/xmllite/xmlelement.h"
36
 
#include "talk/xmpp/constants.h"
37
 
#include "talk/xmpp/jid.h"
38
 
#include "talk/xmpp/saslmechanism.h"
39
 
#include "talk/xmpp/xmppengineimpl.h"
40
 
 
41
 
using talk_base::ConstantLabel;
42
 
 
43
 
namespace buzz {
44
 
 
45
 
#ifdef _DEBUG
46
 
const ConstantLabel XmppLoginTask::LOGINTASK_STATES[] = {
47
 
  KLABEL(LOGINSTATE_INIT),
48
 
  KLABEL(LOGINSTATE_STREAMSTART_SENT),
49
 
  KLABEL(LOGINSTATE_STARTED_XMPP),
50
 
  KLABEL(LOGINSTATE_TLS_INIT),
51
 
  KLABEL(LOGINSTATE_AUTH_INIT),
52
 
  KLABEL(LOGINSTATE_BIND_INIT),
53
 
  KLABEL(LOGINSTATE_TLS_REQUESTED),
54
 
  KLABEL(LOGINSTATE_SASL_RUNNING),
55
 
  KLABEL(LOGINSTATE_BIND_REQUESTED),
56
 
  KLABEL(LOGINSTATE_SESSION_REQUESTED),
57
 
  KLABEL(LOGINSTATE_DONE),
58
 
  LASTLABEL
59
 
};
60
 
#endif  // _DEBUG
61
 
XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) :
62
 
  pctx_(pctx),
63
 
  authNeeded_(true),
64
 
  allowNonGoogleLogin_(true),
65
 
  state_(LOGINSTATE_INIT),
66
 
  pelStanza_(NULL),
67
 
  isStart_(false),
68
 
  iqId_(STR_EMPTY),
69
 
  pelFeatures_(NULL),
70
 
  fullJid_(STR_EMPTY),
71
 
  streamId_(STR_EMPTY),
72
 
  pvecQueuedStanzas_(new std::vector<XmlElement *>()),
73
 
  sasl_mech_(NULL) {
74
 
}
75
 
 
76
 
XmppLoginTask::~XmppLoginTask() {
77
 
  for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1)
78
 
    delete (*pvecQueuedStanzas_)[i];
79
 
}
80
 
 
81
 
void
82
 
XmppLoginTask::IncomingStanza(const XmlElement *element, bool isStart) {
83
 
  pelStanza_ = element;
84
 
  isStart_ = isStart;
85
 
  Advance();
86
 
  pelStanza_ = NULL;
87
 
  isStart_ = false;
88
 
}
89
 
 
90
 
const XmlElement *
91
 
XmppLoginTask::NextStanza() {
92
 
  const XmlElement * result = pelStanza_;
93
 
  pelStanza_ = NULL;
94
 
  return result;
95
 
}
96
 
 
97
 
bool
98
 
XmppLoginTask::Advance() {
99
 
 
100
 
  for (;;) {
101
 
 
102
 
    const XmlElement * element = NULL;
103
 
 
104
 
#if _DEBUG
105
 
    LOG(LS_VERBOSE) << "XmppLoginTask::Advance - "
106
 
      << talk_base::ErrorName(state_, LOGINTASK_STATES);
107
 
#endif  // _DEBUG
108
 
 
109
 
    switch (state_) {
110
 
 
111
 
      case LOGINSTATE_INIT: {
112
 
        pctx_->RaiseReset();
113
 
        pelFeatures_.reset(NULL);
114
 
 
115
 
        // The proper domain to verify against is the real underlying
116
 
        // domain - i.e., the domain that owns the JID.  Our XmppEngineImpl
117
 
        // also allows matching against a proxy domain instead, if it is told
118
 
        // to do so - see the implementation of XmppEngineImpl::StartTls and
119
 
        // XmppEngine::SetTlsServerDomain to see how you can use that feature
120
 
        pctx_->InternalSendStart(pctx_->user_jid_.domain());
121
 
        state_ = LOGINSTATE_STREAMSTART_SENT;
122
 
        break;
123
 
      }
124
 
 
125
 
      case LOGINSTATE_STREAMSTART_SENT: {
126
 
        if (NULL == (element = NextStanza()))
127
 
          return true;
128
 
 
129
 
        if (!isStart_ || !HandleStartStream(element))
130
 
          return Failure(XmppEngine::ERROR_VERSION);
131
 
 
132
 
        state_ = LOGINSTATE_STARTED_XMPP;
133
 
        return true;
134
 
      }
135
 
 
136
 
      case LOGINSTATE_STARTED_XMPP: {
137
 
        if (NULL == (element = NextStanza()))
138
 
          return true;
139
 
 
140
 
        if (!HandleFeatures(element))
141
 
          return Failure(XmppEngine::ERROR_VERSION);
142
 
 
143
 
        bool tls_present = (GetFeature(QN_TLS_STARTTLS) != NULL);
144
 
        // Error if TLS required but not present.
145
 
        if (pctx_->tls_option_ == buzz::TLS_REQUIRED && !tls_present) {
146
 
          return Failure(XmppEngine::ERROR_TLS);
147
 
        }
148
 
        // Use TLS if required or enabled, and also available
149
 
        if ((pctx_->tls_option_ == buzz::TLS_REQUIRED ||
150
 
            pctx_->tls_option_ == buzz::TLS_ENABLED) && tls_present) {
151
 
          state_ = LOGINSTATE_TLS_INIT;
152
 
          continue;
153
 
        }
154
 
 
155
 
        if (authNeeded_) {
156
 
          state_ = LOGINSTATE_AUTH_INIT;
157
 
          continue;
158
 
        }
159
 
 
160
 
        state_ = LOGINSTATE_BIND_INIT;
161
 
        continue;
162
 
      }
163
 
 
164
 
      case LOGINSTATE_TLS_INIT: {
165
 
        const XmlElement * pelTls = GetFeature(QN_TLS_STARTTLS);
166
 
        if (!pelTls)
167
 
          return Failure(XmppEngine::ERROR_TLS);
168
 
 
169
 
        XmlElement el(QN_TLS_STARTTLS, true);
170
 
        pctx_->InternalSendStanza(&el);
171
 
        state_ = LOGINSTATE_TLS_REQUESTED;
172
 
        continue;
173
 
      }
174
 
 
175
 
      case LOGINSTATE_TLS_REQUESTED: {
176
 
        if (NULL == (element = NextStanza()))
177
 
          return true;
178
 
        if (element->Name() != QN_TLS_PROCEED)
179
 
          return Failure(XmppEngine::ERROR_TLS);
180
 
 
181
 
        // The proper domain to verify against is the real underlying
182
 
        // domain - i.e., the domain that owns the JID.  Our XmppEngineImpl
183
 
        // also allows matching against a proxy domain instead, if it is told
184
 
        // to do so - see the implementation of XmppEngineImpl::StartTls and
185
 
        // XmppEngine::SetTlsServerDomain to see how you can use that feature
186
 
        pctx_->StartTls(pctx_->user_jid_.domain());
187
 
        pctx_->tls_option_ = buzz::TLS_ENABLED;
188
 
        state_ = LOGINSTATE_INIT;
189
 
        continue;
190
 
      }
191
 
 
192
 
      case LOGINSTATE_AUTH_INIT: {
193
 
        const XmlElement * pelSaslAuth = GetFeature(QN_SASL_MECHANISMS);
194
 
        if (!pelSaslAuth) {
195
 
          return Failure(XmppEngine::ERROR_AUTH);
196
 
        }
197
 
 
198
 
        // Collect together the SASL auth mechanisms presented by the server
199
 
        std::vector<std::string> mechanisms;
200
 
        for (const XmlElement * pelMech =
201
 
             pelSaslAuth->FirstNamed(QN_SASL_MECHANISM);
202
 
             pelMech;
203
 
             pelMech = pelMech->NextNamed(QN_SASL_MECHANISM)) {
204
 
 
205
 
          mechanisms.push_back(pelMech->BodyText());
206
 
        }
207
 
 
208
 
        // Given all the mechanisms, choose the best
209
 
        std::string choice(pctx_->ChooseBestSaslMechanism(mechanisms, pctx_->IsEncrypted()));
210
 
        if (choice.empty()) {
211
 
          return Failure(XmppEngine::ERROR_AUTH);
212
 
        }
213
 
 
214
 
        // No recognized auth mechanism - that's an error
215
 
        sasl_mech_.reset(pctx_->GetSaslMechanism(choice));
216
 
        if (sasl_mech_.get() == NULL) {
217
 
          return Failure(XmppEngine::ERROR_AUTH);
218
 
        }
219
 
 
220
 
        // OK, let's start it.
221
 
        XmlElement * auth = sasl_mech_->StartSaslAuth();
222
 
        if (auth == NULL) {
223
 
          return Failure(XmppEngine::ERROR_AUTH);
224
 
        }
225
 
        if (allowNonGoogleLogin_) {
226
 
          // Setting the following two attributes is required to support
227
 
          // non-google ids.
228
 
 
229
 
          // Allow login with non-google id accounts.
230
 
          auth->SetAttr(QN_GOOGLE_ALLOW_NON_GOOGLE_ID_XMPP_LOGIN, "true");
231
 
 
232
 
          // Allow login with either the non-google id or the friendly email.
233
 
          auth->SetAttr(QN_GOOGLE_AUTH_CLIENT_USES_FULL_BIND_RESULT, "true");
234
 
        }
235
 
 
236
 
        pctx_->InternalSendStanza(auth);
237
 
        delete auth;
238
 
        state_ = LOGINSTATE_SASL_RUNNING;
239
 
        continue;
240
 
      }
241
 
 
242
 
      case LOGINSTATE_SASL_RUNNING: {
243
 
        if (NULL == (element = NextStanza()))
244
 
          return true;
245
 
        if (element->Name().Namespace() != NS_SASL)
246
 
          return Failure(XmppEngine::ERROR_AUTH);
247
 
        if (element->Name() == QN_SASL_CHALLENGE) {
248
 
          XmlElement * response = sasl_mech_->HandleSaslChallenge(element);
249
 
          if (response == NULL) {
250
 
            return Failure(XmppEngine::ERROR_AUTH);
251
 
          }
252
 
          pctx_->InternalSendStanza(response);
253
 
          delete response;
254
 
          state_ = LOGINSTATE_SASL_RUNNING;
255
 
          continue;
256
 
        }
257
 
        if (element->Name() != QN_SASL_SUCCESS) {
258
 
          return Failure(XmppEngine::ERROR_UNAUTHORIZED);
259
 
        }
260
 
 
261
 
        // Authenticated!
262
 
        authNeeded_ = false;
263
 
        state_ = LOGINSTATE_INIT;
264
 
        continue;
265
 
      }
266
 
 
267
 
      case LOGINSTATE_BIND_INIT: {
268
 
        const XmlElement * pelBindFeature = GetFeature(QN_BIND_BIND);
269
 
        const XmlElement * pelSessionFeature = GetFeature(QN_SESSION_SESSION);
270
 
        if (!pelBindFeature || !pelSessionFeature)
271
 
          return Failure(XmppEngine::ERROR_BIND);
272
 
 
273
 
        XmlElement iq(QN_IQ);
274
 
        iq.AddAttr(QN_TYPE, "set");
275
 
 
276
 
        iqId_ = pctx_->NextId();
277
 
        iq.AddAttr(QN_ID, iqId_);
278
 
        iq.AddElement(new XmlElement(QN_BIND_BIND, true));
279
 
 
280
 
        if (pctx_->requested_resource_ != STR_EMPTY) {
281
 
          iq.AddElement(new XmlElement(QN_BIND_RESOURCE), 1);
282
 
          iq.AddText(pctx_->requested_resource_, 2);
283
 
        }
284
 
        pctx_->InternalSendStanza(&iq);
285
 
        state_ = LOGINSTATE_BIND_REQUESTED;
286
 
        continue;
287
 
      }
288
 
 
289
 
      case LOGINSTATE_BIND_REQUESTED: {
290
 
        if (NULL == (element = NextStanza()))
291
 
          return true;
292
 
 
293
 
        if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ ||
294
 
            element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set")
295
 
          return true;
296
 
 
297
 
        if (element->Attr(QN_TYPE) != "result" || element->FirstElement() == NULL ||
298
 
            element->FirstElement()->Name() != QN_BIND_BIND)
299
 
          return Failure(XmppEngine::ERROR_BIND);
300
 
 
301
 
        fullJid_ = Jid(element->FirstElement()->TextNamed(QN_BIND_JID));
302
 
        if (!fullJid_.IsFull()) {
303
 
          return Failure(XmppEngine::ERROR_BIND);
304
 
        }
305
 
 
306
 
        // now request session
307
 
        XmlElement iq(QN_IQ);
308
 
        iq.AddAttr(QN_TYPE, "set");
309
 
 
310
 
        iqId_ = pctx_->NextId();
311
 
        iq.AddAttr(QN_ID, iqId_);
312
 
        iq.AddElement(new XmlElement(QN_SESSION_SESSION, true));
313
 
        pctx_->InternalSendStanza(&iq);
314
 
 
315
 
        state_ = LOGINSTATE_SESSION_REQUESTED;
316
 
        continue;
317
 
      }
318
 
 
319
 
      case LOGINSTATE_SESSION_REQUESTED: {
320
 
        if (NULL == (element = NextStanza()))
321
 
          return true;
322
 
        if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ ||
323
 
            element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set")
324
 
          return false;
325
 
 
326
 
        if (element->Attr(QN_TYPE) != "result")
327
 
          return Failure(XmppEngine::ERROR_BIND);
328
 
 
329
 
        pctx_->SignalBound(fullJid_);
330
 
        FlushQueuedStanzas();
331
 
        state_ = LOGINSTATE_DONE;
332
 
        return true;
333
 
      }
334
 
 
335
 
      case LOGINSTATE_DONE:
336
 
        return false;
337
 
    }
338
 
  }
339
 
}
340
 
 
341
 
bool
342
 
XmppLoginTask::HandleStartStream(const XmlElement *element) {
343
 
 
344
 
  if (element->Name() != QN_STREAM_STREAM)
345
 
    return false;
346
 
 
347
 
  if (element->Attr(QN_XMLNS) != "jabber:client")
348
 
    return false;
349
 
 
350
 
  if (element->Attr(QN_VERSION) != "1.0")
351
 
    return false;
352
 
 
353
 
  if (!element->HasAttr(QN_ID))
354
 
    return false;
355
 
 
356
 
  streamId_ = element->Attr(QN_ID);
357
 
 
358
 
  return true;
359
 
}
360
 
 
361
 
bool
362
 
XmppLoginTask::HandleFeatures(const XmlElement *element) {
363
 
  if (element->Name() != QN_STREAM_FEATURES)
364
 
    return false;
365
 
 
366
 
  pelFeatures_.reset(new XmlElement(*element));
367
 
  return true;
368
 
}
369
 
 
370
 
const XmlElement *
371
 
XmppLoginTask::GetFeature(const QName & name) {
372
 
  return pelFeatures_->FirstNamed(name);
373
 
}
374
 
 
375
 
bool
376
 
XmppLoginTask::Failure(XmppEngine::Error reason) {
377
 
  state_ = LOGINSTATE_DONE;
378
 
  pctx_->SignalError(reason, 0);
379
 
  return false;
380
 
}
381
 
 
382
 
void
383
 
XmppLoginTask::OutgoingStanza(const XmlElement * element) {
384
 
  XmlElement * pelCopy = new XmlElement(*element);
385
 
  pvecQueuedStanzas_->push_back(pelCopy);
386
 
}
387
 
 
388
 
void
389
 
XmppLoginTask::FlushQueuedStanzas() {
390
 
  for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) {
391
 
    pctx_->InternalSendStanza((*pvecQueuedStanzas_)[i]);
392
 
    delete (*pvecQueuedStanzas_)[i];
393
 
  }
394
 
  pvecQueuedStanzas_->clear();
395
 
}
396
 
 
397
 
}