1// Copyright (c) 2012 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/protocol/negotiating_authenticator_base.h"
6
7#include <algorithm>
8#include <sstream>
9
10#include "base/bind.h"
11#include "base/callback.h"
12#include "base/logging.h"
13#include "base/strings/string_split.h"
14#include "remoting/base/rsa_key_pair.h"
15#include "remoting/protocol/channel_authenticator.h"
16#include "remoting/protocol/v2_authenticator.h"
17#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
18
19namespace remoting {
20namespace protocol {
21
22const buzz::StaticQName NegotiatingAuthenticatorBase::kMethodAttributeQName =
23    { "", "method" };
24const buzz::StaticQName
25NegotiatingAuthenticatorBase::kSupportedMethodsAttributeQName =
26    { "", "supported-methods" };
27const char NegotiatingAuthenticatorBase::kSupportedMethodsSeparator = ',';
28
29NegotiatingAuthenticatorBase::NegotiatingAuthenticatorBase(
30    Authenticator::State initial_state)
31    : current_method_(AuthenticationMethod::Invalid()),
32      state_(initial_state),
33      rejection_reason_(INVALID_CREDENTIALS) {
34}
35
36NegotiatingAuthenticatorBase::~NegotiatingAuthenticatorBase() {
37}
38
39Authenticator::State NegotiatingAuthenticatorBase::state() const {
40  return state_;
41}
42
43bool NegotiatingAuthenticatorBase::started() const {
44  if (!current_authenticator_) {
45    return false;
46  }
47  return current_authenticator_->started();
48}
49
50Authenticator::RejectionReason
51NegotiatingAuthenticatorBase::rejection_reason() const {
52  return rejection_reason_;
53}
54
55void NegotiatingAuthenticatorBase::ProcessMessageInternal(
56    const buzz::XmlElement* message,
57    const base::Closure& resume_callback) {
58  if (current_authenticator_->state() == WAITING_MESSAGE) {
59    // If the message was not discarded and the authenticator is waiting for it,
60    // give it to the underlying authenticator to process.
61    // |current_authenticator_| is owned, so Unretained() is safe here.
62    state_ = PROCESSING_MESSAGE;
63    current_authenticator_->ProcessMessage(message, base::Bind(
64        &NegotiatingAuthenticatorBase::UpdateState,
65        base::Unretained(this), resume_callback));
66  } else {
67    // Otherwise, just discard the message and run the callback.
68    resume_callback.Run();
69  }
70}
71
72void NegotiatingAuthenticatorBase::UpdateState(
73    const base::Closure& resume_callback) {
74  // After the underlying authenticator finishes processing the message, the
75  // NegotiatingAuthenticatorBase must update its own state before running the
76  // |resume_callback| to resume the session negotiation.
77  state_ = current_authenticator_->state();
78  if (state_ == REJECTED)
79    rejection_reason_ = current_authenticator_->rejection_reason();
80  resume_callback.Run();
81}
82
83scoped_ptr<buzz::XmlElement>
84NegotiatingAuthenticatorBase::GetNextMessageInternal() {
85  DCHECK_EQ(state(), MESSAGE_READY);
86  DCHECK(current_method_.is_valid());
87
88  scoped_ptr<buzz::XmlElement> result;
89  if (current_authenticator_->state() == MESSAGE_READY) {
90    result = current_authenticator_->GetNextMessage();
91  } else {
92    result = CreateEmptyAuthenticatorMessage();
93  }
94  state_ = current_authenticator_->state();
95  DCHECK(state_ == ACCEPTED || state_ == WAITING_MESSAGE);
96  result->AddAttr(kMethodAttributeQName, current_method_.ToString());
97  return result.Pass();
98}
99
100void NegotiatingAuthenticatorBase::AddMethod(
101    const AuthenticationMethod& method) {
102  DCHECK(method.is_valid());
103  methods_.push_back(method);
104}
105
106scoped_ptr<ChannelAuthenticator>
107NegotiatingAuthenticatorBase::CreateChannelAuthenticator() const {
108  DCHECK_EQ(state(), ACCEPTED);
109  return current_authenticator_->CreateChannelAuthenticator();
110}
111
112}  // namespace protocol
113}  // namespace remoting
114