1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "process.hpp" 18 19 #include "ipmi.hpp" 20 21 #include <cstring> 22 #include <ipmiblob/crc.hpp> 23 #include <unordered_map> 24 #include <vector> 25 26 namespace blobs 27 { 28 29 /* Used by all commands with data. */ 30 struct BmcRx 31 { 32 uint8_t cmd; 33 uint16_t crc; 34 uint8_t data; /* one byte minimum of data. */ 35 } __attribute__((packed)); 36 37 static const std::unordered_map<BlobOEMCommands, IpmiBlobHandler> handlers = { 38 {BlobOEMCommands::bmcBlobGetCount, getBlobCount}, 39 {BlobOEMCommands::bmcBlobEnumerate, enumerateBlob}, 40 {BlobOEMCommands::bmcBlobOpen, openBlob}, 41 {BlobOEMCommands::bmcBlobRead, readBlob}, 42 {BlobOEMCommands::bmcBlobWrite, writeBlob}, 43 {BlobOEMCommands::bmcBlobCommit, commitBlob}, 44 {BlobOEMCommands::bmcBlobClose, closeBlob}, 45 {BlobOEMCommands::bmcBlobDelete, deleteBlob}, 46 {BlobOEMCommands::bmcBlobStat, statBlob}, 47 {BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob}, 48 {BlobOEMCommands::bmcBlobWriteMeta, writeMeta}, 49 }; 50 51 IpmiBlobHandler validateBlobCommand(const uint8_t* reqBuf, 52 uint8_t* /*replyCmdBuf*/, size_t* dataLen, 53 ipmi_ret_t* code) 54 { 55 size_t requestLength = (*dataLen); 56 /* We know dataLen is at least 1 already */ 57 auto command = static_cast<BlobOEMCommands>(reqBuf[0]); 58 59 /* Validate it's at least well-formed. */ 60 if (!validateRequestLength(command, requestLength)) 61 { 62 *code = IPMI_CC_REQ_DATA_LEN_INVALID; 63 return nullptr; 64 } 65 66 /* If there is a payload. */ 67 if (requestLength > sizeof(uint8_t)) 68 { 69 /* Verify the request includes: command, crc16, data */ 70 if (requestLength < sizeof(struct BmcRx)) 71 { 72 *code = IPMI_CC_REQ_DATA_LEN_INVALID; 73 return nullptr; 74 } 75 76 /* We don't include the command byte at offset 0 as part of the crc 77 * payload area or the crc bytes at the beginning. 78 */ 79 size_t requestBodyLen = requestLength - 3; 80 81 /* We start after the command byte. */ 82 std::vector<uint8_t> bytes(requestBodyLen); 83 84 /* It likely has a well-formed payload. */ 85 struct BmcRx request; 86 std::memcpy(&request, reqBuf, sizeof(request)); 87 uint16_t crcValue = request.crc; 88 89 /* Set the in-place CRC to zero. */ 90 std::memcpy(bytes.data(), &reqBuf[3], requestBodyLen); 91 92 /* Crc expected but didn't match. */ 93 if (crcValue != ipmiblob::generateCrc(bytes)) 94 { 95 *code = IPMI_CC_UNSPECIFIED_ERROR; 96 return nullptr; 97 } 98 } 99 100 /* Grab the corresponding handler for the command. */ 101 auto found = handlers.find(command); 102 if (found == handlers.end()) 103 { 104 *code = IPMI_CC_INVALID_FIELD_REQUEST; 105 return nullptr; 106 } 107 108 return found->second; 109 } 110 111 ipmi_ret_t processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr, 112 const uint8_t* reqBuf, uint8_t* replyCmdBuf, 113 size_t* dataLen) 114 { 115 ipmi_ret_t result = cmd(mgr, reqBuf, replyCmdBuf, dataLen); 116 if (result != IPMI_CC_OK) 117 { 118 return result; 119 } 120 121 size_t replyLength = (*dataLen); 122 123 /* The command, whatever it was, returned success. */ 124 if (replyLength == 0) 125 { 126 return result; 127 } 128 129 /* Read can return 0 bytes, and just a CRC, otherwise you need a CRC and 1 130 * byte, therefore the limit is 2 bytes. 131 */ 132 if (replyLength < (sizeof(uint16_t))) 133 { 134 return IPMI_CC_UNSPECIFIED_ERROR; 135 } 136 137 /* The command, whatever it was, replied, so let's set the CRC. */ 138 std::vector<std::uint8_t> crcBuffer(replyCmdBuf + sizeof(uint16_t), 139 replyCmdBuf + replyLength); 140 /* Copy the CRC into place. */ 141 uint16_t crcValue = ipmiblob::generateCrc(crcBuffer); 142 std::memcpy(replyCmdBuf, &crcValue, sizeof(crcValue)); 143 144 return result; 145 } 146 147 ipmi_ret_t handleBlobCommand(ipmi_cmd_t, const uint8_t* reqBuf, 148 uint8_t* replyCmdBuf, size_t* dataLen) 149 { 150 /* It's holding at least a sub-command. The OEN is trimmed from the bytes 151 * before this is called. 152 */ 153 if ((*dataLen) < 1) 154 { 155 return IPMI_CC_REQ_DATA_LEN_INVALID; 156 } 157 158 /* on failure rc is set to the corresponding IPMI error. */ 159 ipmi_ret_t rc = IPMI_CC_OK; 160 IpmiBlobHandler command = 161 validateBlobCommand(reqBuf, replyCmdBuf, dataLen, &rc); 162 if (command == nullptr) 163 { 164 (*dataLen) = 0; 165 return rc; 166 } 167 168 return processBlobCommand(command, getBlobManager(), reqBuf, replyCmdBuf, 169 dataLen); 170 } 171 172 } // namespace blobs 173