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