18bb10b79STom Joseph #include "rakp12.hpp" 28bb10b79STom Joseph 39e801a2bSVernon Mauery #include "comm_module.hpp" 49e801a2bSVernon Mauery #include "endian.hpp" 59e801a2bSVernon Mauery #include "guid.hpp" 62085ae07SVernon Mauery #include "sessions_manager.hpp" 79e801a2bSVernon Mauery 88bb10b79STom Joseph #include <openssl/rand.h> 98bb10b79STom Joseph 108bb10b79STom Joseph #include <algorithm> 1156527b93STom Joseph #include <cstring> 128bb10b79STom Joseph #include <iomanip> 13fc37e59eSVernon Mauery #include <phosphor-logging/log.hpp> 148bb10b79STom Joseph 15fc37e59eSVernon Mauery using namespace phosphor::logging; 16fc37e59eSVernon Mauery 178bb10b79STom Joseph namespace command 188bb10b79STom Joseph { 198bb10b79STom Joseph 20ecb32fbcSAppaRao Puli bool isChannelAccessModeEnabled(const uint8_t accessMode) 21ecb32fbcSAppaRao Puli { 22ecb32fbcSAppaRao Puli return accessMode != 23ecb32fbcSAppaRao Puli static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled); 24ecb32fbcSAppaRao Puli } 25ecb32fbcSAppaRao Puli 26*0e0546f1Ssunitakx void logInvalidLoginRedfishEvent(const std::string& journalMsg, 27*0e0546f1Ssunitakx const std::optional<std::string>& messageArgs) 28*0e0546f1Ssunitakx { 29*0e0546f1Ssunitakx static constexpr std::string_view openBMCMessageRegistryVersion = "0.1."; 30*0e0546f1Ssunitakx std::string messageID = "OpenBMC." + 31*0e0546f1Ssunitakx std::string(openBMCMessageRegistryVersion) + 32*0e0546f1Ssunitakx "InvalidLoginAttempted"; 33*0e0546f1Ssunitakx phosphor::logging::log<phosphor::logging::level::ERR>( 34*0e0546f1Ssunitakx journalMsg.c_str(), 35*0e0546f1Ssunitakx phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str()), 36*0e0546f1Ssunitakx phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s", 37*0e0546f1Ssunitakx messageArgs.value().c_str())); 38*0e0546f1Ssunitakx } 3918a45e9dSTom Joseph std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, 4041ff9b51SVernon Mauery std::shared_ptr<message::Handler>& handler) 418bb10b79STom Joseph { 4218a45e9dSTom Joseph auto request = reinterpret_cast<const RAKP1request*>(inPayload.data()); 432b1edef0SZhikui Ren // verify inPayload minimum size 442b1edef0SZhikui Ren if (inPayload.size() < (sizeof(*request) - userNameMaxLen)) 452b1edef0SZhikui Ren { 462b1edef0SZhikui Ren std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID}; 472b1edef0SZhikui Ren return errorPayload; 482b1edef0SZhikui Ren } 492b1edef0SZhikui Ren 502b1edef0SZhikui Ren std::vector<uint8_t> outPayload(sizeof(RAKP2response)); 518bb10b79STom Joseph auto response = reinterpret_cast<RAKP2response*>(outPayload.data()); 528bb10b79STom Joseph 538bb10b79STom Joseph // Session ID zero is reserved for Session Setup 548bb10b79STom Joseph if (endian::from_ipmi(request->managedSystemSessionID) == 55f8a34fc4SSuryakanth Sekar session::sessionZero) 568bb10b79STom Joseph { 57fc37e59eSVernon Mauery log<level::INFO>("RAKP12: BMC invalid Session ID"); 588bb10b79STom Joseph response->rmcpStatusCode = 598bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 608bb10b79STom Joseph return outPayload; 618bb10b79STom Joseph } 628bb10b79STom Joseph 638bb10b79STom Joseph std::shared_ptr<session::Session> session; 648bb10b79STom Joseph try 658bb10b79STom Joseph { 662085ae07SVernon Mauery session = session::Manager::get().getSession( 672085ae07SVernon Mauery endian::from_ipmi(request->managedSystemSessionID)); 688bb10b79STom Joseph } 698bb10b79STom Joseph catch (std::exception& e) 708bb10b79STom Joseph { 71fc37e59eSVernon Mauery log<level::ERR>("RAKP12 : session not found", 72fc37e59eSVernon Mauery entry("EXCEPTION=%s", e.what())); 738bb10b79STom Joseph response->rmcpStatusCode = 748bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 758bb10b79STom Joseph return outPayload; 768bb10b79STom Joseph } 778bb10b79STom Joseph 789e801a2bSVernon Mauery auto rakp1Size = 799e801a2bSVernon Mauery sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len); 8056527b93STom Joseph 81*0e0546f1Ssunitakx std::string message = "Invalid login attempted via RCMPP interface "; 8256527b93STom Joseph // Validate user name length in the message 8356527b93STom Joseph if (request->user_name_len > userNameMaxLen || 8456527b93STom Joseph inPayload.size() != rakp1Size) 8556527b93STom Joseph { 8656527b93STom Joseph response->rmcpStatusCode = 8756527b93STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH); 88*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 8956527b93STom Joseph return outPayload; 9056527b93STom Joseph } 9156527b93STom Joseph 9256527b93STom Joseph session->userName.assign(request->user_name, request->user_name_len); 9356527b93STom Joseph 948bb10b79STom Joseph // Update transaction time 958bb10b79STom Joseph session->updateLastTransactionTime(); 968bb10b79STom Joseph 978bb10b79STom Joseph auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); 988bb10b79STom Joseph auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID()); 998bb10b79STom Joseph auto authAlgo = session->getAuthAlgo(); 1008bb10b79STom Joseph 1018bb10b79STom Joseph /* 1028bb10b79STom Joseph * Generate Key Authentication Code - RAKP 2 1038bb10b79STom Joseph * 1048bb10b79STom Joseph * 1) Remote Console Session ID - 4 bytes 1058bb10b79STom Joseph * 2) Managed System Session ID - 4 bytes 1068bb10b79STom Joseph * 3) Remote Console Random Number - 16 bytes 1078bb10b79STom Joseph * 4) Managed System Random Number - 16 bytes 1088bb10b79STom Joseph * 5) Managed System GUID - 16 bytes 1098bb10b79STom Joseph * 6) Requested Privilege Level - 1 byte 1108bb10b79STom Joseph * 7) User Name Length Byte - 1 byte (0 for 'null' username) 1118bb10b79STom Joseph * 8) User Name - variable (absent for 'null' username) 1128bb10b79STom Joseph */ 1138bb10b79STom Joseph 1148bb10b79STom Joseph std::vector<uint8_t> input; 1158bb10b79STom Joseph input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) + 1168bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN + 1179e801a2bSVernon Mauery cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN + 1189e801a2bSVernon Mauery sizeof(request->req_max_privilege_level) + 1199e801a2bSVernon Mauery sizeof(request->user_name_len) + session->userName.size()); 1208bb10b79STom Joseph 1218bb10b79STom Joseph auto iter = input.begin(); 1228bb10b79STom Joseph 1238bb10b79STom Joseph // Remote Console Session ID 1249e801a2bSVernon Mauery std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID), 1259e801a2bSVernon Mauery iter); 1268bb10b79STom Joseph std::advance(iter, sizeof(rcSessionID)); 1278bb10b79STom Joseph 1288bb10b79STom Joseph // Managed System Session ID 1298bb10b79STom Joseph std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID), 1308bb10b79STom Joseph iter); 1318bb10b79STom Joseph std::advance(iter, sizeof(bmcSessionID)); 1328bb10b79STom Joseph 1338bb10b79STom Joseph // Copy the Remote Console Random Number from the RAKP1 request to the 1348bb10b79STom Joseph // Authentication Algorithm 1359e801a2bSVernon Mauery std::copy_n( 1369e801a2bSVernon Mauery reinterpret_cast<const uint8_t*>(request->remote_console_random_number), 1378bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1388bb10b79STom Joseph authAlgo->rcRandomNum.begin()); 1398bb10b79STom Joseph 1409e801a2bSVernon Mauery std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter); 1418bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN); 1428bb10b79STom Joseph 1438bb10b79STom Joseph // Generate the Managed System Random Number 1448bb10b79STom Joseph if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) + 1458bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1468bb10b79STom Joseph cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN)) 1478bb10b79STom Joseph { 1488bb10b79STom Joseph response->rmcpStatusCode = 1498bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE); 1508bb10b79STom Joseph return outPayload; 1518bb10b79STom Joseph } 152d5a4f45eSRichard Marian Thomaiyar // As stated in Set Session Privilege Level command in IPMI Spec, when 1532555e2ecSjayaprakash Mutyala // creating a session through Activate command / RAKP 1 message, it must 1542555e2ecSjayaprakash Mutyala // be established with USER privilege as well as all other sessions are 1552555e2ecSjayaprakash Mutyala // initially set to USER privilege, regardless of the requested maximum 1562555e2ecSjayaprakash Mutyala // privilege. 1572555e2ecSjayaprakash Mutyala if (!(static_cast<session::Privilege>(request->req_max_privilege_level & 158d5a4f45eSRichard Marian Thomaiyar session::reqMaxPrivMask) > 1592555e2ecSjayaprakash Mutyala session::Privilege::CALLBACK)) 160d5a4f45eSRichard Marian Thomaiyar { 1612555e2ecSjayaprakash Mutyala response->rmcpStatusCode = 1622555e2ecSjayaprakash Mutyala static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV); 1632555e2ecSjayaprakash Mutyala return outPayload; 164d5a4f45eSRichard Marian Thomaiyar } 1652555e2ecSjayaprakash Mutyala session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER)); 1662555e2ecSjayaprakash Mutyala 1674021b1f7STom Joseph session->reqMaxPrivLevel = 1684021b1f7STom Joseph static_cast<session::Privilege>(request->req_max_privilege_level); 169d91fd9d2SRichard Marian Thomaiyar if (request->user_name_len == 0) 170127748a8SRichard Marian Thomaiyar { 171d91fd9d2SRichard Marian Thomaiyar // Bail out, if user name is not specified. 172d91fd9d2SRichard Marian Thomaiyar // Yes, NULL user name is not supported for security reasons. 173127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 174127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 175*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 176127748a8SRichard Marian Thomaiyar return outPayload; 177127748a8SRichard Marian Thomaiyar } 178127748a8SRichard Marian Thomaiyar 179127748a8SRichard Marian Thomaiyar // Perform user name based lookup 180127748a8SRichard Marian Thomaiyar std::string userName(request->user_name, request->user_name_len); 181127748a8SRichard Marian Thomaiyar std::string passwd; 182*0e0546f1Ssunitakx 183*0e0546f1Ssunitakx message += "user: " + userName; 184127748a8SRichard Marian Thomaiyar uint8_t userId = ipmi::ipmiUserGetUserId(userName); 185127748a8SRichard Marian Thomaiyar if (userId == ipmi::invalidUserId) 186127748a8SRichard Marian Thomaiyar { 187127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 188127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 189*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 190127748a8SRichard Marian Thomaiyar return outPayload; 191127748a8SRichard Marian Thomaiyar } 192127748a8SRichard Marian Thomaiyar // check user is enabled before proceeding. 193127748a8SRichard Marian Thomaiyar bool userEnabled = false; 194127748a8SRichard Marian Thomaiyar ipmi::ipmiUserCheckEnabled(userId, userEnabled); 195127748a8SRichard Marian Thomaiyar if (!userEnabled) 196127748a8SRichard Marian Thomaiyar { 197127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 198127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 199*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 200127748a8SRichard Marian Thomaiyar return outPayload; 201127748a8SRichard Marian Thomaiyar } 2024c4694efSRichard Marian Thomaiyar // Get the user password for RAKP message authenticate 2034c4694efSRichard Marian Thomaiyar passwd = ipmi::ipmiUserGetPassword(userName); 2044c4694efSRichard Marian Thomaiyar if (passwd.empty()) 2054c4694efSRichard Marian Thomaiyar { 2064c4694efSRichard Marian Thomaiyar response->rmcpStatusCode = 2074c4694efSRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 208*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 2094c4694efSRichard Marian Thomaiyar return outPayload; 2104c4694efSRichard Marian Thomaiyar } 211b31e9695SAyushi Smriti // Check whether user is already locked for failed attempts 212b31e9695SAyushi Smriti if (!ipmi::ipmiUserPamAuthenticate(userName, passwd)) 213b31e9695SAyushi Smriti { 214b31e9695SAyushi Smriti log<level::ERR>("Authentication failed - user already locked out", 215b31e9695SAyushi Smriti entry("USER-ID=%d", static_cast<uint8_t>(userId))); 216b31e9695SAyushi Smriti 217b31e9695SAyushi Smriti response->rmcpStatusCode = 218b31e9695SAyushi Smriti static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 219*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 220b31e9695SAyushi Smriti return outPayload; 221b31e9695SAyushi Smriti } 222d9c86bb7SSaravanan Palanisamy 223d9c86bb7SSaravanan Palanisamy uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex()); 224127748a8SRichard Marian Thomaiyar // Get channel based access information 225992e53c7SRichard Marian Thomaiyar if ((ipmi::ipmiUserGetPrivilegeAccess( 226992e53c7SRichard Marian Thomaiyar userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) || 227992e53c7SRichard Marian Thomaiyar (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) != 228992e53c7SRichard Marian Thomaiyar IPMI_CC_OK)) 229127748a8SRichard Marian Thomaiyar { 230127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 231127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 232*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 233127748a8SRichard Marian Thomaiyar return outPayload; 234127748a8SRichard Marian Thomaiyar } 235ecb32fbcSAppaRao Puli if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode)) 236ecb32fbcSAppaRao Puli { 237ecb32fbcSAppaRao Puli phosphor::logging::log<phosphor::logging::level::ERR>( 238ecb32fbcSAppaRao Puli "Channel access mode disabled."); 239ecb32fbcSAppaRao Puli response->rmcpStatusCode = 240ecb32fbcSAppaRao Puli static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 241*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 242ecb32fbcSAppaRao Puli return outPayload; 243ecb32fbcSAppaRao Puli } 244992e53c7SRichard Marian Thomaiyar if (session->sessionUserPrivAccess.privilege > 245992e53c7SRichard Marian Thomaiyar static_cast<uint8_t>(session::Privilege::OEM)) 2467e5d38d2SRichard Marian Thomaiyar { 2477e5d38d2SRichard Marian Thomaiyar response->rmcpStatusCode = 2487e5d38d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 249*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 2507e5d38d2SRichard Marian Thomaiyar return outPayload; 2517e5d38d2SRichard Marian Thomaiyar } 252f8a34fc4SSuryakanth Sekar session->channelNum(chNum); 253f8a34fc4SSuryakanth Sekar session->userID(userId); 2542555e2ecSjayaprakash Mutyala // minimum privilege of Channel / User / session::privilege::USER 255d5a4f45eSRichard Marian Thomaiyar // has to be used as session current privilege level 256127748a8SRichard Marian Thomaiyar uint8_t minPriv = 0; 257992e53c7SRichard Marian Thomaiyar if (session->sessionChannelAccess.privLimit < 258992e53c7SRichard Marian Thomaiyar session->sessionUserPrivAccess.privilege) 259127748a8SRichard Marian Thomaiyar { 260992e53c7SRichard Marian Thomaiyar minPriv = session->sessionChannelAccess.privLimit; 261127748a8SRichard Marian Thomaiyar } 262127748a8SRichard Marian Thomaiyar else 263127748a8SRichard Marian Thomaiyar { 264992e53c7SRichard Marian Thomaiyar minPriv = session->sessionUserPrivAccess.privilege; 265127748a8SRichard Marian Thomaiyar } 266f8a34fc4SSuryakanth Sekar if (session->currentPrivilege() > minPriv) 267127748a8SRichard Marian Thomaiyar { 268f8a34fc4SSuryakanth Sekar session->currentPrivilege(static_cast<uint8_t>(minPriv)); 269127748a8SRichard Marian Thomaiyar } 270d91fd9d2SRichard Marian Thomaiyar // For username / privilege lookup, fail with UNAUTH_NAME, if requested 271d8e92fe1SRichard Marian Thomaiyar // max privilege does not match user privilege 272d91fd9d2SRichard Marian Thomaiyar if (((request->req_max_privilege_level & userNameOnlyLookupMask) == 273d91fd9d2SRichard Marian Thomaiyar userNamePrivLookup) && 274d8e92fe1SRichard Marian Thomaiyar ((request->req_max_privilege_level & session::reqMaxPrivMask) != 275992e53c7SRichard Marian Thomaiyar session->sessionUserPrivAccess.privilege)) 276d91fd9d2SRichard Marian Thomaiyar { 277fc37e59eSVernon Mauery log<level::INFO>( 278fc37e59eSVernon Mauery "Username/Privilege lookup failed for requested privilege"); 279d91fd9d2SRichard Marian Thomaiyar response->rmcpStatusCode = 280d91fd9d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 281*0e0546f1Ssunitakx 282*0e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 283d91fd9d2SRichard Marian Thomaiyar return outPayload; 284d91fd9d2SRichard Marian Thomaiyar } 285127748a8SRichard Marian Thomaiyar 286127748a8SRichard Marian Thomaiyar std::fill(authAlgo->userKey.data(), 287127748a8SRichard Marian Thomaiyar authAlgo->userKey.data() + authAlgo->userKey.size(), 0); 288127748a8SRichard Marian Thomaiyar std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data()); 28999b87849SRichard Marian Thomaiyar 2908bb10b79STom Joseph // Copy the Managed System Random Number to the Authentication Algorithm 2918bb10b79STom Joseph std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN, 2928bb10b79STom Joseph authAlgo->bmcRandomNum.begin()); 2938bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN); 2948bb10b79STom Joseph 2958bb10b79STom Joseph // Managed System GUID 29683029cb8STom Joseph std::copy_n(cache::guid.data(), cache::guid.size(), iter); 2978bb10b79STom Joseph std::advance(iter, BMC_GUID_LEN); 2988bb10b79STom Joseph 2998bb10b79STom Joseph // Requested Privilege Level 3008bb10b79STom Joseph std::copy_n(&(request->req_max_privilege_level), 3018bb10b79STom Joseph sizeof(request->req_max_privilege_level), iter); 3028bb10b79STom Joseph std::advance(iter, sizeof(request->req_max_privilege_level)); 3038bb10b79STom Joseph 3048bb10b79STom Joseph // User Name Length Byte 3058bb10b79STom Joseph std::copy_n(&(request->user_name_len), sizeof(request->user_name_len), 3068bb10b79STom Joseph iter); 30756527b93STom Joseph std::advance(iter, sizeof(request->user_name_len)); 30856527b93STom Joseph 30956527b93STom Joseph std::copy_n(session->userName.data(), session->userName.size(), iter); 3108bb10b79STom Joseph 3118bb10b79STom Joseph // Generate Key Exchange Authentication Code - RAKP2 3128bb10b79STom Joseph auto output = authAlgo->generateHMAC(input); 3138bb10b79STom Joseph 3148bb10b79STom Joseph response->messageTag = request->messageTag; 3158bb10b79STom Joseph response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR); 3168bb10b79STom Joseph response->reserved = 0; 3178bb10b79STom Joseph response->remoteConsoleSessionID = rcSessionID; 3188bb10b79STom Joseph 3198bb10b79STom Joseph // Copy Managed System Random Number to the Response 3208bb10b79STom Joseph std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(), 3218bb10b79STom Joseph response->managed_system_random_number); 3228bb10b79STom Joseph 3238bb10b79STom Joseph // Copy System GUID to the Response 3249e801a2bSVernon Mauery std::copy_n(cache::guid.data(), cache::guid.size(), 32583029cb8STom Joseph response->managed_system_guid); 3268bb10b79STom Joseph 3278bb10b79STom Joseph // Insert the HMAC output into the payload 3288bb10b79STom Joseph outPayload.insert(outPayload.end(), output.begin(), output.end()); 3298bb10b79STom Joseph return outPayload; 3308bb10b79STom Joseph } 3318bb10b79STom Joseph 3328bb10b79STom Joseph } // namespace command 333