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* reqBuf, 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 103 std::memcpy(&request, reqBuf, sizeof(request)); 104 105 std::string blobId = mgr->getBlobId(request.blobIdx); 106 if (blobId == "") 107 { 108 return IPMI_CC_INVALID; 109 } 110 111 /* TODO(venture): Need to do a hard-code check against the maximum 112 * reply buffer size. */ 113 reply->crc = 0; 114 /* Explicilty copies the NUL-terminator. */ 115 std::memcpy(&reply->blobId, blobId.c_str(), blobId.length() + 1); 116 117 (*dataLen) = sizeof(reply->crc) + blobId.length() + 1; 118 119 return IPMI_CC_OK; 120 } 121 122 ipmi_ret_t openBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 123 uint8_t* replyCmdBuf, size_t* dataLen) 124 { 125 size_t requestLen = (*dataLen); 126 auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(reqBuf); 127 uint16_t session; 128 129 std::string path = stringFromBuffer( 130 request->blobId, (requestLen - sizeof(struct BmcBlobOpenTx))); 131 if (path.empty()) 132 { 133 return IPMI_CC_INVALID; 134 } 135 136 /* Attempt to open. */ 137 if (!mgr->open(request->flags, path, &session)) 138 { 139 return IPMI_CC_INVALID; 140 } 141 142 struct BmcBlobOpenRx reply; 143 reply.crc = 0; 144 reply.sessionId = session; 145 146 std::memcpy(replyCmdBuf, &reply, sizeof(reply)); 147 (*dataLen) = sizeof(reply); 148 149 return IPMI_CC_OK; 150 } 151 152 ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 153 uint8_t* replyCmdBuf, size_t* dataLen) 154 { 155 struct BmcBlobCloseTx request; 156 std::memcpy(&request, reqBuf, sizeof(request)); 157 158 /* Attempt to close. */ 159 if (!mgr->close(request.sessionId)) 160 { 161 return IPMI_CC_INVALID; 162 } 163 164 (*dataLen) = 0; 165 return IPMI_CC_OK; 166 } 167 168 ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 169 uint8_t* replyCmdBuf, size_t* dataLen) 170 { 171 size_t requestLen = (*dataLen); 172 auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf); 173 174 std::string path = stringFromBuffer( 175 request->blobId, (requestLen - sizeof(struct BmcBlobDeleteTx))); 176 if (path.empty()) 177 { 178 return IPMI_CC_INVALID; 179 } 180 181 /* Attempt to delete. */ 182 if (!mgr->deleteBlob(path)) 183 { 184 return IPMI_CC_INVALID; 185 } 186 187 (*dataLen) = 0; 188 return IPMI_CC_OK; 189 } 190 191 static ipmi_ret_t returnStatBlob(struct BlobMeta* meta, uint8_t* replyCmdBuf, 192 size_t* dataLen) 193 { 194 struct BmcBlobStatRx reply; 195 reply.crc = 0; 196 reply.blobState = meta->blobState; 197 reply.size = meta->size; 198 reply.metadataLen = meta->metadata.size(); 199 200 std::memcpy(replyCmdBuf, &reply, sizeof(reply)); 201 202 /* If there is metadata, copy it over. */ 203 if (meta->metadata.size()) 204 { 205 uint8_t* metadata = &replyCmdBuf[sizeof(reply)]; 206 std::memcpy(metadata, meta->metadata.data(), reply.metadataLen); 207 } 208 209 (*dataLen) = sizeof(reply) + reply.metadataLen; 210 return IPMI_CC_OK; 211 } 212 213 ipmi_ret_t statBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 214 uint8_t* replyCmdBuf, size_t* dataLen) 215 { 216 size_t requestLen = (*dataLen); 217 auto request = reinterpret_cast<const struct BmcBlobStatTx*>(reqBuf); 218 219 std::string path = stringFromBuffer( 220 request->blobId, (requestLen - sizeof(struct BmcBlobStatTx))); 221 if (path.empty()) 222 { 223 return IPMI_CC_INVALID; 224 } 225 226 /* Attempt to stat. */ 227 struct BlobMeta meta; 228 if (!mgr->stat(path, &meta)) 229 { 230 return IPMI_CC_INVALID; 231 } 232 233 return returnStatBlob(&meta, replyCmdBuf, dataLen); 234 } 235 236 ipmi_ret_t sessionStatBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 237 uint8_t* replyCmdBuf, size_t* dataLen) 238 { 239 struct BmcBlobSessionStatTx request; 240 std::memcpy(&request, reqBuf, sizeof(request)); 241 242 /* Attempt to stat. */ 243 struct BlobMeta meta; 244 245 if (!mgr->stat(request.sessionId, &meta)) 246 { 247 return IPMI_CC_INVALID; 248 } 249 250 return returnStatBlob(&meta, replyCmdBuf, dataLen); 251 } 252 253 ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 254 uint8_t* replyCmdBuf, size_t* dataLen) 255 { 256 size_t requestLen = (*dataLen); 257 auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf); 258 259 /* Sanity check the commitDataLen */ 260 if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx))) 261 { 262 return IPMI_CC_INVALID; 263 } 264 265 std::vector<uint8_t> data(request->commitDataLen); 266 std::memcpy(data.data(), request->commitData, request->commitDataLen); 267 268 if (!mgr->commit(request->sessionId, data)) 269 { 270 return IPMI_CC_INVALID; 271 } 272 273 (*dataLen) = 0; 274 return IPMI_CC_OK; 275 } 276 277 ipmi_ret_t readBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 278 uint8_t* replyCmdBuf, size_t* dataLen) 279 { 280 struct BmcBlobReadTx request; 281 std::memcpy(&request, reqBuf, sizeof(request)); 282 283 /* TODO(venture): Verify requestedSize can fit in a returned IPMI packet. 284 */ 285 286 std::vector<uint8_t> result = 287 mgr->read(request.sessionId, request.offset, request.requestedSize); 288 289 /* If the Read fails, it returns success but with only the crc and 0 bytes 290 * of data. 291 * If there was data returned, copy into the reply buffer. 292 */ 293 (*dataLen) = sizeof(struct BmcBlobReadRx); 294 295 if (result.size()) 296 { 297 uint8_t* output = &replyCmdBuf[sizeof(struct BmcBlobReadRx)]; 298 std::memcpy(output, result.data(), result.size()); 299 300 (*dataLen) = sizeof(struct BmcBlobReadRx) + result.size(); 301 } 302 303 return IPMI_CC_OK; 304 } 305 306 ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, 307 uint8_t* replyCmdBuf, size_t* dataLen) 308 { 309 size_t requestLen = (*dataLen); 310 auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf); 311 312 uint32_t size = requestLen - sizeof(struct BmcBlobWriteTx); 313 std::vector<uint8_t> data(size); 314 315 std::memcpy(data.data(), request->data, size); 316 317 /* Attempt to write the bytes. */ 318 if (!mgr->write(request->sessionId, request->offset, data)) 319 { 320 return IPMI_CC_INVALID; 321 } 322 323 return IPMI_CC_OK; 324 } 325 326 ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf, 327 uint8_t* replyCmdBuf, size_t* dataLen) 328 { 329 size_t requestLen = (*dataLen); 330 struct BmcBlobWriteMetaTx request; 331 332 /* Copy over the request. */ 333 std::memcpy(&request, reqBuf, sizeof(request)); 334 335 /* Determine number of bytes of metadata to write. */ 336 uint32_t size = requestLen - sizeof(request); 337 338 /* Nothing really else to validate, we just copy those bytes. */ 339 std::vector<uint8_t> data(size); 340 std::memcpy(data.data(), &reqBuf[sizeof(request)], size); 341 342 /* Attempt to write the bytes. */ 343 if (!mgr->writeMeta(request.sessionId, request.offset, data)) 344 { 345 return IPMI_CC_INVALID; 346 } 347 348 return IPMI_CC_OK; 349 } 350 351 } // namespace blobs 352