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(App& 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 parameters (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>
609     static void secureCleanup(T& value)
610     {
611         auto raw = const_cast<typename T::value_type*>(value.data());
612         explicit_bzero(raw, value.size() * sizeof(*raw));
613     }
614 
615     class Credentials
616     {
617       public:
618         Credentials(std::string&& user, std::string&& password) :
619             userBuf(std::move(user)), passBuf(std::move(password))
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>
651         struct Deleter
652         {
653             void operator()(T* buff) const
654             {
655                 if (buff)
656                 {
657                     secureCleanup(*buff);
658                     delete buff;
659                 }
660             }
661         };
662 
663         using Buffer = std::vector<char>;
664         using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
665         // Using explicit definition instead of std::function to avoid implicit
666         // conversions eg. stack copy instead of reference
667         using FormatterFunc = void(const std::string& username,
668                                    const std::string& password, Buffer& dest);
669 
670         CredentialsProvider(std::string&& user, std::string&& password) :
671             credentials(std::move(user), std::move(password))
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>
701     class Pipe
702     {
703       public:
704         using unix_fd = sdbusplus::message::unix_fd;
705 
706         Pipe(boost::asio::io_context& io, Buffer&& buffer) :
707             impl(io), buffer{std::move(buffer)}
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, std::size_t) {
799                     if (ec)
800                     {
801                         BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
802                         messages::internalError(asyncResp->res);
803                     }
804                 });
805         }
806 
807         crow::connections::systemBus->async_method_call(
808             [asyncResp, secretPipe](const boost::system::error_code ec,
809                                     bool success) {
810                 if (ec)
811                 {
812                     BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
813                     messages::internalError(asyncResp->res);
814                 }
815                 else if (!success)
816                 {
817                     BMCWEB_LOG_ERROR << "Service responded with error";
818                     messages::generalError(asyncResp->res);
819                 }
820             },
821             service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
822             "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
823             unixFd);
824     }
825 };
826 
827 /**
828    @brief EjectMedia action class
829  */
830 class VirtualMediaActionEjectMedia : public Node
831 {
832   public:
833     VirtualMediaActionEjectMedia(App& app) :
834         Node(app,
835              "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
836              "VirtualMedia.EjectMedia",
837              std::string(), std::string())
838     {
839         entityPrivileges = {
840             {boost::beast::http::verb::get, {{"Login"}}},
841             {boost::beast::http::verb::head, {{"Login"}}},
842             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
843             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
844             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
845             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
846     }
847 
848   private:
849     /**
850      * @brief Function handles POST method request.
851      *
852      * Analyzes POST body message before sends Reset request data to dbus.
853      */
854     void doPost(crow::Response& res, const crow::Request& req,
855                 const std::vector<std::string>& params) override
856     {
857         auto aResp = std::make_shared<AsyncResp>(res);
858 
859         if (params.size() != 2)
860         {
861             messages::internalError(res);
862             return;
863         }
864 
865         // take resource name from URL
866         const std::string& resName = params[1];
867 
868         if (params[0] != "bmc")
869         {
870             messages::resourceNotFound(res, "VirtualMedia.Eject", resName);
871 
872             return;
873         }
874 
875         crow::connections::systemBus->async_method_call(
876             [this, aResp{std::move(aResp)}, req,
877              resName](const boost::system::error_code ec,
878                       const GetObjectType& getObjectType) {
879                 if (ec)
880                 {
881                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
882                                      << ec;
883                     messages::internalError(aResp->res);
884 
885                     return;
886                 }
887                 std::string service = getObjectType.begin()->first;
888                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
889 
890                 crow::connections::systemBus->async_method_call(
891                     [this, resName, service, req, aResp{std::move(aResp)}](
892                         const boost::system::error_code ec,
893                         ManagedObjectType& subtree) {
894                         if (ec)
895                         {
896                             BMCWEB_LOG_DEBUG << "DBUS response error";
897 
898                             return;
899                         }
900 
901                         for (const auto& object : subtree)
902                         {
903                             const std::string& path =
904                                 static_cast<const std::string&>(object.first);
905 
906                             std::size_t lastIndex = path.rfind("/");
907                             if (lastIndex == std::string::npos)
908                             {
909                                 continue;
910                             }
911 
912                             lastIndex += 1;
913 
914                             if (path.substr(lastIndex) == resName)
915                             {
916                                 lastIndex = path.rfind("Proxy");
917                                 if (lastIndex != std::string::npos)
918                                 {
919                                     // Proxy mode
920                                     doVmAction(std::move(aResp), service,
921                                                resName, false);
922                                 }
923 
924                                 lastIndex = path.rfind("Legacy");
925                                 if (lastIndex != std::string::npos)
926                                 {
927                                     // Legacy mode
928                                     doVmAction(std::move(aResp), service,
929                                                resName, true);
930                                 }
931 
932                                 return;
933                             }
934                         }
935                         BMCWEB_LOG_DEBUG << "Parent item not found";
936                         messages::resourceNotFound(aResp->res, "VirtualMedia",
937                                                    resName);
938                     },
939                     service, "/xyz/openbmc_project/VirtualMedia",
940                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
941             },
942             "xyz.openbmc_project.ObjectMapper",
943             "/xyz/openbmc_project/object_mapper",
944             "xyz.openbmc_project.ObjectMapper", "GetObject",
945             "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
946     }
947 
948     /**
949      * @brief Function transceives data with dbus directly.
950      *
951      * All BMC state properties will be retrieved before sending reset request.
952      */
953     void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
954                     const std::string& service, const std::string& name,
955                     bool legacy)
956     {
957 
958         // Legacy mount requires parameter with image
959         if (legacy)
960         {
961             crow::connections::systemBus->async_method_call(
962                 [asyncResp](const boost::system::error_code ec) {
963                     if (ec)
964                     {
965                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
966 
967                         messages::internalError(asyncResp->res);
968                         return;
969                     }
970                 },
971                 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
972                 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
973         }
974         else // proxy
975         {
976             crow::connections::systemBus->async_method_call(
977                 [asyncResp](const boost::system::error_code ec) {
978                     if (ec)
979                     {
980                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
981 
982                         messages::internalError(asyncResp->res);
983                         return;
984                     }
985                 },
986                 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
987                 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
988         }
989     }
990 };
991 
992 class VirtualMediaCollection : public Node
993 {
994   public:
995     /*
996      * Default Constructor
997      */
998     VirtualMediaCollection(App& app) :
999         Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", std::string())
1000     {
1001         entityPrivileges = {
1002             {boost::beast::http::verb::get, {{"Login"}}},
1003             {boost::beast::http::verb::head, {{"Login"}}},
1004             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1005             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1006             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1007             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1008     }
1009 
1010   private:
1011     /**
1012      * Functions triggers appropriate requests on DBus
1013      */
1014     void doGet(crow::Response& res, const crow::Request&,
1015                const std::vector<std::string>& params) override
1016     {
1017         auto asyncResp = std::make_shared<AsyncResp>(res);
1018 
1019         // Check if there is required param, truly entering this shall be
1020         // impossible
1021         if (params.size() != 1)
1022         {
1023             messages::internalError(res);
1024 
1025             return;
1026         }
1027 
1028         const std::string& name = params[0];
1029 
1030         if (name != "bmc")
1031         {
1032             messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
1033 
1034             return;
1035         }
1036 
1037         res.jsonValue["@odata.type"] =
1038             "#VirtualMediaCollection.VirtualMediaCollection";
1039         res.jsonValue["Name"] = "Virtual Media Services";
1040         res.jsonValue["@odata.id"] =
1041             "/redfish/v1/Managers/" + name + "/VirtualMedia";
1042 
1043         crow::connections::systemBus->async_method_call(
1044             [asyncResp, name](const boost::system::error_code ec,
1045                               const GetObjectType& getObjectType) {
1046                 if (ec)
1047                 {
1048                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1049                                      << ec;
1050                     messages::internalError(asyncResp->res);
1051 
1052                     return;
1053                 }
1054                 std::string service = getObjectType.begin()->first;
1055                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1056 
1057                 getVmResourceList(asyncResp, service, name);
1058             },
1059             "xyz.openbmc_project.ObjectMapper",
1060             "/xyz/openbmc_project/object_mapper",
1061             "xyz.openbmc_project.ObjectMapper", "GetObject",
1062             "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
1063     }
1064 };
1065 
1066 class VirtualMedia : public Node
1067 {
1068   public:
1069     /*
1070      * Default Constructor
1071      */
1072     VirtualMedia(App& app) :
1073         Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/",
1074              std::string(), std::string())
1075     {
1076         entityPrivileges = {
1077             {boost::beast::http::verb::get, {{"Login"}}},
1078             {boost::beast::http::verb::head, {{"Login"}}},
1079             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1080             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1081             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1082             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1083     }
1084 
1085   private:
1086     /**
1087      * Functions triggers appropriate requests on DBus
1088      */
1089     void doGet(crow::Response& res, const crow::Request&,
1090                const std::vector<std::string>& params) override
1091     {
1092         // Check if there is required param, truly entering this shall be
1093         // impossible
1094         if (params.size() != 2)
1095         {
1096             messages::internalError(res);
1097 
1098             res.end();
1099             return;
1100         }
1101         const std::string& name = params[0];
1102         const std::string& resName = params[1];
1103 
1104         auto asyncResp = std::make_shared<AsyncResp>(res);
1105 
1106         if (name != "bmc")
1107         {
1108             messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
1109 
1110             return;
1111         }
1112 
1113         crow::connections::systemBus->async_method_call(
1114             [asyncResp, name, resName](const boost::system::error_code ec,
1115                                        const GetObjectType& getObjectType) {
1116                 if (ec)
1117                 {
1118                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1119                                      << ec;
1120                     messages::internalError(asyncResp->res);
1121 
1122                     return;
1123                 }
1124                 std::string service = getObjectType.begin()->first;
1125                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1126 
1127                 getVmData(asyncResp, service, name, resName);
1128             },
1129             "xyz.openbmc_project.ObjectMapper",
1130             "/xyz/openbmc_project/object_mapper",
1131             "xyz.openbmc_project.ObjectMapper", "GetObject",
1132             "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
1133     }
1134 };
1135 
1136 } // namespace redfish
1137