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, uint8_t* replyCmdBuf,
52                                     size_t* dataLen, ipmi_ret_t* code)
53 {
54     size_t requestLength = (*dataLen);
55     /* We know dataLen is at least 1 already */
56     auto command = static_cast<BlobOEMCommands>(reqBuf[0]);
57 
58     /* Validate it's at least well-formed. */
59     if (!validateRequestLength(command, requestLength))
60     {
61         *code = IPMI_CC_REQ_DATA_LEN_INVALID;
62         return nullptr;
63     }
64 
65     /* If there is a payload. */
66     if (requestLength > sizeof(uint8_t))
67     {
68         /* Verify the request includes: command, crc16, data */
69         if (requestLength < sizeof(struct BmcRx))
70         {
71             *code = IPMI_CC_REQ_DATA_LEN_INVALID;
72             return nullptr;
73         }
74 
75         /* We don't include the command byte at offset 0 as part of the crc
76          * payload area or the crc bytes at the beginning.
77          */
78         size_t requestBodyLen = requestLength - 3;
79 
80         /* We start after the command byte. */
81         std::vector<uint8_t> bytes(requestBodyLen);
82 
83         /* It likely has a well-formed payload. */
84         struct BmcRx request;
85         std::memcpy(&request, reqBuf, sizeof(request));
86         uint16_t crcValue = request.crc;
87 
88         /* Set the in-place CRC to zero. */
89         std::memcpy(bytes.data(), &reqBuf[3], requestBodyLen);
90 
91         /* Crc expected but didn't match. */
92         if (crcValue != ipmiblob::generateCrc(bytes))
93         {
94             *code = IPMI_CC_UNSPECIFIED_ERROR;
95             return nullptr;
96         }
97     }
98 
99     /* Grab the corresponding handler for the command. */
100     auto found = handlers.find(command);
101     if (found == handlers.end())
102     {
103         *code = IPMI_CC_INVALID_FIELD_REQUEST;
104         return nullptr;
105     }
106 
107     return found->second;
108 }
109 
110 ipmi_ret_t processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
111                               const uint8_t* reqBuf, uint8_t* replyCmdBuf,
112                               size_t* dataLen)
113 {
114     ipmi_ret_t result = cmd(mgr, reqBuf, replyCmdBuf, dataLen);
115     if (result != IPMI_CC_OK)
116     {
117         return result;
118     }
119 
120     size_t replyLength = (*dataLen);
121 
122     /* The command, whatever it was, returned success. */
123     if (replyLength == 0)
124     {
125         return result;
126     }
127 
128     /* Read can return 0 bytes, and just a CRC, otherwise you need a CRC and 1
129      * byte, therefore the limit is 2 bytes.
130      */
131     if (replyLength < (sizeof(uint16_t)))
132     {
133         return IPMI_CC_UNSPECIFIED_ERROR;
134     }
135 
136     /* The command, whatever it was, replied, so let's set the CRC. */
137     std::vector<std::uint8_t> crcBuffer(replyCmdBuf + sizeof(uint16_t),
138                                         replyCmdBuf + replyLength);
139     /* Copy the CRC into place. */
140     uint16_t crcValue = ipmiblob::generateCrc(crcBuffer);
141     std::memcpy(replyCmdBuf, &crcValue, sizeof(crcValue));
142 
143     return result;
144 }
145 } // namespace blobs
146