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 "ipmi.hpp" 18 19 #include <cstring> 20 #include <span> 21 #include <string> 22 #include <unordered_map> 23 24 namespace blobs 25 { 26 27 bool validateRequestLength(BlobOEMCommands command, size_t requestLen) 28 { 29 /* The smallest string is one letter and the nul-terminator. */ 30 static const int kMinStrLen = 2; 31 32 static const std::unordered_map<BlobOEMCommands, size_t> minimumLengths = { 33 {BlobOEMCommands::bmcBlobEnumerate, sizeof(struct BmcBlobEnumerateTx)}, 34 {BlobOEMCommands::bmcBlobOpen, 35 sizeof(struct BmcBlobOpenTx) + kMinStrLen}, 36 {BlobOEMCommands::bmcBlobClose, sizeof(struct BmcBlobCloseTx)}, 37 {BlobOEMCommands::bmcBlobDelete, 38 sizeof(struct BmcBlobDeleteTx) + kMinStrLen}, 39 {BlobOEMCommands::bmcBlobStat, 40 sizeof(struct BmcBlobStatTx) + kMinStrLen}, 41 {BlobOEMCommands::bmcBlobSessionStat, 42 sizeof(struct BmcBlobSessionStatTx)}, 43 {BlobOEMCommands::bmcBlobCommit, sizeof(struct BmcBlobCommitTx)}, 44 {BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)}, 45 {BlobOEMCommands::bmcBlobWrite, 46 sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)}, 47 {BlobOEMCommands::bmcBlobWriteMeta, 48 sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)}, 49 }; 50 51 auto results = minimumLengths.find(command); 52 if (results == minimumLengths.end()) 53 { 54 /* Valid length by default if we don't care. */ 55 return true; 56 } 57 58 /* If the request is shorter than the minimum, it's invalid. */ 59 if (requestLen < results->second) 60 { 61 return false; 62 } 63 64 return true; 65 } 66 67 std::string stringFromBuffer(std::span<const uint8_t> data) 68 { 69 if (data.empty() || data.back() != '\0') 70 { 71 return std::string(); 72 } 73 74 // Last index is nul-terminator. 75 return std::string(data.begin(), data.end() - 1); 76 } 77 78 Resp getBlobCount(ManagerInterface* mgr, std::span<const uint8_t>) 79 { 80 struct BmcBlobCountRx resp; 81 resp.crc = 0; 82 resp.blobCount = mgr->buildBlobList(); 83 84 /* Copy the response into the reply buffer */ 85 std::vector<uint8_t> output(sizeof(BmcBlobCountRx), 0); 86 std::memcpy(output.data(), &resp, sizeof(resp)); 87 88 return ipmi::responseSuccess(output); 89 } 90 91 Resp enumerateBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 92 { 93 /* Verify datalen is >= sizeof(request) */ 94 struct BmcBlobEnumerateTx request; 95 96 std::memcpy(&request, data.data(), sizeof(request)); 97 98 std::string blobId = mgr->getBlobId(request.blobIdx); 99 if (blobId.empty()) 100 { 101 return ipmi::responseInvalidFieldRequest(); 102 } 103 104 std::vector<uint8_t> output(sizeof(BmcBlobEnumerateRx), 0); 105 output.insert(output.end(), blobId.c_str(), 106 blobId.c_str() + blobId.length() + 1); 107 return ipmi::responseSuccess(output); 108 } 109 110 Resp openBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 111 { 112 auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(data.data()); 113 uint16_t session; 114 115 std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobOpenTx))); 116 if (path.empty()) 117 { 118 return ipmi::responseReqDataLenInvalid(); 119 } 120 121 /* Attempt to open. */ 122 if (!mgr->open(request->flags, path, &session)) 123 { 124 return ipmi::responseUnspecifiedError(); 125 } 126 127 struct BmcBlobOpenRx reply; 128 reply.crc = 0; 129 reply.sessionId = session; 130 131 std::vector<uint8_t> output(sizeof(BmcBlobOpenRx), 0); 132 std::memcpy(output.data(), &reply, sizeof(reply)); 133 return ipmi::responseSuccess(output); 134 } 135 136 Resp closeBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 137 { 138 struct BmcBlobCloseTx request; 139 if (data.size() < sizeof(request)) 140 { 141 return ipmi::responseReqDataLenInvalid(); 142 } 143 std::memcpy(&request, data.data(), sizeof(request)); 144 145 /* Attempt to close. */ 146 if (!mgr->close(request.sessionId)) 147 { 148 return ipmi::responseUnspecifiedError(); 149 } 150 151 return ipmi::responseSuccess(std::vector<uint8_t>{}); 152 } 153 154 Resp deleteBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 155 { 156 std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobDeleteTx))); 157 if (path.empty()) 158 { 159 return ipmi::responseReqDataLenInvalid(); 160 } 161 162 /* Attempt to delete. */ 163 if (!mgr->deleteBlob(path)) 164 { 165 return ipmi::responseUnspecifiedError(); 166 } 167 168 return ipmi::responseSuccess(std::vector<uint8_t>{}); 169 } 170 171 static Resp returnStatBlob(BlobMeta* meta) 172 { 173 struct BmcBlobStatRx reply; 174 reply.crc = 0; 175 reply.blobState = meta->blobState; 176 reply.size = meta->size; 177 reply.metadataLen = meta->metadata.size(); 178 179 std::vector<uint8_t> output(sizeof(BmcBlobStatRx), 0); 180 std::memcpy(output.data(), &reply, sizeof(reply)); 181 182 /* If there is metadata, insert it to output. */ 183 if (!meta->metadata.empty()) 184 { 185 output.insert(output.end(), meta->metadata.begin(), 186 meta->metadata.end()); 187 } 188 return ipmi::responseSuccess(output); 189 } 190 191 Resp statBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 192 { 193 std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobStatTx))); 194 if (path.empty()) 195 { 196 return ipmi::responseReqDataLenInvalid(); 197 } 198 199 /* Attempt to stat. */ 200 BlobMeta meta; 201 if (!mgr->stat(path, &meta)) 202 { 203 return ipmi::responseUnspecifiedError(); 204 } 205 206 return returnStatBlob(&meta); 207 } 208 209 Resp sessionStatBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 210 { 211 struct BmcBlobSessionStatTx request; 212 if (data.size() < sizeof(request)) 213 { 214 return ipmi::responseReqDataLenInvalid(); 215 } 216 std::memcpy(&request, data.data(), sizeof(request)); 217 218 /* Attempt to stat. */ 219 BlobMeta meta; 220 221 if (!mgr->stat(request.sessionId, &meta)) 222 { 223 return ipmi::responseUnspecifiedError(); 224 } 225 226 return returnStatBlob(&meta); 227 } 228 229 Resp commitBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 230 { 231 auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(data.data()); 232 233 /* Sanity check the commitDataLen */ 234 if (request->commitDataLen > (data.size() - sizeof(struct BmcBlobCommitTx))) 235 { 236 return ipmi::responseReqDataLenInvalid(); 237 } 238 239 data = data.subspan(sizeof(struct BmcBlobCommitTx), request->commitDataLen); 240 241 if (!mgr->commit(request->sessionId, 242 std::vector<uint8_t>(data.begin(), data.end()))) 243 { 244 return ipmi::responseUnspecifiedError(); 245 } 246 247 return ipmi::responseSuccess(std::vector<uint8_t>{}); 248 } 249 250 Resp readBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 251 { 252 struct BmcBlobReadTx request; 253 if (data.size() < sizeof(request)) 254 { 255 return ipmi::responseReqDataLenInvalid(); 256 } 257 std::memcpy(&request, data.data(), sizeof(request)); 258 259 /* TODO(venture): Verify requestedSize can fit in a returned IPMI packet. 260 */ 261 262 std::vector<uint8_t> result = 263 mgr->read(request.sessionId, request.offset, request.requestedSize); 264 265 /* If the Read fails, it returns success but with only the crc and 0 bytes 266 * of data. 267 * If there was data returned, copy into the reply buffer. 268 */ 269 std::vector<uint8_t> output(sizeof(BmcBlobReadRx), 0); 270 271 if (!result.empty()) 272 { 273 output.insert(output.end(), result.begin(), result.end()); 274 } 275 276 return ipmi::responseSuccess(output); 277 } 278 279 Resp writeBlob(ManagerInterface* mgr, std::span<const uint8_t> data) 280 { 281 auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(data.data()); 282 data = data.subspan(sizeof(struct BmcBlobWriteTx)); 283 284 /* Attempt to write the bytes. */ 285 if (!mgr->write(request->sessionId, request->offset, 286 std::vector<uint8_t>(data.begin(), data.end()))) 287 { 288 return ipmi::responseUnspecifiedError(); 289 } 290 291 return ipmi::responseSuccess(std::vector<uint8_t>{}); 292 } 293 294 Resp writeMeta(ManagerInterface* mgr, std::span<const uint8_t> data) 295 { 296 struct BmcBlobWriteMetaTx request; 297 if (data.size() < sizeof(request)) 298 { 299 return ipmi::responseReqDataLenInvalid(); 300 } 301 302 /* Copy over the request. */ 303 std::memcpy(&request, data.data(), sizeof(request)); 304 305 /* Nothing really else to validate, we just copy those bytes. */ 306 data = data.subspan(sizeof(struct BmcBlobWriteMetaTx)); 307 308 /* Attempt to write the bytes. */ 309 if (!mgr->writeMeta(request.sessionId, request.offset, 310 std::vector<uint8_t>(data.begin(), data.end()))) 311 { 312 return ipmi::responseUnspecifiedError(); 313 } 314 315 return ipmi::responseSuccess(std::vector<uint8_t>{}); 316 } 317 318 } // namespace blobs 319