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