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