/* * Copyright 2004 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include #include "webrtc/libjingle/xmllite/xmlelement.h" #include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/libjingle/xmpp/plainsaslhandler.h" #include "webrtc/libjingle/xmpp/saslplainmechanism.h" #include "webrtc/libjingle/xmpp/util_unittest.h" #include "webrtc/libjingle/xmpp/xmppengine.h" #include "webrtc/base/common.h" #include "webrtc/base/cryptstring.h" #include "webrtc/base/gunit.h" #include "webrtc/typedefs.h" using buzz::Jid; using buzz::QName; using buzz::XmlElement; using buzz::XmppEngine; using buzz::XmppTestHandler; enum XlttStage { XLTT_STAGE_CONNECT = 0, XLTT_STAGE_STREAMSTART, XLTT_STAGE_TLS_FEATURES, XLTT_STAGE_TLS_PROCEED, XLTT_STAGE_ENCRYPTED_START, XLTT_STAGE_AUTH_FEATURES, XLTT_STAGE_AUTH_SUCCESS, XLTT_STAGE_AUTHENTICATED_START, XLTT_STAGE_BIND_FEATURES, XLTT_STAGE_BIND_SUCCESS, XLTT_STAGE_SESSION_SUCCESS, }; class XmppLoginTaskTest : public testing::Test { public: XmppEngine* engine() { return engine_.get(); } XmppTestHandler* handler() { return handler_.get(); } virtual void SetUp() { engine_.reset(XmppEngine::Create()); handler_.reset(new XmppTestHandler(engine_.get())); Jid jid("david@my-server"); rtc::InsecureCryptStringImpl pass; pass.password() = "david"; engine_->SetSessionHandler(handler_.get()); engine_->SetOutputHandler(handler_.get()); engine_->AddStanzaHandler(handler_.get()); engine_->SetUser(jid); engine_->SetSaslHandler( new buzz::PlainSaslHandler(jid, rtc::CryptString(pass), true)); } virtual void TearDown() { handler_.reset(); engine_.reset(); } void RunPartialLogin(XlttStage startstage, XlttStage endstage); void SetTlsOptions(buzz::TlsOptions option); private: std::unique_ptr engine_; std::unique_ptr handler_; }; void XmppLoginTaskTest::SetTlsOptions(buzz::TlsOptions option) { engine_->SetTls(option); } void XmppLoginTaskTest::RunPartialLogin(XlttStage startstage, XlttStage endstage) { std::string input; switch (startstage) { case XLTT_STAGE_CONNECT: { engine_->Connect(); XmlElement appStanza(QName("test", "app-stanza")); appStanza.AddText("this-is-a-test"); engine_->SendStanza(&appStanza); EXPECT_EQ("\r\n", handler_->OutputActivity()); EXPECT_EQ("[OPENING]", handler_->SessionActivity()); EXPECT_EQ("", handler_->StanzaActivity()); if (endstage == XLTT_STAGE_CONNECT) return; FALLTHROUGH(); } case XLTT_STAGE_STREAMSTART: { input = ""; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); EXPECT_EQ("", handler_->OutputActivity()); if (endstage == XLTT_STAGE_STREAMSTART) return; FALLTHROUGH(); } case XLTT_STAGE_TLS_FEATURES: { input = "" "" ""; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler_->OutputActivity()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); if (endstage == XLTT_STAGE_TLS_FEATURES) return; FALLTHROUGH(); } case XLTT_STAGE_TLS_PROCEED: { input = std::string(""); engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[START-TLS my-server]" "\r\n", handler_->OutputActivity()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); if (endstage == XLTT_STAGE_TLS_PROCEED) return; FALLTHROUGH(); } case XLTT_STAGE_ENCRYPTED_START: { input = std::string(""); engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); EXPECT_EQ("", handler_->OutputActivity()); if (endstage == XLTT_STAGE_ENCRYPTED_START) return; FALLTHROUGH(); } case XLTT_STAGE_AUTH_FEATURES: { input = "" "" "DIGEST-MD5" "PLAIN" "" ""; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("AGRhdmlkAGRhdmlk", handler_->OutputActivity()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); if (endstage == XLTT_STAGE_AUTH_FEATURES) return; FALLTHROUGH(); } case XLTT_STAGE_AUTH_SUCCESS: { input = ""; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("\r\n", handler_->OutputActivity()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); if (endstage == XLTT_STAGE_AUTH_SUCCESS) return; FALLTHROUGH(); } case XLTT_STAGE_AUTHENTICATED_START: { input = std::string(""); engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); EXPECT_EQ("", handler_->OutputActivity()); if (endstage == XLTT_STAGE_AUTHENTICATED_START) return; FALLTHROUGH(); } case XLTT_STAGE_BIND_FEATURES: { input = "" "" "" ""; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("" "", handler_->OutputActivity()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); if (endstage == XLTT_STAGE_BIND_FEATURES) return; FALLTHROUGH(); } case XLTT_STAGE_BIND_SUCCESS: { input = "" "" "david@my-server/test"; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("" "", handler_->OutputActivity()); EXPECT_EQ("", handler_->StanzaActivity()); EXPECT_EQ("", handler_->SessionActivity()); if (endstage == XLTT_STAGE_BIND_SUCCESS) return; FALLTHROUGH(); } case XLTT_STAGE_SESSION_SUCCESS: { input = ""; engine_->HandleInput(input.c_str(), input.length()); EXPECT_EQ("this-is-a-test" "", handler_->OutputActivity()); EXPECT_EQ("[OPEN]", handler_->SessionActivity()); EXPECT_EQ("", handler_->StanzaActivity()); if (endstage == XLTT_STAGE_SESSION_SUCCESS) return; FALLTHROUGH(); } default: break; } } TEST_F(XmppLoginTaskTest, TestUtf8Good) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_CONNECT); std::string input = "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestNonUtf8Bad) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_CONNECT); std::string input = "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-XML]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestNoFeatures) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-VERSION]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsRequiredNotPresent) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = "" "" "DIGEST-MD5" "PLAIN" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-TLS]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsRequeiredAndPresent) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = "" "" "" "" "" "X-GOOGLE-TOKEN" "PLAIN" "X-OAUTH2" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsEnabledNotPresent) { SetTlsOptions(buzz::TLS_ENABLED); RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = "" "" "DIGEST-MD5" "PLAIN" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("AGRhdmlkAGRhdmlk", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsEnabledAndPresent) { SetTlsOptions(buzz::TLS_ENABLED); RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = "" "" "X-GOOGLE-TOKEN" "PLAIN" "X-OAUTH2" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("AGRhdmlkAGRhdmlk", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsDisabledNotPresent) { SetTlsOptions(buzz::TLS_DISABLED); RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = "" "" "DIGEST-MD5" "PLAIN" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("AGRhdmlkAGRhdmlk", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsDisabledAndPresent) { SetTlsOptions(buzz::TLS_DISABLED); RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART); std::string input = "" "" "X-GOOGLE-TOKEN" "PLAIN" "X-OAUTH2" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("AGRhdmlkAGRhdmlk", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsFailure) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_TLS_FEATURES); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-TLS]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestTlsBadStream) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_TLS_PROCEED); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-VERSION]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestMissingSaslPlain) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_ENCRYPTED_START); std::string input = "" "" "DIGEST-MD5" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-AUTH]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestWrongPassword) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTH_FEATURES); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-UNAUTHORIZED]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestAuthBadStream) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTH_SUCCESS); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-VERSION]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestMissingBindFeature) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTHENTICATED_START); std::string input = "" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity()); } TEST_F(XmppLoginTaskTest, TestMissingSessionFeature) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTHENTICATED_START); std::string input = "" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } /* TODO: Handle this case properly inside XmppLoginTask. TEST_F(XmppLoginTaskTest, TestBindFailure1) { // check wrong JID RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES); std::string input = "" "" "davey@my-server/test"; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } */ TEST_F(XmppLoginTaskTest, TestBindFailure2) { // check missing JID RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES); std::string input = "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestBindFailure3) { // check plain failure RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity()); EXPECT_EQ("", handler()->StanzaActivity()); } TEST_F(XmppLoginTaskTest, TestBindFailure4) { // check wrong id to ignore RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); // continue after an ignored iq RunPartialLogin(XLTT_STAGE_BIND_SUCCESS, XLTT_STAGE_SESSION_SUCCESS); } TEST_F(XmppLoginTaskTest, TestSessionFailurePlain1) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_SUCCESS); std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity()); } TEST_F(XmppLoginTaskTest, TestSessionFailurePlain2) { RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_SUCCESS); // check reverse iq to ignore // TODO: consider queueing or passing through? std::string input = ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("", handler()->OutputActivity()); EXPECT_EQ("", handler()->SessionActivity()); // continue after an ignored iq RunPartialLogin(XLTT_STAGE_SESSION_SUCCESS, XLTT_STAGE_SESSION_SUCCESS); } TEST_F(XmppLoginTaskTest, TestBadXml) { int errorKind = 0; for (XlttStage stage = XLTT_STAGE_CONNECT; stage <= XLTT_STAGE_SESSION_SUCCESS; stage = static_cast(stage + 1)) { RunPartialLogin(XLTT_STAGE_CONNECT, stage); std::string input; switch (errorKind++ % 5) { case 0: input = "&syntax;"; break; case 1: input = ""; break; case 2: input = ""; break; case 3: input = "<>"; break; case 4: input = ""; break; } engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-XML]", handler()->SessionActivity()); TearDown(); SetUp(); } } TEST_F(XmppLoginTaskTest, TestStreamError) { for (XlttStage stage = XLTT_STAGE_CONNECT; stage <= XLTT_STAGE_SESSION_SUCCESS; stage = static_cast(stage + 1)) { switch (stage) { case XLTT_STAGE_CONNECT: case XLTT_STAGE_TLS_PROCEED: case XLTT_STAGE_AUTH_SUCCESS: continue; default: break; } RunPartialLogin(XLTT_STAGE_CONNECT, stage); std::string input = "" "" "" "Some special application diagnostic information!" "" "" ""; engine()->HandleInput(input.c_str(), input.length()); EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); EXPECT_EQ("[CLOSED][ERROR-STREAM]", handler()->SessionActivity()); EXPECT_EQ("" "" "" "Some special application diagnostic information!" "" "" "", engine()->GetStreamError()->Str()); TearDown(); SetUp(); } }