blob: 67c4a4756dcc848bc5870ca25a9bcfb96890995d [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 Weave Network Provisioning Profile, used
* to configure network interfaces.
*
* The Network Provisioning Profile facilitates client-server
* operations such that the client (the controlling device) can
* trigger specific network functionality on the server (the device
* undergoing network provisioning). These operations revolve around
* the steps necessary to provision the server device's network
* interfaces (such as 802.15.4/Thread and 802.11/Wi-Fi) provisioned
* such that the device may participate in those networks. This includes
* scanning and specifying network names and security credentials.
*
*/
#include <Weave/Core/WeaveCore.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Profiles/WeaveProfiles.h>
#include "NetworkProvisioning.h"
#include <Weave/Profiles/common/CommonProfile.h>
namespace nl {
namespace Weave {
namespace Profiles {
namespace NetworkProvisioning {
using namespace nl::Weave::Encoding;
using namespace nl::Weave::TLV;
NetworkProvisioningServer::NetworkProvisioningServer()
{
FabricState = NULL;
ExchangeMgr = NULL;
mCurOp = NULL;
mDelegate = NULL;
}
/**
* Initialize the Network Provisioning Server state and register to receive
* Network Provisioning messages.
*
* @param[in] exchangeMgr A pointer to the system Weave Exchange Manager.
*
* @retval #WEAVE_ERROR_TOO_MANY_UNSOLICITED_MESSAGE_HANDLERS If too many message handlers have
* already been registered.
* @retval #WEAVE_NO_ERROR On success.
*/
WEAVE_ERROR NetworkProvisioningServer::Init(WeaveExchangeManager *exchangeMgr)
{
ExchangeMgr = exchangeMgr;
FabricState = exchangeMgr->FabricState;
mCurOp = NULL;
mDelegate = NULL;
mLastOpResult.StatusProfileId = kWeaveProfile_Common;
mLastOpResult.StatusCode = Common::kStatus_Success;
mLastOpResult.SysError = WEAVE_NO_ERROR;
// Register to receive unsolicited Network Provisioning messages from the exchange manager.
WEAVE_ERROR err =
ExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_NetworkProvisioning, HandleRequest, this);
return err;
}
/**
* Shutdown the Network Provisioning Server.
*
* @retval #WEAVE_NO_ERROR On success.
*/
// TODO: Additional documentation detail required (i.e. how this function impacts object lifecycle).
WEAVE_ERROR NetworkProvisioningServer::Shutdown()
{
ExchangeMgr = NULL;
FabricState = NULL;
mCurOp = NULL;
mDelegate = NULL;
return WEAVE_NO_ERROR;
}
/**
* Set the delegate to process Network Provisioning Server events.
*
* @param[in] delegate A pointer to the Network Provisioning Delegate.
*/
void NetworkProvisioningServer::SetDelegate(NetworkProvisioningDelegate *delegate)
{
mDelegate = delegate;
}
/**
* Send a Network Scan Complete response message containing the results of the scan.
*
* @param[in] resultCount The number of scan results.
* @param[in] scanResultsTLV The scan results.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If the Network Provisioning Server is not initialized correctly.
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL If the results buffer is not large enough.
* @retval #WEAVE_NO_ERROR On success.
* @retval other Other Weave or platform-specific error codes indicating that an error
* occurred preventing the device from sending the Scan Complete response.
*/
WEAVE_ERROR NetworkProvisioningServer::SendNetworkScanComplete(uint8_t resultCount, PacketBuffer *scanResultsTLV)
{
return SendCompleteWithNetworkList(kMsgType_NetworkScanComplete, resultCount, scanResultsTLV);
}
/**
* Send a Get Networks Complete message containing the previously scanned networks.
*
* @param[in] resultCount The number of scan results.
* @param[in] scanResultsTLV The scan results.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If the Network Provisioning Server is not initialized correctly.
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL If the results buffer is not large enough.
* @retval #WEAVE_NO_ERROR On success.
* @retval other Other Weave or platform-specific error codes indicating that an error
* occurred preventing the device from sending the Get Networks Complete message.
*/
WEAVE_ERROR NetworkProvisioningServer::SendGetNetworksComplete(uint8_t resultCount, PacketBuffer *scanResultsTLV)
{
return SendCompleteWithNetworkList(kMsgType_GetNetworksComplete, resultCount, scanResultsTLV);
}
WEAVE_ERROR NetworkProvisioningServer::SendCompleteWithNetworkList(uint8_t msgType, int8_t resultCount, PacketBuffer *resultTLV)
{
WEAVE_ERROR err;
uint8_t *p;
VerifyOrExit(mDelegate != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(mCurOp != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
if (!resultTLV->EnsureReservedSize(WEAVE_SYSTEM_CONFIG_HEADER_RESERVE_SIZE + 1))
ExitNow(err = WEAVE_ERROR_BUFFER_TOO_SMALL);
p = resultTLV->Start() - 1;
resultTLV->SetStart(p);
*p = resultCount;
err = mCurOp->SendMessage(kWeaveProfile_NetworkProvisioning, msgType, resultTLV, 0);
resultTLV = NULL;
mLastOpResult.StatusProfileId = kWeaveProfile_Common;
mLastOpResult.StatusCode = Common::kStatus_Success;
mLastOpResult.SysError = WEAVE_NO_ERROR;
exit:
if (mCurOp != NULL)
{
mCurOp->Close();
mCurOp = NULL;
}
if (resultTLV != NULL)
PacketBuffer::Free(resultTLV);
return err;
}
/**
* Send an Add Network Complete message if the network was successfully added.
*
* @param[in] networkId The ID of the added network.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If the Network Provisioning Server is not initialized correctly.
* @retval #WEAVE_ERROR_NO_MEMORY On failure to allocate an PacketBuffer.
* @retval #WEAVE_NO_ERROR On success.
* @retval other Other Weave or platform-specific error codes indicating that an error
* occurred preventing the device from sending the Add Network Complete message.
*/
WEAVE_ERROR NetworkProvisioningServer::SendAddNetworkComplete(uint32_t networkId)
{
WEAVE_ERROR err;
PacketBuffer* respBuf = NULL;
uint8_t* p;
uint8_t respLen = 4;
VerifyOrExit(mDelegate != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(mCurOp != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
respBuf = PacketBuffer::NewWithAvailableSize(respLen);
VerifyOrExit(respBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
p = respBuf->Start();
LittleEndian::Write32(p, networkId);
respBuf->SetDataLength(respLen);
err = mCurOp->SendMessage(kWeaveProfile_NetworkProvisioning, kMsgType_AddNetworkComplete, respBuf, 0);
respBuf = NULL;
mLastOpResult.StatusProfileId = kWeaveProfile_Common;
mLastOpResult.StatusCode = Common::kStatus_Success;
mLastOpResult.SysError = WEAVE_NO_ERROR;
exit:
if (mCurOp != NULL)
{
mCurOp->Close();
mCurOp = NULL;
}
if (respBuf != NULL)
PacketBuffer::Free(respBuf);
return err;
}
/**
* Send a success response to a Network Provisioning request.
*
* @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 device from sending the success response.
*/
WEAVE_ERROR NetworkProvisioningServer::SendSuccessResponse()
{
return SendStatusReport(kWeaveProfile_Common, Common::kStatus_Success);
}
/**
* 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 device from sending the status report.
*/
WEAVE_ERROR NetworkProvisioningServer::SendStatusReport(uint32_t statusProfileId, uint16_t statusCode, WEAVE_ERROR sysError)
{
WEAVE_ERROR err;
VerifyOrExit(mDelegate != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(mCurOp != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
err = WeaveServerBase::SendStatusReport(mCurOp, statusProfileId, statusCode, sysError);
mLastOpResult.StatusProfileId = statusProfileId;
mLastOpResult.StatusCode = statusCode;
mLastOpResult.SysError = sysError;
exit:
if (mCurOp != NULL)
{
mCurOp->Close();
mCurOp = NULL;
}
return err;
}
void NetworkProvisioningServer::HandleRequest(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId,
uint8_t msgType, PacketBuffer *payload)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
NetworkProvisioningServer *server = (NetworkProvisioningServer *) ec->AppState;
NetworkProvisioningDelegate *delegate = server->mDelegate;
uint32_t networkId;
uint16_t rendezvousMode;
uint8_t networkType;
uint8_t flags;
uint16_t dataLen;
const uint8_t *p;
// Fail messages for the wrong profile. This shouldn't happen, but better safe than sorry.
if (profileId != kWeaveProfile_NetworkProvisioning)
{
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->mCurOp != NULL)
{
WeaveServerBase::SendStatusReport(ec, kWeaveProfile_Common, Common::kStatus_Busy, WEAVE_NO_ERROR);
ec->Close();
ExitNow();
}
// Record that we have a request in process.
server->mCurOp = ec;
server->mCurOpType = msgType;
dataLen = payload->DataLength();
p = payload->Start();
// Decode and dispatch the message.
switch (msgType)
{
case kMsgType_ScanNetworks:
VerifyOrExit(dataLen >= 1, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
networkType = Get8(p);
PacketBuffer::Free(payload);
payload = NULL;
err = delegate->HandleScanNetworks(networkType);
break;
case kMsgType_AddNetwork:
VerifyOrExit(dataLen >= 1, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
err = delegate->HandleAddNetwork(payload);
payload = NULL;
break;
case kMsgType_UpdateNetwork:
VerifyOrExit(dataLen >= 1, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
err = delegate->HandleUpdateNetwork(payload);
payload = NULL;
break;
case kMsgType_RemoveNetwork:
case kMsgType_EnableNetwork:
case kMsgType_DisableNetwork:
case kMsgType_TestConnectivity:
VerifyOrExit(dataLen >= 4, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
networkId = LittleEndian::Read32(p);
PacketBuffer::Free(payload);
payload = NULL;
if (msgType == kMsgType_RemoveNetwork)
err = delegate->HandleRemoveNetwork(networkId);
else if (msgType == kMsgType_EnableNetwork)
err = delegate->HandleEnableNetwork(networkId);
else if (msgType == kMsgType_DisableNetwork)
err = delegate->HandleDisableNetwork(networkId);
else if (msgType == kMsgType_TestConnectivity)
err = delegate->HandleTestConnectivity(networkId);
break;
case kMsgType_GetNetworks:
VerifyOrExit(dataLen >= 1, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
flags = *p;
PacketBuffer::Free(payload);
payload = NULL;
#if WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
// According to Weave Device Access Control Policy,
// When servicing a GetNetworks message from a peer that has authenticated using PASE/PairingCode,
// a device in an unpaired state must reject the message with an access denied error if the peer has
// set the IncludeCredentials flag.
if (msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode && !delegate->IsPairedToAccount()
&& (flags & kGetNetwork_IncludeCredentials) != 0)
{
server->SendStatusReport(kWeaveProfile_Common, Common::kStatus_AccessDenied);
break;
}
#endif // WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
err = delegate->HandleGetNetworks(flags);
break;
case kMsgType_SetRendezvousMode:
VerifyOrExit(dataLen >= 2, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
rendezvousMode = LittleEndian::Read16(p);
PacketBuffer::Free(payload);
payload = NULL;
#if WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
// Per device access control policy, when servicing a SetRendezvousMode message from a peer that has authenticated using
// PASE/PairingCode, a device in a paired state should reject a SetRendezvousMode message with an access denied error if
// the requested mode is not 0--i.e. if the peer requests to *enable* any rendezvous mode.
if (rendezvousMode != 0 && msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode && delegate->IsPairedToAccount())
{
server->SendStatusReport(kWeaveProfile_Common, Common::kStatus_AccessDenied);
break;
}
#endif // WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
err = delegate->HandleSetRendezvousMode(rendezvousMode);
break;
case kMsgType_GetLastResult:
err = server->SendStatusReport(server->mLastOpResult.StatusProfileId, server->mLastOpResult.StatusCode, server->mLastOpResult.SysError);
break;
default:
server->SendStatusReport(kWeaveProfile_Common, Common::kStatus_BadRequest);
break;
}
exit:
if (err != WEAVE_NO_ERROR && server->mCurOp != NULL)
{
uint16_t statusCode =
(err == WEAVE_ERROR_INVALID_MESSAGE_LENGTH) ? Common::kStatus_BadRequest : Common::kStatus_InternalError;
server->SendStatusReport(kWeaveProfile_Common, statusCode, err);
}
if (payload != NULL)
PacketBuffer::Free(payload);
}
void NetworkProvisioningDelegate::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_ScanNetworks:
case kMsgType_AddNetwork:
case kMsgType_UpdateNetwork:
case kMsgType_RemoveNetwork:
case kMsgType_EnableNetwork:
case kMsgType_DisableNetwork:
case kMsgType_TestConnectivity:
case kMsgType_GetNetworks:
case kMsgType_GetLastResult:
#if WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
if (msgInfo->PeerAuthMode == kWeaveAuthMode_CASE_AccessToken ||
(msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode && !IsPairedToAccount()))
#endif // WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
{
result = kAccessControlResult_Accepted;
}
break;
case kMsgType_SetRendezvousMode:
#if WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
if (msgInfo->PeerAuthMode == kWeaveAuthMode_CASE_AccessToken ||
msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode)
#endif // WEAVE_CONFIG_REQUIRE_AUTH_NETWORK_PROV
{
result = kAccessControlResult_Accepted;
}
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);
}
// TODO: eliminate this method when device code provides appropriate implementations.
bool NetworkProvisioningDelegate::IsPairedToAccount() const
{
return false;
}
} // NetworkProvisioning
} // namespace Profiles
} // namespace Weave
} // namespace nl