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