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 <ipmiblob/crc.hpp> 22 #include <ipmid/api-types.hpp> 23 24 #include <cstring> 25 #include <span> 26 #include <unordered_map> 27 #include <utility> 28 #include <vector> 29 30 namespace blobs 31 { 32 33 /* Used by all commands with data. */ 34 struct BmcRx 35 { 36 uint16_t crc; 37 uint8_t data; /* one byte minimum of data. */ 38 } __attribute__((packed)); 39 40 static const std::unordered_map<BlobOEMCommands, IpmiBlobHandler> handlers = { 41 {BlobOEMCommands::bmcBlobGetCount, getBlobCount}, 42 {BlobOEMCommands::bmcBlobEnumerate, enumerateBlob}, 43 {BlobOEMCommands::bmcBlobOpen, openBlob}, 44 {BlobOEMCommands::bmcBlobRead, readBlob}, 45 {BlobOEMCommands::bmcBlobWrite, writeBlob}, 46 {BlobOEMCommands::bmcBlobCommit, commitBlob}, 47 {BlobOEMCommands::bmcBlobClose, closeBlob}, 48 {BlobOEMCommands::bmcBlobDelete, deleteBlob}, 49 {BlobOEMCommands::bmcBlobStat, statBlob}, 50 {BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob}, 51 {BlobOEMCommands::bmcBlobWriteMeta, writeMeta}, 52 }; 53 54 IpmiBlobHandler validateBlobCommand(uint8_t cmd, std::span<const uint8_t> data) 55 { 56 size_t requestLength = data.size(); 57 /* We know dataLen is at least 1 already */ 58 auto command = static_cast<BlobOEMCommands>(cmd); 59 60 /* Validate it's at least well-formed. */ 61 if (!validateRequestLength(command, requestLength)) 62 { 63 return [](ManagerInterface*, std::span<const uint8_t>) { 64 return ipmi::responseReqDataLenInvalid(); 65 }; 66 } 67 68 /* If there is a payload. */ 69 if (requestLength > sizeof(cmd)) 70 { 71 /* Verify the request includes: command, crc16, data */ 72 if (requestLength < sizeof(struct BmcRx)) 73 { 74 return [](ManagerInterface*, std::span<const uint8_t>) { 75 return ipmi::responseReqDataLenInvalid(); 76 }; 77 } 78 79 /* We don't include the command byte at offset 0 as part of the crc 80 * payload area or the crc bytes at the beginning. 81 */ 82 size_t requestBodyLen = requestLength - 3; 83 84 /* We start after the command byte. */ 85 std::vector<uint8_t> bytes(requestBodyLen); 86 87 /* It likely has a well-formed payload. 88 * Get the first two bytes of the request for crc. 89 */ 90 uint16_t crc; 91 if (data.size() < sizeof(crc)) 92 { 93 return [](ManagerInterface*, std::span<const uint8_t>) { 94 return ipmi::responseReqDataLenInvalid(); 95 }; 96 } 97 std::memcpy(&crc, data.data(), sizeof(crc)); 98 99 /* Set the in-place CRC to zero. 100 * Remove the first two bytes for crc and get the reset of the request. 101 */ 102 data = data.subspan(sizeof(crc)); 103 104 /* Crc expected but didn't match. */ 105 if (crc != ipmiblob::generateCrc( 106 std::vector<uint8_t>(data.begin(), data.end()))) 107 { 108 return [](ManagerInterface*, std::span<const uint8_t>) { 109 return ipmi::responseUnspecifiedError(); 110 }; 111 }; 112 } 113 114 /* Grab the corresponding handler for the command. */ 115 auto found = handlers.find(command); 116 if (found == handlers.end()) 117 { 118 return [](ManagerInterface*, std::span<const uint8_t>) { 119 return ipmi::responseInvalidFieldRequest(); 120 }; 121 } 122 123 return found->second; 124 } 125 126 Resp processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr, 127 std::span<const uint8_t> data, size_t maxSize) 128 { 129 Resp result = cmd(mgr, data); 130 if (std::get<0>(result) != ipmi::ccSuccess) 131 { 132 return result; 133 } 134 135 std::vector<uint8_t>& response = std::get<0>( 136 // std::variant<std::vector<uint8_t>> 137 *std::get<1>(result)); 138 size_t replyLength = response.size(); 139 140 /* The command, whatever it was, returned success. */ 141 if (replyLength == 0) 142 { 143 return result; 144 } 145 146 /* Read can return 0 bytes, and just a CRC, otherwise you need a CRC and 1 147 * byte, therefore the limit is 2 bytes. 148 */ 149 if (replyLength < (sizeof(uint16_t))) 150 { 151 return ipmi::responseUnspecifiedError(); 152 } 153 154 /* Make sure the reply size fits the ipmi buffer */ 155 if (replyLength > maxSize) 156 { 157 return ipmi::responseResponseError(); 158 } 159 160 /* The command, whatever it was, replied, so let's set the CRC. */ 161 std::span<const uint8_t> responseView = response; 162 responseView = responseView.subspan(sizeof(uint16_t)); 163 std::vector<std::uint8_t> crcBuffer(responseView.begin(), 164 responseView.end()); 165 /* Copy the CRC into place. */ 166 uint16_t crcValue = ipmiblob::generateCrc(crcBuffer); 167 if (response.size() < sizeof(crcValue)) 168 { 169 return ipmi::responseReqDataLenInvalid(); 170 } 171 std::memcpy(response.data(), &crcValue, sizeof(crcValue)); 172 173 return result; 174 } 175 176 Resp handleBlobCommand(uint8_t cmd, std::vector<uint8_t> data, size_t maxSize) 177 { 178 /* on failure rc is set to the corresponding IPMI error. */ 179 return processBlobCommand(validateBlobCommand(cmd, data), getBlobManager(), 180 data, maxSize); 181 } 182 183 } // namespace blobs 184