1*a3702c1fSVernon Mauery /*
2*a3702c1fSVernon Mauery // Copyright (c) 2018 Intel Corporation
3*a3702c1fSVernon Mauery //
4*a3702c1fSVernon Mauery // Licensed under the Apache License, Version 2.0 (the "License");
5*a3702c1fSVernon Mauery // you may not use this file except in compliance with the License.
6*a3702c1fSVernon Mauery // You may obtain a copy of the License at
7*a3702c1fSVernon Mauery //
8*a3702c1fSVernon Mauery //      http://www.apache.org/licenses/LICENSE-2.0
9*a3702c1fSVernon Mauery //
10*a3702c1fSVernon Mauery // Unless required by applicable law or agreed to in writing, software
11*a3702c1fSVernon Mauery // distributed under the License is distributed on an "AS IS" BASIS,
12*a3702c1fSVernon Mauery // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*a3702c1fSVernon Mauery // See the License for the specific language governing permissions and
14*a3702c1fSVernon Mauery // limitations under the License.
15*a3702c1fSVernon Mauery */
16*a3702c1fSVernon Mauery 
17*a3702c1fSVernon Mauery #include "oemcommands.hpp"
18*a3702c1fSVernon Mauery 
19*a3702c1fSVernon Mauery #include <openssl/hmac.h>
20*a3702c1fSVernon Mauery 
21*a3702c1fSVernon Mauery #include <ipmid/api.hpp>
22*a3702c1fSVernon Mauery #include <ipmid/utils.hpp>
23*a3702c1fSVernon Mauery #include <phosphor-logging/log.hpp>
24*a3702c1fSVernon Mauery #include <sdbusplus/bus.hpp>
25*a3702c1fSVernon Mauery 
26*a3702c1fSVernon Mauery void register_netfn_bmc_control_functions() __attribute__((constructor));
27*a3702c1fSVernon Mauery 
28*a3702c1fSVernon Mauery enum ipmi_bmc_control_services_return_codes
29*a3702c1fSVernon Mauery {
30*a3702c1fSVernon Mauery     ipmiCCBmcControlInvalidBitMask = 0xCC,
31*a3702c1fSVernon Mauery     ipmiCCBmcControlPasswdInvalid = 0xCD,
32*a3702c1fSVernon Mauery     ipmiCCBmcControlInvalidChannel = 0xD4,
33*a3702c1fSVernon Mauery };
34*a3702c1fSVernon Mauery 
35*a3702c1fSVernon Mauery // TODO: Add other services, once they are supported
36*a3702c1fSVernon Mauery static const std::unordered_map<uint8_t, std::string> bmcServices = {
37*a3702c1fSVernon Mauery     {3, "netipmid"},
38*a3702c1fSVernon Mauery     {5, "web"},
39*a3702c1fSVernon Mauery     {6, "ssh"},
40*a3702c1fSVernon Mauery };
41*a3702c1fSVernon Mauery 
42*a3702c1fSVernon Mauery static constexpr const char* objectManagerIntf =
43*a3702c1fSVernon Mauery     "org.freedesktop.DBus.ObjectManager";
44*a3702c1fSVernon Mauery static constexpr const char* serviceConfigBasePath =
45*a3702c1fSVernon Mauery     "/xyz/openbmc_project/control/service";
46*a3702c1fSVernon Mauery static constexpr const char* serviceConfigAttrIntf =
47*a3702c1fSVernon Mauery     "xyz.openbmc_project.Control.Service.Attributes";
48*a3702c1fSVernon Mauery static constexpr const char* serviceStateProperty = "State";
49*a3702c1fSVernon Mauery static std::string disableServiceValue = "disabled";
50*a3702c1fSVernon Mauery 
51*a3702c1fSVernon Mauery static ipmi_ret_t disableBmcServices(const std::string& objName)
52*a3702c1fSVernon Mauery {
53*a3702c1fSVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
54*a3702c1fSVernon Mauery     static std::string serviceCfgMgr{};
55*a3702c1fSVernon Mauery     if (serviceCfgMgr.empty())
56*a3702c1fSVernon Mauery     {
57*a3702c1fSVernon Mauery         try
58*a3702c1fSVernon Mauery         {
59*a3702c1fSVernon Mauery             serviceCfgMgr = ipmi::getService(*dbus, objectManagerIntf,
60*a3702c1fSVernon Mauery                                              serviceConfigBasePath);
61*a3702c1fSVernon Mauery         }
62*a3702c1fSVernon Mauery         catch (const sdbusplus::exception::SdBusError& e)
63*a3702c1fSVernon Mauery         {
64*a3702c1fSVernon Mauery             serviceCfgMgr.clear();
65*a3702c1fSVernon Mauery             phosphor::logging::log<phosphor::logging::level::ERR>(
66*a3702c1fSVernon Mauery                 "Error: In fetching disabling service manager name");
67*a3702c1fSVernon Mauery             return IPMI_CC_UNSPECIFIED_ERROR;
68*a3702c1fSVernon Mauery         }
69*a3702c1fSVernon Mauery     }
70*a3702c1fSVernon Mauery     auto path = std::string(serviceConfigBasePath) + "/" + objName;
71*a3702c1fSVernon Mauery     try
72*a3702c1fSVernon Mauery     {
73*a3702c1fSVernon Mauery         ipmi::setDbusProperty(*dbus, serviceCfgMgr, path, serviceConfigAttrIntf,
74*a3702c1fSVernon Mauery                               serviceStateProperty,
75*a3702c1fSVernon Mauery                               ipmi::Value(disableServiceValue));
76*a3702c1fSVernon Mauery         phosphor::logging::log<phosphor::logging::level::INFO>(
77*a3702c1fSVernon Mauery             "Disabling service",
78*a3702c1fSVernon Mauery             phosphor::logging::entry("PATH=%s", path.c_str()),
79*a3702c1fSVernon Mauery             phosphor::logging::entry("MGR_NAME=%s", serviceCfgMgr.c_str()));
80*a3702c1fSVernon Mauery         return IPMI_CC_OK;
81*a3702c1fSVernon Mauery     }
82*a3702c1fSVernon Mauery     catch (const sdbusplus::exception_t&)
83*a3702c1fSVernon Mauery     {
84*a3702c1fSVernon Mauery         phosphor::logging::log<phosphor::logging::level::ERR>(
85*a3702c1fSVernon Mauery             "Error: Disabling service",
86*a3702c1fSVernon Mauery             phosphor::logging::entry("PATH=%s", path.c_str()),
87*a3702c1fSVernon Mauery             phosphor::logging::entry("MGR_NAME=%s", serviceCfgMgr.c_str()));
88*a3702c1fSVernon Mauery         return IPMI_CC_UNSPECIFIED_ERROR;
89*a3702c1fSVernon Mauery     }
90*a3702c1fSVernon Mauery }
91*a3702c1fSVernon Mauery 
92*a3702c1fSVernon Mauery static constexpr size_t controlPasswdSize = 32;
93*a3702c1fSVernon Mauery 
94*a3702c1fSVernon Mauery ipmi::RspType<> bmcIntelControlServices(
95*a3702c1fSVernon Mauery     ipmi::Context::ptr ctx,
96*a3702c1fSVernon Mauery     const std::array<uint8_t, controlPasswdSize>& passwd, uint8_t stdServices,
97*a3702c1fSVernon Mauery     uint8_t oemServices)
98*a3702c1fSVernon Mauery {
99*a3702c1fSVernon Mauery     // Execute this command only in KCS interface
100*a3702c1fSVernon Mauery     if (ctx->channel != interfaceKCS)
101*a3702c1fSVernon Mauery     {
102*a3702c1fSVernon Mauery         return ipmi::response(ipmiCCBmcControlInvalidChannel);
103*a3702c1fSVernon Mauery     }
104*a3702c1fSVernon Mauery 
105*a3702c1fSVernon Mauery     static std::string hashData("Intel 0penBMC");
106*a3702c1fSVernon Mauery     static std::vector<uint8_t> hashedValue = {
107*a3702c1fSVernon Mauery         0x89, 0x6A, 0xAB, 0x7D, 0xB0, 0x5A, 0x2D, 0x92, 0x41, 0xAD, 0x92,
108*a3702c1fSVernon Mauery         0xEE, 0xD4, 0x82, 0xDE, 0x62, 0x66, 0x16, 0xC1, 0x08, 0xFD, 0x23,
109*a3702c1fSVernon Mauery         0xC6, 0xD8, 0x75, 0xB3, 0x52, 0x53, 0x31, 0x3C, 0x7F, 0x69};
110*a3702c1fSVernon Mauery     std::vector<uint8_t> hashedOutput(EVP_MAX_MD_SIZE, 0);
111*a3702c1fSVernon Mauery     unsigned int outputLen = 0;
112*a3702c1fSVernon Mauery     HMAC(EVP_sha256(), passwd.data(), passwd.size(),
113*a3702c1fSVernon Mauery          reinterpret_cast<const uint8_t*>(hashData.c_str()), hashData.length(),
114*a3702c1fSVernon Mauery          &hashedOutput[0], &outputLen);
115*a3702c1fSVernon Mauery     hashedOutput.resize(outputLen);
116*a3702c1fSVernon Mauery 
117*a3702c1fSVernon Mauery     if (hashedOutput != hashedValue)
118*a3702c1fSVernon Mauery     {
119*a3702c1fSVernon Mauery         return ipmi::response(ipmiCCBmcControlPasswdInvalid);
120*a3702c1fSVernon Mauery     }
121*a3702c1fSVernon Mauery 
122*a3702c1fSVernon Mauery     if (stdServices == 0 && oemServices == 0)
123*a3702c1fSVernon Mauery     {
124*a3702c1fSVernon Mauery         return ipmi::response(ipmiCCBmcControlInvalidBitMask);
125*a3702c1fSVernon Mauery     }
126*a3702c1fSVernon Mauery 
127*a3702c1fSVernon Mauery     ipmi_ret_t retVal = IPMI_CC_OK;
128*a3702c1fSVernon Mauery     for (size_t bitIndex = 0; bitIndex < 8; ++bitIndex)
129*a3702c1fSVernon Mauery     {
130*a3702c1fSVernon Mauery         if (stdServices & (1 << bitIndex))
131*a3702c1fSVernon Mauery         {
132*a3702c1fSVernon Mauery             auto it = bmcServices.find(bitIndex);
133*a3702c1fSVernon Mauery             if (it == bmcServices.end())
134*a3702c1fSVernon Mauery             {
135*a3702c1fSVernon Mauery                 return ipmi::response(ipmiCCBmcControlInvalidBitMask);
136*a3702c1fSVernon Mauery             }
137*a3702c1fSVernon Mauery             retVal = disableBmcServices(it->second);
138*a3702c1fSVernon Mauery             if (retVal != IPMI_CC_OK)
139*a3702c1fSVernon Mauery             {
140*a3702c1fSVernon Mauery                 return ipmi::response(retVal);
141*a3702c1fSVernon Mauery             }
142*a3702c1fSVernon Mauery         }
143*a3702c1fSVernon Mauery     }
144*a3702c1fSVernon Mauery     return ipmi::responseSuccess();
145*a3702c1fSVernon Mauery }
146*a3702c1fSVernon Mauery 
147*a3702c1fSVernon Mauery void register_netfn_bmc_control_functions()
148*a3702c1fSVernon Mauery {
149*a3702c1fSVernon Mauery     ipmi::registerHandler(ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
150*a3702c1fSVernon Mauery                           static_cast<ipmi_cmd_t>(
151*a3702c1fSVernon Mauery                               IPMINetFnIntelOemGeneralCmds::BmcControlServices),
152*a3702c1fSVernon Mauery                           ipmi::Privilege::User, bmcIntelControlServices);
153*a3702c1fSVernon Mauery }
154