1 #include "sessions_manager.hpp"
2 
3 #include "main.hpp"
4 #include "session.hpp"
5 
6 #include <algorithm>
7 #include <cstdlib>
8 #include <iomanip>
9 #include <memory>
10 #include <phosphor-logging/log.hpp>
11 #include <sdbusplus/asio/connection.hpp>
12 #include <user_channel/channel_layer.hpp>
13 
14 using namespace phosphor::logging;
15 
16 namespace session
17 {
18 
19 static std::array<uint8_t, session::maxNetworkInstanceSupported>
20     ipmiNetworkChannelNumList = {0};
21 
22 void Manager::setNetworkInstance(void)
23 {
24 
25     uint8_t index = 0, ch = 1;
26     // Constructing net-ipmid instances list based on channel info
27     // valid channel start from 1 to 15  and assuming max 4 LAN channel
28     // supported
29 
30     while (ch < ipmi::maxIpmiChannels &&
31            index < session::maxNetworkInstanceSupported)
32     {
33         ipmi::ChannelInfo chInfo;
34         ipmi::getChannelInfo(ch, chInfo);
35         if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
36             ipmi::EChannelMediumType::lan8032)
37         {
38 
39             if (getInterfaceIndex() == ch)
40             {
41                 ipmiNetworkInstance = index;
42             }
43 
44             ipmiNetworkChannelNumList[index] = ch;
45             index++;
46         }
47         ch++;
48     }
49 }
50 
51 uint8_t Manager::getNetworkInstance(void)
52 {
53     return ipmiNetworkInstance;
54 }
55 
56 Manager::Manager()
57 {
58 }
59 
60 void Manager::managerInit(const std::string& channel)
61 {
62 
63     /*
64      * Session ID is 0000_0000h for messages that are sent outside the session.
65      * The session setup commands are sent on this session, so when the session
66      * manager comes up, is creates the Session ID  0000_0000h. It is active
67      * through the lifetime of the Session Manager.
68      */
69 
70     objManager = std::make_unique<sdbusplus::server::manager::manager>(
71         *getSdBus(), session::sessionManagerRootPath);
72 
73     auto objPath =
74         std::string(session::sessionManagerRootPath) + "/" + channel + "/0";
75 
76     chName = channel;
77     setNetworkInstance();
78     sessionsMap.emplace(
79         0, std::make_shared<Session>(*getSdBus(), objPath.c_str(), 0, 0, 0));
80 }
81 
82 std::shared_ptr<Session>
83     Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
84                           cipher::rakp_auth::Algorithms authAlgo,
85                           cipher::integrity::Algorithms intAlgo,
86                           cipher::crypt::Algorithms cryptAlgo)
87 {
88     std::shared_ptr<Session> session = nullptr;
89     SessionID bmcSessionID = 0;
90     cleanStaleEntries();
91     uint8_t sessionHandle = 0;
92 
93     auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
94 
95     if (activeSessions < session::maxSessionCountPerChannel)
96     {
97         do
98         {
99             bmcSessionID = (crypto::prng::rand());
100             bmcSessionID &= session::multiIntfaceSessionIDMask;
101             // In sessionID , BIT 31 BIT30 are used for netipmid instance
102             bmcSessionID |= ipmiNetworkInstance << 30;
103             /*
104              * Every IPMI Session has two ID's attached to it Remote Console
105              * Session ID and BMC Session ID. The remote console ID is passed
106              * along with the Open Session request command. The BMC session ID
107              * is the key for the session map and is generated using std::rand.
108              * There is a rare chance for collision of BMC session ID, so the
109              * following check validates that. In the case of collision the
110              * created session is reset and a new session is created for
111              * validating collision.
112              */
113             auto iterator = sessionsMap.find(bmcSessionID);
114             if (iterator != sessionsMap.end())
115             {
116                 // Detected BMC Session ID collisions
117                 continue;
118             }
119             else
120             {
121                 break;
122             }
123         } while (1);
124 
125         sessionHandle = storeSessionHandle(bmcSessionID);
126 
127         if (!sessionHandle)
128         {
129             throw std::runtime_error(
130                 "Invalid sessionHandle - No sessionID slot ");
131         }
132         sessionHandle &= session::multiIntfaceSessionHandleMask;
133         // In sessionID , BIT 31 BIT30 are used for netipmid instance
134         sessionHandle |= ipmiNetworkInstance << 6;
135         std::stringstream sstream;
136         sstream << std::hex << bmcSessionID;
137         std::stringstream shstream;
138         shstream << std::hex << (int)sessionHandle;
139         auto objPath = std::string(session::sessionManagerRootPath) + "/" +
140                        chName + "/" + sstream.str() + "_" + shstream.str();
141         session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
142                                             remoteConsoleSessID, bmcSessionID,
143                                             static_cast<uint8_t>(priv));
144 
145         // Set the Authentication Algorithm
146         switch (authAlgo)
147         {
148             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
149             {
150                 session->setAuthAlgo(
151                     std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
152                                                                   cryptAlgo));
153                 break;
154             }
155             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
156             {
157                 session->setAuthAlgo(
158                     std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
159                                                                     cryptAlgo));
160                 break;
161             }
162             default:
163             {
164                 throw std::runtime_error("Invalid Authentication Algorithm");
165             }
166         }
167 
168         sessionsMap.emplace(bmcSessionID, session);
169         session->sessionHandle(sessionHandle);
170 
171         return session;
172     }
173 
174     log<level::INFO>("No free RMCP+ sessions left");
175 
176     throw std::runtime_error("No free sessions left");
177 }
178 
179 bool Manager::stopSession(SessionID bmcSessionID)
180 {
181     auto iter = sessionsMap.find(bmcSessionID);
182     if (iter != sessionsMap.end())
183     {
184         iter->second->state(
185             static_cast<uint8_t>(session::State::tearDownInProgress));
186         return true;
187     }
188     else
189     {
190         return false;
191     }
192 }
193 
194 std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
195                                              RetrieveOption option)
196 {
197     switch (option)
198     {
199         case RetrieveOption::BMC_SESSION_ID:
200         {
201             auto iter = sessionsMap.find(sessionID);
202             if (iter != sessionsMap.end())
203             {
204                 return iter->second;
205             }
206             break;
207         }
208         case RetrieveOption::RC_SESSION_ID:
209         {
210             auto iter = std::find_if(
211                 sessionsMap.begin(), sessionsMap.end(),
212                 [sessionID](
213                     const std::pair<const uint32_t, std::shared_ptr<Session>>&
214                         in) -> bool {
215                     return sessionID == in.second->getRCSessionID();
216                 });
217 
218             if (iter != sessionsMap.end())
219             {
220                 return iter->second;
221             }
222             break;
223         }
224         default:
225             throw std::runtime_error("Invalid retrieval option");
226     }
227 
228     throw std::runtime_error("Session ID not found");
229 }
230 
231 void Manager::cleanStaleEntries()
232 {
233     for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
234     {
235 
236         auto session = iter->second;
237         if ((session->getBMCSessionID() != session::sessionZero) &&
238             !(session->isSessionActive(session->state())))
239         {
240             sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
241             iter = sessionsMap.erase(iter);
242         }
243         else
244         {
245             ++iter;
246         }
247     }
248 }
249 
250 uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
251 {
252     // Handler index 0 is  reserved for invalid session.
253     // index starts with 1, for direct usage. Index 0 reserved
254     for (uint8_t i = 1; i <= session::maxSessionCountPerChannel; i++)
255     {
256         if (sessionHandleMap[i] == 0)
257         {
258             sessionHandleMap[i] = bmcSessionID;
259             return i;
260         }
261     }
262     return 0;
263 }
264 
265 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
266 {
267     if (sessionHandle <= session::maxSessionCountPerChannel)
268     {
269         return sessionHandleMap[sessionHandle];
270     }
271     return 0;
272 }
273 
274 uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
275 {
276 
277     // Handler index 0 is reserved for invalid session.
278     // index starts with 1, for direct usage. Index 0 reserved
279 
280     for (uint8_t i = 1; i <= session::maxSessionCountPerChannel; i++)
281     {
282         if (sessionHandleMap[i] == bmcSessionID)
283         {
284             return (i);
285         }
286     }
287     return 0;
288 }
289 uint8_t Manager::getActiveSessionCount() const
290 {
291 
292     return (std::count_if(
293         sessionsMap.begin(), sessionsMap.end(),
294         [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
295             -> bool {
296             return in.second->state() ==
297                    static_cast<uint8_t>(session::State::active);
298         }));
299 }
300 } // namespace session
301