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