xref: /openbmc/phosphor-net-ipmid/command/session_cmds.cpp (revision ecc8efad10bc2101a434a0c1fbd253eeaa1a3a99)
1 #include "session_cmds.hpp"
2 
3 #include "endian.hpp"
4 #include "main.hpp"
5 
6 #include <ipmid/api.h>
7 
8 #include <chrono>
9 #include <ipmid/sessionhelper.hpp>
10 #include <ipmid/utils.hpp>
11 #include <phosphor-logging/log.hpp>
12 
13 using namespace std::chrono_literals;
14 
15 namespace command
16 {
17 using namespace phosphor::logging;
18 
19 std::vector<uint8_t>
20     setSessionPrivilegeLevel(const std::vector<uint8_t>& inPayload,
21                              const message::Handler& handler)
22 {
23     auto request =
24         reinterpret_cast<const SetSessionPrivLevelReq*>(inPayload.data());
25     if (inPayload.size() != sizeof(*request))
26     {
27         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
28         return errorPayload;
29     }
30 
31     std::vector<uint8_t> outPayload(sizeof(SetSessionPrivLevelResp));
32     auto response =
33         reinterpret_cast<SetSessionPrivLevelResp*>(outPayload.data());
34     response->completionCode = IPMI_CC_OK;
35     uint8_t reqPrivilegeLevel = request->reqPrivLevel;
36 
37     auto session = std::get<session::Manager&>(singletonPool)
38                        .getSession(handler.sessionID);
39 
40     if (reqPrivilegeLevel == 0) // Just return present privilege level
41     {
42         response->newPrivLevel = session->currentPrivilege();
43         return outPayload;
44     }
45     if (reqPrivilegeLevel > (static_cast<uint8_t>(session->reqMaxPrivLevel) &
46                              session::reqMaxPrivMask))
47     {
48         // Requested level exceeds Channel and/or User Privilege Limit
49         response->completionCode = IPMI_CC_EXCEEDS_USER_PRIV;
50         return outPayload;
51     }
52     // Use the minimum privilege of user or channel
53     uint8_t minPriv = 0;
54     if (session->sessionChannelAccess.privLimit <
55         session->sessionUserPrivAccess.privilege)
56     {
57         minPriv = session->sessionChannelAccess.privLimit;
58     }
59     else
60     {
61         minPriv = session->sessionUserPrivAccess.privilege;
62     }
63     if (reqPrivilegeLevel > minPriv)
64     {
65         // Requested level exceeds Channel and/or User Privilege Limit
66         response->completionCode = IPMI_CC_EXCEEDS_USER_PRIV;
67     }
68     else
69     {
70         // update current privilege of the session.
71         session->currentPrivilege(static_cast<uint8_t>(reqPrivilegeLevel));
72         response->newPrivLevel = reqPrivilegeLevel;
73     }
74 
75     return outPayload;
76 }
77 
78 /**
79  * @brief set the session state as teardown
80  *
81  * This function is to set the session state to tear down in progress if the
82  * state is active.
83  *
84  * @param[in] busp - Dbus obj
85  * @param[in] service - service name
86  * @param[in] obj - object path
87  *
88  * @return success completion code if it sets the session state to
89  * tearDownInProgress else return the corresponding error completion code.
90  **/
91 uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
92                         const std::string& service, const std::string& obj)
93 {
94     try
95     {
96         uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
97             *busp, service, obj, session::sessionIntf, "State"));
98 
99         if (sessionState == static_cast<uint8_t>(session::State::active))
100         {
101             ipmi::setDbusProperty(
102                 *busp, service, obj, session::sessionIntf, "State",
103                 static_cast<uint8_t>(session::State::tearDownInProgress));
104             return ipmi::ccSuccess;
105         }
106     }
107     catch (std::exception& e)
108     {
109         log<level::ERR>("Failed in getting session state property",
110                         entry("service=%s", service.c_str()),
111                         entry("object path=%s", obj.c_str()),
112                         entry("interface=%s", session::sessionIntf));
113         return ipmi::ccUnspecifiedError;
114     }
115 
116     return ipmi::ccInvalidFieldRequest;
117 }
118 
119 uint8_t closeOtherNetInstanceSession(const uint32_t reqSessionId,
120                                      const uint8_t reqSessionHandle,
121                                      const uint8_t currentSessionPriv)
122 {
123     auto busp = getSdBus();
124 
125     try
126     {
127         ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
128             *busp, session::sessionManagerRootPath, session::sessionIntf);
129 
130         for (auto& objectTreeItr : objectTree)
131         {
132             const std::string obj = objectTreeItr.first;
133 
134             if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
135             {
136                 auto& serviceMap = objectTreeItr.second;
137 
138                 if (serviceMap.size() != 1)
139                 {
140                     return ipmi::ccUnspecifiedError;
141                 }
142 
143                 auto itr = serviceMap.begin();
144                 const std::string service = itr->first;
145                 uint8_t closeSessionPriv =
146                     std::get<uint8_t>(ipmi::getDbusProperty(
147                         *busp, service, obj, session::sessionIntf,
148                         "CurrentPrivilege"));
149 
150                 if (currentSessionPriv < closeSessionPriv)
151                 {
152                     return ipmi::ccInsufficientPrivilege;
153                 }
154                 return setSessionState(busp, service, obj);
155             }
156         }
157     }
158     catch (sdbusplus::exception::SdBusError& e)
159     {
160         log<level::ERR>("Failed to fetch object from dbus",
161                         entry("INTERFACE=%s", session::sessionIntf),
162                         entry("ERRMSG=%s", e.what()));
163         return ipmi::ccUnspecifiedError;
164     }
165 
166     return ipmi::ccInvalidFieldRequest;
167 }
168 
169 uint8_t closeMyNetInstanceSession(uint32_t reqSessionId,
170                                   uint8_t reqSessionHandle,
171                                   const uint8_t currentSessionPriv)
172 {
173     bool status = false;
174 
175     try
176     {
177         if (reqSessionId == session::sessionZero)
178         {
179             reqSessionId = std::get<session::Manager&>(singletonPool)
180                                .getSessionIDbyHandle(
181                                    reqSessionHandle &
182                                    session::multiIntfaceSessionHandleMask);
183             if (!reqSessionId)
184             {
185                 return session::ccInvalidSessionHandle;
186             }
187         }
188 
189         auto closeSessionInstance =
190             std::get<session::Manager&>(singletonPool).getSession(reqSessionId);
191         uint8_t closeSessionPriv = closeSessionInstance->currentPrivilege();
192 
193         if (currentSessionPriv < closeSessionPriv)
194         {
195             return ipmi::ccInsufficientPrivilege;
196         }
197         status = std::get<session::Manager&>(singletonPool)
198                      .stopSession(reqSessionId);
199 
200         if (!status)
201         {
202             return session::ccInvalidSessionId;
203         }
204     }
205     catch (std::exception& e)
206     {
207         log<level::ERR>("Failed to get session manager instance",
208                         entry("ERRMSG=%s", e.what()));
209         return ipmi::ccUnspecifiedError;
210     }
211 
212     return ipmi::ccSuccess;
213 }
214 
215 std::vector<uint8_t> closeSession(const std::vector<uint8_t>& inPayload,
216                                   const message::Handler& handler)
217 {
218     // minimum inPayload size is reqSessionId (uint32_t)
219     // maximum inPayload size is struct CloseSessionRequest
220     if (inPayload.size() != sizeof(uint32_t) &&
221         inPayload.size() != sizeof(CloseSessionRequest))
222     {
223         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
224         return errorPayload;
225     }
226 
227     auto request =
228         reinterpret_cast<const CloseSessionRequest*>(inPayload.data());
229 
230     std::vector<uint8_t> outPayload(sizeof(CloseSessionResponse));
231     auto response = reinterpret_cast<CloseSessionResponse*>(outPayload.data());
232     uint32_t reqSessionId = request->sessionID;
233     uint8_t ipmiNetworkInstance = 0;
234     uint8_t currentSessionPriv = 0;
235     uint8_t reqSessionHandle = session::invalidSessionHandle;
236 
237     if (inPayload.size() == sizeof(CloseSessionRequest))
238     {
239         reqSessionHandle = request->sessionHandle;
240     }
241 
242     if (reqSessionId == session::sessionZero &&
243         reqSessionHandle == session::invalidSessionHandle)
244     {
245         response->completionCode = session::ccInvalidSessionHandle;
246         return outPayload;
247     }
248 
249     if (inPayload.size() == sizeof(reqSessionId) &&
250         reqSessionId == session::sessionZero)
251     {
252         response->completionCode = session::ccInvalidSessionId;
253         return outPayload;
254     }
255 
256     if (reqSessionId != session::sessionZero &&
257         inPayload.size() != sizeof(reqSessionId))
258     {
259         response->completionCode = ipmi::ccInvalidFieldRequest;
260         return outPayload;
261     }
262 
263     try
264     {
265         ipmiNetworkInstance =
266             std::get<session::Manager&>(singletonPool).getNetworkInstance();
267         auto currentSession = std::get<session::Manager&>(singletonPool)
268                                   .getSession(handler.sessionID);
269         currentSessionPriv = currentSession->currentPrivilege();
270     }
271     catch (sdbusplus::exception::SdBusError& e)
272     {
273         log<level::ERR>("Failed to fetch object from dbus",
274                         entry("INTERFACE=%s", session::sessionIntf),
275                         entry("ERRMSG=%s", e.what()));
276         response->completionCode = ipmi::ccUnspecifiedError;
277         return outPayload;
278     }
279 
280     if (reqSessionId >> myNetInstanceSessionIdShiftMask ==
281             ipmiNetworkInstance ||
282         (reqSessionId == session::sessionZero &&
283          (reqSessionHandle >> myNetInstanceSessionHandleShiftMask ==
284           ipmiNetworkInstance)))
285     {
286         response->completionCode = closeMyNetInstanceSession(
287             reqSessionId, reqSessionHandle, currentSessionPriv);
288         std::get<session::Manager&>(singletonPool)
289             .scheduleSessionCleaner(100us);
290     }
291     else
292     {
293         response->completionCode = closeOtherNetInstanceSession(
294             reqSessionId, reqSessionHandle, currentSessionPriv);
295     }
296 
297     return outPayload;
298 }
299 
300 } // namespace command
301