xref: /openbmc/phosphor-net-ipmid/command/session_cmds.cpp (revision 5819666c23ee1d01a54fc5fb2c068bb1da1f29c7)
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         auto closeSessionInstance =
200             session::Manager::get().getSession(reqSessionId);
201         uint8_t closeSessionPriv = closeSessionInstance->currentPrivilege();
202 
203         if (currentSessionPriv < closeSessionPriv)
204         {
205             return ipmi::ccInsufficientPrivilege;
206         }
207         status = session::Manager::get().stopSession(reqSessionId);
208 
209         if (!status)
210         {
211             return session::ccInvalidSessionId;
212         }
213     }
214     catch (const std::exception& e)
215     {
216         log<level::ERR>("Failed to get session manager instance",
217                         entry("ERRMSG=%s", e.what()));
218         return ipmi::ccUnspecifiedError;
219     }
220 
221     return ipmi::ccSuccess;
222 }
223 
224 std::vector<uint8_t> closeSession(const std::vector<uint8_t>& inPayload,
225                                   std::shared_ptr<message::Handler>& handler)
226 {
227     // minimum inPayload size is reqSessionId (uint32_t)
228     // maximum inPayload size is struct CloseSessionRequest
229     if (inPayload.size() != sizeof(uint32_t) &&
230         inPayload.size() != sizeof(CloseSessionRequest))
231     {
232         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
233         return errorPayload;
234     }
235 
236     auto request =
237         reinterpret_cast<const CloseSessionRequest*>(inPayload.data());
238 
239     std::vector<uint8_t> outPayload(sizeof(CloseSessionResponse));
240     auto response = reinterpret_cast<CloseSessionResponse*>(outPayload.data());
241     uint32_t reqSessionId = request->sessionID;
242     uint8_t ipmiNetworkInstance = 0;
243     uint8_t currentSessionPriv = 0;
244     uint8_t reqSessionHandle = session::invalidSessionHandle;
245 
246     if (inPayload.size() == sizeof(CloseSessionRequest))
247     {
248         reqSessionHandle = request->sessionHandle;
249     }
250 
251     if (reqSessionId == session::sessionZero &&
252         reqSessionHandle == session::invalidSessionHandle)
253     {
254         response->completionCode = session::ccInvalidSessionHandle;
255         return outPayload;
256     }
257 
258     if (inPayload.size() == sizeof(reqSessionId) &&
259         reqSessionId == session::sessionZero)
260     {
261         response->completionCode = session::ccInvalidSessionId;
262         return outPayload;
263     }
264 
265     if (reqSessionId != session::sessionZero &&
266         inPayload.size() != sizeof(reqSessionId))
267     {
268         response->completionCode = ipmi::ccInvalidFieldRequest;
269         return outPayload;
270     }
271 
272     try
273     {
274         ipmiNetworkInstance = session::Manager::get().getNetworkInstance();
275         auto currentSession =
276             session::Manager::get().getSession(handler->sessionID);
277         currentSessionPriv = currentSession->currentPrivilege();
278     }
279     catch (const sdbusplus::exception::exception& e)
280     {
281         log<level::ERR>("Failed to fetch object from dbus",
282                         entry("INTERFACE=%s", session::sessionIntf),
283                         entry("ERRMSG=%s", e.what()));
284         response->completionCode = ipmi::ccUnspecifiedError;
285         return outPayload;
286     }
287 
288     if (reqSessionId >> myNetInstanceSessionIdShiftMask ==
289             ipmiNetworkInstance ||
290         (reqSessionId == session::sessionZero &&
291          (reqSessionHandle >> myNetInstanceSessionHandleShiftMask ==
292           ipmiNetworkInstance)))
293     {
294         response->completionCode = closeMyNetInstanceSession(
295             reqSessionId, reqSessionHandle, currentSessionPriv);
296         session::Manager::get().scheduleSessionCleaner(100us);
297     }
298     else
299     {
300         response->completionCode = closeOtherNetInstanceSession(
301             reqSessionId, reqSessionHandle, currentSessionPriv);
302     }
303 
304     return outPayload;
305 }
306 
307 } // namespace command
308