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 if (!ipmi::isValidPayloadType(static_cast<ipmi::PayloadType>(payloadType))) 132 { 133 log<level::DEBUG>("Get channel cipher suites - Invalid payload type"); 134 constexpr uint8_t ccPayloadTypeNotSupported = 0x80; 135 return ipmi::response(ccPayloadTypeNotSupported); 136 } 137 138 if (!recordInit) 139 { 140 try 141 { 142 std::tie(cipherRecords, supportedAlgorithms) = 143 cipher::getCipherRecords(); 144 recordInit = true; 145 } 146 catch (const std::exception& e) 147 { 148 return ipmi::responseUnspecifiedError(); 149 } 150 } 151 152 const std::vector<uint8_t>& records = 153 algoSelectBit ? cipherRecords : supportedAlgorithms; 154 static constexpr auto respSize = 16; 155 156 // Session support is available in active LAN channels. 157 if ((ipmi::getChannelSessionSupport(rspChannel) == 158 ipmi::EChannelSessSupported::none) || 159 !(ipmi::doesDeviceExist(rspChannel))) 160 { 161 log<level::DEBUG>("Get channel cipher suites - Device does not exist"); 162 return ipmi::responseInvalidFieldRequest(); 163 } 164 165 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next 166 // set of 16 and so on. 167 168 // Calculate the number of record data bytes to be returned. 169 auto start = 170 std::min(static_cast<size_t>(listIndex) * respSize, records.size()); 171 auto end = std::min((static_cast<size_t>(listIndex) * respSize) + respSize, 172 records.size()); 173 auto size = end - start; 174 175 std::vector<uint8_t> rspRecords; 176 std::copy_n(records.data() + start, size, std::back_inserter(rspRecords)); 177 178 return ipmi::responseSuccess(rspChannel, rspRecords); 179 } 180 181 template <typename... ArgTypes> 182 static int executeCmd(const char* path, ArgTypes&&... tArgs) 183 { 184 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 185 execProg.wait(); 186 return execProg.exit_code(); 187 } 188 189 /** @brief Enable the network IPMI service on the specified ethernet interface. 190 * 191 * @param[in] intf - ethernet interface on which to enable IPMI 192 */ 193 void enableNetworkIPMI(const std::string& intf) 194 { 195 // Check if there is a iptable filter to drop IPMI packets for the 196 // interface. 197 auto retCode = 198 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 199 intf.c_str(), "--dport", "623", "-j", "DROP"); 200 201 // If the iptable filter exists, delete the filter. 202 if (!retCode) 203 { 204 auto response = 205 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i", 206 intf.c_str(), "--dport", "623", "-j", "DROP"); 207 208 if (response) 209 { 210 log<level::ERR>("Dropping the iptables filter failed", 211 entry("INTF=%s", intf.c_str()), 212 entry("RETURN_CODE:%d", response)); 213 } 214 } 215 } 216 217 /** @brief Disable the network IPMI service on the specified ethernet interface. 218 * 219 * @param[in] intf - ethernet interface on which to disable IPMI 220 */ 221 void disableNetworkIPMI(const std::string& intf) 222 { 223 // Check if there is a iptable filter to drop IPMI packets for the 224 // interface. 225 auto retCode = 226 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 227 intf.c_str(), "--dport", "623", "-j", "DROP"); 228 229 // If the iptable filter does not exist, add filter to drop network IPMI 230 // packets 231 if (retCode) 232 { 233 auto response = 234 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i", 235 intf.c_str(), "--dport", "623", "-j", "DROP"); 236 237 if (response) 238 { 239 log<level::ERR>("Inserting iptables filter failed", 240 entry("INTF=%s", intf.c_str()), 241 entry("RETURN_CODE:%d", response)); 242 } 243 } 244 } 245