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 <unordered_map>
23 #include <vector>
24 
25 namespace blobs
26 {
27 
28 /* Used by all commands with data. */
29 struct BmcRx
30 {
31     uint8_t cmd;
32     uint16_t crc;
33     uint8_t data; /* one byte minimum of data. */
34 } __attribute__((packed));
35 
36 static const std::unordered_map<BlobOEMCommands, IpmiBlobHandler> handlers = {
37     {BlobOEMCommands::bmcBlobGetCount, getBlobCount},
38     {BlobOEMCommands::bmcBlobEnumerate, enumerateBlob},
39     {BlobOEMCommands::bmcBlobOpen, openBlob},
40     {BlobOEMCommands::bmcBlobRead, readBlob},
41     {BlobOEMCommands::bmcBlobWrite, writeBlob},
42     {BlobOEMCommands::bmcBlobCommit, commitBlob},
43     {BlobOEMCommands::bmcBlobClose, closeBlob},
44     {BlobOEMCommands::bmcBlobDelete, deleteBlob},
45     {BlobOEMCommands::bmcBlobStat, statBlob},
46     {BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob},
47     {BlobOEMCommands::bmcBlobWriteMeta, writeMeta},
48 };
49 
50 IpmiBlobHandler validateBlobCommand(CrcInterface* crc, const uint8_t* reqBuf,
51                                     uint8_t* replyCmdBuf, size_t* dataLen,
52                                     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->clear();
92         crc->compute(bytes.data(), bytes.size());
93 
94         /* Crc expected but didn't match. */
95         if (crcValue != crc->get())
96         {
97             *code = IPMI_CC_UNSPECIFIED_ERROR;
98             return nullptr;
99         }
100     }
101 
102     /* Grab the corresponding handler for the command. */
103     auto found = handlers.find(command);
104     if (found == handlers.end())
105     {
106         *code = IPMI_CC_INVALID_FIELD_REQUEST;
107         return nullptr;
108     }
109 
110     return found->second;
111 }
112 
113 ipmi_ret_t processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
114                               CrcInterface* crc, const uint8_t* reqBuf,
115                               uint8_t* replyCmdBuf, size_t* dataLen)
116 {
117     ipmi_ret_t result = cmd(mgr, reqBuf, replyCmdBuf, dataLen);
118     if (result != IPMI_CC_OK)
119     {
120         return result;
121     }
122 
123     size_t replyLength = (*dataLen);
124 
125     /* The command, whatever it was, returned success. */
126     if (replyLength == 0)
127     {
128         return result;
129     }
130 
131     /* The response, if it has one byte, has three, to include the crc16. */
132     if (replyLength < (sizeof(uint16_t) + 1))
133     {
134         return IPMI_CC_UNSPECIFIED_ERROR;
135     }
136 
137     /* The command, whatever it was, replied, so let's set the CRC. */
138     crc->clear();
139     crc->compute(replyCmdBuf + sizeof(uint16_t),
140                  replyLength - sizeof(uint16_t));
141 
142     /* Copy the CRC into place. */
143     uint16_t crcValue = crc->get();
144     std::memcpy(replyCmdBuf, &crcValue, sizeof(crcValue));
145 
146     return result;
147 }
148 } // namespace blobs
149