blob: 6942b117a666f464da25cde44861242923252a35 [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)},
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
64std::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
80ipmi_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
94ipmi_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
120ipmi_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
150ipmi_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
166ipmi_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
189static 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
211ipmi_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
234ipmi_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
251ipmi_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
275ipmi_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
304ipmi_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