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