1 #include "channel.hpp" 2 3 #include "net.hpp" 4 #include "transporthandler.hpp" 5 #include "types.hpp" 6 #include "utils.hpp" 7 8 #include <arpa/inet.h> 9 10 #include <boost/process/child.hpp> 11 #include <fstream> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/log.hpp> 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 /** @struct GetChannelAccessRequest 21 * 22 * IPMI payload for Get Channel access command request. 23 */ 24 struct GetChannelAccessRequest 25 { 26 uint8_t channelNumber; //!< Channel number. 27 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting. 28 } __attribute__((packed)); 29 30 /** @struct GetChannelAccessResponse 31 * 32 * IPMI payload for Get Channel access command response. 33 */ 34 struct GetChannelAccessResponse 35 { 36 uint8_t settings; //!< Channel settings. 37 uint8_t privilegeLimit; //!< Channel privilege level limit. 38 } __attribute__((packed)); 39 40 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 41 ipmi_request_t request, 42 ipmi_response_t response, 43 ipmi_data_len_t data_len, 44 ipmi_context_t context) 45 { 46 auto requestData = 47 reinterpret_cast<const GetChannelAccessRequest*>(request); 48 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse)); 49 auto responseData = 50 reinterpret_cast<GetChannelAccessResponse*>(outPayload.data()); 51 52 /* 53 * The value Eh is used as a way to identify the current channel that 54 * the command is being received from. 55 */ 56 constexpr auto channelE = 0x0E; 57 int channel = requestData->channelNumber; 58 auto ethdevice = ipmi::network::ChanneltoEthernet(channel); 59 60 if (channel != channelE && ethdevice.empty()) 61 { 62 *data_len = 0; 63 return IPMI_CC_INVALID_FIELD_REQUEST; 64 } 65 66 /* 67 * [7:6] - reserved 68 * [5] - 1b = Alerting disabled 69 * [4] - 1b = per message authentication disabled 70 * [3] - 0b = User level authentication enabled 71 * [2:0] - 2h = always available 72 */ 73 constexpr auto channelSetting = 0x32; 74 75 responseData->settings = channelSetting; 76 // Defaulting the channel privilege to administrator level. 77 responseData->privilegeLimit = PRIVILEGE_ADMIN; 78 79 *data_len = outPayload.size(); 80 memcpy(response, outPayload.data(), *data_len); 81 82 return IPMI_CC_OK; 83 } 84 85 // ATTENTION: This ipmi function is very hardcoded on purpose 86 // OpenBMC does not fully support IPMI. This command is useful 87 // to have around because it enables testing of interfaces with 88 // the IPMI tool. 89 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0 90 // IPMI Table 6-2 91 #define IPMI_CHANNEL_TYPE_IPMB 1 92 // IPMI Table 6-3 93 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6 94 95 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 96 ipmi_request_t request, 97 ipmi_response_t response, 98 ipmi_data_len_t data_len, 99 ipmi_context_t context) 100 { 101 ipmi_ret_t rc = IPMI_CC_OK; 102 uint8_t resp[] = {1, 103 IPMI_CHANNEL_MEDIUM_TYPE_OTHER, 104 IPMI_CHANNEL_TYPE_IPMB, 105 1, 106 0x41, 107 0xA7, 108 0x00, 109 0, 110 0}; 111 uint8_t* p = (uint8_t*)request; 112 int channel = (*p) & CHANNEL_MASK; 113 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel); 114 115 // The supported channels numbers are those which are configured. 116 // Channel Number E is used as way to identify the current channel 117 // that the command is being is received from. 118 if (channel != 0xe && ethdevice.empty()) 119 { 120 rc = IPMI_CC_PARM_OUT_OF_RANGE; 121 *data_len = 0; 122 } 123 else 124 { 125 *data_len = sizeof(resp); 126 memcpy(response, resp, *data_len); 127 } 128 129 return rc; 130 } 131 132 namespace cipher 133 { 134 135 /** @brief Get the supported Cipher records 136 * 137 * The cipher records are read from the JSON file and converted into cipher 138 * suite record format mentioned in the IPMI specification. The records can be 139 * either OEM or standard cipher. Each json entry is parsed and converted into 140 * the cipher record format and pushed into the vector. 141 * 142 * @return vector containing all the cipher suite records. 143 * 144 */ 145 std::vector<uint8_t> getCipherRecords() 146 { 147 std::vector<uint8_t> records; 148 149 std::ifstream jsonFile(configFile); 150 if (!jsonFile.is_open()) 151 { 152 log<level::ERR>("Channel Cipher suites file not found"); 153 elog<InternalFailure>(); 154 } 155 156 auto data = Json::parse(jsonFile, nullptr, false); 157 if (data.is_discarded()) 158 { 159 log<level::ERR>("Parsing channel cipher suites JSON failed"); 160 elog<InternalFailure>(); 161 } 162 163 for (const auto& record : data) 164 { 165 if (record.find(oem) != record.end()) 166 { 167 // OEM cipher suite - 0xC1 168 records.push_back(oemCipherSuite); 169 // Cipher Suite ID 170 records.push_back(record.value(cipher, 0)); 171 // OEM IANA - 3 bytes 172 records.push_back(record.value(oem, 0)); 173 records.push_back(record.value(oem, 0) >> 8); 174 records.push_back(record.value(oem, 0) >> 16); 175 } 176 else 177 { 178 // OEM cipher suite - 0xC0 179 records.push_back(stdCipherSuite); 180 // Cipher Suite ID 181 records.push_back(record.value(cipher, 0)); 182 } 183 184 // Authentication algorithm number 185 records.push_back(record.value(auth, 0)); 186 // Integrity algorithm number 187 records.push_back(record.value(integrity, 0) | integrityTag); 188 // Confidentiality algorithm number 189 records.push_back(record.value(conf, 0) | confTag); 190 } 191 192 return records; 193 } 194 195 } // namespace cipher 196 197 ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 198 ipmi_request_t request, 199 ipmi_response_t response, 200 ipmi_data_len_t data_len, 201 ipmi_context_t context) 202 { 203 static std::vector<uint8_t> records; 204 static auto recordInit = false; 205 206 auto requestData = 207 reinterpret_cast<const GetChannelCipherRequest*>(request); 208 209 if (*data_len < sizeof(GetChannelCipherRequest)) 210 { 211 *data_len = 0; 212 return IPMI_CC_REQ_DATA_LEN_INVALID; 213 } 214 215 *data_len = 0; 216 217 // Support only for list algorithms by cipher suite 218 if (cipher::listCipherSuite != 219 (requestData->listIndex & cipher::listTypeMask)) 220 { 221 return IPMI_CC_INVALID_FIELD_REQUEST; 222 } 223 224 if (!recordInit) 225 { 226 try 227 { 228 records = cipher::getCipherRecords(); 229 recordInit = true; 230 } 231 catch (const std::exception& e) 232 { 233 return IPMI_CC_UNSPECIFIED_ERROR; 234 } 235 } 236 237 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next 238 // set of 16 and so on. 239 auto index = 240 static_cast<size_t>(requestData->listIndex & cipher::listIndexMask); 241 242 // Calculate the number of record data bytes to be returned. 243 auto start = std::min(index * cipher::respSize, records.size()); 244 auto end = 245 std::min((index * cipher::respSize) + cipher::respSize, records.size()); 246 auto size = end - start; 247 248 auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response); 249 responseData->channelNumber = cipher::defaultChannelNumber; 250 251 if (!size) 252 { 253 *data_len = sizeof(GetChannelCipherRespHeader); 254 } 255 else 256 { 257 std::copy_n(records.data() + start, size, 258 static_cast<uint8_t*>(response) + 1); 259 *data_len = size + sizeof(GetChannelCipherRespHeader); 260 } 261 262 return IPMI_CC_OK; 263 } 264 265 template <typename... ArgTypes> 266 static int executeCmd(const char* path, ArgTypes&&... tArgs) 267 { 268 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 269 execProg.wait(); 270 return execProg.exit_code(); 271 } 272 273 /** @brief Enable the network IPMI service on the specified ethernet interface. 274 * 275 * @param[in] intf - ethernet interface on which to enable IPMI 276 */ 277 void enableNetworkIPMI(const std::string& intf) 278 { 279 // Check if there is a iptable filter to drop IPMI packets for the 280 // interface. 281 auto retCode = 282 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 283 intf.c_str(), "--dport", "623", "-j", "DROP"); 284 285 // If the iptable filter exists, delete the filter. 286 if (!retCode) 287 { 288 auto response = 289 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i", 290 intf.c_str(), "--dport", "623", "-j", "DROP"); 291 292 if (response) 293 { 294 log<level::ERR>("Dropping the iptables filter failed", 295 entry("INTF=%s", intf.c_str()), 296 entry("RETURN_CODE:%d", response)); 297 } 298 } 299 } 300 301 /** @brief Disable the network IPMI service on the specified ethernet interface. 302 * 303 * @param[in] intf - ethernet interface on which to disable IPMI 304 */ 305 void disableNetworkIPMI(const std::string& intf) 306 { 307 // Check if there is a iptable filter to drop IPMI packets for the 308 // interface. 309 auto retCode = 310 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 311 intf.c_str(), "--dport", "623", "-j", "DROP"); 312 313 // If the iptable filter does not exist, add filter to drop network IPMI 314 // packets 315 if (retCode) 316 { 317 auto response = 318 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i", 319 intf.c_str(), "--dport", "623", "-j", "DROP"); 320 321 if (response) 322 { 323 log<level::ERR>("Inserting iptables filter failed", 324 entry("INTF=%s", intf.c_str()), 325 entry("RETURN_CODE:%d", response)); 326 } 327 } 328 } 329 330 /** @struct SetChannelAccessRequest 331 * 332 * IPMI payload for Set Channel access command request. 333 */ 334 struct SetChannelAccessRequest 335 { 336 uint8_t channelNumber; //!< Channel number 337 uint8_t accessMode; //!< Access mode for IPMI messaging 338 uint8_t privLevel; //!< Channel Privilege Level 339 } __attribute__((packed)); 340 341 ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 342 ipmi_request_t request, 343 ipmi_response_t response, 344 ipmi_data_len_t data_len, 345 ipmi_context_t context) 346 { 347 auto requestData = 348 reinterpret_cast<const SetChannelAccessRequest*>(request); 349 350 int channel = requestData->channelNumber; 351 // Validate the channel number corresponds to any of the network channel. 352 auto ethdevice = ipmi::network::ChanneltoEthernet(channel); 353 if (ethdevice.empty()) 354 { 355 *data_len = 0; 356 return IPMI_CC_INVALID_FIELD_REQUEST; 357 } 358 359 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 360 // Bits[2:0] indicates the Access Mode, this mask field will extract the 361 // access mode from the command data. 362 static constexpr auto accessModeMask = 0x07; 363 auto accessMode = requestData->accessMode & accessModeMask; 364 static constexpr auto disabled = 0; 365 static constexpr auto enabled = 2; 366 367 try 368 { 369 if (accessMode == enabled) 370 { 371 enableNetworkIPMI(ethdevice); 372 } 373 else if (accessMode == disabled) 374 { 375 disableNetworkIPMI(ethdevice); 376 } 377 } 378 catch (const sdbusplus::exception::SdBusError& e) 379 { 380 log<level::ERR>(e.what()); 381 *data_len = 0; 382 return IPMI_CC_UNSPECIFIED_ERROR; 383 } 384 385 return IPMI_CC_OK; 386 } 387