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