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> 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 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 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 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 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