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 <phosphor-logging/log.hpp> 14 15 using namespace phosphor::logging; 16 17 namespace command 18 { 19 20 bool isChannelAccessModeEnabled(const uint8_t accessMode) 21 { 22 return accessMode != 23 static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled); 24 } 25 26 void logInvalidLoginRedfishEvent(const std::string& journalMsg, 27 const std::optional<std::string>& messageArgs) 28 { 29 static constexpr std::string_view openBMCMessageRegistryVersion = "0.1."; 30 std::string messageID = "OpenBMC." + 31 std::string(openBMCMessageRegistryVersion) + 32 "InvalidLoginAttempted"; 33 phosphor::logging::log<phosphor::logging::level::ERR>( 34 journalMsg.c_str(), 35 phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str()), 36 phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s", 37 messageArgs.value().c_str())); 38 } 39 std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, 40 std::shared_ptr<message::Handler>& handler) 41 { 42 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data()); 43 // verify inPayload minimum size 44 if (inPayload.size() < (sizeof(*request) - userNameMaxLen)) 45 { 46 std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID}; 47 return errorPayload; 48 } 49 50 std::vector<uint8_t> outPayload(sizeof(RAKP2response)); 51 auto response = reinterpret_cast<RAKP2response*>(outPayload.data()); 52 53 // Session ID zero is reserved for Session Setup 54 if (endian::from_ipmi(request->managedSystemSessionID) == 55 session::sessionZero) 56 { 57 log<level::INFO>("RAKP12: BMC invalid Session ID"); 58 response->rmcpStatusCode = 59 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 60 return outPayload; 61 } 62 63 std::shared_ptr<session::Session> session; 64 try 65 { 66 session = session::Manager::get().getSession( 67 endian::from_ipmi(request->managedSystemSessionID)); 68 } 69 catch (std::exception& e) 70 { 71 log<level::ERR>("RAKP12 : session not found", 72 entry("EXCEPTION=%s", e.what())); 73 response->rmcpStatusCode = 74 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 75 return outPayload; 76 } 77 78 auto rakp1Size = 79 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len); 80 81 std::string message = "Invalid login attempted via RCMPP interface "; 82 // Validate user name length in the message 83 if (request->user_name_len > userNameMaxLen || 84 inPayload.size() != rakp1Size) 85 { 86 response->rmcpStatusCode = 87 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH); 88 logInvalidLoginRedfishEvent(message); 89 return outPayload; 90 } 91 92 session->userName.assign(request->user_name, request->user_name_len); 93 94 // Update transaction time 95 session->updateLastTransactionTime(); 96 97 auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); 98 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID()); 99 auto authAlgo = session->getAuthAlgo(); 100 101 /* 102 * Generate Key Authentication Code - RAKP 2 103 * 104 * 1) Remote Console Session ID - 4 bytes 105 * 2) Managed System Session ID - 4 bytes 106 * 3) Remote Console Random Number - 16 bytes 107 * 4) Managed System Random Number - 16 bytes 108 * 5) Managed System GUID - 16 bytes 109 * 6) Requested Privilege Level - 1 byte 110 * 7) User Name Length Byte - 1 byte (0 for 'null' username) 111 * 8) User Name - variable (absent for 'null' username) 112 */ 113 114 std::vector<uint8_t> input; 115 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) + 116 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN + 117 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN + 118 sizeof(request->req_max_privilege_level) + 119 sizeof(request->user_name_len) + session->userName.size()); 120 121 auto iter = input.begin(); 122 123 // Remote Console Session ID 124 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID), 125 iter); 126 std::advance(iter, sizeof(rcSessionID)); 127 128 // Managed System Session ID 129 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID), 130 iter); 131 std::advance(iter, sizeof(bmcSessionID)); 132 133 // Copy the Remote Console Random Number from the RAKP1 request to the 134 // Authentication Algorithm 135 std::copy_n( 136 reinterpret_cast<const uint8_t*>(request->remote_console_random_number), 137 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 138 authAlgo->rcRandomNum.begin()); 139 140 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter); 141 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN); 142 143 // Generate the Managed System Random Number 144 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) + 145 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 146 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN)) 147 { 148 response->rmcpStatusCode = 149 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE); 150 return outPayload; 151 } 152 // As stated in Set Session Privilege Level command in IPMI Spec, when 153 // creating a session through Activate command / RAKP 1 message, it must 154 // be established with USER privilege as well as all other sessions are 155 // initially set to USER privilege, regardless of the requested maximum 156 // privilege. 157 if (!(static_cast<session::Privilege>(request->req_max_privilege_level & 158 session::reqMaxPrivMask) > 159 session::Privilege::CALLBACK)) 160 { 161 response->rmcpStatusCode = 162 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV); 163 return outPayload; 164 } 165 session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER)); 166 167 session->reqMaxPrivLevel = 168 static_cast<session::Privilege>(request->req_max_privilege_level); 169 if (request->user_name_len == 0) 170 { 171 // Bail out, if user name is not specified. 172 // Yes, NULL user name is not supported for security reasons. 173 response->rmcpStatusCode = 174 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 175 logInvalidLoginRedfishEvent(message); 176 return outPayload; 177 } 178 179 // Perform user name based lookup 180 std::string userName(request->user_name, request->user_name_len); 181 std::string passwd; 182 183 message += "user: " + userName; 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