1 #include "rakp12.hpp" 2 3 #include "comm_module.hpp" 4 #include "endian.hpp" 5 #include "guid.hpp" 6 #include "sessions_manager.hpp" 7 8 #include <openssl/rand.h> 9 10 #include <ipmid/types.hpp> 11 #include <phosphor-logging/log.hpp> 12 13 #include <algorithm> 14 #include <cstring> 15 #include <iomanip> 16 17 using namespace phosphor::logging; 18 19 namespace command 20 { 21 22 bool isChannelAccessModeEnabled(const uint8_t accessMode) 23 { 24 return accessMode != 25 static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled); 26 } 27 28 void logInvalidLoginRedfishEvent(const std::string& journalMsg, 29 const std::optional<std::string>& messageArgs) 30 { 31 static constexpr std::string_view openBMCMessageRegistryVersion = "0.1."; 32 std::string messageID = "OpenBMC." + 33 std::string(openBMCMessageRegistryVersion) + 34 "InvalidLoginAttempted"; 35 phosphor::logging::log<phosphor::logging::level::ERR>( 36 journalMsg.c_str(), 37 phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str()), 38 phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s", 39 messageArgs.value().c_str())); 40 } 41 std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, 42 std::shared_ptr<message::Handler>& handler) 43 { 44 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data()); 45 // verify inPayload minimum size 46 if (inPayload.size() < (sizeof(*request) - userNameMaxLen)) 47 { 48 std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID}; 49 return errorPayload; 50 } 51 52 std::vector<uint8_t> outPayload(sizeof(RAKP2response)); 53 auto response = reinterpret_cast<RAKP2response*>(outPayload.data()); 54 55 // Session ID zero is reserved for Session Setup 56 if (endian::from_ipmi(request->managedSystemSessionID) == 57 session::sessionZero) 58 { 59 log<level::INFO>("RAKP12: BMC invalid Session ID"); 60 response->rmcpStatusCode = 61 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 62 return outPayload; 63 } 64 65 std::shared_ptr<session::Session> session; 66 try 67 { 68 session = session::Manager::get().getSession( 69 endian::from_ipmi(request->managedSystemSessionID)); 70 } 71 catch (const std::exception& e) 72 { 73 log<level::ERR>("RAKP12 : session not found", 74 entry("EXCEPTION=%s", e.what())); 75 response->rmcpStatusCode = 76 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 77 return outPayload; 78 } 79 80 auto rakp1Size = 81 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len); 82 83 std::string message = "Invalid login attempted via RCMPP interface "; 84 // Validate user name length in the message 85 if (request->user_name_len > userNameMaxLen || 86 inPayload.size() != rakp1Size) 87 { 88 response->rmcpStatusCode = 89 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH); 90 logInvalidLoginRedfishEvent(message); 91 return outPayload; 92 } 93 94 session->userName.assign(request->user_name, request->user_name_len); 95 96 // Update transaction time 97 session->updateLastTransactionTime(); 98 99 auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); 100 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID()); 101 auto authAlgo = session->getAuthAlgo(); 102 103 /* 104 * Generate Key Authentication Code - RAKP 2 105 * 106 * 1) Remote Console Session ID - 4 bytes 107 * 2) Managed System Session ID - 4 bytes 108 * 3) Remote Console Random Number - 16 bytes 109 * 4) Managed System Random Number - 16 bytes 110 * 5) Managed System GUID - 16 bytes 111 * 6) Requested Privilege Level - 1 byte 112 * 7) User Name Length Byte - 1 byte (0 for 'null' username) 113 * 8) User Name - variable (absent for 'null' username) 114 */ 115 116 std::vector<uint8_t> input; 117 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) + 118 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN + 119 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN + 120 sizeof(request->req_max_privilege_level) + 121 sizeof(request->user_name_len) + session->userName.size()); 122 123 auto iter = input.begin(); 124 125 // Remote Console Session ID 126 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID), 127 iter); 128 std::advance(iter, sizeof(rcSessionID)); 129 130 // Managed System Session ID 131 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID), 132 iter); 133 std::advance(iter, sizeof(bmcSessionID)); 134 135 // Copy the Remote Console Random Number from the RAKP1 request to the 136 // Authentication Algorithm 137 std::copy_n( 138 reinterpret_cast<const uint8_t*>(request->remote_console_random_number), 139 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 140 authAlgo->rcRandomNum.begin()); 141 142 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter); 143 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN); 144 145 // Generate the Managed System Random Number 146 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) + 147 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 148 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN)) 149 { 150 response->rmcpStatusCode = 151 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE); 152 return outPayload; 153 } 154 // As stated in Set Session Privilege Level command in IPMI Spec, when 155 // creating a session through Activate command / RAKP 1 message, it must 156 // be established with USER privilege as well as all other sessions are 157 // initially set to USER privilege, regardless of the requested maximum 158 // privilege. 159 if (!(static_cast<session::Privilege>(request->req_max_privilege_level & 160 session::reqMaxPrivMask) > 161 session::Privilege::CALLBACK)) 162 { 163 response->rmcpStatusCode = 164 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV); 165 return outPayload; 166 } 167 session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER)); 168 169 session->reqMaxPrivLevel = 170 static_cast<session::Privilege>(request->req_max_privilege_level); 171 if (request->user_name_len == 0) 172 { 173 // Bail out, if user name is not specified. 174 // Yes, NULL user name is not supported for security reasons. 175 response->rmcpStatusCode = 176 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 177 logInvalidLoginRedfishEvent(message); 178 return outPayload; 179 } 180 181 // Perform user name based lookup 182 std::string userName(request->user_name, request->user_name_len); 183 ipmi::SecureString passwd; 184 uint8_t userId = ipmi::ipmiUserGetUserId(userName); 185 if (userId == ipmi::invalidUserId) 186 { 187 response->rmcpStatusCode = 188 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 189 logInvalidLoginRedfishEvent(message); 190 return outPayload; 191 } 192 // check user is enabled before proceeding. 193 bool userEnabled = false; 194 ipmi::ipmiUserCheckEnabled(userId, userEnabled); 195 if (!userEnabled) 196 { 197 response->rmcpStatusCode = 198 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 199 logInvalidLoginRedfishEvent(message); 200 return outPayload; 201 } 202 // Get the user password for RAKP message authenticate 203 passwd = ipmi::ipmiUserGetPassword(userName); 204 if (passwd.empty()) 205 { 206 response->rmcpStatusCode = 207 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 208 logInvalidLoginRedfishEvent(message); 209 return outPayload; 210 } 211 // Check whether user is already locked for failed attempts 212 if (!ipmi::ipmiUserPamAuthenticate(userName, passwd)) 213 { 214 log<level::ERR>("Authentication failed - user already locked out", 215 entry("USER-ID=%d", static_cast<uint8_t>(userId))); 216 217 response->rmcpStatusCode = 218 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 219 logInvalidLoginRedfishEvent(message); 220 return outPayload; 221 } 222 223 uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex()); 224 // Get channel based access information 225 if ((ipmi::ipmiUserGetPrivilegeAccess( 226 userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) || 227 (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) != 228 IPMI_CC_OK)) 229 { 230 response->rmcpStatusCode = 231 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 232 logInvalidLoginRedfishEvent(message); 233 return outPayload; 234 } 235 if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode)) 236 { 237 phosphor::logging::log<phosphor::logging::level::ERR>( 238 "Channel access mode disabled."); 239 response->rmcpStatusCode = 240 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 241 logInvalidLoginRedfishEvent(message); 242 return outPayload; 243 } 244 if (session->sessionUserPrivAccess.privilege > 245 static_cast<uint8_t>(session::Privilege::OEM)) 246 { 247 response->rmcpStatusCode = 248 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 249 logInvalidLoginRedfishEvent(message); 250 return outPayload; 251 } 252 session->channelNum(chNum); 253 session->userID(userId); 254 // minimum privilege of Channel / User / session::privilege::USER 255 // has to be used as session current privilege level 256 uint8_t minPriv = 0; 257 if (session->sessionChannelAccess.privLimit < 258 session->sessionUserPrivAccess.privilege) 259 { 260 minPriv = session->sessionChannelAccess.privLimit; 261 } 262 else 263 { 264 minPriv = session->sessionUserPrivAccess.privilege; 265 } 266 if (session->currentPrivilege() > minPriv) 267 { 268 session->currentPrivilege(static_cast<uint8_t>(minPriv)); 269 } 270 // For username / privilege lookup, fail with UNAUTH_NAME, if requested 271 // max privilege does not match user privilege 272 if (((request->req_max_privilege_level & userNameOnlyLookupMask) == 273 userNamePrivLookup) && 274 ((request->req_max_privilege_level & session::reqMaxPrivMask) != 275 session->sessionUserPrivAccess.privilege)) 276 { 277 log<level::INFO>( 278 "Username/Privilege lookup failed for requested privilege"); 279 response->rmcpStatusCode = 280 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 281 282 logInvalidLoginRedfishEvent(message); 283 return outPayload; 284 } 285 286 std::fill(authAlgo->userKey.data(), 287 authAlgo->userKey.data() + authAlgo->userKey.size(), 0); 288 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data()); 289 290 // Copy the Managed System Random Number to the Authentication Algorithm 291 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN, 292 authAlgo->bmcRandomNum.begin()); 293 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN); 294 295 // Managed System GUID 296 std::copy_n(cache::guid.data(), cache::guid.size(), iter); 297 std::advance(iter, BMC_GUID_LEN); 298 299 // Requested Privilege Level 300 std::copy_n(&(request->req_max_privilege_level), 301 sizeof(request->req_max_privilege_level), iter); 302 std::advance(iter, sizeof(request->req_max_privilege_level)); 303 304 // User Name Length Byte 305 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len), 306 iter); 307 std::advance(iter, sizeof(request->user_name_len)); 308 309 std::copy_n(session->userName.data(), session->userName.size(), iter); 310 311 // Generate Key Exchange Authentication Code - RAKP2 312 auto output = authAlgo->generateHMAC(input); 313 314 response->messageTag = request->messageTag; 315 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR); 316 response->reserved = 0; 317 response->remoteConsoleSessionID = rcSessionID; 318 319 // Copy Managed System Random Number to the Response 320 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(), 321 response->managed_system_random_number); 322 323 // Copy System GUID to the Response 324 std::copy_n(cache::guid.data(), cache::guid.size(), 325 response->managed_system_guid); 326 327 // Insert the HMAC output into the payload 328 outPayload.insert(outPayload.end(), output.begin(), output.end()); 329 return outPayload; 330 } 331 332 } // namespace command 333