xref: /openbmc/phosphor-ipmi-blobs/ipmi.cpp (revision 97e69ca1)
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 <span>
21 #include <string>
22 #include <unordered_map>
23 
24 namespace blobs
25 {
26 
validateRequestLength(BlobOEMCommands command,size_t requestLen)27 bool validateRequestLength(BlobOEMCommands command, size_t requestLen)
28 {
29     /* The smallest string is one letter and the nul-terminator. */
30     static const int kMinStrLen = 2;
31 
32     static const std::unordered_map<BlobOEMCommands, size_t> minimumLengths = {
33         {BlobOEMCommands::bmcBlobEnumerate, sizeof(struct BmcBlobEnumerateTx)},
34         {BlobOEMCommands::bmcBlobOpen,
35          sizeof(struct BmcBlobOpenTx) + kMinStrLen},
36         {BlobOEMCommands::bmcBlobClose, sizeof(struct BmcBlobCloseTx)},
37         {BlobOEMCommands::bmcBlobDelete,
38          sizeof(struct BmcBlobDeleteTx) + kMinStrLen},
39         {BlobOEMCommands::bmcBlobStat,
40          sizeof(struct BmcBlobStatTx) + kMinStrLen},
41         {BlobOEMCommands::bmcBlobSessionStat,
42          sizeof(struct BmcBlobSessionStatTx)},
43         {BlobOEMCommands::bmcBlobCommit, sizeof(struct BmcBlobCommitTx)},
44         {BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)},
45         {BlobOEMCommands::bmcBlobWrite,
46          sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)},
47         {BlobOEMCommands::bmcBlobWriteMeta,
48          sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)},
49     };
50 
51     auto results = minimumLengths.find(command);
52     if (results == minimumLengths.end())
53     {
54         /* Valid length by default if we don't care. */
55         return true;
56     }
57 
58     /* If the request is shorter than the minimum, it's invalid. */
59     if (requestLen < results->second)
60     {
61         return false;
62     }
63 
64     return true;
65 }
66 
stringFromBuffer(std::span<const uint8_t> data)67 std::string stringFromBuffer(std::span<const uint8_t> data)
68 {
69     if (data.empty() || data.back() != '\0')
70     {
71         return std::string();
72     }
73 
74     // Last index is nul-terminator.
75     return std::string(data.begin(), data.end() - 1);
76 }
77 
getBlobCount(ManagerInterface * mgr,std::span<const uint8_t>)78 Resp getBlobCount(ManagerInterface* mgr, std::span<const uint8_t>)
79 {
80     struct BmcBlobCountRx resp;
81     resp.crc = 0;
82     resp.blobCount = mgr->buildBlobList();
83 
84     /* Copy the response into the reply buffer */
85     std::vector<uint8_t> output(sizeof(BmcBlobCountRx), 0);
86     std::memcpy(output.data(), &resp, sizeof(resp));
87 
88     return ipmi::responseSuccess(output);
89 }
90 
enumerateBlob(ManagerInterface * mgr,std::span<const uint8_t> data)91 Resp enumerateBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
92 {
93     /* Verify datalen is >= sizeof(request) */
94     struct BmcBlobEnumerateTx request;
95 
96     std::memcpy(&request, data.data(), sizeof(request));
97 
98     std::string blobId = mgr->getBlobId(request.blobIdx);
99     if (blobId.empty())
100     {
101         return ipmi::responseInvalidFieldRequest();
102     }
103 
104     std::vector<uint8_t> output(sizeof(BmcBlobEnumerateRx), 0);
105     output.insert(output.end(), blobId.c_str(),
106                   blobId.c_str() + blobId.length() + 1);
107     return ipmi::responseSuccess(output);
108 }
109 
openBlob(ManagerInterface * mgr,std::span<const uint8_t> data)110 Resp openBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
111 {
112     auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(data.data());
113     uint16_t session;
114 
115     std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobOpenTx)));
116     if (path.empty())
117     {
118         return ipmi::responseReqDataLenInvalid();
119     }
120 
121     /* Attempt to open. */
122     if (!mgr->open(request->flags, path, &session))
123     {
124         return ipmi::responseUnspecifiedError();
125     }
126 
127     struct BmcBlobOpenRx reply;
128     reply.crc = 0;
129     reply.sessionId = session;
130 
131     std::vector<uint8_t> output(sizeof(BmcBlobOpenRx), 0);
132     std::memcpy(output.data(), &reply, sizeof(reply));
133     return ipmi::responseSuccess(output);
134 }
135 
closeBlob(ManagerInterface * mgr,std::span<const uint8_t> data)136 Resp closeBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
137 {
138     struct BmcBlobCloseTx request;
139     if (data.size() < sizeof(request))
140     {
141         return ipmi::responseReqDataLenInvalid();
142     }
143     std::memcpy(&request, data.data(), sizeof(request));
144 
145     /* Attempt to close. */
146     if (!mgr->close(request.sessionId))
147     {
148         return ipmi::responseUnspecifiedError();
149     }
150 
151     return ipmi::responseSuccess(std::vector<uint8_t>{});
152 }
153 
deleteBlob(ManagerInterface * mgr,std::span<const uint8_t> data)154 Resp deleteBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
155 {
156     std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobDeleteTx)));
157     if (path.empty())
158     {
159         return ipmi::responseReqDataLenInvalid();
160     }
161 
162     /* Attempt to delete. */
163     if (!mgr->deleteBlob(path))
164     {
165         return ipmi::responseUnspecifiedError();
166     }
167 
168     return ipmi::responseSuccess(std::vector<uint8_t>{});
169 }
170 
returnStatBlob(BlobMeta * meta)171 static Resp returnStatBlob(BlobMeta* meta)
172 {
173     struct BmcBlobStatRx reply;
174     reply.crc = 0;
175     reply.blobState = meta->blobState;
176     reply.size = meta->size;
177     reply.metadataLen = meta->metadata.size();
178 
179     std::vector<uint8_t> output(sizeof(BmcBlobStatRx), 0);
180     std::memcpy(output.data(), &reply, sizeof(reply));
181 
182     /* If there is metadata, insert it to output. */
183     if (!meta->metadata.empty())
184     {
185         output.insert(output.end(), meta->metadata.begin(),
186                       meta->metadata.end());
187     }
188     return ipmi::responseSuccess(output);
189 }
190 
statBlob(ManagerInterface * mgr,std::span<const uint8_t> data)191 Resp statBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
192 {
193     std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobStatTx)));
194     if (path.empty())
195     {
196         return ipmi::responseReqDataLenInvalid();
197     }
198 
199     /* Attempt to stat. */
200     BlobMeta meta;
201     if (!mgr->stat(path, &meta))
202     {
203         return ipmi::responseUnspecifiedError();
204     }
205 
206     return returnStatBlob(&meta);
207 }
208 
sessionStatBlob(ManagerInterface * mgr,std::span<const uint8_t> data)209 Resp sessionStatBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
210 {
211     struct BmcBlobSessionStatTx request;
212     if (data.size() < sizeof(request))
213     {
214         return ipmi::responseReqDataLenInvalid();
215     }
216     std::memcpy(&request, data.data(), sizeof(request));
217 
218     /* Attempt to stat. */
219     BlobMeta meta;
220 
221     if (!mgr->stat(request.sessionId, &meta))
222     {
223         return ipmi::responseUnspecifiedError();
224     }
225 
226     return returnStatBlob(&meta);
227 }
228 
commitBlob(ManagerInterface * mgr,std::span<const uint8_t> data)229 Resp commitBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
230 {
231     auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(data.data());
232 
233     /* Sanity check the commitDataLen */
234     if (request->commitDataLen > (data.size() - sizeof(struct BmcBlobCommitTx)))
235     {
236         return ipmi::responseReqDataLenInvalid();
237     }
238 
239     data = data.subspan(sizeof(struct BmcBlobCommitTx), request->commitDataLen);
240 
241     if (!mgr->commit(request->sessionId,
242                      std::vector<uint8_t>(data.begin(), data.end())))
243     {
244         return ipmi::responseUnspecifiedError();
245     }
246 
247     return ipmi::responseSuccess(std::vector<uint8_t>{});
248 }
249 
readBlob(ManagerInterface * mgr,std::span<const uint8_t> data)250 Resp readBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
251 {
252     struct BmcBlobReadTx request;
253     if (data.size() < sizeof(request))
254     {
255         return ipmi::responseReqDataLenInvalid();
256     }
257     std::memcpy(&request, data.data(), sizeof(request));
258 
259     std::vector<uint8_t> result =
260         mgr->read(request.sessionId, request.offset, request.requestedSize);
261 
262     /* If the Read fails, it returns success but with only the crc and 0 bytes
263      * of data.
264      * If there was data returned, copy into the reply buffer.
265      */
266     std::vector<uint8_t> output(sizeof(BmcBlobReadRx), 0);
267 
268     if (!result.empty())
269     {
270         output.insert(output.end(), result.begin(), result.end());
271     }
272 
273     return ipmi::responseSuccess(output);
274 }
275 
writeBlob(ManagerInterface * mgr,std::span<const uint8_t> data)276 Resp writeBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
277 {
278     auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(data.data());
279     data = data.subspan(sizeof(struct BmcBlobWriteTx));
280 
281     /* Attempt to write the bytes. */
282     if (!mgr->write(request->sessionId, request->offset,
283                     std::vector<uint8_t>(data.begin(), data.end())))
284     {
285         return ipmi::responseUnspecifiedError();
286     }
287 
288     return ipmi::responseSuccess(std::vector<uint8_t>{});
289 }
290 
writeMeta(ManagerInterface * mgr,std::span<const uint8_t> data)291 Resp writeMeta(ManagerInterface* mgr, std::span<const uint8_t> data)
292 {
293     struct BmcBlobWriteMetaTx request;
294     if (data.size() < sizeof(request))
295     {
296         return ipmi::responseReqDataLenInvalid();
297     }
298 
299     /* Copy over the request. */
300     std::memcpy(&request, data.data(), sizeof(request));
301 
302     /* Nothing really else to validate, we just copy those bytes. */
303     data = data.subspan(sizeof(struct BmcBlobWriteMetaTx));
304 
305     /* Attempt to write the bytes. */
306     if (!mgr->writeMeta(request.sessionId, request.offset,
307                         std::vector<uint8_t>(data.begin(), data.end())))
308     {
309         return ipmi::responseUnspecifiedError();
310     }
311 
312     return ipmi::responseSuccess(std::vector<uint8_t>{});
313 }
314 
315 } // namespace blobs
316