xref: /openbmc/phosphor-ipmi-blobs/ipmi.cpp (revision 993f5419)
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 == "")
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->blobId, 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 = stringFromBuffer(
132         request->blobId, (requestLen - sizeof(struct BmcBlobOpenTx)));
133     if (path.empty())
134     {
135         return IPMI_CC_REQ_DATA_LEN_INVALID;
136     }
137 
138     /* Attempt to open. */
139     if (!mgr->open(request->flags, path, &session))
140     {
141         return IPMI_CC_UNSPECIFIED_ERROR;
142     }
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 
154 ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
155                      size_t* dataLen)
156 {
157     struct BmcBlobCloseTx request;
158     std::memcpy(&request, reqBuf, sizeof(request));
159     (*dataLen) = 0;
160 
161     /* Attempt to close. */
162     if (!mgr->close(request.sessionId))
163     {
164         return IPMI_CC_UNSPECIFIED_ERROR;
165     }
166 
167     return IPMI_CC_OK;
168 }
169 
170 ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
171                       size_t* dataLen)
172 {
173     size_t requestLen = (*dataLen);
174     auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf);
175     (*dataLen) = 0;
176 
177     std::string path = stringFromBuffer(
178         request->blobId, (requestLen - sizeof(struct BmcBlobDeleteTx)));
179     if (path.empty())
180     {
181         return IPMI_CC_REQ_DATA_LEN_INVALID;
182     }
183 
184     /* Attempt to delete. */
185     if (!mgr->deleteBlob(path))
186     {
187         return IPMI_CC_UNSPECIFIED_ERROR;
188     }
189 
190     return IPMI_CC_OK;
191 }
192 
193 static ipmi_ret_t returnStatBlob(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 
215 ipmi_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);
220     (*dataLen) = 0;
221 
222     std::string path = stringFromBuffer(
223         request->blobId, (requestLen - sizeof(struct BmcBlobStatTx)));
224     if (path.empty())
225     {
226         return IPMI_CC_REQ_DATA_LEN_INVALID;
227     }
228 
229     /* Attempt to stat. */
230     BlobMeta meta;
231     if (!mgr->stat(path, &meta))
232     {
233         return IPMI_CC_UNSPECIFIED_ERROR;
234     }
235 
236     return returnStatBlob(&meta, replyCmdBuf, dataLen);
237 }
238 
239 ipmi_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));
244     (*dataLen) = 0;
245 
246     /* Attempt to stat. */
247     BlobMeta meta;
248 
249     if (!mgr->stat(request.sessionId, &meta))
250     {
251         return IPMI_CC_UNSPECIFIED_ERROR;
252     }
253 
254     return returnStatBlob(&meta, replyCmdBuf, dataLen);
255 }
256 
257 ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
258                       size_t* dataLen)
259 {
260     size_t requestLen = (*dataLen);
261     auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf);
262     (*dataLen) = 0;
263 
264     /* Sanity check the commitDataLen */
265     if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx)))
266     {
267         return IPMI_CC_REQ_DATA_LEN_INVALID;
268     }
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     {
275         return IPMI_CC_UNSPECIFIED_ERROR;
276     }
277 
278     return IPMI_CC_OK;
279 }
280 
281 ipmi_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 
310 ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
311                      size_t* dataLen)
312 {
313     size_t requestLen = (*dataLen);
314     auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf);
315     (*dataLen) = 0;
316 
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     {
325         return IPMI_CC_UNSPECIFIED_ERROR;
326     }
327 
328     return IPMI_CC_OK;
329 }
330 
331 ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
332                      size_t* dataLen)
333 {
334     size_t requestLen = (*dataLen);
335     struct BmcBlobWriteMetaTx request;
336     (*dataLen) = 0;
337 
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     {
351         return IPMI_CC_UNSPECIFIED_ERROR;
352     }
353 
354     return IPMI_CC_OK;
355 }
356 
357 } // namespace blobs
358