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