xref: /openbmc/phosphor-ipmi-blobs/ipmi.cpp (revision 5c4b17b2)
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