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