/* * 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 "process.hpp" #include "ipmi.hpp" #include #include #include #include #include #include #include namespace blobs { /* Used by all commands with data. */ struct BmcRx { uint16_t crc; uint8_t data; /* one byte minimum of data. */ } __attribute__((packed)); static const std::unordered_map handlers = { {BlobOEMCommands::bmcBlobGetCount, getBlobCount}, {BlobOEMCommands::bmcBlobEnumerate, enumerateBlob}, {BlobOEMCommands::bmcBlobOpen, openBlob}, {BlobOEMCommands::bmcBlobRead, readBlob}, {BlobOEMCommands::bmcBlobWrite, writeBlob}, {BlobOEMCommands::bmcBlobCommit, commitBlob}, {BlobOEMCommands::bmcBlobClose, closeBlob}, {BlobOEMCommands::bmcBlobDelete, deleteBlob}, {BlobOEMCommands::bmcBlobStat, statBlob}, {BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob}, {BlobOEMCommands::bmcBlobWriteMeta, writeMeta}, }; IpmiBlobHandler validateBlobCommand(uint8_t cmd, std::span data) { size_t requestLength = data.size(); /* We know dataLen is at least 1 already */ auto command = static_cast(cmd); /* Validate it's at least well-formed. */ if (!validateRequestLength(command, requestLength)) { return [](ManagerInterface*, std::span) { return ipmi::responseReqDataLenInvalid(); }; } /* If there is a payload. */ if (requestLength > sizeof(cmd)) { /* Verify the request includes: command, crc16, data */ if (requestLength < sizeof(struct BmcRx)) { return [](ManagerInterface*, std::span) { return ipmi::responseReqDataLenInvalid(); }; } /* We don't include the command byte at offset 0 as part of the crc * payload area or the crc bytes at the beginning. */ size_t requestBodyLen = requestLength - 3; /* We start after the command byte. */ std::vector bytes(requestBodyLen); /* It likely has a well-formed payload. * Get the first two bytes of the request for crc. */ uint16_t crc; if (data.size() < sizeof(crc)) { return [](ManagerInterface*, std::span) { return ipmi::responseReqDataLenInvalid(); }; } std::memcpy(&crc, data.data(), sizeof(crc)); /* Set the in-place CRC to zero. * Remove the first two bytes for crc and get the reset of the request. */ data = data.subspan(sizeof(crc)); /* Crc expected but didn't match. */ if (crc != ipmiblob::generateCrc( std::vector(data.begin(), data.end()))) { return [](ManagerInterface*, std::span) { return ipmi::responseUnspecifiedError(); }; }; } /* Grab the corresponding handler for the command. */ auto found = handlers.find(command); if (found == handlers.end()) { return [](ManagerInterface*, std::span) { return ipmi::responseInvalidFieldRequest(); }; } return found->second; } Resp processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr, std::span data, size_t maxSize) { Resp result = cmd(mgr, data); if (std::get<0>(result) != ipmi::ccSuccess) { return result; } std::vector& response = std::get<0>( // std::variant> *std::get<1>(result)); size_t replyLength = response.size(); /* The command, whatever it was, returned success. */ if (replyLength == 0) { return result; } /* Read can return 0 bytes, and just a CRC, otherwise you need a CRC and 1 * byte, therefore the limit is 2 bytes. */ if (replyLength < (sizeof(uint16_t))) { return ipmi::responseUnspecifiedError(); } /* Make sure the reply size fits the ipmi buffer */ if (replyLength > maxSize) { return ipmi::responseResponseError(); } /* The command, whatever it was, replied, so let's set the CRC. */ std::span responseView = response; responseView = responseView.subspan(sizeof(uint16_t)); std::vector crcBuffer(responseView.begin(), responseView.end()); /* Copy the CRC into place. */ uint16_t crcValue = ipmiblob::generateCrc(crcBuffer); if (response.size() < sizeof(crcValue)) { return ipmi::responseReqDataLenInvalid(); } std::memcpy(response.data(), &crcValue, sizeof(crcValue)); return result; } Resp handleBlobCommand(uint8_t cmd, std::vector data, size_t maxSize) { /* on failure rc is set to the corresponding IPMI error. */ return processBlobCommand(validateBlobCommand(cmd, data), getBlobManager(), data, maxSize); } } // namespace blobs