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