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 <fstream> 11 #include <phosphor-logging/elog-errors.hpp> 12 #include <phosphor-logging/log.hpp> 13 #include <string> 14 #include <xyz/openbmc_project/Common/error.hpp> 15 16 using namespace phosphor::logging; 17 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 18 19 /** @struct GetChannelAccessRequest 20 * 21 * IPMI payload for Get Channel access command request. 22 */ 23 struct GetChannelAccessRequest 24 { 25 uint8_t channelNumber; //!< Channel number. 26 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting. 27 } __attribute__((packed)); 28 29 /** @struct GetChannelAccessResponse 30 * 31 * IPMI payload for Get Channel access command response. 32 */ 33 struct GetChannelAccessResponse 34 { 35 uint8_t settings; //!< Channel settings. 36 uint8_t privilegeLimit; //!< Channel privilege level limit. 37 } __attribute__((packed)); 38 39 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 40 ipmi_request_t request, 41 ipmi_response_t response, 42 ipmi_data_len_t data_len, 43 ipmi_context_t context) 44 { 45 auto requestData = 46 reinterpret_cast<const GetChannelAccessRequest*>(request); 47 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse)); 48 auto responseData = 49 reinterpret_cast<GetChannelAccessResponse*>(outPayload.data()); 50 51 /* 52 * The value Eh is used as a way to identify the current channel that 53 * the command is being received from. 54 */ 55 constexpr auto channelE = 0x0E; 56 int channel = requestData->channelNumber; 57 auto ethdevice = ipmi::network::ChanneltoEthernet(channel); 58 59 if (channel != channelE && ethdevice.empty()) 60 { 61 *data_len = 0; 62 return IPMI_CC_INVALID_FIELD_REQUEST; 63 } 64 65 /* 66 * [7:6] - reserved 67 * [5] - 1b = Alerting disabled 68 * [4] - 1b = per message authentication disabled 69 * [3] - 0b = User level authentication enabled 70 * [2:0] - 2h = always available 71 */ 72 constexpr auto channelSetting = 0x32; 73 74 responseData->settings = channelSetting; 75 // Defaulting the channel privilege to administrator level. 76 responseData->privilegeLimit = PRIVILEGE_ADMIN; 77 78 *data_len = outPayload.size(); 79 memcpy(response, outPayload.data(), *data_len); 80 81 return IPMI_CC_OK; 82 } 83 84 // ATTENTION: This ipmi function is very hardcoded on purpose 85 // OpenBMC does not fully support IPMI. This command is useful 86 // to have around because it enables testing of interfaces with 87 // the IPMI tool. 88 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0 89 // IPMI Table 6-2 90 #define IPMI_CHANNEL_TYPE_IPMB 1 91 // IPMI Table 6-3 92 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6 93 94 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 95 ipmi_request_t request, 96 ipmi_response_t response, 97 ipmi_data_len_t data_len, 98 ipmi_context_t context) 99 { 100 ipmi_ret_t rc = IPMI_CC_OK; 101 uint8_t resp[] = {1, 102 IPMI_CHANNEL_MEDIUM_TYPE_OTHER, 103 IPMI_CHANNEL_TYPE_IPMB, 104 1, 105 0x41, 106 0xA7, 107 0x00, 108 0, 109 0}; 110 uint8_t* p = (uint8_t*)request; 111 int channel = (*p) & CHANNEL_MASK; 112 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel); 113 114 // The supported channels numbers are those which are configured. 115 // Channel Number E is used as way to identify the current channel 116 // that the command is being is received from. 117 if (channel != 0xe && ethdevice.empty()) 118 { 119 rc = IPMI_CC_PARM_OUT_OF_RANGE; 120 *data_len = 0; 121 } 122 else 123 { 124 *data_len = sizeof(resp); 125 memcpy(response, resp, *data_len); 126 } 127 128 return rc; 129 } 130 131 namespace cipher 132 { 133 134 /** @brief Get the supported Cipher records 135 * 136 * The cipher records are read from the JSON file and converted into cipher 137 * suite record format mentioned in the IPMI specification. The records can be 138 * either OEM or standard cipher. Each json entry is parsed and converted into 139 * the cipher record format and pushed into the vector. 140 * 141 * @return vector containing all the cipher suite records. 142 * 143 */ 144 std::vector<uint8_t> getCipherRecords() 145 { 146 std::vector<uint8_t> records; 147 148 std::ifstream jsonFile(configFile); 149 if (!jsonFile.is_open()) 150 { 151 log<level::ERR>("Channel Cipher suites file not found"); 152 elog<InternalFailure>(); 153 } 154 155 auto data = Json::parse(jsonFile, nullptr, false); 156 if (data.is_discarded()) 157 { 158 log<level::ERR>("Parsing channel cipher suites JSON failed"); 159 elog<InternalFailure>(); 160 } 161 162 for (const auto& record : data) 163 { 164 if (record.find(oem) != record.end()) 165 { 166 // OEM cipher suite - 0xC1 167 records.push_back(oemCipherSuite); 168 // Cipher Suite ID 169 records.push_back(record.value(cipher, 0)); 170 // OEM IANA - 3 bytes 171 records.push_back(record.value(oem, 0)); 172 records.push_back(record.value(oem, 0) >> 8); 173 records.push_back(record.value(oem, 0) >> 16); 174 } 175 else 176 { 177 // OEM cipher suite - 0xC0 178 records.push_back(stdCipherSuite); 179 // Cipher Suite ID 180 records.push_back(record.value(cipher, 0)); 181 } 182 183 // Authentication algorithm number 184 records.push_back(record.value(auth, 0)); 185 // Integrity algorithm number 186 records.push_back(record.value(integrity, 0) | integrityTag); 187 // Confidentiality algorithm number 188 records.push_back(record.value(conf, 0) | confTag); 189 } 190 191 return records; 192 } 193 194 } // namespace cipher 195 196 ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 197 ipmi_request_t request, 198 ipmi_response_t response, 199 ipmi_data_len_t data_len, 200 ipmi_context_t context) 201 { 202 static std::vector<uint8_t> records; 203 static auto recordInit = false; 204 205 auto requestData = 206 reinterpret_cast<const GetChannelCipherRequest*>(request); 207 208 if (*data_len < sizeof(GetChannelCipherRequest)) 209 { 210 *data_len = 0; 211 return IPMI_CC_REQ_DATA_LEN_INVALID; 212 } 213 214 *data_len = 0; 215 216 // Support only for list algorithms by cipher suite 217 if (cipher::listCipherSuite != 218 (requestData->listIndex & cipher::listTypeMask)) 219 { 220 return IPMI_CC_INVALID_FIELD_REQUEST; 221 } 222 223 if (!recordInit) 224 { 225 try 226 { 227 records = cipher::getCipherRecords(); 228 recordInit = true; 229 } 230 catch (const std::exception& e) 231 { 232 return IPMI_CC_UNSPECIFIED_ERROR; 233 } 234 } 235 236 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next 237 // set of 16 and so on. 238 auto index = 239 static_cast<size_t>(requestData->listIndex & cipher::listIndexMask); 240 241 // Calculate the number of record data bytes to be returned. 242 auto start = std::min(index * cipher::respSize, records.size()); 243 auto end = 244 std::min((index * cipher::respSize) + cipher::respSize, records.size()); 245 auto size = end - start; 246 247 auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response); 248 responseData->channelNumber = cipher::defaultChannelNumber; 249 250 if (!size) 251 { 252 *data_len = sizeof(GetChannelCipherRespHeader); 253 } 254 else 255 { 256 std::copy_n(records.data() + start, size, 257 static_cast<uint8_t*>(response) + 1); 258 *data_len = size + sizeof(GetChannelCipherRespHeader); 259 } 260 261 return IPMI_CC_OK; 262 } 263