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