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