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             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
78             {
79                 session->setAuthAlgo(
80                         std::make_unique<cipher::rakp_auth::AlgoSHA256>(
81                             intAlgo, 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, std::move(session));
91     }
92     else
93     {
94         std::cerr << "E> No free sessions left: Active: " << activeSessions <<
95                   " Allowed: " <<
96                   MAX_SESSION_COUNT << "\n";
97 
98         for (const auto& iterator : sessionsMap)
99         {
100             std::cerr << "E> Active Session: 0x" << std::hex
101                       << std::setfill('0') << std::setw(8)
102                       << (iterator.second)->getBMCSessionID() << "\n";
103         }
104         throw std::runtime_error("No free sessions left");
105     }
106 
107     return getSession(sessionID);
108 }
109 
110 bool Manager::stopSession(SessionID bmcSessionID)
111 {
112     auto iter = sessionsMap.find(bmcSessionID);
113     if (iter != sessionsMap.end())
114     {
115         iter->second->state = State::TEAR_DOWN_IN_PROGRESS;
116         return true;
117     }
118     else
119     {
120         return false;
121     }
122 }
123 
124 std::weak_ptr<Session> Manager::getSession(SessionID sessionID,
125                                            RetrieveOption option)
126 {
127     switch (option)
128     {
129         case RetrieveOption::BMC_SESSION_ID:
130         {
131             auto iter = sessionsMap.find(sessionID);
132             if (iter != sessionsMap.end())
133             {
134                 return iter->second;
135             }
136             break;
137         }
138         case RetrieveOption::RC_SESSION_ID:
139         {
140             auto iter = std::find_if(sessionsMap.begin(),
141                                      sessionsMap.end(),
142                                      [sessionID](const std::pair<const uint32_t,
143                                                  std::shared_ptr<Session>>& in)
144                                                  -> bool
145             {
146                 return sessionID == in.second->getRCSessionID();
147             });
148 
149             if (iter != sessionsMap.end())
150             {
151                 return iter->second;
152             }
153             break;
154         }
155         default:
156             throw std::runtime_error("Invalid retrieval option");
157     }
158 
159     throw std::runtime_error("Session ID not found");
160 }
161 
162 void Manager::cleanStaleEntries()
163 {
164     for(auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
165     {
166         auto session = iter->second;
167         if ((session->getBMCSessionID() != SESSION_ZERO) &&
168             !(session->isSessionActive()))
169         {
170             iter = sessionsMap.erase(iter);
171         }
172         else
173         {
174             ++iter;
175         }
176     }
177 }
178 
179 } // namespace session
180