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