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