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