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