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