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