1 #include "sessions_manager.hpp"
2 
3 #include "session.hpp"
4 
5 #include <algorithm>
6 #include <cstdlib>
7 #include <iomanip>
8 #include <memory>
9 #include <phosphor-logging/log.hpp>
10 
11 using namespace phosphor::logging;
12 
13 namespace session
14 {
15 
16 Manager::Manager()
17 {
18     /*
19      * Session ID is 0000_0000h for messages that are sent outside the session.
20      * The session setup commands are sent on this session, so when the session
21      * manager comes up, is creates the Session ID  0000_0000h. It is active
22      * through the lifetime of the Session Manager.
23      */
24     sessionsMap.emplace(0, std::make_shared<Session>());
25 }
26 
27 std::shared_ptr<Session>
28     Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
29                           cipher::rakp_auth::Algorithms authAlgo,
30                           cipher::integrity::Algorithms intAlgo,
31                           cipher::crypt::Algorithms cryptAlgo)
32 {
33     std::shared_ptr<Session> session = nullptr;
34     SessionID sessionID = 0;
35     cleanStaleEntries();
36     auto activeSessions = sessionsMap.size() - MAX_SESSIONLESS_COUNT;
37 
38     if (activeSessions < MAX_SESSION_COUNT)
39     {
40         do
41         {
42             session = std::make_shared<Session>(remoteConsoleSessID, priv);
43 
44             /*
45              * Every IPMI Session has two ID's attached to it Remote Console
46              * Session ID and BMC Session ID. The remote console ID is passed
47              * along with the Open Session request command. The BMC session ID
48              * is the key for the session map and is generated using std::rand.
49              * There is a rare chance for collision of BMC session ID, so the
50              * following check validates that. In the case of collision the
51              * created session is reset and a new session is created for
52              * validating collision.
53              */
54             auto iterator = sessionsMap.find(session->getBMCSessionID());
55             if (iterator != sessionsMap.end())
56             {
57                 // Detected BMC Session ID collisions
58                 session.reset();
59                 continue;
60             }
61             else
62             {
63                 break;
64             }
65         } while (1);
66 
67         // Set the Authentication Algorithm
68         switch (authAlgo)
69         {
70             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
71             {
72                 session->setAuthAlgo(
73                     std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
74                                                                   cryptAlgo));
75                 break;
76             }
77             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
78             {
79                 session->setAuthAlgo(
80                     std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
81                                                                     cryptAlgo));
82                 break;
83             }
84             default:
85             {
86                 throw std::runtime_error("Invalid Authentication Algorithm");
87             }
88         }
89         sessionID = session->getBMCSessionID();
90         sessionsMap.emplace(sessionID, session);
91         return session;
92     }
93 
94     log<level::INFO>("No free RMCP+ sessions left");
95 
96     throw std::runtime_error("No free sessions left");
97 }
98 
99 bool Manager::stopSession(SessionID bmcSessionID)
100 {
101     auto iter = sessionsMap.find(bmcSessionID);
102     if (iter != sessionsMap.end())
103     {
104         iter->second->state = State::TEAR_DOWN_IN_PROGRESS;
105         return true;
106     }
107     else
108     {
109         return false;
110     }
111 }
112 
113 std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
114                                              RetrieveOption option)
115 {
116     switch (option)
117     {
118         case RetrieveOption::BMC_SESSION_ID:
119         {
120             auto iter = sessionsMap.find(sessionID);
121             if (iter != sessionsMap.end())
122             {
123                 return iter->second;
124             }
125             break;
126         }
127         case RetrieveOption::RC_SESSION_ID:
128         {
129             auto iter = std::find_if(
130                 sessionsMap.begin(), sessionsMap.end(),
131                 [sessionID](
132                     const std::pair<const uint32_t, std::shared_ptr<Session>>&
133                         in) -> bool {
134                     return sessionID == in.second->getRCSessionID();
135                 });
136 
137             if (iter != sessionsMap.end())
138             {
139                 return iter->second;
140             }
141             break;
142         }
143         default:
144             throw std::runtime_error("Invalid retrieval option");
145     }
146 
147     throw std::runtime_error("Session ID not found");
148 }
149 
150 void Manager::cleanStaleEntries()
151 {
152     for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
153     {
154         auto session = iter->second;
155         if ((session->getBMCSessionID() != SESSION_ZERO) &&
156             !(session->isSessionActive()))
157         {
158             iter = sessionsMap.erase(iter);
159         }
160         else
161         {
162             ++iter;
163         }
164     }
165 }
166 
167 } // namespace session
168