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