/* * 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 #include #include #include 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 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(std::span data) { if (data.empty() || data.back() != '\0') { return std::string(); } // Last index is nul-terminator. return std::string(data.begin(), data.end() - 1); } Resp getBlobCount(ManagerInterface* mgr, std::span) { struct BmcBlobCountRx resp; resp.crc = 0; resp.blobCount = mgr->buildBlobList(); /* Copy the response into the reply buffer */ std::vector output(sizeof(BmcBlobCountRx), 0); std::memcpy(output.data(), &resp, sizeof(resp)); return ipmi::responseSuccess(output); } Resp enumerateBlob(ManagerInterface* mgr, std::span data) { /* Verify datalen is >= sizeof(request) */ struct BmcBlobEnumerateTx request; std::memcpy(&request, data.data(), sizeof(request)); std::string blobId = mgr->getBlobId(request.blobIdx); if (blobId.empty()) { return ipmi::responseInvalidFieldRequest(); } std::vector output(sizeof(BmcBlobEnumerateRx), 0); output.insert(output.end(), blobId.c_str(), blobId.c_str() + blobId.length() + 1); return ipmi::responseSuccess(output); } Resp openBlob(ManagerInterface* mgr, std::span data) { auto request = reinterpret_cast(data.data()); uint16_t session; std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobOpenTx))); if (path.empty()) { return ipmi::responseReqDataLenInvalid(); } /* Attempt to open. */ if (!mgr->open(request->flags, path, &session)) { return ipmi::responseUnspecifiedError(); } struct BmcBlobOpenRx reply; reply.crc = 0; reply.sessionId = session; std::vector output(sizeof(BmcBlobOpenRx), 0); std::memcpy(output.data(), &reply, sizeof(reply)); return ipmi::responseSuccess(output); } Resp closeBlob(ManagerInterface* mgr, std::span data) { struct BmcBlobCloseTx request; if (data.size() < sizeof(request)) { return ipmi::responseReqDataLenInvalid(); } std::memcpy(&request, data.data(), sizeof(request)); /* Attempt to close. */ if (!mgr->close(request.sessionId)) { return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(std::vector{}); } Resp deleteBlob(ManagerInterface* mgr, std::span data) { std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobDeleteTx))); if (path.empty()) { return ipmi::responseReqDataLenInvalid(); } /* Attempt to delete. */ if (!mgr->deleteBlob(path)) { return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(std::vector{}); } static Resp returnStatBlob(BlobMeta* meta) { struct BmcBlobStatRx reply; reply.crc = 0; reply.blobState = meta->blobState; reply.size = meta->size; reply.metadataLen = meta->metadata.size(); std::vector output(sizeof(BmcBlobStatRx), 0); std::memcpy(output.data(), &reply, sizeof(reply)); /* If there is metadata, insert it to output. */ if (!meta->metadata.empty()) { output.insert(output.end(), meta->metadata.begin(), meta->metadata.end()); } return ipmi::responseSuccess(output); } Resp statBlob(ManagerInterface* mgr, std::span data) { std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobStatTx))); if (path.empty()) { return ipmi::responseReqDataLenInvalid(); } /* Attempt to stat. */ BlobMeta meta; if (!mgr->stat(path, &meta)) { return ipmi::responseUnspecifiedError(); } return returnStatBlob(&meta); } Resp sessionStatBlob(ManagerInterface* mgr, std::span data) { struct BmcBlobSessionStatTx request; if (data.size() < sizeof(request)) { return ipmi::responseReqDataLenInvalid(); } std::memcpy(&request, data.data(), sizeof(request)); /* Attempt to stat. */ BlobMeta meta; if (!mgr->stat(request.sessionId, &meta)) { return ipmi::responseUnspecifiedError(); } return returnStatBlob(&meta); } Resp commitBlob(ManagerInterface* mgr, std::span data) { auto request = reinterpret_cast(data.data()); /* Sanity check the commitDataLen */ if (request->commitDataLen > (data.size() - sizeof(struct BmcBlobCommitTx))) { return ipmi::responseReqDataLenInvalid(); } data = data.subspan(sizeof(struct BmcBlobCommitTx), request->commitDataLen); if (!mgr->commit(request->sessionId, std::vector(data.begin(), data.end()))) { return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(std::vector{}); } Resp readBlob(ManagerInterface* mgr, std::span data) { struct BmcBlobReadTx request; if (data.size() < sizeof(request)) { return ipmi::responseReqDataLenInvalid(); } std::memcpy(&request, data.data(), sizeof(request)); /* TODO(venture): Verify requestedSize can fit in a returned IPMI packet. */ std::vector 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. */ std::vector output(sizeof(BmcBlobReadRx), 0); if (!result.empty()) { output.insert(output.end(), result.begin(), result.end()); } return ipmi::responseSuccess(output); } Resp writeBlob(ManagerInterface* mgr, std::span data) { auto request = reinterpret_cast(data.data()); data = data.subspan(sizeof(struct BmcBlobWriteTx)); /* Attempt to write the bytes. */ if (!mgr->write(request->sessionId, request->offset, std::vector(data.begin(), data.end()))) { return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(std::vector{}); } Resp writeMeta(ManagerInterface* mgr, std::span data) { struct BmcBlobWriteMetaTx request; if (data.size() < sizeof(request)) { return ipmi::responseReqDataLenInvalid(); } /* Copy over the request. */ std::memcpy(&request, data.data(), sizeof(request)); /* Nothing really else to validate, we just copy those bytes. */ data = data.subspan(sizeof(struct BmcBlobWriteMetaTx)); /* Attempt to write the bytes. */ if (!mgr->writeMeta(request.sessionId, request.offset, std::vector(data.begin(), data.end()))) { return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(std::vector{}); } } // namespace blobs