xref: /openbmc/phosphor-net-ipmid/session.hpp (revision 2085ae07)
1 #pragma once
2 
3 #include "auth_algo.hpp"
4 #include "crypt_algo.hpp"
5 #include "endian.hpp"
6 #include "integrity_algo.hpp"
7 #include "prng.hpp"
8 #include "socket_channel.hpp"
9 
10 #include <chrono>
11 #include <exception>
12 #include <ipmid/api.hpp>
13 #include <ipmid/sessiondef.hpp>
14 #include <list>
15 #include <memory>
16 #include <sdbusplus/bus.hpp>
17 #include <sdbusplus/server/object.hpp>
18 #include <string>
19 #include <unordered_map>
20 #include <user_channel/channel_layer.hpp>
21 #include <user_channel/user_layer.hpp>
22 #include <vector>
23 #include <xyz/openbmc_project/Ipmi/SessionInfo/server.hpp>
24 
25 namespace session
26 {
27 
28 using namespace std::chrono_literals;
29 using SessionID = uint32_t;
30 
31 enum class Privilege : uint8_t
32 {
33     HIGHEST_MATCHING,
34     CALLBACK,
35     USER,
36     OPERATOR,
37     ADMIN,
38     OEM,
39 };
40 
41 // Mask to get only the privilege from requested maximum privlege (RAKP message
42 // 1)
43 constexpr uint8_t reqMaxPrivMask = 0xF;
44 
45 /**
46  * @struct SequenceNumbers Session Sequence Numbers
47  *
48  * IPMI v2.0 RMCP+ Session Sequence Numbers are used for rejecting packets that
49  * may have been duplicated by the network or intentionally replayed. There are
50  * two sets of Session SequenceNumbers for a given session.One set of inbound
51  * and outbound sequence numbers is used for authenticated (signed) packets,
52  * and the other set is used for unauthenticated packets.
53  *
54  * The individual Session Sequence Numbers is are initialized to zero whenever
55  * a session is created and incremented by one at the start of outbound
56  * processing for a given packet (i.e. the first transmitted packet has a ‘1’
57  * as the sequence number, not 0). Session Sequence numbers are incremented for
58  * every packet that is transmitted by a given sender, regardless of whether
59  * the payload for the packet is a ‘retry’ or not.
60  */
61 struct SequenceNumbers
62 {
63     auto get(bool inbound = true) const
64     {
65         return inbound ? in : out;
66     }
67 
68     void set(uint32_t seqNumber, bool inbound = true)
69     {
70         inbound ? (in = seqNumber) : (out = seqNumber);
71     }
72 
73     auto increment()
74     {
75         return ++out;
76     }
77 
78   private:
79     uint32_t in = 0;
80     uint32_t out = 0;
81 };
82 /**
83  * @class Session
84  *
85  * Encapsulates the data related to an IPMI Session
86  *
87  * Authenticated IPMI communication to the BMC is accomplished by establishing
88  * a session. Once established, a session is identified by a Session ID. The
89  * Session ID may be thought of as a handle that identifies a connection between
90  * a given remote user and the BMC. The specification supports having multiple
91  * active sessions established with the BMC. It is recommended that a BMC
92  * implementation support at least four simultaneous sessions
93  */
94 
95 using SessionIface = sdbusplus::server::object::object<
96     sdbusplus::xyz::openbmc_project::Ipmi::server::SessionInfo>;
97 
98 class Session : public SessionIface
99 {
100   public:
101     Session() = default;
102     ~Session() = default;
103     Session(const Session&) = delete;
104     Session& operator=(const Session&) = delete;
105     Session(Session&&) = default;
106     Session& operator=(Session&&) = default;
107 
108     /**
109      * @brief Session Constructor
110      *
111      * This is issued by the Session Manager when a session is started for
112      * the Open SessionRequest command
113      *
114      * @param[in] inRemoteConsoleSessID - Remote Console Session ID
115      * @param[in] priv - Privilege Level requested in the Command
116      */
117     Session(sdbusplus::bus::bus& bus, const char* path,
118             SessionID inRemoteConsoleSessID, SessionID BMCSessionID,
119             char priv) :
120         SessionIface(bus, path)
121     {
122         reqMaxPrivLevel = static_cast<session::Privilege>(priv);
123         bmcSessionID = BMCSessionID;
124         remoteConsoleSessionID = inRemoteConsoleSessID;
125     }
126 
127     auto getBMCSessionID() const
128     {
129         return bmcSessionID;
130     }
131 
132     auto getRCSessionID() const
133     {
134         return remoteConsoleSessionID;
135     }
136 
137     auto getAuthAlgo() const
138     {
139         if (authAlgoInterface)
140         {
141             return authAlgoInterface.get();
142         }
143         else
144         {
145             throw std::runtime_error("Authentication Algorithm Empty");
146         }
147     }
148 
149     void setAuthAlgo(std::unique_ptr<cipher::rakp_auth::Interface>&& inAuthAlgo)
150     {
151         authAlgoInterface = std::move(inAuthAlgo);
152     }
153 
154     /**
155      * @brief Get Session's Integrity Algorithm
156      *
157      * @return pointer to the integrity algorithm
158      */
159     auto getIntegrityAlgo() const
160     {
161         if (integrityAlgoInterface)
162         {
163             return integrityAlgoInterface.get();
164         }
165         else
166         {
167             throw std::runtime_error("Integrity Algorithm Empty");
168         }
169     }
170 
171     /**
172      * @brief Set Session's Integrity Algorithm
173      *
174      * @param[in] integrityAlgo - unique pointer to integrity algorithm
175      *                              instance
176      */
177     void setIntegrityAlgo(
178         std::unique_ptr<cipher::integrity::Interface>&& integrityAlgo)
179     {
180         integrityAlgoInterface = std::move(integrityAlgo);
181     }
182 
183     /** @brief Check if integrity algorithm is enabled for this session.
184      *
185      *  @return true if integrity algorithm is enabled else false.
186      */
187     auto isIntegrityAlgoEnabled()
188     {
189         return integrityAlgoInterface ? true : false;
190     }
191 
192     /**
193      * @brief Get Session's Confidentiality Algorithm
194      *
195      * @return pointer to the confidentiality algorithm
196      */
197     auto getCryptAlgo() const
198     {
199         if (cryptAlgoInterface)
200         {
201             return cryptAlgoInterface.get();
202         }
203         else
204         {
205             throw std::runtime_error("Confidentiality Algorithm Empty");
206         }
207     }
208 
209     /**
210      * @brief Set Session's Confidentiality Algorithm
211      *
212      * @param[in] confAlgo - unique pointer to confidentiality algorithm
213      *                       instance
214      */
215     void setCryptAlgo(std::unique_ptr<cipher::crypt::Interface>&& cryptAlgo)
216     {
217         cryptAlgoInterface = std::move(cryptAlgo);
218     }
219 
220     /** @brief Check if confidentiality algorithm is enabled for this
221      *         session.
222      *
223      *  @return true if confidentiality algorithm is enabled else false.
224      */
225     auto isCryptAlgoEnabled()
226     {
227         return cryptAlgoInterface ? true : false;
228     }
229 
230     void updateLastTransactionTime()
231     {
232         lastTime = std::chrono::steady_clock::now();
233     }
234 
235     /**
236      * @brief Session Active Status
237      *
238      * Session Active status is decided upon the Session State and the last
239      * transaction time is compared against the session inactivity timeout.
240      *
241      * @param[in] activeGrace - microseconds of idle time for active sessions
242      * @param[in] setupGrace - microseconds of idle time for sessions in setup
243      *
244      */
245     bool isSessionActive(const std::chrono::microseconds& activeGrace,
246                          const std::chrono::microseconds& setupGrace)
247     {
248         auto currentTime = std::chrono::steady_clock::now();
249         auto elapsedMicros =
250             std::chrono::duration_cast<std::chrono::microseconds>(currentTime -
251                                                                   lastTime);
252 
253         State state = static_cast<session::State>(this->state());
254 
255         switch (state)
256         {
257             case State::active:
258                 if (elapsedMicros < activeGrace)
259                 {
260                     return true;
261                 }
262                 break;
263             case State::setupInProgress:
264                 if (elapsedMicros < setupGrace)
265                 {
266                     return true;
267                 }
268                 break;
269             case State::tearDownInProgress:
270                 break;
271             default:
272                 break;
273         }
274         return false;
275     }
276 
277     /**
278      * @brief Session's Requested Maximum Privilege Level
279      */
280     Privilege reqMaxPrivLevel;
281 
282     /**
283      * @brief session's user & channel access details
284      */
285     ipmi::PrivAccess sessionUserPrivAccess{};
286     ipmi::ChannelAccess sessionChannelAccess{};
287 
288     SequenceNumbers sequenceNums; // Session Sequence Numbers
289     std::string userName{};       // User Name
290 
291     /** @brief Socket channel for communicating with the remote client.*/
292     std::shared_ptr<udpsocket::Channel> channelPtr;
293 
294   private:
295     SessionID bmcSessionID = 0;           // BMC Session ID
296     SessionID remoteConsoleSessionID = 0; // Remote Console Session ID
297 
298     // Authentication Algorithm Interface for the Session
299     std::unique_ptr<cipher::rakp_auth::Interface> authAlgoInterface;
300 
301     // Integrity Algorithm Interface for the Session
302     std::unique_ptr<cipher::integrity::Interface> integrityAlgoInterface =
303         nullptr;
304 
305     // Confidentiality Algorithm Interface for the Session
306     std::unique_ptr<cipher::crypt::Interface> cryptAlgoInterface = nullptr;
307 
308     // Last Transaction Time
309     decltype(std::chrono::steady_clock::now()) lastTime;
310 };
311 
312 } // namespace session
313