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