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