blob: 92955f9b583c4bf452eef810945f4f90ab714d3d [file] [log] [blame]
Patrick Ventureef3aead2018-09-12 08:53:29 -07001/*
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
23namespace blobs
24{
25
26bool 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)},
Patrick Venture5c4b17b2018-10-04 10:32:22 -070046 {BlobOEMCommands::bmcBlobWriteMeta,
47 sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)},
Patrick Ventureef3aead2018-09-12 08:53:29 -070048 };
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
66std::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
82ipmi_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
96ipmi_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);
Patrick Venture50539d32018-12-03 09:01:55 -0800102 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700103
104 std::memcpy(&request, reqBuf, sizeof(request));
105
106 std::string blobId = mgr->getBlobId(request.blobIdx);
107 if (blobId == "")
108 {
Patrick Venture41258802018-11-12 10:46:30 -0800109 return IPMI_CC_INVALID_FIELD_REQUEST;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700110 }
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
123ipmi_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;
Patrick Venture50539d32018-12-03 09:01:55 -0800129 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700130
131 std::string path = stringFromBuffer(
132 request->blobId, (requestLen - sizeof(struct BmcBlobOpenTx)));
133 if (path.empty())
134 {
Patrick Venture41258802018-11-12 10:46:30 -0800135 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700136 }
137
138 /* Attempt to open. */
139 if (!mgr->open(request->flags, path, &session))
140 {
Patrick Venture41258802018-11-12 10:46:30 -0800141 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700142 }
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
154ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
155 uint8_t* replyCmdBuf, size_t* dataLen)
156{
157 struct BmcBlobCloseTx request;
158 std::memcpy(&request, reqBuf, sizeof(request));
Patrick Venture50539d32018-12-03 09:01:55 -0800159 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700160
161 /* Attempt to close. */
162 if (!mgr->close(request.sessionId))
163 {
Patrick Venture41258802018-11-12 10:46:30 -0800164 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700165 }
166
Patrick Ventureef3aead2018-09-12 08:53:29 -0700167 return IPMI_CC_OK;
168}
169
170ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
171 uint8_t* replyCmdBuf, size_t* dataLen)
172{
173 size_t requestLen = (*dataLen);
174 auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf);
Patrick Venture50539d32018-12-03 09:01:55 -0800175 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700176
177 std::string path = stringFromBuffer(
178 request->blobId, (requestLen - sizeof(struct BmcBlobDeleteTx)));
179 if (path.empty())
180 {
Patrick Venture41258802018-11-12 10:46:30 -0800181 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700182 }
183
184 /* Attempt to delete. */
185 if (!mgr->deleteBlob(path))
186 {
Patrick Venture41258802018-11-12 10:46:30 -0800187 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700188 }
189
Patrick Ventureef3aead2018-09-12 08:53:29 -0700190 return IPMI_CC_OK;
191}
192
193static ipmi_ret_t returnStatBlob(struct 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
215ipmi_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);
Patrick Venture50539d32018-12-03 09:01:55 -0800220 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700221
222 std::string path = stringFromBuffer(
223 request->blobId, (requestLen - sizeof(struct BmcBlobStatTx)));
224 if (path.empty())
225 {
Patrick Venture41258802018-11-12 10:46:30 -0800226 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700227 }
228
229 /* Attempt to stat. */
230 struct BlobMeta meta;
231 if (!mgr->stat(path, &meta))
232 {
Patrick Venture41258802018-11-12 10:46:30 -0800233 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700234 }
235
236 return returnStatBlob(&meta, replyCmdBuf, dataLen);
237}
238
239ipmi_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));
Patrick Venture50539d32018-12-03 09:01:55 -0800244 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700245
246 /* Attempt to stat. */
247 struct BlobMeta meta;
248
249 if (!mgr->stat(request.sessionId, &meta))
250 {
Patrick Venture41258802018-11-12 10:46:30 -0800251 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700252 }
253
254 return returnStatBlob(&meta, replyCmdBuf, dataLen);
255}
256
257ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
258 uint8_t* replyCmdBuf, size_t* dataLen)
259{
260 size_t requestLen = (*dataLen);
261 auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf);
Patrick Venture50539d32018-12-03 09:01:55 -0800262 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700263
264 /* Sanity check the commitDataLen */
265 if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx)))
266 {
Patrick Venture41258802018-11-12 10:46:30 -0800267 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700268 }
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 {
Patrick Venture41258802018-11-12 10:46:30 -0800275 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700276 }
277
Patrick Ventureef3aead2018-09-12 08:53:29 -0700278 return IPMI_CC_OK;
279}
280
281ipmi_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
310ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
311 uint8_t* replyCmdBuf, size_t* dataLen)
312{
313 size_t requestLen = (*dataLen);
314 auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf);
Patrick Venture50539d32018-12-03 09:01:55 -0800315 (*dataLen) = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700316
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 {
Patrick Venture41258802018-11-12 10:46:30 -0800325 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700326 }
327
328 return IPMI_CC_OK;
329}
330
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700331ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf,
332 uint8_t* replyCmdBuf, size_t* dataLen)
333{
334 size_t requestLen = (*dataLen);
335 struct BmcBlobWriteMetaTx request;
Patrick Venture50539d32018-12-03 09:01:55 -0800336 (*dataLen) = 0;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700337
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 {
Patrick Venture41258802018-11-12 10:46:30 -0800351 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700352 }
353
354 return IPMI_CC_OK;
355}
356
Patrick Ventureef3aead2018-09-12 08:53:29 -0700357} // namespace blobs