1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <boost/container/flat_map.hpp>
19 #include <boost/process/async_pipe.hpp>
20 #include <boost/type_traits/has_dereference.hpp>
21 #include <node.hpp>
22 #include <utils/json_utils.hpp>
23 // for GetObjectType and ManagedObjectType
24 #include <account_service.hpp>
25 
26 namespace redfish
27 
28 {
29 
30 /**
31  * @brief Read all known properties from VM object interfaces
32  */
33 static void vmParseInterfaceObject(const DbusInterfaceType &interface,
34                                    std::shared_ptr<AsyncResp> aResp)
35 {
36     const auto mountPointIface =
37         interface.find("xyz.openbmc_project.VirtualMedia.MountPoint");
38     if (mountPointIface == interface.cend())
39     {
40         BMCWEB_LOG_DEBUG << "Interface MountPoint not found";
41         return;
42     }
43 
44     const auto processIface =
45         interface.find("xyz.openbmc_project.VirtualMedia.Process");
46     if (processIface == interface.cend())
47     {
48         BMCWEB_LOG_DEBUG << "Interface Process not found";
49         return;
50     }
51 
52     const auto endpointIdProperty = mountPointIface->second.find("EndpointId");
53     if (endpointIdProperty == mountPointIface->second.cend())
54     {
55         BMCWEB_LOG_DEBUG << "Property EndpointId not found";
56         return;
57     }
58 
59     const auto activeProperty = processIface->second.find("Active");
60     if (activeProperty == processIface->second.cend())
61     {
62         BMCWEB_LOG_DEBUG << "Property Active not found";
63         return;
64     }
65 
66     const bool *activeValue = std::get_if<bool>(&activeProperty->second);
67     if (!activeValue)
68     {
69         BMCWEB_LOG_DEBUG << "Value Active not found";
70         return;
71     }
72 
73     const std::string *endpointIdValue =
74         std::get_if<std::string>(&endpointIdProperty->second);
75     if (endpointIdValue)
76     {
77         if (!endpointIdValue->empty())
78         {
79             // Proxy mode
80             aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
81                 *endpointIdValue;
82             aResp->res.jsonValue["TransferProtocolType"] = "OEM";
83             aResp->res.jsonValue["Inserted"] = *activeValue;
84             if (*activeValue == true)
85             {
86                 aResp->res.jsonValue["ConnectedVia"] = "Applet";
87             }
88         }
89         else
90         {
91             // Legacy mode
92             const auto imageUrlProperty =
93                 mountPointIface->second.find("ImageURL");
94             if (imageUrlProperty != processIface->second.cend())
95             {
96                 const std::string *imageUrlValue =
97                     std::get_if<std::string>(&imageUrlProperty->second);
98                 if (imageUrlValue && !imageUrlValue->empty())
99                 {
100                     aResp->res.jsonValue["ImageName"] = *imageUrlValue;
101                     aResp->res.jsonValue["Inserted"] = *activeValue;
102                     if (*activeValue == true)
103                     {
104                         aResp->res.jsonValue["ConnectedVia"] = "URI";
105                     }
106                 }
107             }
108         }
109     }
110 }
111 
112 /**
113  * @brief Fill template for Virtual Media Item.
114  */
115 static nlohmann::json vmItemTemplate(const std::string &name,
116                                      const std::string &resName)
117 {
118     nlohmann::json item;
119     item["@odata.id"] =
120         "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName;
121     item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
122     item["Name"] = "Virtual Removable Media";
123     item["Id"] = resName;
124     item["Image"] = nullptr;
125     item["Inserted"] = nullptr;
126     item["ImageName"] = nullptr;
127     item["WriteProtected"] = true;
128     item["ConnectedVia"] = "NotConnected";
129     item["MediaTypes"] = {"CD", "USBStick"};
130     item["TransferMethod"] = "Stream";
131     item["TransferProtocolType"] = nullptr;
132     item["Oem"]["OpenBmc"]["WebSocketEndpoint"] = nullptr;
133     item["Oem"]["OpenBMC"]["@odata.type"] =
134         "#OemVirtualMedia.v1_0_0.VirtualMedia";
135 
136     return item;
137 }
138 
139 /**
140  *  @brief Fills collection data
141  */
142 static void getVmResourceList(std::shared_ptr<AsyncResp> aResp,
143                               const std::string &service,
144                               const std::string &name)
145 {
146     BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
147     crow::connections::systemBus->async_method_call(
148         [name, aResp{std::move(aResp)}](const boost::system::error_code ec,
149                                         ManagedObjectType &subtree) {
150             if (ec)
151             {
152                 BMCWEB_LOG_DEBUG << "DBUS response error";
153                 return;
154             }
155             nlohmann::json &members = aResp->res.jsonValue["Members"];
156             members = nlohmann::json::array();
157 
158             for (const auto &object : subtree)
159             {
160                 nlohmann::json item;
161                 const std::string &path =
162                     static_cast<const std::string &>(object.first);
163                 std::size_t lastIndex = path.rfind("/");
164                 if (lastIndex == std::string::npos)
165                 {
166                     continue;
167                 }
168 
169                 lastIndex += 1;
170 
171                 item["@odata.id"] = "/redfish/v1/Managers/" + name +
172                                     "/VirtualMedia/" + path.substr(lastIndex);
173 
174                 members.emplace_back(std::move(item));
175             }
176             aResp->res.jsonValue["Members@odata.count"] = members.size();
177         },
178         service, "/xyz/openbmc_project/VirtualMedia",
179         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
180 }
181 
182 /**
183  *  @brief Fills data for specific resource
184  */
185 static void getVmData(std::shared_ptr<AsyncResp> aResp,
186                       const std::string &service, const std::string &name,
187                       const std::string &resName)
188 {
189     BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
190 
191     crow::connections::systemBus->async_method_call(
192         [resName, name, aResp](const boost::system::error_code ec,
193                                ManagedObjectType &subtree) {
194             if (ec)
195             {
196                 BMCWEB_LOG_DEBUG << "DBUS response error";
197 
198                 return;
199             }
200 
201             for (auto &item : subtree)
202             {
203                 const std::string &path =
204                     static_cast<const std::string &>(item.first);
205 
206                 std::size_t lastItem = path.rfind("/");
207                 if (lastItem == std::string::npos)
208                 {
209                     continue;
210                 }
211 
212                 if (path.substr(lastItem + 1) != resName)
213                 {
214                     continue;
215                 }
216 
217                 aResp->res.jsonValue = vmItemTemplate(name, resName);
218 
219                 // Check if dbus path is Legacy type
220                 if (path.find("VirtualMedia/Legacy") != std::string::npos)
221                 {
222                     aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
223                                         ["target"] =
224                         "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
225                         resName + "/Actions/VirtualMedia.InsertMedia";
226                 }
227 
228                 vmParseInterfaceObject(item.second, aResp);
229 
230                 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
231                                     ["target"] =
232                     "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
233                     resName + "/Actions/VirtualMedia.EjectMedia";
234 
235                 return;
236             }
237 
238             messages::resourceNotFound(
239                 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
240         },
241         service, "/xyz/openbmc_project/VirtualMedia",
242         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
243 }
244 
245 /**
246    @brief InsertMedia action class
247  */
248 class VirtualMediaActionInsertMedia : public Node
249 {
250   public:
251     VirtualMediaActionInsertMedia(CrowApp &app) :
252         Node(app,
253              "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
254              "VirtualMedia.InsertMedia",
255              std::string(), std::string())
256     {
257         entityPrivileges = {
258             {boost::beast::http::verb::get, {{"Login"}}},
259             {boost::beast::http::verb::head, {{"Login"}}},
260             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
261             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
262             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
263             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
264     }
265 
266   private:
267     /**
268      * @brief Transfer protocols supported for InsertMedia action.
269      *
270      */
271     enum class TransferProtocol
272     {
273         https,
274         smb,
275         invalid
276     };
277 
278     /**
279      * @brief Function extracts transfer protocol type from URI.
280      *
281      */
282     std::optional<TransferProtocol>
283         getTransferProtocolFromUri(const std::string &imageUri)
284     {
285         if (imageUri.find("smb://") != std::string::npos)
286         {
287             return TransferProtocol::smb;
288         }
289         else if (imageUri.find("https://") != std::string::npos)
290         {
291             return TransferProtocol::https;
292         }
293         else if (imageUri.find("://") != std::string::npos)
294         {
295             return TransferProtocol::invalid;
296         }
297         else
298         {
299             return {};
300         }
301     }
302 
303     /**
304      * @brief Function convert transfer protocol from string param.
305      *
306      */
307     std::optional<TransferProtocol> getTransferProtocolFromParam(
308         const std::optional<std::string> &transferProtocolType)
309     {
310         if (transferProtocolType == std::nullopt)
311         {
312             return {};
313         }
314 
315         if (*transferProtocolType == "CIFS")
316         {
317             return TransferProtocol::smb;
318         }
319 
320         if (*transferProtocolType == "HTTPS")
321         {
322             return TransferProtocol::https;
323         }
324 
325         return TransferProtocol::invalid;
326     }
327 
328     /**
329      * @brief Function extends URI with transfer protocol type.
330      *
331      */
332     const std::string
333         getUriWithTransferProtocol(const std::string &imageUri,
334                                    const TransferProtocol &transferProtocol)
335     {
336         if (transferProtocol == TransferProtocol::smb)
337         {
338             return "smb://" + imageUri;
339         }
340 
341         if (transferProtocol == TransferProtocol::https)
342         {
343             return "https://" + imageUri;
344         }
345 
346         return imageUri;
347     }
348 
349     /**
350      * @brief Function validate parameters of insert media request.
351      *
352      */
353     bool validateParams(crow::Response &res, std::string &imageUrl,
354                         const std::optional<bool> &inserted,
355                         const std::optional<std::string> &transferMethod,
356                         const std::optional<std::string> &transferProtocolType)
357     {
358         BMCWEB_LOG_DEBUG << "Validation started";
359         // required param imageUrl must not be empty
360         if (imageUrl.empty())
361         {
362             BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
363 
364             messages::propertyValueFormatError(res, "<empty>", "Image");
365 
366             return false;
367         }
368 
369         // optional param inserted must be true
370         if ((inserted != std::nullopt) && (*inserted != true))
371         {
372             BMCWEB_LOG_ERROR
373                 << "Request action optional parameter Inserted must be true.";
374 
375             messages::actionParameterNotSupported(res, "Inserted",
376                                                   "InsertMedia");
377 
378             return false;
379         }
380 
381         // optional param transferMethod must be stream
382         if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
383         {
384             BMCWEB_LOG_ERROR << "Request action optional parameter "
385                                 "TransferMethod must be Stream.";
386 
387             messages::actionParameterNotSupported(res, "TransferMethod",
388                                                   "InsertMedia");
389 
390             return false;
391         }
392 
393         std::optional<TransferProtocol> uriTransferProtocolType =
394             getTransferProtocolFromUri(imageUrl);
395 
396         std::optional<TransferProtocol> paramTransferProtocolType =
397             getTransferProtocolFromParam(transferProtocolType);
398 
399         // ImageUrl does not contain valid protocol type
400         if (*uriTransferProtocolType == TransferProtocol::invalid)
401         {
402             BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
403                                 "contain specified protocol type from list: "
404                                 "(smb, https).";
405 
406             messages::resourceAtUriInUnknownFormat(res, imageUrl);
407 
408             return false;
409         }
410 
411         // transferProtocolType should contain value from list
412         if (*paramTransferProtocolType == TransferProtocol::invalid)
413         {
414             BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
415                                 "must be provided with value from list: "
416                                 "(CIFS, HTTPS).";
417 
418             messages::propertyValueNotInList(res, *transferProtocolType,
419                                              "TransferProtocolType");
420             return false;
421         }
422 
423         // valid transfer protocol not provided either with URI nor param
424         if ((uriTransferProtocolType == std::nullopt) &&
425             (paramTransferProtocolType == std::nullopt))
426         {
427             BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
428                                 "contain specified protocol type or param "
429                                 "TransferProtocolType must be provided.";
430 
431             messages::resourceAtUriInUnknownFormat(res, imageUrl);
432 
433             return false;
434         }
435 
436         // valid transfer protocol provided both with URI and param
437         if ((paramTransferProtocolType != std::nullopt) &&
438             (uriTransferProtocolType != std::nullopt))
439         {
440             // check if protocol is the same for URI and param
441             if (*paramTransferProtocolType != *uriTransferProtocolType)
442             {
443                 BMCWEB_LOG_ERROR << "Request action parameter "
444                                     "TransferProtocolType must  contain the "
445                                     "same protocol type as protocol type "
446                                     "provided with param imageUrl.";
447 
448                 messages::actionParameterValueTypeError(
449                     res, *transferProtocolType, "TransferProtocolType",
450                     "InsertMedia");
451 
452                 return false;
453             }
454         }
455 
456         // validation passed
457         // add protocol to URI if needed
458         if (uriTransferProtocolType == std::nullopt)
459         {
460             imageUrl = getUriWithTransferProtocol(imageUrl,
461                                                   *paramTransferProtocolType);
462         }
463 
464         return true;
465     }
466 
467     /**
468      * @brief Function handles POST method request.
469      *
470      * Analyzes POST body message before sends Reset request data to dbus.
471      */
472     void doPost(crow::Response &res, const crow::Request &req,
473                 const std::vector<std::string> &params) override
474     {
475         auto aResp = std::make_shared<AsyncResp>(res);
476 
477         if (params.size() != 2)
478         {
479             messages::internalError(res);
480             return;
481         }
482 
483         // take resource name from URL
484         const std::string &resName = params[1];
485 
486         if (params[0] != "bmc")
487         {
488             messages::resourceNotFound(res, "VirtualMedia.Insert", resName);
489 
490             return;
491         }
492 
493         crow::connections::systemBus->async_method_call(
494             [this, aResp{std::move(aResp)}, req,
495              resName](const boost::system::error_code ec,
496                       const GetObjectType &getObjectType) {
497                 if (ec)
498                 {
499                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
500                                      << ec;
501                     messages::internalError(aResp->res);
502 
503                     return;
504                 }
505                 std::string service = getObjectType.begin()->first;
506                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
507 
508                 crow::connections::systemBus->async_method_call(
509                     [this, service, resName, req, aResp{std::move(aResp)}](
510                         const boost::system::error_code ec,
511                         ManagedObjectType &subtree) {
512                         if (ec)
513                         {
514                             BMCWEB_LOG_DEBUG << "DBUS response error";
515 
516                             return;
517                         }
518 
519                         for (const auto &object : subtree)
520                         {
521                             const std::string &path =
522                                 static_cast<const std::string &>(object.first);
523 
524                             std::size_t lastIndex = path.rfind("/");
525                             if (lastIndex == std::string::npos)
526                             {
527                                 continue;
528                             }
529 
530                             lastIndex += 1;
531 
532                             if (path.substr(lastIndex) == resName)
533                             {
534                                 lastIndex = path.rfind("Proxy");
535                                 if (lastIndex != std::string::npos)
536                                 {
537                                     // Not possible in proxy mode
538                                     BMCWEB_LOG_DEBUG << "InsertMedia not "
539                                                         "allowed in proxy mode";
540                                     messages::resourceNotFound(
541                                         aResp->res, "VirtualMedia.InsertMedia",
542                                         resName);
543 
544                                     return;
545                                 }
546 
547                                 lastIndex = path.rfind("Legacy");
548                                 if (lastIndex == std::string::npos)
549                                 {
550                                     continue;
551                                 }
552 
553                                 // Legacy mode
554                                 std::string imageUrl;
555                                 std::optional<std::string> userName;
556                                 std::optional<std::string> password;
557                                 std::optional<std::string> transferMethod;
558                                 std::optional<std::string> transferProtocolType;
559                                 std::optional<bool> writeProtected = true;
560                                 std::optional<bool> inserted;
561 
562                                 // Read obligatory paramters (url of image)
563                                 if (!json_util::readJson(
564                                         req, aResp->res, "Image", imageUrl,
565                                         "WriteProtected", writeProtected,
566                                         "UserName", userName, "Password",
567                                         password, "Inserted", inserted,
568                                         "TransferMethod", transferMethod,
569                                         "TransferProtocolType",
570                                         transferProtocolType))
571                                 {
572                                     BMCWEB_LOG_DEBUG << "Image is not provided";
573                                     return;
574                                 }
575 
576                                 bool paramsValid = validateParams(
577                                     aResp->res, imageUrl, inserted,
578                                     transferMethod, transferProtocolType);
579 
580                                 if (paramsValid == false)
581                                 {
582                                     return;
583                                 }
584 
585                                 // manager is irrelevant for VirtualMedia dbus
586                                 // calls
587                                 doMountVmLegacy(
588                                     std::move(aResp), service, resName,
589                                     imageUrl, !(*writeProtected),
590                                     std::move(*userName), std::move(*password));
591 
592                                 return;
593                             }
594                         }
595                         BMCWEB_LOG_DEBUG << "Parent item not found";
596                         messages::resourceNotFound(aResp->res, "VirtualMedia",
597                                                    resName);
598                     },
599                     service, "/xyz/openbmc_project/VirtualMedia",
600                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
601             },
602             "xyz.openbmc_project.ObjectMapper",
603             "/xyz/openbmc_project/object_mapper",
604             "xyz.openbmc_project.ObjectMapper", "GetObject",
605             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
606     }
607 
608     template <typename T> static void secureCleanup(T &value)
609     {
610         auto raw = const_cast<typename T::value_type *>(value.data());
611         explicit_bzero(raw, value.size() * sizeof(*raw));
612     }
613 
614     class Credentials
615     {
616       public:
617         Credentials(std::string &&user, std::string &&password) :
618             userBuf(std::move(user)), passBuf(std::move(password))
619         {
620         }
621 
622         ~Credentials()
623         {
624             secureCleanup(userBuf);
625             secureCleanup(passBuf);
626         }
627 
628         const std::string &user()
629         {
630             return userBuf;
631         }
632 
633         const std::string &password()
634         {
635             return passBuf;
636         }
637 
638       private:
639         Credentials() = delete;
640         Credentials(const Credentials &) = delete;
641         Credentials &operator=(const Credentials &) = delete;
642 
643         std::string userBuf;
644         std::string passBuf;
645     };
646 
647     class CredentialsProvider
648     {
649       public:
650         template <typename T> struct Deleter
651         {
652             void operator()(T *buff) const
653             {
654                 if (buff)
655                 {
656                     secureCleanup(*buff);
657                     delete buff;
658                 }
659             }
660         };
661 
662         using Buffer = std::vector<char>;
663         using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
664         // Using explicit definition instead of std::function to avoid implicit
665         // conversions eg. stack copy instead of reference
666         using FormatterFunc = void(const std::string &username,
667                                    const std::string &password, Buffer &dest);
668 
669         CredentialsProvider(std::string &&user, std::string &&password) :
670             credentials(std::move(user), std::move(password))
671         {
672         }
673 
674         const std::string &user()
675         {
676             return credentials.user();
677         }
678 
679         const std::string &password()
680         {
681             return credentials.password();
682         }
683 
684         SecureBuffer pack(const FormatterFunc formatter)
685         {
686             SecureBuffer packed{new Buffer{}};
687             if (formatter)
688             {
689                 formatter(credentials.user(), credentials.password(), *packed);
690             }
691 
692             return packed;
693         }
694 
695       private:
696         Credentials credentials;
697     };
698 
699     // Wrapper for boost::async_pipe ensuring proper pipe cleanup
700     template <typename Buffer> class Pipe
701     {
702       public:
703         using unix_fd = sdbusplus::message::unix_fd;
704 
705         Pipe(boost::asio::io_context &io, Buffer &&buffer) :
706             impl(io), buffer{std::move(buffer)}
707         {
708         }
709 
710         ~Pipe()
711         {
712             // Named pipe needs to be explicitly removed
713             impl.close();
714         }
715 
716         unix_fd fd()
717         {
718             return unix_fd{impl.native_source()};
719         }
720 
721         template <typename WriteHandler>
722         void async_write(WriteHandler &&handler)
723         {
724             impl.async_write_some(data(), std::forward<WriteHandler>(handler));
725         }
726 
727       private:
728         // Specialization for pointer types
729         template <typename B = Buffer>
730         typename std::enable_if<boost::has_dereference<B>::value,
731                                 boost::asio::const_buffer>::type
732             data()
733         {
734             return boost::asio::buffer(*buffer);
735         }
736 
737         template <typename B = Buffer>
738         typename std::enable_if<!boost::has_dereference<B>::value,
739                                 boost::asio::const_buffer>::type
740             data()
741         {
742             return boost::asio::buffer(buffer);
743         }
744 
745         const std::string name;
746         boost::process::async_pipe impl;
747         Buffer buffer;
748     };
749 
750     /**
751      * @brief Function transceives data with dbus directly.
752      *
753      * All BMC state properties will be retrieved before sending reset request.
754      */
755     void doMountVmLegacy(std::shared_ptr<AsyncResp> asyncResp,
756                          const std::string &service, const std::string &name,
757                          const std::string &imageUrl, const bool rw,
758                          std::string &&userName, std::string &&password)
759     {
760         using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
761         constexpr const size_t secretLimit = 1024;
762 
763         std::shared_ptr<SecurePipe> secretPipe;
764         std::variant<int, SecurePipe::unix_fd> unixFd = -1;
765 
766         if (!userName.empty() || !password.empty())
767         {
768             // Encapsulate in safe buffer
769             CredentialsProvider credentials(std::move(userName),
770                                             std::move(password));
771 
772             // Payload must contain data + NULL delimiters
773             if (credentials.user().size() + credentials.password().size() + 2 >
774                 secretLimit)
775             {
776                 BMCWEB_LOG_ERROR << "Credentials too long to handle";
777                 messages::unrecognizedRequestBody(asyncResp->res);
778                 return;
779             }
780 
781             // Pack secret
782             auto secret = credentials.pack([](const auto &user,
783                                               const auto &pass, auto &buff) {
784                 std::copy(user.begin(), user.end(), std::back_inserter(buff));
785                 buff.push_back('\0');
786                 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
787                 buff.push_back('\0');
788             });
789 
790             // Open pipe
791             secretPipe = std::make_shared<SecurePipe>(
792                 crow::connections::systemBus->get_io_context(),
793                 std::move(secret));
794             unixFd = secretPipe->fd();
795 
796             // Pass secret over pipe
797             secretPipe->async_write(
798                 [asyncResp](const boost::system::error_code &ec,
799                             std::size_t size) {
800                     if (ec)
801                     {
802                         BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
803                         messages::internalError(asyncResp->res);
804                     }
805                 });
806         }
807 
808         crow::connections::systemBus->async_method_call(
809             [asyncResp, secretPipe](const boost::system::error_code ec,
810                                     bool success) {
811                 if (ec)
812                 {
813                     BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
814                     messages::internalError(asyncResp->res);
815                 }
816                 else if (!success)
817                 {
818                     BMCWEB_LOG_ERROR << "Service responded with error";
819                     messages::generalError(asyncResp->res);
820                 }
821             },
822             service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
823             "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
824             unixFd);
825     }
826 };
827 
828 /**
829    @brief EjectMedia action class
830  */
831 class VirtualMediaActionEjectMedia : public Node
832 {
833   public:
834     VirtualMediaActionEjectMedia(CrowApp &app) :
835         Node(app,
836              "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
837              "VirtualMedia.EjectMedia",
838              std::string(), std::string())
839     {
840         entityPrivileges = {
841             {boost::beast::http::verb::get, {{"Login"}}},
842             {boost::beast::http::verb::head, {{"Login"}}},
843             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
844             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
845             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
846             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
847     }
848 
849   private:
850     /**
851      * @brief Function handles POST method request.
852      *
853      * Analyzes POST body message before sends Reset request data to dbus.
854      */
855     void doPost(crow::Response &res, const crow::Request &req,
856                 const std::vector<std::string> &params) override
857     {
858         auto aResp = std::make_shared<AsyncResp>(res);
859 
860         if (params.size() != 2)
861         {
862             messages::internalError(res);
863             return;
864         }
865 
866         // take resource name from URL
867         const std::string &resName = params[1];
868 
869         if (params[0] != "bmc")
870         {
871             messages::resourceNotFound(res, "VirtualMedia.Eject", resName);
872 
873             return;
874         }
875 
876         crow::connections::systemBus->async_method_call(
877             [this, aResp{std::move(aResp)}, req,
878              resName](const boost::system::error_code ec,
879                       const GetObjectType &getObjectType) {
880                 if (ec)
881                 {
882                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
883                                      << ec;
884                     messages::internalError(aResp->res);
885 
886                     return;
887                 }
888                 std::string service = getObjectType.begin()->first;
889                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
890 
891                 crow::connections::systemBus->async_method_call(
892                     [this, resName, service, req, aResp{std::move(aResp)}](
893                         const boost::system::error_code ec,
894                         ManagedObjectType &subtree) {
895                         if (ec)
896                         {
897                             BMCWEB_LOG_DEBUG << "DBUS response error";
898 
899                             return;
900                         }
901 
902                         for (const auto &object : subtree)
903                         {
904                             const std::string &path =
905                                 static_cast<const std::string &>(object.first);
906 
907                             std::size_t lastIndex = path.rfind("/");
908                             if (lastIndex == std::string::npos)
909                             {
910                                 continue;
911                             }
912 
913                             lastIndex += 1;
914 
915                             if (path.substr(lastIndex) == resName)
916                             {
917                                 lastIndex = path.rfind("Proxy");
918                                 if (lastIndex != std::string::npos)
919                                 {
920                                     // Proxy mode
921                                     doVmAction(std::move(aResp), service,
922                                                resName, false);
923                                 }
924 
925                                 lastIndex = path.rfind("Legacy");
926                                 if (lastIndex != std::string::npos)
927                                 {
928                                     // Legacy mode
929                                     doVmAction(std::move(aResp), service,
930                                                resName, true);
931                                 }
932 
933                                 return;
934                             }
935                         }
936                         BMCWEB_LOG_DEBUG << "Parent item not found";
937                         messages::resourceNotFound(aResp->res, "VirtualMedia",
938                                                    resName);
939                     },
940                     service, "/xyz/openbmc_project/VirtualMedia",
941                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
942             },
943             "xyz.openbmc_project.ObjectMapper",
944             "/xyz/openbmc_project/object_mapper",
945             "xyz.openbmc_project.ObjectMapper", "GetObject",
946             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
947     }
948 
949     /**
950      * @brief Function transceives data with dbus directly.
951      *
952      * All BMC state properties will be retrieved before sending reset request.
953      */
954     void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
955                     const std::string &service, const std::string &name,
956                     bool legacy)
957     {
958 
959         // Legacy mount requires parameter with image
960         if (legacy)
961         {
962             crow::connections::systemBus->async_method_call(
963                 [asyncResp](const boost::system::error_code ec) {
964                     if (ec)
965                     {
966                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
967 
968                         messages::internalError(asyncResp->res);
969                         return;
970                     }
971                 },
972                 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
973                 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
974         }
975         else // proxy
976         {
977             crow::connections::systemBus->async_method_call(
978                 [asyncResp](const boost::system::error_code ec) {
979                     if (ec)
980                     {
981                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
982 
983                         messages::internalError(asyncResp->res);
984                         return;
985                     }
986                 },
987                 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
988                 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
989         }
990     }
991 };
992 
993 class VirtualMediaCollection : public Node
994 {
995   public:
996     /*
997      * Default Constructor
998      */
999     VirtualMediaCollection(CrowApp &app) :
1000         Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", std::string())
1001     {
1002         entityPrivileges = {
1003             {boost::beast::http::verb::get, {{"Login"}}},
1004             {boost::beast::http::verb::head, {{"Login"}}},
1005             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1006             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1007             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1008             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1009     }
1010 
1011   private:
1012     /**
1013      * Functions triggers appropriate requests on DBus
1014      */
1015     void doGet(crow::Response &res, const crow::Request &req,
1016                const std::vector<std::string> &params) override
1017     {
1018         auto asyncResp = std::make_shared<AsyncResp>(res);
1019 
1020         // Check if there is required param, truly entering this shall be
1021         // impossible
1022         if (params.size() != 1)
1023         {
1024             messages::internalError(res);
1025 
1026             return;
1027         }
1028 
1029         const std::string &name = params[0];
1030 
1031         if (name != "bmc")
1032         {
1033             messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
1034 
1035             return;
1036         }
1037 
1038         res.jsonValue["@odata.type"] =
1039             "#VirtualMediaCollection.VirtualMediaCollection";
1040         res.jsonValue["Name"] = "Virtual Media Services";
1041         res.jsonValue["@odata.id"] =
1042             "/redfish/v1/Managers/" + name + "/VirtualMedia/";
1043 
1044         crow::connections::systemBus->async_method_call(
1045             [asyncResp, name](const boost::system::error_code ec,
1046                               const GetObjectType &getObjectType) {
1047                 if (ec)
1048                 {
1049                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1050                                      << ec;
1051                     messages::internalError(asyncResp->res);
1052 
1053                     return;
1054                 }
1055                 std::string service = getObjectType.begin()->first;
1056                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1057 
1058                 getVmResourceList(asyncResp, service, name);
1059             },
1060             "xyz.openbmc_project.ObjectMapper",
1061             "/xyz/openbmc_project/object_mapper",
1062             "xyz.openbmc_project.ObjectMapper", "GetObject",
1063             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
1064     }
1065 };
1066 
1067 class VirtualMedia : public Node
1068 {
1069   public:
1070     /*
1071      * Default Constructor
1072      */
1073     VirtualMedia(CrowApp &app) :
1074         Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/",
1075              std::string(), std::string())
1076     {
1077         entityPrivileges = {
1078             {boost::beast::http::verb::get, {{"Login"}}},
1079             {boost::beast::http::verb::head, {{"Login"}}},
1080             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1081             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1082             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1083             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1084     }
1085 
1086   private:
1087     /**
1088      * Functions triggers appropriate requests on DBus
1089      */
1090     void doGet(crow::Response &res, const crow::Request &req,
1091                const std::vector<std::string> &params) override
1092     {
1093         // Check if there is required param, truly entering this shall be
1094         // impossible
1095         if (params.size() != 2)
1096         {
1097             messages::internalError(res);
1098 
1099             res.end();
1100             return;
1101         }
1102         const std::string &name = params[0];
1103         const std::string &resName = params[1];
1104 
1105         auto asyncResp = std::make_shared<AsyncResp>(res);
1106 
1107         if (name != "bmc")
1108         {
1109             messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
1110 
1111             return;
1112         }
1113 
1114         crow::connections::systemBus->async_method_call(
1115             [asyncResp, name, resName](const boost::system::error_code ec,
1116                                        const GetObjectType &getObjectType) {
1117                 if (ec)
1118                 {
1119                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1120                                      << ec;
1121                     messages::internalError(asyncResp->res);
1122 
1123                     return;
1124                 }
1125                 std::string service = getObjectType.begin()->first;
1126                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1127 
1128                 getVmData(asyncResp, service, name, resName);
1129             },
1130             "xyz.openbmc_project.ObjectMapper",
1131             "/xyz/openbmc_project/object_mapper",
1132             "xyz.openbmc_project.ObjectMapper", "GetObject",
1133             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
1134     }
1135 };
1136 
1137 } // namespace redfish
1138