1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "dbus_utility.hpp" 8 #include "error_messages.hpp" 9 #include "http_request.hpp" 10 #include "logging.hpp" 11 #include "utils/collection.hpp" 12 #include "utils/hex_utils.hpp" 13 #include "utils/json_utils.hpp" 14 15 #include <boost/beast/http/verb.hpp> 16 #include <boost/system/error_code.hpp> 17 #include <boost/url/format.hpp> 18 #include <boost/url/url.hpp> 19 #include <nlohmann/json.hpp> 20 #include <sdbusplus/message/native_types.hpp> 21 22 #include <array> 23 #include <cstdint> 24 #include <functional> 25 #include <memory> 26 #include <string_view> 27 #include <utility> 28 #include <vector> 29 30 namespace crow 31 { 32 namespace google_api 33 { 34 35 inline void handleGoogleV1Get( 36 const crow::Request& /*req*/, 37 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 38 { 39 asyncResp->res.jsonValue["@odata.type"] = 40 "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot"; 41 asyncResp->res.jsonValue["@odata.id"] = "/google/v1"; 42 asyncResp->res.jsonValue["Id"] = "Google Rest RootService"; 43 asyncResp->res.jsonValue["Name"] = "Google Service Root"; 44 asyncResp->res.jsonValue["Version"] = "1.0.0"; 45 asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] = 46 "/google/v1/RootOfTrustCollection"; 47 } 48 49 inline void handleRootOfTrustCollectionGet( 50 const crow::Request& /*req*/, 51 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 52 { 53 asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection"; 54 asyncResp->res.jsonValue["@odata.type"] = 55 "#RootOfTrustCollection.RootOfTrustCollection"; 56 const std::array<std::string_view, 1> interfaces{ 57 "xyz.openbmc_project.Control.Hoth"}; 58 redfish::collection_util::getCollectionMembers( 59 asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"), 60 interfaces, "/xyz/openbmc_project"); 61 } 62 63 // Helper struct to identify a resolved D-Bus object interface 64 struct ResolvedEntity 65 { 66 std::string id; 67 std::string service; 68 std::string object; 69 std::string interface; 70 }; 71 72 using ResolvedEntityHandler = std::function<void( 73 const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&, 74 const ResolvedEntity&)>; 75 76 inline void hothGetSubtreeCallback( 77 const std::string& command, 78 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 79 const std::string& rotId, const ResolvedEntityHandler& entityHandler, 80 const boost::system::error_code& ec, 81 const dbus::utility::MapperGetSubTreeResponse& subtree) 82 { 83 if (ec) 84 { 85 redfish::messages::internalError(asyncResp->res); 86 return; 87 } 88 for (const auto& [path, services] : subtree) 89 { 90 sdbusplus::message::object_path objPath(path); 91 if (objPath.filename() != rotId || services.empty()) 92 { 93 continue; 94 } 95 96 ResolvedEntity resolvedEntity = { 97 .id = rotId, 98 .service = services[0].first, 99 .object = path, 100 .interface = "xyz.openbmc_project.Control.Hoth"}; 101 entityHandler(command, asyncResp, resolvedEntity); 102 return; 103 } 104 105 // Couldn't find an object with that name. return an error 106 redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId); 107 } 108 109 inline void resolveRoT(const std::string& command, 110 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 111 const std::string& rotId, 112 ResolvedEntityHandler&& entityHandler) 113 { 114 constexpr std::array<std::string_view, 1> hothIfaces = { 115 "xyz.openbmc_project.Control.Hoth"}; 116 dbus::utility::getSubTree( 117 "/xyz/openbmc_project", 0, hothIfaces, 118 [command, asyncResp, rotId, entityHandler{std::move(entityHandler)}]( 119 const boost::system::error_code& ec, 120 const dbus::utility::MapperGetSubTreeResponse& subtree) { 121 hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec, 122 subtree); 123 }); 124 } 125 126 inline void populateRootOfTrustEntity( 127 const std::string& /*unused*/, 128 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 129 const ResolvedEntity& resolvedEntity) 130 { 131 asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust"; 132 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 133 "/google/v1/RootOfTrustCollection/{}", resolvedEntity.id); 134 135 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 136 asyncResp->res.jsonValue["Id"] = resolvedEntity.id; 137 // Need to fix this later to a stabler property. 138 asyncResp->res.jsonValue["Name"] = resolvedEntity.id; 139 asyncResp->res.jsonValue["Description"] = "Google Root Of Trust"; 140 asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] = 141 "/google/v1/RootOfTrustCollection/" + resolvedEntity.id + 142 "/Actions/RootOfTrust.SendCommand"; 143 144 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 145 resolvedEntity.id; 146 asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] = 147 "Embedded"; 148 } 149 150 inline void handleRootOfTrustGet( 151 const crow::Request& /*req*/, 152 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 153 const std::string& param) 154 { 155 std::string emptyCommand; 156 resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity); 157 } 158 159 inline void invocationCallback( 160 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 161 const boost::system::error_code& ec, 162 const std::vector<uint8_t>& responseBytes) 163 { 164 if (ec) 165 { 166 BMCWEB_LOG_ERROR("RootOfTrust.Actions.SendCommand failed: {}", 167 ec.message()); 168 redfish::messages::internalError(asyncResp->res); 169 return; 170 } 171 172 asyncResp->res.jsonValue["CommandResponse"] = 173 bytesToHexString(responseBytes); 174 } 175 176 inline void invokeRoTCommand( 177 const std::string& command, 178 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 179 const ResolvedEntity& resolvedEntity) 180 { 181 std::vector<uint8_t> bytes = hexStringToBytes(command); 182 if (bytes.empty()) 183 { 184 BMCWEB_LOG_DEBUG("Invalid command: {}", command); 185 redfish::messages::actionParameterValueTypeError(command, "Command", 186 "SendCommand"); 187 return; 188 } 189 190 dbus::utility::async_method_call( 191 asyncResp, 192 [asyncResp{asyncResp}](const boost::system::error_code& ec, 193 const std::vector<uint8_t>& responseBytes) { 194 invocationCallback(asyncResp, ec, responseBytes); 195 }, 196 resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface, 197 "SendHostCommand", bytes); 198 } 199 200 inline void handleRoTSendCommandPost( 201 const crow::Request& request, 202 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 203 const std::string& rotId) 204 { 205 std::string command; 206 if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command", 207 command)) 208 { 209 BMCWEB_LOG_DEBUG("Missing property Command."); 210 redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand", 211 "Command"); 212 return; 213 } 214 215 resolveRoT(command, asyncResp, rotId, invokeRoTCommand); 216 } 217 218 inline void requestRoutes(App& app) 219 { 220 BMCWEB_ROUTE(app, "/google/v1/") 221 .methods(boost::beast::http::verb::get)(handleGoogleV1Get); 222 223 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection") 224 .privileges({{"ConfigureManager"}}) 225 .methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet); 226 227 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>") 228 .privileges({{"ConfigureManager"}}) 229 .methods(boost::beast::http::verb::get)(handleRootOfTrustGet); 230 231 BMCWEB_ROUTE( 232 app, 233 "/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand") 234 .privileges({{"ConfigureManager"}}) 235 .methods(boost::beast::http::verb::post)(handleRoTSendCommandPost); 236 } 237 238 } // namespace google_api 239 } // namespace crow 240