1 #include "channel.hpp"
2 
3 #include "transporthandler.hpp"
4 #include "user_channel/channel_layer.hpp"
5 
6 #include <arpa/inet.h>
7 
8 #include <boost/process/child.hpp>
9 #include <fstream>
10 #include <ipmid/types.hpp>
11 #include <ipmid/utils.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/log.hpp>
14 #include <set>
15 #include <string>
16 #include <xyz/openbmc_project/Common/error.hpp>
17 
18 using namespace phosphor::logging;
19 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
20 
21 namespace cipher
22 {
23 
24 /** @brief Get the supported Cipher records
25  *
26  * The cipher records are read from the JSON file and converted into
27  * 1. cipher suite record format mentioned in the IPMI specification. The
28  * records can be either OEM or standard cipher. Each json entry is parsed and
29  * converted into the cipher record format and pushed into the vector.
30  * 2. Algorithms listed in vector format
31  *
32  * @return pair of vector containing 1. all the cipher suite records. 2.
33  * Algorithms supported
34  *
35  */
36 std::pair<std::vector<uint8_t>, std::vector<uint8_t>> getCipherRecords()
37 {
38     std::vector<uint8_t> cipherRecords;
39     std::vector<uint8_t> supportedAlgorithmRecords;
40     // create set to get the unique supported algorithms
41     std::set<uint8_t> supportedAlgorithmSet;
42 
43     std::ifstream jsonFile(configFile);
44     if (!jsonFile.is_open())
45     {
46         log<level::ERR>("Channel Cipher suites file not found");
47         elog<InternalFailure>();
48     }
49 
50     auto data = Json::parse(jsonFile, nullptr, false);
51     if (data.is_discarded())
52     {
53         log<level::ERR>("Parsing channel cipher suites JSON failed");
54         elog<InternalFailure>();
55     }
56 
57     for (const auto& record : data)
58     {
59         if (record.find(oem) != record.end())
60         {
61             // OEM cipher suite - 0xC1
62             cipherRecords.push_back(oemCipherSuite);
63             // Cipher Suite ID
64             cipherRecords.push_back(record.value(cipher, 0));
65             // OEM IANA - 3 bytes
66             cipherRecords.push_back(record.value(oem, 0));
67             cipherRecords.push_back(record.value(oem, 0) >> 8);
68             cipherRecords.push_back(record.value(oem, 0) >> 16);
69         }
70         else
71         {
72             // Standard cipher suite - 0xC0
73             cipherRecords.push_back(stdCipherSuite);
74             // Cipher Suite ID
75             cipherRecords.push_back(record.value(cipher, 0));
76         }
77 
78         // Authentication algorithm number
79         cipherRecords.push_back(record.value(auth, 0));
80         supportedAlgorithmSet.insert(record.value(auth, 0));
81 
82         // Integrity algorithm number
83         cipherRecords.push_back(record.value(integrity, 0) | integrityTag);
84         supportedAlgorithmSet.insert(record.value(integrity, 0) | integrityTag);
85 
86         // Confidentiality algorithm number
87         cipherRecords.push_back(record.value(conf, 0) | confTag);
88         supportedAlgorithmSet.insert(record.value(conf, 0) | confTag);
89     }
90 
91     // copy the set to supportedAlgorithmRecord which is vector based.
92     std::copy(supportedAlgorithmSet.begin(), supportedAlgorithmSet.end(),
93               std::back_inserter(supportedAlgorithmRecords));
94 
95     return std::make_pair(cipherRecords, supportedAlgorithmRecords);
96 }
97 
98 } // namespace cipher
99 
100 ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
101                                   ipmi_request_t request,
102                                   ipmi_response_t response,
103                                   ipmi_data_len_t data_len,
104                                   ipmi_context_t context)
105 {
106     static std::vector<uint8_t> cipherRecords;
107     static std::vector<uint8_t> supportedAlgorithms;
108     static auto recordInit = false;
109 
110     auto requestData =
111         reinterpret_cast<const GetChannelCipherRequest*>(request);
112 
113     if (*data_len < sizeof(GetChannelCipherRequest))
114     {
115         *data_len = 0;
116         return IPMI_CC_REQ_DATA_LEN_INVALID;
117     }
118 
119     *data_len = 0;
120 
121     if (!recordInit)
122     {
123         try
124         {
125             std::tie(cipherRecords, supportedAlgorithms) =
126                 cipher::getCipherRecords();
127             recordInit = true;
128         }
129         catch (const std::exception& e)
130         {
131             return IPMI_CC_UNSPECIFIED_ERROR;
132         }
133     }
134 
135     const auto& records = (cipher::listCipherSuite ==
136                            (requestData->listIndex & cipher::listTypeMask))
137                               ? cipherRecords
138                               : supportedAlgorithms;
139 
140     // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
141     // set of 16 and so on.
142     auto index =
143         static_cast<size_t>(requestData->listIndex & cipher::listIndexMask);
144 
145     // Calculate the number of record data bytes to be returned.
146     auto start = std::min(index * cipher::respSize, records.size());
147     auto end =
148         std::min((index * cipher::respSize) + cipher::respSize, records.size());
149     auto size = end - start;
150 
151     auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response);
152     responseData->channelNumber = cipher::defaultChannelNumber;
153 
154     if (!size)
155     {
156         *data_len = sizeof(GetChannelCipherRespHeader);
157     }
158     else
159     {
160         std::copy_n(records.data() + start, size,
161                     static_cast<uint8_t*>(response) + 1);
162         *data_len = size + sizeof(GetChannelCipherRespHeader);
163     }
164 
165     return IPMI_CC_OK;
166 }
167 
168 template <typename... ArgTypes>
169 static int executeCmd(const char* path, ArgTypes&&... tArgs)
170 {
171     boost::process::child execProg(path, const_cast<char*>(tArgs)...);
172     execProg.wait();
173     return execProg.exit_code();
174 }
175 
176 /** @brief Enable the network IPMI service on the specified ethernet interface.
177  *
178  *  @param[in] intf - ethernet interface on which to enable IPMI
179  */
180 void enableNetworkIPMI(const std::string& intf)
181 {
182     // Check if there is a iptable filter to drop IPMI packets for the
183     // interface.
184     auto retCode =
185         executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
186                    intf.c_str(), "--dport", "623", "-j", "DROP");
187 
188     // If the iptable filter exists, delete the filter.
189     if (!retCode)
190     {
191         auto response =
192             executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i",
193                        intf.c_str(), "--dport", "623", "-j", "DROP");
194 
195         if (response)
196         {
197             log<level::ERR>("Dropping the iptables filter failed",
198                             entry("INTF=%s", intf.c_str()),
199                             entry("RETURN_CODE:%d", response));
200         }
201     }
202 }
203 
204 /** @brief Disable the network IPMI service on the specified ethernet interface.
205  *
206  *  @param[in] intf - ethernet interface on which to disable IPMI
207  */
208 void disableNetworkIPMI(const std::string& intf)
209 {
210     // Check if there is a iptable filter to drop IPMI packets for the
211     // interface.
212     auto retCode =
213         executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i",
214                    intf.c_str(), "--dport", "623", "-j", "DROP");
215 
216     // If the iptable filter does not exist, add filter to drop network IPMI
217     // packets
218     if (retCode)
219     {
220         auto response =
221             executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i",
222                        intf.c_str(), "--dport", "623", "-j", "DROP");
223 
224         if (response)
225         {
226             log<level::ERR>("Inserting iptables filter failed",
227                             entry("INTF=%s", intf.c_str()),
228                             entry("RETURN_CODE:%d", response));
229         }
230     }
231 }
232