1 /** 2 * Copyright © 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 #include "config.h" 17 18 #include "settings.hpp" 19 20 #include <dlfcn.h> 21 22 #include <boost/algorithm/string.hpp> 23 #include <boost/asio/io_context.hpp> 24 #include <host-cmd-manager.hpp> 25 #include <ipmid-host/cmd.hpp> 26 #include <ipmid/api.hpp> 27 #include <ipmid/handler.hpp> 28 #include <ipmid/message.hpp> 29 #include <ipmid/oemrouter.hpp> 30 #include <ipmid/types.hpp> 31 #include <phosphor-logging/lg2.hpp> 32 #include <sdbusplus/asio/connection.hpp> 33 #include <sdbusplus/asio/object_server.hpp> 34 #include <sdbusplus/bus.hpp> 35 #include <sdbusplus/bus/match.hpp> 36 #include <sdbusplus/timer.hpp> 37 38 #include <algorithm> 39 #include <any> 40 #include <exception> 41 #include <filesystem> 42 #include <forward_list> 43 #include <map> 44 #include <memory> 45 #include <optional> 46 #include <tuple> 47 #include <unordered_map> 48 #include <utility> 49 #include <vector> 50 51 namespace fs = std::filesystem; 52 53 using namespace phosphor::logging; 54 55 // IPMI Spec, shared Reservation ID. 56 static unsigned short selReservationID = 0xFFFF; 57 static bool selReservationValid = false; 58 59 unsigned short reserveSel(void) 60 { 61 // IPMI spec, Reservation ID, the value simply increases against each 62 // execution of the Reserve SEL command. 63 if (++selReservationID == 0) 64 { 65 selReservationID = 1; 66 } 67 selReservationValid = true; 68 return selReservationID; 69 } 70 71 bool checkSELReservation(unsigned short id) 72 { 73 return (selReservationValid && selReservationID == id); 74 } 75 76 void cancelSELReservation(void) 77 { 78 selReservationValid = false; 79 } 80 81 EInterfaceIndex getInterfaceIndex(void) 82 { 83 return interfaceKCS; 84 } 85 86 sd_bus* bus; 87 sd_bus* ipmid_get_sd_bus_connection(void) 88 { 89 return bus; 90 } 91 92 namespace ipmi 93 { 94 95 static inline unsigned int makeCmdKey(unsigned int cluster, unsigned int cmd) 96 { 97 return (cluster << 8) | cmd; 98 } 99 100 using HandlerTuple = std::tuple<int, /* prio */ 101 Privilege, HandlerBase::ptr /* handler */ 102 >; 103 104 /* map to handle standard registered commands */ 105 static std::unordered_map<unsigned int, /* key is NetFn/Cmd */ 106 HandlerTuple> 107 handlerMap; 108 109 /* special map for decoding Group registered commands (NetFn 2Ch) */ 110 static std::unordered_map<unsigned int, /* key is Group/Cmd (NetFn is 2Ch) */ 111 HandlerTuple> 112 groupHandlerMap; 113 114 /* special map for decoding OEM registered commands (NetFn 2Eh) */ 115 static std::unordered_map<unsigned int, /* key is Iana/Cmd (NetFn is 2Eh) */ 116 HandlerTuple> 117 oemHandlerMap; 118 119 using FilterTuple = std::tuple<int, /* prio */ 120 FilterBase::ptr /* filter */ 121 >; 122 123 /* list to hold all registered ipmi command filters */ 124 static std::forward_list<FilterTuple> filterList; 125 126 namespace impl 127 { 128 /* common function to register all standard IPMI handlers */ 129 bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv, 130 HandlerBase::ptr handler) 131 { 132 // check for valid NetFn: even; 00-0Ch, 30-3Eh 133 if (netFn & 1 || (netFn > netFnTransport && netFn < netFnGroup) || 134 netFn > netFnOemEight) 135 { 136 return false; 137 } 138 139 // create key and value for this handler 140 unsigned int netFnCmd = makeCmdKey(netFn, cmd); 141 HandlerTuple item(prio, priv, handler); 142 143 // consult the handler map and look for a match 144 auto& mapCmd = handlerMap[netFnCmd]; 145 if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio) 146 { 147 mapCmd = item; 148 return true; 149 } 150 return false; 151 } 152 153 /* common function to register all Group IPMI handlers */ 154 bool registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv, 155 HandlerBase::ptr handler) 156 { 157 // create key and value for this handler 158 unsigned int netFnCmd = makeCmdKey(group, cmd); 159 HandlerTuple item(prio, priv, handler); 160 161 // consult the handler map and look for a match 162 auto& mapCmd = groupHandlerMap[netFnCmd]; 163 if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio) 164 { 165 mapCmd = item; 166 return true; 167 } 168 return false; 169 } 170 171 /* common function to register all OEM IPMI handlers */ 172 bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv, 173 HandlerBase::ptr handler) 174 { 175 // create key and value for this handler 176 unsigned int netFnCmd = makeCmdKey(iana, cmd); 177 HandlerTuple item(prio, priv, handler); 178 179 // consult the handler map and look for a match 180 auto& mapCmd = oemHandlerMap[netFnCmd]; 181 if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio) 182 { 183 mapCmd = item; 184 lg2::debug("registered OEM Handler: NetFn/Cmd={NETFNCMD}", "IANA", 185 lg2::hex, iana, "CMD", lg2::hex, cmd, "NETFNCMD", lg2::hex, 186 netFnCmd); 187 return true; 188 } 189 190 lg2::warning("could not register OEM Handler: NetFn/Cmd={NETFNCMD}", "IANA", 191 lg2::hex, iana, "CMD", lg2::hex, cmd, "NETFNCMD", lg2::hex, 192 netFnCmd); 193 return false; 194 } 195 196 /* common function to register all IPMI filter handlers */ 197 void registerFilter(int prio, FilterBase::ptr filter) 198 { 199 // check for initial placement 200 if (filterList.empty() || std::get<int>(filterList.front()) < prio) 201 { 202 filterList.emplace_front(std::make_tuple(prio, filter)); 203 return; 204 } 205 // walk the list and put it in the right place 206 auto j = filterList.begin(); 207 for (auto i = j; i != filterList.end() && std::get<int>(*i) > prio; i++) 208 { 209 j = i; 210 } 211 filterList.emplace_after(j, std::make_tuple(prio, filter)); 212 } 213 214 } // namespace impl 215 216 message::Response::ptr filterIpmiCommand(message::Request::ptr request) 217 { 218 // pass the command through the filter mechanism 219 // This can be the firmware firewall or any OEM mechanism like 220 // whitelist filtering based on operational mode 221 for (auto& item : filterList) 222 { 223 FilterBase::ptr filter = std::get<FilterBase::ptr>(item); 224 ipmi::Cc cc = filter->call(request); 225 if (ipmi::ccSuccess != cc) 226 { 227 return errorResponse(request, cc); 228 } 229 } 230 return message::Response::ptr(); 231 } 232 233 message::Response::ptr executeIpmiCommandCommon( 234 std::unordered_map<unsigned int, HandlerTuple>& handlers, 235 unsigned int keyCommon, message::Request::ptr request) 236 { 237 // filter the command first; a non-null message::Response::ptr 238 // means that the message has been rejected for some reason 239 message::Response::ptr filterResponse = filterIpmiCommand(request); 240 241 Cmd cmd = request->ctx->cmd; 242 unsigned int key = makeCmdKey(keyCommon, cmd); 243 auto cmdIter = handlers.find(key); 244 if (cmdIter != handlers.end()) 245 { 246 // only return the filter response if the command is found 247 if (filterResponse) 248 { 249 lg2::debug("request for NetFn/Cmd {NETFN}/{CMD} has been filtered", 250 "NETFN", lg2::hex, keyCommon, "CMD", lg2::hex, cmd); 251 return filterResponse; 252 } 253 HandlerTuple& chosen = cmdIter->second; 254 if (request->ctx->priv < std::get<Privilege>(chosen)) 255 { 256 return errorResponse(request, ccInsufficientPrivilege); 257 } 258 return std::get<HandlerBase::ptr>(chosen)->call(request); 259 } 260 else 261 { 262 unsigned int wildcard = makeCmdKey(keyCommon, cmdWildcard); 263 cmdIter = handlers.find(wildcard); 264 if (cmdIter != handlers.end()) 265 { 266 // only return the filter response if the command is found 267 if (filterResponse) 268 { 269 lg2::debug( 270 "request for NetFn/Cmd {NETFN}/{CMD} has been filtered", 271 "NETFN", lg2::hex, keyCommon, "CMD", lg2::hex, cmd); 272 return filterResponse; 273 } 274 HandlerTuple& chosen = cmdIter->second; 275 if (request->ctx->priv < std::get<Privilege>(chosen)) 276 { 277 return errorResponse(request, ccInsufficientPrivilege); 278 } 279 return std::get<HandlerBase::ptr>(chosen)->call(request); 280 } 281 } 282 return errorResponse(request, ccInvalidCommand); 283 } 284 285 message::Response::ptr executeIpmiGroupCommand(message::Request::ptr request) 286 { 287 // look up the group for this request 288 uint8_t bytes; 289 if (0 != request->payload.unpack(bytes)) 290 { 291 return errorResponse(request, ccReqDataLenInvalid); 292 } 293 auto group = static_cast<Group>(bytes); 294 message::Response::ptr response = 295 executeIpmiCommandCommon(groupHandlerMap, group, request); 296 ipmi::message::Payload prefix; 297 prefix.pack(bytes); 298 response->prepend(prefix); 299 return response; 300 } 301 302 message::Response::ptr executeIpmiOemCommand(message::Request::ptr request) 303 { 304 // look up the iana for this request 305 uint24_t bytes; 306 if (0 != request->payload.unpack(bytes)) 307 { 308 return errorResponse(request, ccReqDataLenInvalid); 309 } 310 auto iana = static_cast<Iana>(bytes); 311 312 lg2::debug("unpack IANA {IANA}", "IANA", lg2::hex, iana); 313 314 message::Response::ptr response = 315 executeIpmiCommandCommon(oemHandlerMap, iana, request); 316 ipmi::message::Payload prefix; 317 prefix.pack(bytes); 318 response->prepend(prefix); 319 return response; 320 } 321 322 message::Response::ptr executeIpmiCommand(message::Request::ptr request) 323 { 324 NetFn netFn = request->ctx->netFn; 325 if (netFnGroup == netFn) 326 { 327 return executeIpmiGroupCommand(request); 328 } 329 else if (netFnOem == netFn) 330 { 331 return executeIpmiOemCommand(request); 332 } 333 return executeIpmiCommandCommon(handlerMap, netFn, request); 334 } 335 336 namespace utils 337 { 338 template <typename AssocContainer, typename UnaryPredicate> 339 void assoc_erase_if(AssocContainer& c, UnaryPredicate p) 340 { 341 typename AssocContainer::iterator next = c.begin(); 342 typename AssocContainer::iterator last = c.end(); 343 while ((next = std::find_if(next, last, p)) != last) 344 { 345 c.erase(next++); 346 } 347 } 348 } // namespace utils 349 350 namespace 351 { 352 std::unordered_map<std::string, uint8_t> uniqueNameToChannelNumber; 353 354 // sdbusplus::bus::match::rules::arg0namespace() wants the prefix 355 // to match without any trailing '.' 356 constexpr const char ipmiDbusChannelMatch[] = 357 "xyz.openbmc_project.Ipmi.Channel"; 358 void updateOwners(sdbusplus::asio::connection& conn, const std::string& name) 359 { 360 conn.async_method_call( 361 [name](const boost::system::error_code ec, 362 const std::string& nameOwner) { 363 if (ec) 364 { 365 lg2::error("Error getting dbus owner for {INTERFACE}", 366 "INTERFACE", name); 367 return; 368 } 369 // start after ipmiDbusChannelPrefix (after the '.') 370 std::string chName = 371 name.substr(std::strlen(ipmiDbusChannelMatch) + 1); 372 try 373 { 374 uint8_t channel = getChannelByName(chName); 375 uniqueNameToChannelNumber[nameOwner] = channel; 376 lg2::info( 377 "New interface mapping: {INTERFACE} -> channel {CHANNEL}", 378 "INTERFACE", name, "CHANNEL", channel); 379 } 380 catch (const std::exception& e) 381 { 382 lg2::info("Failed interface mapping, no such name: {INTERFACE}", 383 "INTERFACE", name); 384 } 385 }, 386 "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner", 387 name); 388 } 389 390 void doListNames(boost::asio::io_context& io, sdbusplus::asio::connection& conn) 391 { 392 conn.async_method_call( 393 [&io, &conn](const boost::system::error_code ec, 394 std::vector<std::string> busNames) { 395 if (ec) 396 { 397 lg2::error("Error getting dbus names: {ERROR}", "ERROR", 398 ec.message()); 399 std::exit(EXIT_FAILURE); 400 return; 401 } 402 // Try to make startup consistent 403 std::sort(busNames.begin(), busNames.end()); 404 405 const std::string channelPrefix = 406 std::string(ipmiDbusChannelMatch) + "."; 407 for (const std::string& busName : busNames) 408 { 409 if (busName.find(channelPrefix) == 0) 410 { 411 updateOwners(conn, busName); 412 } 413 } 414 }, 415 "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", 416 "ListNames"); 417 } 418 419 void nameChangeHandler(sdbusplus::message_t& message) 420 { 421 std::string name; 422 std::string oldOwner; 423 std::string newOwner; 424 425 message.read(name, oldOwner, newOwner); 426 427 if (!oldOwner.empty()) 428 { 429 if (boost::starts_with(oldOwner, ":")) 430 { 431 // Connection removed 432 auto it = uniqueNameToChannelNumber.find(oldOwner); 433 if (it != uniqueNameToChannelNumber.end()) 434 { 435 uniqueNameToChannelNumber.erase(it); 436 } 437 } 438 } 439 if (!newOwner.empty()) 440 { 441 // start after ipmiDbusChannelMatch (and after the '.') 442 std::string chName = name.substr(std::strlen(ipmiDbusChannelMatch) + 1); 443 try 444 { 445 uint8_t channel = getChannelByName(chName); 446 uniqueNameToChannelNumber[newOwner] = channel; 447 lg2::info("New interface mapping: {INTERFACE} -> channel {CHANNEL}", 448 "INTERFACE", name, "CHANNEL", channel); 449 } 450 catch (const std::exception& e) 451 { 452 lg2::info("Failed interface mapping, no such name: {INTERFACE}", 453 "INTERFACE", name); 454 } 455 } 456 }; 457 458 } // anonymous namespace 459 460 static constexpr const char intraBmcName[] = "INTRABMC"; 461 uint8_t channelFromMessage(sdbusplus::message_t& msg) 462 { 463 // channel name for ipmitool to resolve to 464 std::string sender = msg.get_sender(); 465 auto chIter = uniqueNameToChannelNumber.find(sender); 466 if (chIter != uniqueNameToChannelNumber.end()) 467 { 468 return chIter->second; 469 } 470 // FIXME: currently internal connections are ephemeral and hard to pin down 471 try 472 { 473 return getChannelByName(intraBmcName); 474 } 475 catch (const std::exception& e) 476 { 477 return invalidChannel; 478 } 479 } // namespace ipmi 480 481 /* called from sdbus async server context */ 482 auto executionEntry(boost::asio::yield_context yield, sdbusplus::message_t& m, 483 NetFn netFn, uint8_t lun, Cmd cmd, ipmi::SecureBuffer& data, 484 std::map<std::string, ipmi::Value>& options) 485 { 486 const auto dbusResponse = 487 [netFn, lun, cmd](Cc cc, const ipmi::SecureBuffer& data = {}) { 488 constexpr uint8_t netFnResponse = 0x01; 489 uint8_t retNetFn = netFn | netFnResponse; 490 return std::make_tuple(retNetFn, lun, cmd, cc, data); 491 }; 492 std::string sender = m.get_sender(); 493 Privilege privilege = Privilege::None; 494 int rqSA = 0; 495 int hostIdx = 0; 496 uint8_t userId = 0; // undefined user 497 uint32_t sessionId = 0; 498 499 // figure out what channel the request came in on 500 uint8_t channel = channelFromMessage(m); 501 if (channel == invalidChannel) 502 { 503 // unknown sender channel; refuse to service the request 504 lg2::error("ERROR determining source IPMI channel from " 505 "{SENDER} NetFn/Cmd {NETFN}/{CMD}", 506 "SENDER", sender, "NETFN", lg2::hex, netFn, "CMD", lg2::hex, 507 cmd); 508 return dbusResponse(ipmi::ccDestinationUnavailable); 509 } 510 511 // session-based channels are required to provide userId, privilege and 512 // sessionId 513 if (getChannelSessionSupport(channel) != EChannelSessSupported::none) 514 { 515 try 516 { 517 Value requestPriv = options.at("privilege"); 518 Value requestUserId = options.at("userId"); 519 Value requestSessionId = options.at("currentSessionId"); 520 privilege = static_cast<Privilege>(std::get<int>(requestPriv)); 521 userId = static_cast<uint8_t>(std::get<int>(requestUserId)); 522 sessionId = 523 static_cast<uint32_t>(std::get<uint32_t>(requestSessionId)); 524 } 525 catch (const std::exception& e) 526 { 527 lg2::error("ERROR determining IPMI session credentials on " 528 "channel {CHANNEL} for userid {USERID}", 529 "CHANNEL", channel, "USERID", userId, "NETFN", lg2::hex, 530 netFn, "CMD", lg2::hex, cmd); 531 return dbusResponse(ipmi::ccUnspecifiedError); 532 } 533 } 534 else 535 { 536 // get max privilege for session-less channels 537 // For now, there is not a way to configure this, default to Admin 538 privilege = Privilege::Admin; 539 540 // ipmb should supply rqSA 541 ChannelInfo chInfo; 542 getChannelInfo(channel, chInfo); 543 if (static_cast<EChannelMediumType>(chInfo.mediumType) == 544 EChannelMediumType::ipmb) 545 { 546 const auto iter = options.find("rqSA"); 547 if (iter != options.end()) 548 { 549 if (std::holds_alternative<int>(iter->second)) 550 { 551 rqSA = std::get<int>(iter->second); 552 } 553 } 554 const auto iteration = options.find("hostId"); 555 if (iteration != options.end()) 556 { 557 if (std::holds_alternative<int>(iteration->second)) 558 { 559 hostIdx = std::get<int>(iteration->second); 560 } 561 } 562 } 563 } 564 // check to see if the requested priv/username is valid 565 lg2::debug("Set up ipmi context: Ch:NetFn/Cmd={CHANNEL}:{NETFN}/{CMD}", 566 "SENDER", sender, "NETFN", lg2::hex, netFn, "LUN", lg2::hex, lun, 567 "CMD", lg2::hex, cmd, "CHANNEL", channel, "USERID", userId, 568 "SESSIONID", lg2::hex, sessionId, "PRIVILEGE", 569 static_cast<uint8_t>(privilege), "RQSA", lg2::hex, rqSA); 570 571 auto ctx = std::make_shared<ipmi::Context>( 572 getSdBus(), netFn, lun, cmd, channel, userId, sessionId, privilege, 573 rqSA, hostIdx, yield); 574 auto request = std::make_shared<ipmi::message::Request>( 575 ctx, std::forward<ipmi::SecureBuffer>(data)); 576 message::Response::ptr response = executeIpmiCommand(request); 577 578 return dbusResponse(response->cc, response->payload.raw); 579 } 580 581 /** @struct IpmiProvider 582 * 583 * RAII wrapper for dlopen so that dlclose gets called on exit 584 */ 585 struct IpmiProvider 586 { 587 public: 588 /** @brief address of the opened library */ 589 void* addr; 590 std::string name; 591 592 IpmiProvider() = delete; 593 IpmiProvider(const IpmiProvider&) = delete; 594 IpmiProvider& operator=(const IpmiProvider&) = delete; 595 IpmiProvider(IpmiProvider&&) = delete; 596 IpmiProvider& operator=(IpmiProvider&&) = delete; 597 598 /** @brief dlopen a shared object file by path 599 * @param[in] filename - path of shared object to open 600 */ 601 explicit IpmiProvider(const char* fname) : addr(nullptr), name(fname) 602 { 603 lg2::debug("Open IPMI provider library: {PROVIDER}", "PROVIDER", name); 604 try 605 { 606 addr = dlopen(name.c_str(), RTLD_NOW); 607 } 608 catch (const std::exception& e) 609 { 610 lg2::error("ERROR opening IPMI provider {PROVIDER}: {ERROR}", 611 "PROVIDER", name, "ERROR", e); 612 } 613 catch (...) 614 { 615 const char* what = currentExceptionType(); 616 lg2::error("ERROR opening IPMI provider {PROVIDER}: {ERROR}", 617 "PROVIDER", name, "ERROR", what); 618 } 619 if (!isOpen()) 620 { 621 lg2::error("ERROR opening IPMI provider {PROVIDER}: {ERROR}", 622 "PROVIDER", name, "ERROR", dlerror()); 623 } 624 } 625 626 ~IpmiProvider() 627 { 628 if (isOpen()) 629 { 630 dlclose(addr); 631 } 632 } 633 bool isOpen() const 634 { 635 return (nullptr != addr); 636 } 637 }; 638 639 // Plugin libraries need to contain .so either at the end or in the middle 640 constexpr const char ipmiPluginExtn[] = ".so"; 641 642 /* return a list of self-closing library handles */ 643 std::forward_list<IpmiProvider> loadProviders(const fs::path& ipmiLibsPath) 644 { 645 std::vector<fs::path> libs; 646 for (const auto& libPath : fs::directory_iterator(ipmiLibsPath)) 647 { 648 std::error_code ec; 649 fs::path fname = libPath.path(); 650 if (fs::is_symlink(fname, ec) || ec) 651 { 652 // it's a symlink or some other error; skip it 653 continue; 654 } 655 while (fname.has_extension()) 656 { 657 fs::path extn = fname.extension(); 658 if (extn == ipmiPluginExtn) 659 { 660 libs.push_back(libPath.path()); 661 break; 662 } 663 fname.replace_extension(); 664 } 665 } 666 std::sort(libs.begin(), libs.end()); 667 668 std::forward_list<IpmiProvider> handles; 669 for (auto& lib : libs) 670 { 671 #ifdef __IPMI_DEBUG__ 672 lg2::debug("Registering handler {HANDLER}", "HANDLER", lib); 673 #endif 674 handles.emplace_front(lib.c_str()); 675 } 676 return handles; 677 } 678 679 } // namespace ipmi 680 681 #ifdef ALLOW_DEPRECATED_API 682 /* legacy registration */ 683 void ipmi_register_callback(ipmi_netfn_t netFn, ipmi_cmd_t cmd, 684 ipmi_context_t context, ipmid_callback_t handler, 685 ipmi_cmd_privilege_t priv) 686 { 687 auto h = ipmi::makeLegacyHandler(handler, context); 688 // translate priv from deprecated enum to current 689 ipmi::Privilege realPriv; 690 switch (priv) 691 { 692 case PRIVILEGE_CALLBACK: 693 realPriv = ipmi::Privilege::Callback; 694 break; 695 case PRIVILEGE_USER: 696 realPriv = ipmi::Privilege::User; 697 break; 698 case PRIVILEGE_OPERATOR: 699 realPriv = ipmi::Privilege::Operator; 700 break; 701 case PRIVILEGE_ADMIN: 702 realPriv = ipmi::Privilege::Admin; 703 break; 704 case PRIVILEGE_OEM: 705 realPriv = ipmi::Privilege::Oem; 706 break; 707 case SYSTEM_INTERFACE: 708 realPriv = ipmi::Privilege::Admin; 709 break; 710 default: 711 realPriv = ipmi::Privilege::Admin; 712 break; 713 } 714 // The original ipmi_register_callback allowed for group OEM handlers 715 // to be registered via this same interface. It just so happened that 716 // all the handlers were part of the DCMI group, so default to that. 717 if (netFn == NETFUN_GRPEXT) 718 { 719 ipmi::impl::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI, 720 cmd, realPriv, h); 721 } 722 else 723 { 724 ipmi::impl::registerHandler(ipmi::prioOpenBmcBase, netFn, cmd, realPriv, 725 h); 726 } 727 } 728 729 namespace oem 730 { 731 732 class LegacyRouter : public oem::Router 733 { 734 public: 735 virtual ~LegacyRouter() {} 736 737 /// Enable message routing to begin. 738 void activate() override {} 739 740 void registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler) override 741 { 742 auto h = ipmi::makeLegacyHandler(std::forward<Handler>(handler)); 743 ipmi::impl::registerOemHandler(ipmi::prioOpenBmcBase, oen, cmd, 744 ipmi::Privilege::Admin, h); 745 } 746 }; 747 static LegacyRouter legacyRouter; 748 749 Router* mutableRouter() 750 { 751 return &legacyRouter; 752 } 753 754 } // namespace oem 755 756 /* legacy alternative to executionEntry */ 757 void handleLegacyIpmiCommand(sdbusplus::message_t& m) 758 { 759 // make a copy so the next two moves don't wreak havoc on the stack 760 sdbusplus::message_t b{m}; 761 boost::asio::spawn( 762 *getIoContext(), 763 [b = std::move(b)](boost::asio::yield_context yield) { 764 sdbusplus::message_t m{std::move(b)}; 765 unsigned char seq = 0, netFn = 0, lun = 0, cmd = 0; 766 ipmi::SecureBuffer data; 767 768 m.read(seq, netFn, lun, cmd, data); 769 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 770 auto ctx = std::make_shared<ipmi::Context>( 771 bus, netFn, lun, cmd, 0, 0, 0, ipmi::Privilege::Admin, 0, 0, 772 yield); 773 auto request = std::make_shared<ipmi::message::Request>( 774 ctx, std::forward<ipmi::SecureBuffer>(data)); 775 ipmi::message::Response::ptr response = 776 ipmi::executeIpmiCommand(request); 777 778 // Responses in IPMI require a bit set. So there ya go... 779 netFn |= 0x01; 780 781 const char *dest, *path; 782 constexpr const char* DBUS_INTF = "org.openbmc.HostIpmi"; 783 784 dest = m.get_sender(); 785 path = m.get_path(); 786 boost::system::error_code ec; 787 bus->yield_method_call(yield, ec, dest, path, DBUS_INTF, 788 "sendMessage", seq, netFn, lun, cmd, 789 response->cc, response->payload.raw); 790 if (ec) 791 { 792 lg2::error( 793 "Failed to send response to requestor ({NETFN}/{CMD}): {ERROR}", 794 "ERROR", ec.message(), "SENDER", dest, "NETFN", lg2::hex, 795 netFn, "CMD", lg2::hex, cmd); 796 } 797 }, 798 {}); 799 } 800 801 #endif /* ALLOW_DEPRECATED_API */ 802 803 // Calls host command manager to do the right thing for the command 804 using CommandHandler = phosphor::host::command::CommandHandler; 805 std::unique_ptr<phosphor::host::command::Manager> cmdManager; 806 void ipmid_send_cmd_to_host(CommandHandler&& cmd) 807 { 808 cmdManager->execute(std::forward<CommandHandler>(cmd)); 809 } 810 811 std::unique_ptr<phosphor::host::command::Manager>& ipmid_get_host_cmd_manager() 812 { 813 return cmdManager; 814 } 815 816 // These are symbols that are present in libipmid, but not expected 817 // to be used except here (or maybe a unit test), so declare them here 818 extern void setIoContext(std::shared_ptr<boost::asio::io_context>& newIo); 819 extern void setSdBus(std::shared_ptr<sdbusplus::asio::connection>& newBus); 820 821 int main(int argc, char* argv[]) 822 { 823 // Connect to system bus 824 auto io = std::make_shared<boost::asio::io_context>(); 825 setIoContext(io); 826 if (argc > 1 && std::string(argv[1]) == "-session") 827 { 828 sd_bus_default_user(&bus); 829 } 830 else 831 { 832 sd_bus_default_system(&bus); 833 } 834 auto sdbusp = std::make_shared<sdbusplus::asio::connection>(*io, bus); 835 setSdBus(sdbusp); 836 837 cmdManager = std::make_unique<phosphor::host::command::Manager>(*sdbusp); 838 839 // Register all command providers and filters 840 std::forward_list<ipmi::IpmiProvider> providers = 841 ipmi::loadProviders(HOST_IPMI_LIB_PATH); 842 843 #ifdef ALLOW_DEPRECATED_API 844 // listen on deprecated signal interface for kcs/bt commands 845 constexpr const char* FILTER = "type='signal',interface='org.openbmc." 846 "HostIpmi',member='ReceivedMessage'"; 847 sdbusplus::bus::match_t oldIpmiInterface(*sdbusp, FILTER, 848 handleLegacyIpmiCommand); 849 #endif /* ALLOW_DEPRECATED_API */ 850 851 // set up bus name watching to match channels with bus names 852 sdbusplus::bus::match_t nameOwnerChanged( 853 *sdbusp, 854 sdbusplus::bus::match::rules::nameOwnerChanged() + 855 sdbusplus::bus::match::rules::arg0namespace( 856 ipmi::ipmiDbusChannelMatch), 857 ipmi::nameChangeHandler); 858 ipmi::doListNames(*io, *sdbusp); 859 860 int exitCode = 0; 861 // set up boost::asio signal handling 862 std::function<SignalResponse(int)> stopAsioRunLoop = [&io, &exitCode]( 863 int signalNumber) { 864 lg2::info("Received signal {SIGNAL}; quitting", "SIGNAL", signalNumber); 865 io->stop(); 866 exitCode = signalNumber; 867 return SignalResponse::breakExecution; 868 }; 869 registerSignalHandler(ipmi::prioOpenBmcBase, SIGINT, stopAsioRunLoop); 870 registerSignalHandler(ipmi::prioOpenBmcBase, SIGTERM, stopAsioRunLoop); 871 872 sdbusp->request_name("xyz.openbmc_project.Ipmi.Host"); 873 // Add bindings for inbound IPMI requests 874 auto server = sdbusplus::asio::object_server(sdbusp); 875 auto iface = server.add_interface("/xyz/openbmc_project/Ipmi", 876 "xyz.openbmc_project.Ipmi.Server"); 877 iface->register_method("execute", ipmi::executionEntry); 878 iface->initialize(); 879 880 io->run(); 881 882 // destroy all the IPMI handlers so the providers can unload safely 883 ipmi::handlerMap.clear(); 884 ipmi::groupHandlerMap.clear(); 885 ipmi::oemHandlerMap.clear(); 886 ipmi::filterList.clear(); 887 // unload the provider libraries 888 providers.clear(); 889 890 std::exit(exitCode); 891 } 892