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