blob: 8fe31d59cea5cbb3dfdc5b96de21de5b0cbed0c8 [file] [log] [blame]
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001/*
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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070018#include <app.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020019#include <boost/container/flat_map.hpp>
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +010020#include <boost/process/async_pipe.hpp>
21#include <boost/type_traits/has_dereference.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020022#include <utils/json_utils.hpp>
23// for GetObjectType and ManagedObjectType
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +020024#include <account_service.hpp>
Anna Platash9e319cf2020-11-17 10:18:31 +010025#include <boost/url/url_view.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020026
27namespace redfish
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020028{
Anna Platash9e319cf2020-11-17 10:18:31 +010029/**
30 * @brief Function extracts transfer protocol name from URI.
31 */
Ed Tanous22db1722021-06-09 10:53:51 -070032inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri)
Anna Platash9e319cf2020-11-17 10:18:31 +010033{
34 try
35 {
36 std::string_view scheme = boost::urls::url_view(imageUri).scheme();
37 if (scheme == "smb")
38 {
39 return "CIFS";
40 }
Ed Tanous22db1722021-06-09 10:53:51 -070041 if (scheme == "https")
Anna Platash9e319cf2020-11-17 10:18:31 +010042 {
43 return "HTTPS";
44 }
45 }
46 catch (std::exception& p)
47 {
48 BMCWEB_LOG_ERROR << p.what();
49 }
50 return "None";
51}
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020052
53/**
54 * @brief Read all known properties from VM object interfaces
55 */
Ed Tanous22db1722021-06-09 10:53:51 -070056inline void
zhanghch058d1b46d2021-04-01 11:18:24 +080057 vmParseInterfaceObject(const DbusInterfaceType& interface,
58 const std::shared_ptr<bmcweb::AsyncResp>& aResp)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020059{
60 const auto mountPointIface =
61 interface.find("xyz.openbmc_project.VirtualMedia.MountPoint");
62 if (mountPointIface == interface.cend())
63 {
64 BMCWEB_LOG_DEBUG << "Interface MountPoint not found";
65 return;
66 }
67
68 const auto processIface =
69 interface.find("xyz.openbmc_project.VirtualMedia.Process");
70 if (processIface == interface.cend())
71 {
72 BMCWEB_LOG_DEBUG << "Interface Process not found";
73 return;
74 }
75
76 const auto endpointIdProperty = mountPointIface->second.find("EndpointId");
77 if (endpointIdProperty == mountPointIface->second.cend())
78 {
79 BMCWEB_LOG_DEBUG << "Property EndpointId not found";
80 return;
81 }
82
83 const auto activeProperty = processIface->second.find("Active");
84 if (activeProperty == processIface->second.cend())
85 {
86 BMCWEB_LOG_DEBUG << "Property Active not found";
87 return;
88 }
89
Gunnar Mills1214b7e2020-06-04 10:11:30 -050090 const bool* activeValue = std::get_if<bool>(&activeProperty->second);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020091 if (!activeValue)
92 {
93 BMCWEB_LOG_DEBUG << "Value Active not found";
94 return;
95 }
96
Gunnar Mills1214b7e2020-06-04 10:11:30 -050097 const std::string* endpointIdValue =
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020098 std::get_if<std::string>(&endpointIdProperty->second);
99 if (endpointIdValue)
100 {
101 if (!endpointIdValue->empty())
102 {
103 // Proxy mode
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100104 aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
105 *endpointIdValue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200106 aResp->res.jsonValue["TransferProtocolType"] = "OEM";
107 aResp->res.jsonValue["Inserted"] = *activeValue;
108 if (*activeValue == true)
109 {
110 aResp->res.jsonValue["ConnectedVia"] = "Applet";
111 }
112 }
113 else
114 {
115 // Legacy mode
Anna Platash9e319cf2020-11-17 10:18:31 +0100116 for (const auto& property : mountPointIface->second)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200117 {
Anna Platash9e319cf2020-11-17 10:18:31 +0100118 if (property.first == "ImageURL")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200119 {
Anna Platash9e319cf2020-11-17 10:18:31 +0100120 const std::string* imageUrlValue =
121 std::get_if<std::string>(&property.second);
122 if (imageUrlValue && !imageUrlValue->empty())
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +0100123 {
Anna Platash9e319cf2020-11-17 10:18:31 +0100124 std::filesystem::path filePath = *imageUrlValue;
125 if (!filePath.has_filename())
126 {
127 // this will handle https share, which not
128 // necessarily has to have filename given.
129 aResp->res.jsonValue["ImageName"] = "";
130 }
131 else
132 {
133 aResp->res.jsonValue["ImageName"] =
134 filePath.filename();
135 }
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +0100136
Anna Platash9e319cf2020-11-17 10:18:31 +0100137 aResp->res.jsonValue["Image"] = *imageUrlValue;
138 aResp->res.jsonValue["Inserted"] = *activeValue;
139 aResp->res.jsonValue["TransferProtocolType"] =
140 getTransferProtocolTypeFromUri(*imageUrlValue);
141
142 if (*activeValue == true)
143 {
144 aResp->res.jsonValue["ConnectedVia"] = "URI";
145 }
146 }
147 }
148 else if (property.first == "WriteProtected")
149 {
150 const bool* writeProtectedValue =
151 std::get_if<bool>(&property.second);
152 if (writeProtectedValue)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200153 {
Anna Platash9e319cf2020-11-17 10:18:31 +0100154 aResp->res.jsonValue["WriteProtected"] =
155 *writeProtectedValue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200156 }
157 }
158 }
159 }
160 }
161}
162
163/**
164 * @brief Fill template for Virtual Media Item.
165 */
Ed Tanous22db1722021-06-09 10:53:51 -0700166inline nlohmann::json vmItemTemplate(const std::string& name,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500167 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200168{
169 nlohmann::json item;
Ed Tanous22db1722021-06-09 10:53:51 -0700170
171 std::string id = "/redfish/v1/Managers/";
172 id += name;
173 id += "/VirtualMedia/";
174 id += resName;
175 item["@odata.id"] = std::move(id);
176
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100177 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200178 item["Name"] = "Virtual Removable Media";
179 item["Id"] = resName;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200180 item["WriteProtected"] = true;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200181 item["MediaTypes"] = {"CD", "USBStick"};
182 item["TransferMethod"] = "Stream";
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100183 item["Oem"]["OpenBMC"]["@odata.type"] =
184 "#OemVirtualMedia.v1_0_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200185
186 return item;
187}
188
189/**
190 * @brief Fills collection data
191 */
Ed Tanous22db1722021-06-09 10:53:51 -0700192inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500193 const std::string& service,
194 const std::string& name)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200195{
196 BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
197 crow::connections::systemBus->async_method_call(
198 [name, aResp{std::move(aResp)}](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500199 ManagedObjectType& subtree) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200200 if (ec)
201 {
202 BMCWEB_LOG_DEBUG << "DBUS response error";
203 return;
204 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500205 nlohmann::json& members = aResp->res.jsonValue["Members"];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200206 members = nlohmann::json::array();
207
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500208 for (const auto& object : subtree)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200209 {
210 nlohmann::json item;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000211 std::string path = object.first.filename();
212 if (path.empty())
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200213 {
214 continue;
215 }
216
Ed Tanous22db1722021-06-09 10:53:51 -0700217 std::string id = "/redfish/v1/Managers/";
218 id += name;
219 id += "/VirtualMedia/";
220 id += path;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200221
Ed Tanous22db1722021-06-09 10:53:51 -0700222 item["@odata.id"] = std::move(id);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200223 members.emplace_back(std::move(item));
224 }
225 aResp->res.jsonValue["Members@odata.count"] = members.size();
226 },
227 service, "/xyz/openbmc_project/VirtualMedia",
228 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
229}
230
231/**
232 * @brief Fills data for specific resource
233 */
Ed Tanous22db1722021-06-09 10:53:51 -0700234inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500235 const std::string& service, const std::string& name,
236 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200237{
238 BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
239
240 crow::connections::systemBus->async_method_call(
241 [resName, name, aResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500242 ManagedObjectType& subtree) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200243 if (ec)
244 {
245 BMCWEB_LOG_DEBUG << "DBUS response error";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200246
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200247 return;
248 }
249
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500250 for (auto& item : subtree)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200251 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000252 std::string thispath = item.first.filename();
253 if (thispath.empty())
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200254 {
255 continue;
256 }
257
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000258 if (thispath != resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200259 {
260 continue;
261 }
262
Przemyslaw Czarnowski1a6258d2021-04-14 11:02:46 +0200263 // "Legacy"/"Proxy"
264 auto mode = item.first.parent_path();
265 // "VirtualMedia"
266 auto type = mode.parent_path();
267 if (mode.filename().empty() || type.filename().empty())
268 {
269 continue;
270 }
271
272 if (type.filename() != "VirtualMedia")
273 {
274 continue;
275 }
276
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200277 aResp->res.jsonValue = vmItemTemplate(name, resName);
Ed Tanous22db1722021-06-09 10:53:51 -0700278 std::string actionsId = "/redfish/v1/Managers/";
279 actionsId += name;
280 actionsId += "/VirtualMedia/";
281 actionsId += resName;
282 actionsId += "/Actions";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200283
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200284 // Check if dbus path is Legacy type
Przemyslaw Czarnowski1a6258d2021-04-14 11:02:46 +0200285 if (mode.filename() == "Legacy")
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200286 {
287 aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
288 ["target"] =
Ed Tanous22db1722021-06-09 10:53:51 -0700289 actionsId + "/VirtualMedia.InsertMedia";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200290 }
291
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200292 vmParseInterfaceObject(item.second, aResp);
293
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200294 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
295 ["target"] =
Ed Tanous22db1722021-06-09 10:53:51 -0700296 actionsId + "/VirtualMedia.EjectMedia";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200297
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200298 return;
299 }
300
301 messages::resourceNotFound(
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100302 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200303 },
304 service, "/xyz/openbmc_project/VirtualMedia",
305 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
306}
307
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200308/**
Ed Tanous22db1722021-06-09 10:53:51 -0700309 * @brief Transfer protocols supported for InsertMedia action.
310 *
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200311 */
Ed Tanous22db1722021-06-09 10:53:51 -0700312enum class TransferProtocol
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200313{
Ed Tanous22db1722021-06-09 10:53:51 -0700314 https,
315 smb,
316 invalid
317};
318
319/**
320 * @brief Function extracts transfer protocol type from URI.
321 *
322 */
323inline std::optional<TransferProtocol>
324 getTransferProtocolFromUri(const std::string& imageUri)
325{
326 try
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200327 {
Ed Tanous22db1722021-06-09 10:53:51 -0700328 std::string_view scheme = boost::urls::url_view(imageUri).scheme();
329 if (scheme == "smb")
Agata Olenderc6f4e012020-03-11 15:19:07 +0100330 {
331 return TransferProtocol::smb;
332 }
Ed Tanous22db1722021-06-09 10:53:51 -0700333 if (scheme == "https")
Agata Olenderc6f4e012020-03-11 15:19:07 +0100334 {
335 return TransferProtocol::https;
336 }
Ed Tanous22db1722021-06-09 10:53:51 -0700337 if (!scheme.empty())
338 {
339 return TransferProtocol::invalid;
340 }
Agata Olenderc6f4e012020-03-11 15:19:07 +0100341 }
Ed Tanous22db1722021-06-09 10:53:51 -0700342 catch (std::exception& p)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100343 {
Ed Tanous22db1722021-06-09 10:53:51 -0700344 BMCWEB_LOG_ERROR << p.what();
Agata Olenderc6f4e012020-03-11 15:19:07 +0100345 }
346
Ed Tanous22db1722021-06-09 10:53:51 -0700347 return {};
348}
349
350/**
351 * @brief Function convert transfer protocol from string param.
352 *
353 */
354inline std::optional<TransferProtocol> getTransferProtocolFromParam(
355 const std::optional<std::string>& transferProtocolType)
356{
357 if (transferProtocolType == std::nullopt)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100358 {
Ed Tanous22db1722021-06-09 10:53:51 -0700359 return {};
Agata Olenderc6f4e012020-03-11 15:19:07 +0100360 }
361
Ed Tanous22db1722021-06-09 10:53:51 -0700362 if (*transferProtocolType == "CIFS")
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200363 {
Ed Tanous22db1722021-06-09 10:53:51 -0700364 return TransferProtocol::smb;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200365 }
366
Ed Tanous22db1722021-06-09 10:53:51 -0700367 if (*transferProtocolType == "HTTPS")
368 {
369 return TransferProtocol::https;
370 }
371
372 return TransferProtocol::invalid;
373}
374
375/**
376 * @brief Function extends URI with transfer protocol type.
377 *
378 */
379inline std::string
380 getUriWithTransferProtocol(const std::string& imageUri,
381 const TransferProtocol& transferProtocol)
382{
383 if (transferProtocol == TransferProtocol::smb)
384 {
385 return "smb://" + imageUri;
386 }
387
388 if (transferProtocol == TransferProtocol::https)
389 {
390 return "https://" + imageUri;
391 }
392
393 return imageUri;
394}
395
396/**
397 * @brief Function validate parameters of insert media request.
398 *
399 */
400inline bool
401 validateParams(crow::Response& res, std::string& imageUrl,
402 const std::optional<bool>& inserted,
403 const std::optional<std::string>& transferMethod,
404 const std::optional<std::string>& transferProtocolType)
405{
406 BMCWEB_LOG_DEBUG << "Validation started";
407 // required param imageUrl must not be empty
408 if (imageUrl.empty())
409 {
410 BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
411
412 messages::propertyValueFormatError(res, "<empty>", "Image");
413
414 return false;
415 }
416
417 // optional param inserted must be true
418 if ((inserted != std::nullopt) && (*inserted != true))
419 {
420 BMCWEB_LOG_ERROR
421 << "Request action optional parameter Inserted must be true.";
422
423 messages::actionParameterNotSupported(res, "Inserted", "InsertMedia");
424
425 return false;
426 }
427
428 // optional param transferMethod must be stream
429 if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
430 {
431 BMCWEB_LOG_ERROR << "Request action optional parameter "
432 "TransferMethod must be Stream.";
433
434 messages::actionParameterNotSupported(res, "TransferMethod",
435 "InsertMedia");
436
437 return false;
438 }
439
440 std::optional<TransferProtocol> uriTransferProtocolType =
441 getTransferProtocolFromUri(imageUrl);
442
443 std::optional<TransferProtocol> paramTransferProtocolType =
444 getTransferProtocolFromParam(transferProtocolType);
445
446 // ImageUrl does not contain valid protocol type
447 if (*uriTransferProtocolType == TransferProtocol::invalid)
448 {
449 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
450 "contain specified protocol type from list: "
451 "(smb, https).";
452
453 messages::resourceAtUriInUnknownFormat(res, imageUrl);
454
455 return false;
456 }
457
458 // transferProtocolType should contain value from list
459 if (*paramTransferProtocolType == TransferProtocol::invalid)
460 {
461 BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
462 "must be provided with value from list: "
463 "(CIFS, HTTPS).";
464
465 messages::propertyValueNotInList(res, *transferProtocolType,
466 "TransferProtocolType");
467 return false;
468 }
469
470 // valid transfer protocol not provided either with URI nor param
471 if ((uriTransferProtocolType == std::nullopt) &&
472 (paramTransferProtocolType == std::nullopt))
473 {
474 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
475 "contain specified protocol type or param "
476 "TransferProtocolType must be provided.";
477
478 messages::resourceAtUriInUnknownFormat(res, imageUrl);
479
480 return false;
481 }
482
483 // valid transfer protocol provided both with URI and param
484 if ((paramTransferProtocolType != std::nullopt) &&
485 (uriTransferProtocolType != std::nullopt))
486 {
487 // check if protocol is the same for URI and param
488 if (*paramTransferProtocolType != *uriTransferProtocolType)
489 {
490 BMCWEB_LOG_ERROR << "Request action parameter "
491 "TransferProtocolType must contain the "
492 "same protocol type as protocol type "
493 "provided with param imageUrl.";
494
495 messages::actionParameterValueTypeError(res, *transferProtocolType,
496 "TransferProtocolType",
497 "InsertMedia");
498
499 return false;
500 }
501 }
502
503 // validation passed
504 // add protocol to URI if needed
505 if (uriTransferProtocolType == std::nullopt)
506 {
507 imageUrl =
508 getUriWithTransferProtocol(imageUrl, *paramTransferProtocolType);
509 }
510
511 return true;
512}
513
514template <typename T>
515static void secureCleanup(T& value)
516{
517 auto raw = const_cast<typename T::value_type*>(value.data());
518 explicit_bzero(raw, value.size() * sizeof(*raw));
519}
520
521class Credentials
522{
523 public:
524 Credentials(std::string&& user, std::string&& password) :
525 userBuf(std::move(user)), passBuf(std::move(password))
526 {}
527
528 ~Credentials()
529 {
530 secureCleanup(userBuf);
531 secureCleanup(passBuf);
532 }
533
534 const std::string& user()
535 {
536 return userBuf;
537 }
538
539 const std::string& password()
540 {
541 return passBuf;
542 }
543
544 Credentials() = delete;
545 Credentials(const Credentials&) = delete;
546 Credentials& operator=(const Credentials&) = delete;
547
548 private:
549 std::string userBuf;
550 std::string passBuf;
551};
552
553class CredentialsProvider
554{
555 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500556 template <typename T>
Ed Tanous22db1722021-06-09 10:53:51 -0700557 struct Deleter
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100558 {
Ed Tanous22db1722021-06-09 10:53:51 -0700559 void operator()(T* buff) const
560 {
561 if (buff)
562 {
563 secureCleanup(*buff);
564 delete buff;
565 }
566 }
567 };
568
569 using Buffer = std::vector<char>;
570 using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
571 // Using explicit definition instead of std::function to avoid implicit
572 // conversions eg. stack copy instead of reference
573 using FormatterFunc = void(const std::string& username,
574 const std::string& password, Buffer& dest);
575
576 CredentialsProvider(std::string&& user, std::string&& password) :
577 credentials(std::move(user), std::move(password))
578 {}
579
580 const std::string& user()
581 {
582 return credentials.user();
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100583 }
584
Ed Tanous22db1722021-06-09 10:53:51 -0700585 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100586 {
Ed Tanous22db1722021-06-09 10:53:51 -0700587 return credentials.password();
588 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100589
Ed Tanous22db1722021-06-09 10:53:51 -0700590 SecureBuffer pack(FormatterFunc formatter)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100591 {
Ed Tanous22db1722021-06-09 10:53:51 -0700592 SecureBuffer packed{new Buffer{}};
593 if (formatter)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100594 {
Ed Tanous22db1722021-06-09 10:53:51 -0700595 formatter(credentials.user(), credentials.password(), *packed);
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100596 }
597
Ed Tanous22db1722021-06-09 10:53:51 -0700598 return packed;
599 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100600
Ed Tanous22db1722021-06-09 10:53:51 -0700601 private:
602 Credentials credentials;
603};
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100604
Ed Tanous22db1722021-06-09 10:53:51 -0700605// Wrapper for boost::async_pipe ensuring proper pipe cleanup
606template <typename Buffer>
607class Pipe
608{
609 public:
610 using unix_fd = sdbusplus::message::unix_fd;
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100611
Ed Tanous22db1722021-06-09 10:53:51 -0700612 Pipe(boost::asio::io_context& io, Buffer&& buffer) :
613 impl(io), buffer{std::move(buffer)}
614 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100615
Ed Tanous22db1722021-06-09 10:53:51 -0700616 ~Pipe()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100617 {
Ed Tanous22db1722021-06-09 10:53:51 -0700618 // Named pipe needs to be explicitly removed
619 impl.close();
620 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100621
Ed Tanous22db1722021-06-09 10:53:51 -0700622 unix_fd fd()
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200623 {
Ed Tanous22db1722021-06-09 10:53:51 -0700624 return unix_fd{impl.native_source()};
625 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100626
Ed Tanous22db1722021-06-09 10:53:51 -0700627 template <typename WriteHandler>
628 void asyncWrite(WriteHandler&& handler)
629 {
630 impl.async_write_some(data(), std::forward<WriteHandler>(handler));
631 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100632
Ed Tanous22db1722021-06-09 10:53:51 -0700633 private:
634 // Specialization for pointer types
635 template <typename B = Buffer>
636 typename std::enable_if<boost::has_dereference<B>::value,
637 boost::asio::const_buffer>::type
638 data()
639 {
640 return boost::asio::buffer(*buffer);
641 }
642
643 template <typename B = Buffer>
644 typename std::enable_if<!boost::has_dereference<B>::value,
645 boost::asio::const_buffer>::type
646 data()
647 {
648 return boost::asio::buffer(buffer);
649 }
650
651 const std::string name;
652 boost::process::async_pipe impl;
653 Buffer buffer;
654};
655
656/**
657 * @brief Function transceives data with dbus directly.
658 *
659 * All BMC state properties will be retrieved before sending reset request.
660 */
661inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
662 const std::string& service, const std::string& name,
663 const std::string& imageUrl, const bool rw,
664 std::string&& userName, std::string&& password)
665{
666 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
667 constexpr const size_t secretLimit = 1024;
668
669 std::shared_ptr<SecurePipe> secretPipe;
670 std::variant<int, SecurePipe::unix_fd> unixFd = -1;
671
672 if (!userName.empty() || !password.empty())
673 {
674 // Encapsulate in safe buffer
675 CredentialsProvider credentials(std::move(userName),
676 std::move(password));
677
678 // Payload must contain data + NULL delimiters
679 if (credentials.user().size() + credentials.password().size() + 2 >
680 secretLimit)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100681 {
Ed Tanous22db1722021-06-09 10:53:51 -0700682 BMCWEB_LOG_ERROR << "Credentials too long to handle";
683 messages::unrecognizedRequestBody(asyncResp->res);
684 return;
685 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100686
Ed Tanous22db1722021-06-09 10:53:51 -0700687 // Pack secret
688 auto secret = credentials.pack(
689 [](const auto& user, const auto& pass, auto& buff) {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100690 std::copy(user.begin(), user.end(), std::back_inserter(buff));
691 buff.push_back('\0');
692 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
693 buff.push_back('\0');
694 });
695
Ed Tanous22db1722021-06-09 10:53:51 -0700696 // Open pipe
697 secretPipe = std::make_shared<SecurePipe>(
698 crow::connections::systemBus->get_io_context(), std::move(secret));
699 unixFd = secretPipe->fd();
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100700
Ed Tanous22db1722021-06-09 10:53:51 -0700701 // Pass secret over pipe
702 secretPipe->asyncWrite(
703 [asyncResp](const boost::system::error_code& ec, std::size_t) {
704 if (ec)
705 {
706 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
707 messages::internalError(asyncResp->res);
708 }
709 });
710 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100711
Ed Tanous22db1722021-06-09 10:53:51 -0700712 crow::connections::systemBus->async_method_call(
713 [asyncResp, secretPipe](const boost::system::error_code ec,
714 bool success) {
715 if (ec)
716 {
717 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
718 messages::internalError(asyncResp->res);
719 }
720 else if (!success)
721 {
722 BMCWEB_LOG_ERROR << "Service responded with error";
723 messages::generalError(asyncResp->res);
724 }
725 },
726 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
727 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
728 unixFd);
729}
730
731/**
732 * @brief Function transceives data with dbus directly.
733 *
734 * All BMC state properties will be retrieved before sending reset request.
735 */
736inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
737 const std::string& service, const std::string& name,
738 bool legacy)
739{
740
741 // Legacy mount requires parameter with image
742 if (legacy)
743 {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100744 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700745 [asyncResp](const boost::system::error_code ec) {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100746 if (ec)
747 {
748 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
Ed Tanous22db1722021-06-09 10:53:51 -0700749
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100750 messages::internalError(asyncResp->res);
Ed Tanous22db1722021-06-09 10:53:51 -0700751 return;
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100752 }
753 },
754 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
Ed Tanous22db1722021-06-09 10:53:51 -0700755 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200756 }
Ed Tanous22db1722021-06-09 10:53:51 -0700757 else // proxy
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200758 {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200759 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700760 [asyncResp](const boost::system::error_code ec) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200761 if (ec)
762 {
Ed Tanous22db1722021-06-09 10:53:51 -0700763 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
764
zhanghch058d1b46d2021-04-01 11:18:24 +0800765 messages::internalError(asyncResp->res);
Ed Tanous22db1722021-06-09 10:53:51 -0700766 return;
767 }
768 },
769 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
770 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
771 }
772}
773
774inline void requestNBDVirtualMediaRoutes(App& app)
775{
776 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
777 "VirtualMedia.InsertMedia")
Ed Tanous432a8902021-06-14 15:28:56 -0700778 .privileges({{"ConfigureManager"}})
Ed Tanous22db1722021-06-09 10:53:51 -0700779 .methods(boost::beast::http::verb::post)(
780 [](const crow::Request& req,
781 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
782 const std::string& name, const std::string& resName) {
783 if (name != "bmc")
784 {
785 messages::resourceNotFound(asyncResp->res,
786 "VirtualMedia.Insert", resName);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200787
788 return;
789 }
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200790
791 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700792 [asyncResp, req,
793 resName](const boost::system::error_code ec,
794 const GetObjectType& getObjectType) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200795 if (ec)
796 {
Ed Tanous22db1722021-06-09 10:53:51 -0700797 BMCWEB_LOG_ERROR
798 << "ObjectMapper::GetObject call failed: "
799 << ec;
800 messages::internalError(asyncResp->res);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200801
802 return;
803 }
Ed Tanous22db1722021-06-09 10:53:51 -0700804 std::string service = getObjectType.begin()->first;
805 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200806
Ed Tanous22db1722021-06-09 10:53:51 -0700807 crow::connections::systemBus->async_method_call(
808 [service, resName, req,
809 asyncResp](const boost::system::error_code ec,
810 ManagedObjectType& subtree) {
811 if (ec)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200812 {
Ed Tanous22db1722021-06-09 10:53:51 -0700813 BMCWEB_LOG_DEBUG << "DBUS response error";
814
815 return;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200816 }
817
Ed Tanous22db1722021-06-09 10:53:51 -0700818 for (const auto& object : subtree)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200819 {
Ed Tanous22db1722021-06-09 10:53:51 -0700820 const std::string& path =
821 static_cast<const std::string&>(
822 object.first);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200823
Ed Tanous22db1722021-06-09 10:53:51 -0700824 std::size_t lastIndex = path.rfind('/');
825 if (lastIndex == std::string::npos)
826 {
827 continue;
828 }
829
830 lastIndex += 1;
831
832 if (path.substr(lastIndex) == resName)
833 {
834 lastIndex = path.rfind("Proxy");
835 if (lastIndex != std::string::npos)
836 {
837 // Not possible in proxy mode
838 BMCWEB_LOG_DEBUG
839 << "InsertMedia not "
840 "allowed in proxy mode";
841 messages::resourceNotFound(
842 asyncResp->res,
843 "VirtualMedia.InsertMedia",
844 resName);
845
846 return;
847 }
848
849 lastIndex = path.rfind("Legacy");
850 if (lastIndex == std::string::npos)
851 {
852 continue;
853 }
854
855 // Legacy mode
856 std::string imageUrl;
857 std::optional<std::string> userName;
858 std::optional<std::string> password;
859 std::optional<std::string>
860 transferMethod;
861 std::optional<std::string>
862 transferProtocolType;
863 std::optional<bool> writeProtected =
864 true;
865 std::optional<bool> inserted;
866
867 // Read obligatory parameters (url of
868 // image)
869 if (!json_util::readJson(
870 req, asyncResp->res, "Image",
871 imageUrl, "WriteProtected",
872 writeProtected, "UserName",
873 userName, "Password", password,
874 "Inserted", inserted,
875 "TransferMethod",
876 transferMethod,
877 "TransferProtocolType",
878 transferProtocolType))
879 {
880 BMCWEB_LOG_DEBUG
881 << "Image is not provided";
882 return;
883 }
884
885 bool paramsValid = validateParams(
886 asyncResp->res, imageUrl, inserted,
887 transferMethod,
888 transferProtocolType);
889
890 if (paramsValid == false)
891 {
892 return;
893 }
894
895 // manager is irrelevant for
896 // VirtualMedia dbus calls
897 doMountVmLegacy(asyncResp, service,
898 resName, imageUrl,
899 !(*writeProtected),
900 std::move(*userName),
901 std::move(*password));
902
903 return;
904 }
905 }
906 BMCWEB_LOG_DEBUG << "Parent item not found";
907 messages::resourceNotFound(
908 asyncResp->res, "VirtualMedia", resName);
909 },
910 service, "/xyz/openbmc_project/VirtualMedia",
911 "org.freedesktop.DBus.ObjectManager",
912 "GetManagedObjects");
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200913 },
Ed Tanous22db1722021-06-09 10:53:51 -0700914 "xyz.openbmc_project.ObjectMapper",
915 "/xyz/openbmc_project/object_mapper",
916 "xyz.openbmc_project.ObjectMapper", "GetObject",
917 "/xyz/openbmc_project/VirtualMedia",
918 std::array<const char*, 0>());
919 });
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200920
Ed Tanous22db1722021-06-09 10:53:51 -0700921 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
922 "VirtualMedia.EjectMedia")
Ed Tanous432a8902021-06-14 15:28:56 -0700923 .privileges({{"ConfigureManager"}})
Ed Tanous22db1722021-06-09 10:53:51 -0700924 .methods(boost::beast::http::verb::post)(
925 [](const crow::Request& req,
926 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
927 const std::string& name, const std::string& resName) {
928 if (name != "bmc")
929 {
930 messages::resourceNotFound(asyncResp->res,
931 "VirtualMedia.Eject", resName);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200932
Ed Tanous22db1722021-06-09 10:53:51 -0700933 return;
934 }
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200935
Ed Tanous22db1722021-06-09 10:53:51 -0700936 crow::connections::systemBus->async_method_call(
937 [asyncResp, req,
938 resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500939 const GetObjectType& getObjectType) {
Ed Tanous22db1722021-06-09 10:53:51 -0700940 if (ec)
941 {
942 BMCWEB_LOG_ERROR
943 << "ObjectMapper::GetObject call failed: "
944 << ec;
945 messages::internalError(asyncResp->res);
946
947 return;
948 }
949 std::string service = getObjectType.begin()->first;
950 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
951
952 crow::connections::systemBus->async_method_call(
953 [resName, service, req, asyncResp{asyncResp}](
954 const boost::system::error_code ec,
955 ManagedObjectType& subtree) {
956 if (ec)
957 {
958 BMCWEB_LOG_DEBUG << "DBUS response error";
959
960 return;
961 }
962
963 for (const auto& object : subtree)
964 {
965 const std::string& path =
966 static_cast<const std::string&>(
967 object.first);
968
969 std::size_t lastIndex = path.rfind('/');
970 if (lastIndex == std::string::npos)
971 {
972 continue;
973 }
974
975 lastIndex += 1;
976
977 if (path.substr(lastIndex) == resName)
978 {
979 lastIndex = path.rfind("Proxy");
980 if (lastIndex != std::string::npos)
981 {
982 // Proxy mode
983 doVmAction(asyncResp, service,
984 resName, false);
985 }
986
987 lastIndex = path.rfind("Legacy");
988 if (lastIndex != std::string::npos)
989 {
990 // Legacy mode
991 doVmAction(asyncResp, service,
992 resName, true);
993 }
994
995 return;
996 }
997 }
998 BMCWEB_LOG_DEBUG << "Parent item not found";
999 messages::resourceNotFound(
1000 asyncResp->res, "VirtualMedia", resName);
1001 },
1002 service, "/xyz/openbmc_project/VirtualMedia",
1003 "org.freedesktop.DBus.ObjectManager",
1004 "GetManagedObjects");
1005 },
1006 "xyz.openbmc_project.ObjectMapper",
1007 "/xyz/openbmc_project/object_mapper",
1008 "xyz.openbmc_project.ObjectMapper", "GetObject",
1009 "/xyz/openbmc_project/VirtualMedia",
1010 std::array<const char*, 0>());
1011 });
1012 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
Ed Tanous432a8902021-06-14 15:28:56 -07001013 .privileges({{"Login"}})
Ed Tanous22db1722021-06-09 10:53:51 -07001014 .methods(boost::beast::http::verb::get)(
1015 [](const crow::Request& /* req */,
1016 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1017 const std::string& name) {
1018 if (name != "bmc")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001019 {
Ed Tanous22db1722021-06-09 10:53:51 -07001020 messages::resourceNotFound(asyncResp->res, "VirtualMedia",
1021 name);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001022
1023 return;
1024 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001025
Ed Tanous22db1722021-06-09 10:53:51 -07001026 asyncResp->res.jsonValue["@odata.type"] =
1027 "#VirtualMediaCollection.VirtualMediaCollection";
1028 asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
1029 asyncResp->res.jsonValue["@odata.id"] =
1030 "/redfish/v1/Managers/" + name + "/VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001031
Ed Tanous22db1722021-06-09 10:53:51 -07001032 crow::connections::systemBus->async_method_call(
1033 [asyncResp, name](const boost::system::error_code ec,
1034 const GetObjectType& getObjectType) {
1035 if (ec)
1036 {
1037 BMCWEB_LOG_ERROR
1038 << "ObjectMapper::GetObject call failed: "
1039 << ec;
1040 messages::internalError(asyncResp->res);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001041
Ed Tanous22db1722021-06-09 10:53:51 -07001042 return;
1043 }
1044 std::string service = getObjectType.begin()->first;
1045 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001046
Ed Tanous22db1722021-06-09 10:53:51 -07001047 getVmResourceList(asyncResp, service, name);
1048 },
1049 "xyz.openbmc_project.ObjectMapper",
1050 "/xyz/openbmc_project/object_mapper",
1051 "xyz.openbmc_project.ObjectMapper", "GetObject",
1052 "/xyz/openbmc_project/VirtualMedia",
1053 std::array<const char*, 0>());
1054 });
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001055
Ed Tanous22db1722021-06-09 10:53:51 -07001056 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001057 .privileges({{"Login"}})
Ed Tanous22db1722021-06-09 10:53:51 -07001058 .methods(boost::beast::http::verb::get)(
1059 [](const crow::Request& /* req */,
1060 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1061 const std::string& name, const std::string& resName) {
1062 if (name != "bmc")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001063 {
Ed Tanous22db1722021-06-09 10:53:51 -07001064 messages::resourceNotFound(asyncResp->res, "VirtualMedia",
1065 resName);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001066
1067 return;
1068 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001069
Ed Tanous22db1722021-06-09 10:53:51 -07001070 crow::connections::systemBus->async_method_call(
1071 [asyncResp, name,
1072 resName](const boost::system::error_code ec,
1073 const GetObjectType& getObjectType) {
1074 if (ec)
1075 {
1076 BMCWEB_LOG_ERROR
1077 << "ObjectMapper::GetObject call failed: "
1078 << ec;
1079 messages::internalError(asyncResp->res);
1080
1081 return;
1082 }
1083 std::string service = getObjectType.begin()->first;
1084 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1085
1086 getVmData(asyncResp, service, name, resName);
1087 },
1088 "xyz.openbmc_project.ObjectMapper",
1089 "/xyz/openbmc_project/object_mapper",
1090 "xyz.openbmc_project.ObjectMapper", "GetObject",
1091 "/xyz/openbmc_project/VirtualMedia",
1092 std::array<const char*, 0>());
1093 });
1094}
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001095
1096} // namespace redfish