blob: e2e0feff90f0325041f848e77273ea972a51c393 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* 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.
*/
#include "ipmi.hpp"
#include <cstring>
#include <string>
#include <unordered_map>
namespace blobs
{
bool validateRequestLength(BlobOEMCommands command, size_t requestLen)
{
/* The smallest string is one letter and the nul-terminator. */
static const int kMinStrLen = 2;
static const std::unordered_map<BlobOEMCommands, size_t> minimumLengths = {
{BlobOEMCommands::bmcBlobEnumerate, sizeof(struct BmcBlobEnumerateTx)},
{BlobOEMCommands::bmcBlobOpen,
sizeof(struct BmcBlobOpenTx) + kMinStrLen},
{BlobOEMCommands::bmcBlobClose, sizeof(struct BmcBlobCloseTx)},
{BlobOEMCommands::bmcBlobDelete,
sizeof(struct BmcBlobDeleteTx) + kMinStrLen},
{BlobOEMCommands::bmcBlobStat,
sizeof(struct BmcBlobStatTx) + kMinStrLen},
{BlobOEMCommands::bmcBlobSessionStat,
sizeof(struct BmcBlobSessionStatTx)},
{BlobOEMCommands::bmcBlobCommit, sizeof(struct BmcBlobCommitTx)},
{BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)},
{BlobOEMCommands::bmcBlobWrite,
sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)},
{BlobOEMCommands::bmcBlobWriteMeta,
sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)},
};
auto results = minimumLengths.find(command);
if (results == minimumLengths.end())
{
/* Valid length by default if we don't care. */
return true;
}
/* If the request is shorter than the minimum, it's invalid. */
if (requestLen < results->second)
{
return false;
}
return true;
}
std::string stringFromBuffer(const char* start, size_t length)
{
if (!start)
{
return "";
}
auto end = static_cast<const char*>(std::memchr(start, '\0', length));
if (end != &start[length - 1])
{
return "";
}
return (end == nullptr) ? std::string() : std::string(start, end);
}
ipmi_ret_t getBlobCount(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
struct BmcBlobCountRx resp;
resp.crc = 0;
resp.blobCount = mgr->buildBlobList();
/* Copy the response into the reply buffer */
std::memcpy(replyCmdBuf, &resp, sizeof(resp));
(*dataLen) = sizeof(resp);
return IPMI_CC_OK;
}
ipmi_ret_t enumerateBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
/* Verify datalen is >= sizeof(request) */
struct BmcBlobEnumerateTx request;
auto reply = reinterpret_cast<struct BmcBlobEnumerateRx*>(replyCmdBuf);
std::memcpy(&request, reqBuf, sizeof(request));
std::string blobId = mgr->getBlobId(request.blobIdx);
if (blobId == "")
{
return IPMI_CC_INVALID_FIELD_REQUEST;
}
/* TODO(venture): Need to do a hard-code check against the maximum
* reply buffer size. */
reply->crc = 0;
/* Explicilty copies the NUL-terminator. */
std::memcpy(&reply->blobId, blobId.c_str(), blobId.length() + 1);
(*dataLen) = sizeof(reply->crc) + blobId.length() + 1;
return IPMI_CC_OK;
}
ipmi_ret_t openBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
size_t requestLen = (*dataLen);
auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(reqBuf);
uint16_t session;
std::string path = stringFromBuffer(
request->blobId, (requestLen - sizeof(struct BmcBlobOpenTx)));
if (path.empty())
{
return IPMI_CC_REQ_DATA_LEN_INVALID;
}
/* Attempt to open. */
if (!mgr->open(request->flags, path, &session))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
struct BmcBlobOpenRx reply;
reply.crc = 0;
reply.sessionId = session;
std::memcpy(replyCmdBuf, &reply, sizeof(reply));
(*dataLen) = sizeof(reply);
return IPMI_CC_OK;
}
ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
struct BmcBlobCloseTx request;
std::memcpy(&request, reqBuf, sizeof(request));
/* Attempt to close. */
if (!mgr->close(request.sessionId))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
(*dataLen) = 0;
return IPMI_CC_OK;
}
ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
size_t requestLen = (*dataLen);
auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf);
std::string path = stringFromBuffer(
request->blobId, (requestLen - sizeof(struct BmcBlobDeleteTx)));
if (path.empty())
{
return IPMI_CC_REQ_DATA_LEN_INVALID;
}
/* Attempt to delete. */
if (!mgr->deleteBlob(path))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
(*dataLen) = 0;
return IPMI_CC_OK;
}
static ipmi_ret_t returnStatBlob(struct BlobMeta* meta, uint8_t* replyCmdBuf,
size_t* dataLen)
{
struct BmcBlobStatRx reply;
reply.crc = 0;
reply.blobState = meta->blobState;
reply.size = meta->size;
reply.metadataLen = meta->metadata.size();
std::memcpy(replyCmdBuf, &reply, sizeof(reply));
/* If there is metadata, copy it over. */
if (meta->metadata.size())
{
uint8_t* metadata = &replyCmdBuf[sizeof(reply)];
std::memcpy(metadata, meta->metadata.data(), reply.metadataLen);
}
(*dataLen) = sizeof(reply) + reply.metadataLen;
return IPMI_CC_OK;
}
ipmi_ret_t statBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
size_t requestLen = (*dataLen);
auto request = reinterpret_cast<const struct BmcBlobStatTx*>(reqBuf);
std::string path = stringFromBuffer(
request->blobId, (requestLen - sizeof(struct BmcBlobStatTx)));
if (path.empty())
{
return IPMI_CC_REQ_DATA_LEN_INVALID;
}
/* Attempt to stat. */
struct BlobMeta meta;
if (!mgr->stat(path, &meta))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
return returnStatBlob(&meta, replyCmdBuf, dataLen);
}
ipmi_ret_t sessionStatBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
struct BmcBlobSessionStatTx request;
std::memcpy(&request, reqBuf, sizeof(request));
/* Attempt to stat. */
struct BlobMeta meta;
if (!mgr->stat(request.sessionId, &meta))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
return returnStatBlob(&meta, replyCmdBuf, dataLen);
}
ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
size_t requestLen = (*dataLen);
auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf);
/* Sanity check the commitDataLen */
if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx)))
{
return IPMI_CC_REQ_DATA_LEN_INVALID;
}
std::vector<uint8_t> data(request->commitDataLen);
std::memcpy(data.data(), request->commitData, request->commitDataLen);
if (!mgr->commit(request->sessionId, data))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
(*dataLen) = 0;
return IPMI_CC_OK;
}
ipmi_ret_t readBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
struct BmcBlobReadTx request;
std::memcpy(&request, reqBuf, sizeof(request));
/* TODO(venture): Verify requestedSize can fit in a returned IPMI packet.
*/
std::vector<uint8_t> result =
mgr->read(request.sessionId, request.offset, request.requestedSize);
/* If the Read fails, it returns success but with only the crc and 0 bytes
* of data.
* If there was data returned, copy into the reply buffer.
*/
(*dataLen) = sizeof(struct BmcBlobReadRx);
if (result.size())
{
uint8_t* output = &replyCmdBuf[sizeof(struct BmcBlobReadRx)];
std::memcpy(output, result.data(), result.size());
(*dataLen) = sizeof(struct BmcBlobReadRx) + result.size();
}
return IPMI_CC_OK;
}
ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
size_t requestLen = (*dataLen);
auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf);
uint32_t size = requestLen - sizeof(struct BmcBlobWriteTx);
std::vector<uint8_t> data(size);
std::memcpy(data.data(), request->data, size);
/* Attempt to write the bytes. */
if (!mgr->write(request->sessionId, request->offset, data))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
return IPMI_CC_OK;
}
ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
size_t requestLen = (*dataLen);
struct BmcBlobWriteMetaTx request;
/* Copy over the request. */
std::memcpy(&request, reqBuf, sizeof(request));
/* Determine number of bytes of metadata to write. */
uint32_t size = requestLen - sizeof(request);
/* Nothing really else to validate, we just copy those bytes. */
std::vector<uint8_t> data(size);
std::memcpy(data.data(), &reqBuf[sizeof(request)], size);
/* Attempt to write the bytes. */
if (!mgr->writeMeta(request.sessionId, request.offset, data))
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
return IPMI_CC_OK;
}
} // namespace blobs