xref: /openbmc/phosphor-ipmi-blobs/ipmi.cpp (revision ef3aeadc)
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     };
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 
64 std::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 
80 ipmi_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 
94 ipmi_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 
120 ipmi_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 
150 ipmi_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 
166 ipmi_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 
189 static 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 
211 ipmi_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 
234 ipmi_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 
251 ipmi_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 
275 ipmi_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 
304 ipmi_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
325