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