blob: 0d11eafa9ab80b4e5a0d9e9d8e79dc8fb10f1555 [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements the Token Pairing Server, which receives
* and processes PairTokenRequest messages, responding as appropriate.
*/
#include <string.h>
#include <ctype.h>
#include <Weave/Core/WeaveCore.h>
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Profiles/token-pairing/TokenPairing.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Profiles/common/CommonProfile.h>
namespace nl {
namespace Weave {
namespace Profiles {
namespace TokenPairing {
using namespace nl::Weave::Encoding;
TokenPairingServer::TokenPairingServer() :
mCurClientOp(NULL),
mDelegate(NULL),
mCertificateSent(false)
{
FabricState = NULL;
ExchangeMgr = NULL;
}
/**
* Initialize the Token Pairing Server state and register to receive
* Token Pairing messages.
*
* param[in] exchangeMgr A pointer to the Weave Exchange Manager.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE When a token pairing server
* has already been registered.
* @retval #WEAVE_ERROR_TOO_MANY_UNSOLICITED_MESSAGE_HANDLERS When too many unsolicited message
* handlers are registered.
* @retval #WEAVE_NO_ERROR On success.
*/
WEAVE_ERROR TokenPairingServer::Init(WeaveExchangeManager *exchangeMgr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (ExchangeMgr != NULL)
return WEAVE_ERROR_INCORRECT_STATE;
ExchangeMgr = exchangeMgr;
FabricState = exchangeMgr->FabricState;
mCurClientOp = NULL;
// Register to receive unsolicited Token Pairing messages from the exchange manager.
err = ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_TokenPairing, kMsgType_PairTokenRequest, HandleClientRequest, this);
SuccessOrExit(err);
err = ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_TokenPairing, kMsgType_UnpairTokenRequest, HandleClientRequest, this);
exit:
return err;
}
/**
* Shutdown the Token Pairing Server.
*
* @retval #WEAVE_NO_ERROR unconditionally.
*/
// TODO: Additional documentation detail required (i.e. how this function impacts object lifecycle).
WEAVE_ERROR TokenPairingServer::Shutdown()
{
if (ExchangeMgr != NULL)
{
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_TokenPairing, kMsgType_UnpairTokenRequest);
ExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_TokenPairing, kMsgType_PairTokenRequest);
ExchangeMgr = NULL;
}
CloseClientOp();
FabricState = NULL;
return WEAVE_NO_ERROR;
}
/**
* Set the delegate to process Device Control Server events.
*
* @param[in] delegate A pointer to the Device Control Delegate.
*/
void TokenPairingServer::SetDelegate(TokenPairingDelegate *delegate)
{
mDelegate = delegate;
}
/**
* Send a status report response to a request.
*
* @param[in] statusProfileId The Weave profile ID this status report pertains to.
* @param[in] statusCode The status code to be included in this response.
* @param[in] sysError The system error code to be included in this response.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If there is no request being processed.
* @retval #WEAVE_NO_ERROR On success.
* @retval other Other Weave or platform-specific error codes indicating that an error
* occurred preventing the status report from sending.
*/
WEAVE_ERROR TokenPairingServer::SendStatusReport(uint32_t statusProfileId, uint16_t statusCode, WEAVE_ERROR sysError)
{
WEAVE_ERROR err;
VerifyOrExit(mCurClientOp != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
err = WeaveServerBase::SendStatusReport(mCurClientOp, statusProfileId, statusCode, sysError);
exit:
CloseClientOp();
return err;
}
WEAVE_ERROR TokenPairingServer::SendTokenCertificateResponse(PacketBuffer *certificateBuf)
{
WEAVE_ERROR err;
WeaveLogError(TokenPairing, "SendTokenCertificateResponse");
VerifyOrExit(mCurClientOp != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
// The optional TokenCertificateResponse may only be sent once.
VerifyOrExit(mCertificateSent == false, err = WEAVE_ERROR_INCORRECT_STATE);
err = mCurClientOp->SendMessage(kWeaveProfile_TokenPairing, kMsgType_TokenCertificateResponse, certificateBuf, 0);
certificateBuf = NULL;
mCertificateSent = true;
exit:
if (certificateBuf != NULL)
{
PacketBuffer::Free(certificateBuf);
}
return err;
}
WEAVE_ERROR TokenPairingServer::SendTokenPairedResponse(PacketBuffer *tokenBundleBuf)
{
WEAVE_ERROR err;
WeaveLogError(TokenPairing, "SendTokenPairedResponse");
VerifyOrExit(mCurClientOp != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
err = mCurClientOp->SendMessage(kWeaveProfile_TokenPairing, kMsgType_TokenPairedResponse, tokenBundleBuf, 0);
tokenBundleBuf = NULL;
exit:
if (tokenBundleBuf != NULL)
{
PacketBuffer::Free(tokenBundleBuf);
}
CloseClientOp();
return err;
}
void TokenPairingServer::CloseClientOp()
{
if (mCurClientOp != NULL)
{
mCurClientOp->Close();
mCurClientOp = NULL;
}
mCertificateSent = false;
}
void TokenPairingServer::HandleClientRequest(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TokenPairingServer *server = (TokenPairingServer *) ec->AppState;
// Fail messages for the wrong profile. This shouldn't happen, but better safe than sorry.
if (profileId != kWeaveProfile_TokenPairing)
{
WeaveServerBase::SendStatusReport(ec, kWeaveProfile_Common, Common::kStatus_BadRequest, WEAVE_NO_ERROR);
ec->Close();
ExitNow();
}
// Call on the delegate to enforce message-level access control. If policy dictates the message should NOT
// be processed, then simply end the exchange and return. If an error response was warranted, the appropriate
// response will have been sent within EnforceAccessControl().
if (!server->EnforceAccessControl(ec, profileId, msgType, msgInfo, server->mDelegate))
{
ec->Close();
ExitNow();
}
// Disallow simultaneous requests.
if (server->mCurClientOp != NULL)
{
WeaveServerBase::SendStatusReport(ec, kWeaveProfile_Common, Common::kStatus_Busy, WEAVE_NO_ERROR);
ec->Close();
ExitNow();
}
// Record that we have a request in process.
server->mCurClientOp = ec;
server->mCertificateSent = false;
// Decode and dispatch the message.
switch (msgType)
{
case kMsgType_PairTokenRequest:
if (server->mDelegate != NULL)
{
err = server->mDelegate->OnPairTokenRequest((TokenPairingServer *)ec->AppState, msgBuf->Start(), msgBuf->DataLength());
}
SuccessOrExit(err);
break;
case kMsgType_UnpairTokenRequest:
if (server->mDelegate != NULL)
{
err = server->mDelegate->OnUnpairTokenRequest((TokenPairingServer *)ec->AppState);
}
SuccessOrExit(err);
break;
default:
err = server->SendStatusReport(kWeaveProfile_Common, Common::kStatus_BadRequest);
ExitNow();
break;
}
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (err != WEAVE_NO_ERROR && server->mCurClientOp != NULL && ec == server->mCurClientOp)
{
WeaveLogError(TokenPairing, "Error handling TokenPairing client request, err = %d", err);
server->SendStatusReport(kWeaveProfile_Common, Common::kStatus_InternalError, err);
}
}
void TokenPairingDelegate::EnforceAccessControl(ExchangeContext *ec, uint32_t msgProfileId, uint8_t msgType,
const WeaveMessageInfo *msgInfo, AccessControlResult& result)
{
// If the result has not already been determined by a subclass...
if (result == kAccessControlResult_NotDetermined)
{
switch (msgType)
{
case kMsgType_PairTokenRequest:
case kMsgType_UnpairTokenRequest:
#if WEAVE_CONFIG_REQUIRE_AUTH_DEVICE_CONTROL
if (msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode)
{
result = kAccessControlResult_Accepted;
}
#else // WEAVE_CONFIG_REQUIRE_AUTH_DEVICE_CONTROL
result = kAccessControlResult_Accepted;
#endif // WEAVE_CONFIG_REQUIRE_AUTH_DEVICE_CONTROL
break;
default:
WeaveServerBase::SendStatusReport(ec, kWeaveProfile_Common, Common::kStatus_UnsupportedMessage, WEAVE_NO_ERROR);
result = kAccessControlResult_Rejected_RespSent;
break;
}
}
// Call up to the base class.
WeaveServerDelegateBase::EnforceAccessControl(ec, msgProfileId, msgType, msgInfo, result);
}
} // namespace TokenPairing
} // namespace Profiles
} // namespace Weave
} // namespace nl