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 /** @brief this command is used to look up what authentication, integrity, 101 * confidentiality algorithms are supported. 102 * 103 * @ param ctx - context pointer 104 * @ param channelNumber - channel number 105 * @ param payloadType - payload type 106 * @ param listIndex - list index 107 * @ param algoSelectBit - list algorithms 108 * 109 * @returns ipmi completion code plus response data 110 * - rspChannel - channel number for authentication algorithm. 111 * - rspRecords - cipher suite records. 112 **/ 113 ipmi::RspType<uint8_t, // Channel Number 114 std::vector<uint8_t> // Cipher Records 115 > 116 getChannelCipherSuites(ipmi::Context::ptr ctx, uint4_t channelNumber, 117 uint4_t reserved1, uint8_t payloadType, 118 uint6_t listIndex, uint1_t reserved2, 119 uint1_t algoSelectBit) 120 { 121 static std::vector<uint8_t> cipherRecords; 122 static std::vector<uint8_t> supportedAlgorithms; 123 static auto recordInit = false; 124 125 uint8_t rspChannel = ipmi::convertCurrentChannelNum( 126 static_cast<uint8_t>(channelNumber), ctx->channel); 127 128 if (!ipmi::isValidChannel(rspChannel) || reserved1 != 0 || reserved2 != 0) 129 { 130 return ipmi::responseInvalidFieldRequest(); 131 } 132 133 if (!recordInit) 134 { 135 try 136 { 137 std::tie(cipherRecords, supportedAlgorithms) = 138 cipher::getCipherRecords(); 139 recordInit = true; 140 } 141 catch (const std::exception& e) 142 { 143 return ipmi::responseUnspecifiedError(); 144 } 145 } 146 147 const std::vector<uint8_t>& records = 148 algoSelectBit ? cipherRecords : supportedAlgorithms; 149 static constexpr auto respSize = 16; 150 151 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next 152 // set of 16 and so on. 153 154 // Calculate the number of record data bytes to be returned. 155 auto start = 156 std::min(static_cast<size_t>(listIndex) * respSize, records.size()); 157 auto end = std::min((static_cast<size_t>(listIndex) * respSize) + respSize, 158 records.size()); 159 auto size = end - start; 160 161 std::vector<uint8_t> rspRecords; 162 std::copy_n(records.data() + start, size, std::back_inserter(rspRecords)); 163 164 return ipmi::responseSuccess(rspChannel, rspRecords); 165 } 166 167 template <typename... ArgTypes> 168 static int executeCmd(const char* path, ArgTypes&&... tArgs) 169 { 170 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 171 execProg.wait(); 172 return execProg.exit_code(); 173 } 174 175 /** @brief Enable the network IPMI service on the specified ethernet interface. 176 * 177 * @param[in] intf - ethernet interface on which to enable IPMI 178 */ 179 void enableNetworkIPMI(const std::string& intf) 180 { 181 // Check if there is a iptable filter to drop IPMI packets for the 182 // interface. 183 auto retCode = 184 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 185 intf.c_str(), "--dport", "623", "-j", "DROP"); 186 187 // If the iptable filter exists, delete the filter. 188 if (!retCode) 189 { 190 auto response = 191 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i", 192 intf.c_str(), "--dport", "623", "-j", "DROP"); 193 194 if (response) 195 { 196 log<level::ERR>("Dropping the iptables filter failed", 197 entry("INTF=%s", intf.c_str()), 198 entry("RETURN_CODE:%d", response)); 199 } 200 } 201 } 202 203 /** @brief Disable the network IPMI service on the specified ethernet interface. 204 * 205 * @param[in] intf - ethernet interface on which to disable IPMI 206 */ 207 void disableNetworkIPMI(const std::string& intf) 208 { 209 // Check if there is a iptable filter to drop IPMI packets for the 210 // interface. 211 auto retCode = 212 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 213 intf.c_str(), "--dport", "623", "-j", "DROP"); 214 215 // If the iptable filter does not exist, add filter to drop network IPMI 216 // packets 217 if (retCode) 218 { 219 auto response = 220 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i", 221 intf.c_str(), "--dport", "623", "-j", "DROP"); 222 223 if (response) 224 { 225 log<level::ERR>("Inserting iptables filter failed", 226 entry("INTF=%s", intf.c_str()), 227 entry("RETURN_CODE:%d", response)); 228 } 229 } 230 } 231