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