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