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