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 "process.hpp"
18
19 #include "ipmi.hpp"
20
21 #include <ipmiblob/crc.hpp>
22 #include <ipmid/api-types.hpp>
23
24 #include <cstring>
25 #include <span>
26 #include <unordered_map>
27 #include <utility>
28 #include <vector>
29
30 namespace blobs
31 {
32
33 /* Used by all commands with data. */
34 struct BmcRx
35 {
36 uint16_t crc;
37 uint8_t data; /* one byte minimum of data. */
38 } __attribute__((packed));
39
40 static const std::unordered_map<BlobOEMCommands, IpmiBlobHandler> handlers = {
41 {BlobOEMCommands::bmcBlobGetCount, getBlobCount},
42 {BlobOEMCommands::bmcBlobEnumerate, enumerateBlob},
43 {BlobOEMCommands::bmcBlobOpen, openBlob},
44 {BlobOEMCommands::bmcBlobRead, readBlob},
45 {BlobOEMCommands::bmcBlobWrite, writeBlob},
46 {BlobOEMCommands::bmcBlobCommit, commitBlob},
47 {BlobOEMCommands::bmcBlobClose, closeBlob},
48 {BlobOEMCommands::bmcBlobDelete, deleteBlob},
49 {BlobOEMCommands::bmcBlobStat, statBlob},
50 {BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob},
51 {BlobOEMCommands::bmcBlobWriteMeta, writeMeta},
52 };
53
validateBlobCommand(uint8_t cmd,std::span<const uint8_t> data)54 IpmiBlobHandler validateBlobCommand(uint8_t cmd, std::span<const uint8_t> data)
55 {
56 size_t requestLength = data.size();
57 /* We know dataLen is at least 1 already */
58 auto command = static_cast<BlobOEMCommands>(cmd);
59
60 /* Validate it's at least well-formed. */
61 if (!validateRequestLength(command, requestLength))
62 {
63 return [](ManagerInterface*, std::span<const uint8_t>) {
64 return ipmi::responseReqDataLenInvalid();
65 };
66 }
67
68 /* If there is a payload. */
69 if (requestLength > sizeof(cmd))
70 {
71 /* Verify the request includes: command, crc16, data */
72 if (requestLength < sizeof(struct BmcRx))
73 {
74 return [](ManagerInterface*, std::span<const uint8_t>) {
75 return ipmi::responseReqDataLenInvalid();
76 };
77 }
78
79 /* We don't include the command byte at offset 0 as part of the crc
80 * payload area or the crc bytes at the beginning.
81 */
82 size_t requestBodyLen = requestLength - 3;
83
84 /* We start after the command byte. */
85 std::vector<uint8_t> bytes(requestBodyLen);
86
87 /* It likely has a well-formed payload.
88 * Get the first two bytes of the request for crc.
89 */
90 uint16_t crc;
91 if (data.size() < sizeof(crc))
92 {
93 return [](ManagerInterface*, std::span<const uint8_t>) {
94 return ipmi::responseReqDataLenInvalid();
95 };
96 }
97 std::memcpy(&crc, data.data(), sizeof(crc));
98
99 /* Set the in-place CRC to zero.
100 * Remove the first two bytes for crc and get the reset of the request.
101 */
102 data = data.subspan(sizeof(crc));
103
104 /* Crc expected but didn't match. */
105 if (crc != ipmiblob::generateCrc(
106 std::vector<uint8_t>(data.begin(), data.end())))
107 {
108 return [](ManagerInterface*, std::span<const uint8_t>) {
109 return ipmi::responseUnspecifiedError();
110 };
111 };
112 }
113
114 /* Grab the corresponding handler for the command. */
115 auto found = handlers.find(command);
116 if (found == handlers.end())
117 {
118 return [](ManagerInterface*, std::span<const uint8_t>) {
119 return ipmi::responseInvalidFieldRequest();
120 };
121 }
122
123 return found->second;
124 }
125
processBlobCommand(IpmiBlobHandler cmd,ManagerInterface * mgr,std::span<const uint8_t> data,size_t maxSize)126 Resp processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
127 std::span<const uint8_t> data, size_t maxSize)
128 {
129 Resp result = cmd(mgr, data);
130 if (std::get<0>(result) != ipmi::ccSuccess)
131 {
132 return result;
133 }
134
135 std::vector<uint8_t>& response = std::get<0>(
136 // std::variant<std::vector<uint8_t>>
137 *std::get<1>(result));
138 size_t replyLength = response.size();
139
140 /* The command, whatever it was, returned success. */
141 if (replyLength == 0)
142 {
143 return result;
144 }
145
146 /* Read can return 0 bytes, and just a CRC, otherwise you need a CRC and 1
147 * byte, therefore the limit is 2 bytes.
148 */
149 if (replyLength < (sizeof(uint16_t)))
150 {
151 return ipmi::responseUnspecifiedError();
152 }
153
154 /* Make sure the reply size fits the ipmi buffer */
155 if (replyLength > maxSize)
156 {
157 return ipmi::responseResponseError();
158 }
159
160 /* The command, whatever it was, replied, so let's set the CRC. */
161 std::span<const uint8_t> responseView = response;
162 responseView = responseView.subspan(sizeof(uint16_t));
163 std::vector<std::uint8_t> crcBuffer(responseView.begin(),
164 responseView.end());
165 /* Copy the CRC into place. */
166 uint16_t crcValue = ipmiblob::generateCrc(crcBuffer);
167 if (response.size() < sizeof(crcValue))
168 {
169 return ipmi::responseReqDataLenInvalid();
170 }
171 std::memcpy(response.data(), &crcValue, sizeof(crcValue));
172
173 return result;
174 }
175
handleBlobCommand(uint8_t cmd,std::vector<uint8_t> data,size_t maxSize)176 Resp handleBlobCommand(uint8_t cmd, std::vector<uint8_t> data, size_t maxSize)
177 {
178 /* on failure rc is set to the corresponding IPMI error. */
179 return processBlobCommand(validateBlobCommand(cmd, data), getBlobManager(),
180 data, maxSize);
181 }
182
183 } // namespace blobs
184