1 #include "session_cmds.hpp"
2 
3 #include "endian.hpp"
4 #include "sessions_manager.hpp"
5 
6 #include <ipmid/api.h>
7 
8 #include <ipmid/sessionhelper.hpp>
9 #include <ipmid/utils.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 
12 #include <chrono>
13 
14 using namespace std::chrono_literals;
15 
16 namespace command
17 {
18 
19 std::vector<uint8_t>
setSessionPrivilegeLevel(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> & handler)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  **/
setSessionState(std::shared_ptr<sdbusplus::asio::connection> & busp,const std::string & service,const std::string & obj)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         lg2::error(
122             "Failed in getting session state property: {SERVICE}, {PATH}, {INTERFACE}",
123             "SERVICE", service, "PATH", obj, "INTERFACE", session::sessionIntf);
124         return ipmi::ccUnspecifiedError;
125     }
126 
127     return ipmi::ccInvalidFieldRequest;
128 }
129 
closeOtherNetInstanceSession(const uint32_t reqSessionId,const uint8_t reqSessionHandle,const uint8_t currentSessionPriv)130 uint8_t closeOtherNetInstanceSession(const uint32_t reqSessionId,
131                                      const uint8_t reqSessionHandle,
132                                      const uint8_t currentSessionPriv)
133 {
134     auto busp = getSdBus();
135 
136     try
137     {
138         ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
139             *busp, session::sessionManagerRootPath, session::sessionIntf);
140 
141         for (auto& objectTreeItr : objectTree)
142         {
143             const std::string obj = objectTreeItr.first;
144 
145             if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
146             {
147                 auto& serviceMap = objectTreeItr.second;
148 
149                 if (serviceMap.size() != 1)
150                 {
151                     return ipmi::ccUnspecifiedError;
152                 }
153 
154                 auto itr = serviceMap.begin();
155                 const std::string service = itr->first;
156                 uint8_t closeSessionPriv = std::get<uint8_t>(
157                     ipmi::getDbusProperty(*busp, service, obj,
158                                           session::sessionIntf,
159                                           "CurrentPrivilege"));
160 
161                 if (currentSessionPriv < closeSessionPriv)
162                 {
163                     return ipmi::ccInsufficientPrivilege;
164                 }
165                 return setSessionState(busp, service, obj);
166             }
167         }
168     }
169     catch (const sdbusplus::exception_t& e)
170     {
171         lg2::error(
172             "Failed to fetch object from dbus, interface: {INTERFACE}, error: {ERROR}",
173             "INTERFACE", session::sessionIntf, "ERROR", e);
174         return ipmi::ccUnspecifiedError;
175     }
176 
177     return ipmi::ccInvalidFieldRequest;
178 }
179 
closeMyNetInstanceSession(uint32_t reqSessionId,uint8_t reqSessionHandle,const uint8_t currentSessionPriv)180 uint8_t closeMyNetInstanceSession(uint32_t reqSessionId,
181                                   uint8_t reqSessionHandle,
182                                   const uint8_t currentSessionPriv)
183 {
184     bool status = false;
185 
186     try
187     {
188         if (reqSessionId == session::sessionZero)
189         {
190             reqSessionId = session::Manager::get().getSessionIDbyHandle(
191                 reqSessionHandle & session::multiIntfaceSessionHandleMask);
192             if (!reqSessionId)
193             {
194                 return session::ccInvalidSessionHandle;
195             }
196         }
197     }
198     catch (const std::exception& e)
199     {
200         lg2::error(
201             "Failed to get session manager instance or sessionID by sessionHandle: {ERROR}",
202             "ERROR", e);
203         return session::ccInvalidSessionHandle;
204     }
205 
206     try
207     {
208         auto closeSessionInstance =
209             session::Manager::get().getSession(reqSessionId);
210         uint8_t closeSessionPriv = closeSessionInstance->currentPrivilege();
211 
212         if (currentSessionPriv < closeSessionPriv)
213         {
214             return ipmi::ccInsufficientPrivilege;
215         }
216     }
217     catch (const std::exception& e)
218     {
219         lg2::error(
220             "Failed to get session manager instance or sessionID: {ERROR}",
221             "ERROR", e);
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         lg2::error(
237             "Failed to get session manager instance or stop session: {ERROR}",
238             "ERROR", e);
239         return ipmi::ccUnspecifiedError;
240     }
241 
242     return ipmi::ccSuccess;
243 }
244 
closeSession(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> & handler)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_t& e)
301     {
302         lg2::error(
303             "Failed to fetch object from dbus, interface: {INTERFACE}, error: {ERROR}",
304             "INTERFACE", session::sessionIntf, "ERROR", e);
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