1 #include "payload_cmds.hpp"
2 
3 #include "sessions_manager.hpp"
4 #include "sol/sol_manager.hpp"
5 #include "sol_cmds.hpp"
6 
7 #include <ipmid/api.h>
8 
9 #include <ipmid/api-types.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 
12 namespace sol
13 {
14 
15 namespace command
16 {
17 
18 std::vector<uint8_t> activatePayload(const std::vector<uint8_t>& inPayload,
19                                      std::shared_ptr<message::Handler>& handler)
20 {
21     auto request =
22         reinterpret_cast<const ActivatePayloadRequest*>(inPayload.data());
23     if (inPayload.size() != sizeof(*request))
24     {
25         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
26         return errorPayload;
27     }
28 
29     std::vector<uint8_t> outPayload(sizeof(ActivatePayloadResponse));
30     auto response =
31         reinterpret_cast<ActivatePayloadResponse*>(outPayload.data());
32 
33     response->completionCode = IPMI_CC_OK;
34 
35     // SOL is the payload currently supported for activation.
36     if (static_cast<uint8_t>(message::PayloadType::SOL) != request->payloadType)
37     {
38         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
39         return outPayload;
40     }
41 
42     sol::Manager::get().updateSOLParameter(ipmi::convertCurrentChannelNum(
43         ipmi::currentChNum, getInterfaceIndex()));
44     if (!sol::Manager::get().enable)
45     {
46         response->completionCode = IPMI_CC_PAYLOAD_TYPE_DISABLED;
47         return outPayload;
48     }
49 
50     // Only one instance of SOL is currently supported.
51     if (request->payloadInstance != 1)
52     {
53         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
54         return outPayload;
55     }
56 
57     auto session = session::Manager::get().getSession(handler->sessionID);
58 
59     if (!request->encryption && session->isCryptAlgoEnabled())
60     {
61         response->completionCode = IPMI_CC_PAYLOAD_WITHOUT_ENCRYPTION;
62         return outPayload;
63     }
64 
65     if (session->currentPrivilege() <
66         static_cast<uint8_t>(sol::Manager::get().solMinPrivilege))
67     {
68         response->completionCode = IPMI_CC_INSUFFICIENT_PRIVILEGE;
69         return outPayload;
70     }
71 
72     // Is SOL Payload enabled for this user & channel.
73     auto userId = ipmi::ipmiUserGetUserId(session->userName);
74     ipmi::PayloadAccess payloadAccess = {};
75     if ((ipmi::ipmiUserGetUserPayloadAccess(session->channelNum(), userId,
76                                             payloadAccess) != IPMI_CC_OK) ||
77         !(payloadAccess.stdPayloadEnables1[static_cast<uint8_t>(
78             message::PayloadType::SOL)]))
79     {
80         response->completionCode = IPMI_CC_PAYLOAD_TYPE_DISABLED;
81         return outPayload;
82     }
83 
84     auto status = sol::Manager::get().isPayloadActive(request->payloadInstance);
85     if (status)
86     {
87         response->completionCode = IPMI_CC_PAYLOAD_ALREADY_ACTIVE;
88         return outPayload;
89     }
90 
91     // Set the current command's socket channel to the session
92     handler->setChannelInSession();
93 
94     // Start the SOL payload
95     try
96     {
97         sol::Manager::get().startPayloadInstance(request->payloadInstance,
98                                                  handler->sessionID);
99     }
100     catch (const std::exception& e)
101     {
102         lg2::error("Failed to start SOL payload: {ERROR}", "ERROR", e);
103         response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
104         return outPayload;
105     }
106 
107     response->inPayloadSize = endian::to_ipmi<uint16_t>(MAX_PAYLOAD_SIZE);
108     response->outPayloadSize = endian::to_ipmi<uint16_t>(MAX_PAYLOAD_SIZE);
109     response->portNum = endian::to_ipmi<uint16_t>(IPMI_STD_PORT);
110 
111     // VLAN addressing is not used
112     response->vlanNum = 0xFFFF;
113 
114     return outPayload;
115 }
116 
117 std::vector<uint8_t>
118     deactivatePayload(const std::vector<uint8_t>& inPayload,
119                       std::shared_ptr<message::Handler>& handler)
120 {
121     auto request =
122         reinterpret_cast<const DeactivatePayloadRequest*>(inPayload.data());
123     if (inPayload.size() != sizeof(*request))
124     {
125         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
126         return errorPayload;
127     }
128 
129     std::vector<uint8_t> outPayload(sizeof(DeactivatePayloadResponse));
130     auto response =
131         reinterpret_cast<DeactivatePayloadResponse*>(outPayload.data());
132     response->completionCode = IPMI_CC_OK;
133 
134     // SOL is the payload currently supported for deactivation
135     if (static_cast<uint8_t>(message::PayloadType::SOL) != request->payloadType)
136     {
137         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
138         return outPayload;
139     }
140 
141     // Only one instance of SOL is supported
142     if (request->payloadInstance != 1)
143     {
144         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
145         return outPayload;
146     }
147 
148     auto status = sol::Manager::get().isPayloadActive(request->payloadInstance);
149     if (!status)
150     {
151         response->completionCode = IPMI_CC_PAYLOAD_DEACTIVATED;
152         return outPayload;
153     }
154 
155     auto currentSession =
156         session::Manager::get().getSession(handler->sessionID);
157     auto solSessionID =
158         sol::Manager::get().getContext(request->payloadInstance).sessionID;
159     auto solActiveSession =
160         sol::Manager::get().getContext(request->payloadInstance).session;
161     // The session owner or the ADMIN could deactivate the session
162     if (currentSession->userName != solActiveSession->userName &&
163         currentSession->currentPrivilege() !=
164             static_cast<uint8_t>(session::Privilege::ADMIN))
165     {
166         response->completionCode = IPMI_CC_INSUFFICIENT_PRIVILEGE;
167         return outPayload;
168     }
169 
170     try
171     {
172         sol::Manager::get().stopPayloadInstance(request->payloadInstance);
173 
174         try
175         {
176             activating(request->payloadInstance, solSessionID);
177         }
178         catch (const std::exception& e)
179         {
180             lg2::info("Failed to call the activating method: {ERROR}", "ERROR",
181                       e);
182             /*
183              * In case session has been closed (like in the case of inactivity
184              * timeout), then activating function would throw an exception,
185              * since solSessionID is not found. As session is already closed,
186              * returning IPMI status code for Payload already deactivated
187              * as BMC automatically deactivates all active payloads when
188              * session is terminated.
189              */
190             response->completionCode = IPMI_CC_PAYLOAD_DEACTIVATED;
191             return outPayload;
192         }
193     }
194     catch (const std::exception& e)
195     {
196         lg2::error("Failed to call the getContext method: {ERROR}", "ERROR", e);
197         response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
198         return outPayload;
199     }
200 
201     return outPayload;
202 }
203 
204 std::vector<uint8_t>
205     getPayloadStatus(const std::vector<uint8_t>& inPayload,
206                      std::shared_ptr<message::Handler>& /* handler */)
207 {
208     auto request =
209         reinterpret_cast<const GetPayloadStatusRequest*>(inPayload.data());
210     if (inPayload.size() != sizeof(*request))
211     {
212         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
213         return errorPayload;
214     }
215 
216     std::vector<uint8_t> outPayload(sizeof(GetPayloadStatusResponse));
217     auto response =
218         reinterpret_cast<GetPayloadStatusResponse*>(outPayload.data());
219 
220     // SOL is the payload currently supported for payload status
221     if (static_cast<uint8_t>(message::PayloadType::SOL) != request->payloadType)
222     {
223         response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
224         return outPayload;
225     }
226 
227     response->completionCode = IPMI_CC_OK;
228 
229     constexpr size_t maxSolPayloadInstances = 1;
230     response->capacity = maxSolPayloadInstances;
231 
232     // Currently we support only one SOL session
233     response->instance1 = sol::Manager::get().isPayloadActive(1);
234 
235     return outPayload;
236 }
237 
238 std::vector<uint8_t>
239     getPayloadInfo(const std::vector<uint8_t>& inPayload,
240                    std::shared_ptr<message::Handler>& /* handler */)
241 {
242     auto request =
243         reinterpret_cast<const GetPayloadInfoRequest*>(inPayload.data());
244 
245     if (inPayload.size() != sizeof(*request))
246     {
247         std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
248         return errorPayload;
249     }
250 
251     std::vector<uint8_t> outPayload(sizeof(GetPayloadInfoResponse));
252     auto response =
253         reinterpret_cast<GetPayloadInfoResponse*>(outPayload.data());
254 
255     // SOL is the payload currently supported for payload status & only one
256     // instance of SOL is supported.
257     if (static_cast<uint8_t>(message::PayloadType::SOL) !=
258             request->payloadType ||
259         request->payloadInstance != 1)
260     {
261         response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST;
262         return outPayload;
263     }
264     auto status = sol::Manager::get().isPayloadActive(request->payloadInstance);
265 
266     if (status)
267     {
268         auto& context =
269             sol::Manager::get().getContext(request->payloadInstance);
270         response->sessionID = context.sessionID;
271     }
272     else
273     {
274         // No active payload - return session id as 0
275         response->sessionID = 0;
276     }
277     response->completionCode = IPMI_CC_OK;
278     return outPayload;
279 }
280 
281 } // namespace command
282 
283 } // namespace sol
284