Line data Source code
1 : /**
2 : * Copyright Notice:
3 : * Copyright 2023-2025 DMTF. All rights reserved.
4 : * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
5 : **/
6 :
7 : #include "internal/libspdm_requester_lib.h"
8 :
9 : #if LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES
10 :
11 : #define SPDM_MAX_VENDOR_PAYLOAD_LEN (SPDM_MAX_VENDOR_ID_LENGTH + 2 + 4 + \
12 : SPDM_MAX_VENDOR_DEFINED_DATA_LEN_14)
13 :
14 : #pragma pack(1)
15 : typedef struct {
16 : spdm_message_header_t header;
17 : uint16_t standard_id;
18 : uint8_t vendor_id_len;
19 : uint8_t vendor_plus_request[SPDM_MAX_VENDOR_PAYLOAD_LEN];
20 : } libspdm_vendor_defined_response_msg_max_t;
21 : #pragma pack()
22 :
23 10 : libspdm_return_t libspdm_try_vendor_send_request_receive_response(
24 : libspdm_context_t *spdm_context,
25 : const uint32_t *session_id,
26 : uint16_t req_standard_id,
27 : uint8_t req_vendor_id_len,
28 : const void *req_vendor_id,
29 : uint32_t req_size,
30 : const void *req_data,
31 : uint16_t *resp_standard_id,
32 : uint8_t *resp_vendor_id_len,
33 : void *resp_vendor_id,
34 : uint32_t *resp_size,
35 : void *resp_data)
36 : {
37 : libspdm_return_t status;
38 : spdm_vendor_defined_request_msg_t *spdm_request;
39 : size_t spdm_request_size;
40 : libspdm_vendor_defined_response_msg_max_t *spdm_response;
41 : size_t spdm_response_size;
42 : uint8_t *message;
43 10 : size_t message_size = 0;
44 : size_t transport_header_size;
45 10 : size_t max_payload = 0;
46 10 : uint8_t* vendor_request = NULL;
47 10 : uint8_t *response_ptr = NULL;
48 10 : uint32_t response_size = 0;
49 : bool use_large_payload;
50 :
51 : /* -=[Check Parameters Phase]=- */
52 10 : if (spdm_context == NULL ||
53 10 : (req_size != 0 && req_data == NULL) ||
54 10 : resp_standard_id == NULL ||
55 10 : resp_vendor_id_len == NULL ||
56 10 : resp_vendor_id == NULL ||
57 10 : resp_size == NULL ||
58 10 : (*resp_size != 0 && resp_data == NULL)
59 : ) {
60 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
61 : }
62 :
63 10 : if (spdm_context->connection_info.connection_state < LIBSPDM_CONNECTION_STATE_NEGOTIATED) {
64 0 : return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
65 : }
66 :
67 10 : transport_header_size = spdm_context->local_context.capability.transport_header_size;
68 :
69 10 : if (req_size > SPDM_MAX_VENDOR_DEFINED_DATA_LEN) {
70 0 : if (libspdm_get_connection_version (spdm_context) < SPDM_MESSAGE_VERSION_14) {
71 0 : return LIBSPDM_STATUS_UNSUPPORTED_CAP;
72 0 : } else if (!libspdm_is_capabilities_flag_supported(
73 : spdm_context, true,
74 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_LARGE_RESP_CAP,
75 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_LARGE_RESP_CAP)) {
76 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
77 : }
78 : }
79 :
80 14 : if ((libspdm_get_connection_version (spdm_context) >= SPDM_MESSAGE_VERSION_14) &&
81 4 : libspdm_is_capabilities_flag_supported(
82 : spdm_context, true,
83 : SPDM_GET_CAPABILITIES_REQUEST_FLAGS_LARGE_RESP_CAP,
84 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_LARGE_RESP_CAP)) {
85 4 : use_large_payload = true;
86 : } else {
87 6 : use_large_payload = false;
88 : }
89 :
90 : /* -=[Construct Request Phase]=- */
91 10 : status = libspdm_acquire_sender_buffer (spdm_context, &message_size, (void **)&message);
92 10 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
93 0 : return status;
94 : }
95 :
96 : /* calculate useful payload the sender buffer can hold after
97 : * removing all protocol, spdm and vendor defined message headers
98 : * -3 bytes is for the standard_id and vendor_id_len fields in the vendor header
99 : * -2 bytes is for the payload length field */
100 10 : max_payload = message_size - transport_header_size -
101 10 : spdm_context->local_context.capability.transport_tail_size
102 10 : - sizeof(spdm_request->header) - 3 - req_vendor_id_len - 2;
103 :
104 10 : LIBSPDM_ASSERT (message_size >= transport_header_size +
105 : spdm_context->local_context.capability.transport_tail_size);
106 :
107 : /* do not accept requests exceeding maximum allowed payload */
108 10 : if ((size_t)req_size > max_payload) {
109 0 : return LIBSPDM_STATUS_INVALID_PARAMETER;
110 : }
111 :
112 10 : spdm_request = (void *)(message + transport_header_size);
113 10 : spdm_request_size = message_size - transport_header_size -
114 10 : spdm_context->local_context.capability.transport_tail_size;
115 :
116 10 : LIBSPDM_ASSERT(spdm_request_size >= sizeof(spdm_vendor_defined_request_msg_t) +
117 : req_vendor_id_len + sizeof(uint16_t) + req_size);
118 10 : spdm_request->header.spdm_version = libspdm_get_connection_version (spdm_context);
119 10 : spdm_request->header.request_response_code = SPDM_VENDOR_DEFINED_REQUEST;
120 10 : spdm_request->header.param1 = 0;
121 10 : spdm_request->header.param2 = 0;
122 : /* Message header here */
123 10 : spdm_request->standard_id = req_standard_id;
124 10 : spdm_request->len = req_vendor_id_len;
125 :
126 : /* Copy Vendor id */
127 10 : vendor_request = ((uint8_t *)spdm_request) + sizeof(spdm_vendor_defined_request_msg_t);
128 10 : if (req_vendor_id_len != 0) {
129 10 : libspdm_copy_mem(vendor_request, req_vendor_id_len, req_vendor_id, req_vendor_id_len);
130 10 : vendor_request += req_vendor_id_len;
131 : }
132 :
133 10 : if (use_large_payload) {
134 4 : spdm_request->header.param1 |= SPDM_VENDOR_DEFINED_REQUEST_LARGE_REQ;
135 : /* skip request_len */
136 4 : vendor_request += sizeof(uint16_t);
137 :
138 : /* Copy large_request_len */
139 4 : libspdm_copy_mem(vendor_request, sizeof(uint32_t), &req_size, sizeof(uint32_t));
140 4 : vendor_request += sizeof(uint32_t);
141 :
142 : /* Copy payload */
143 4 : if (req_size != 0) {
144 4 : libspdm_copy_mem(vendor_request, req_size, req_data, req_size);
145 : }
146 :
147 4 : spdm_request_size = sizeof(spdm_vendor_defined_request_msg_t) +
148 4 : req_vendor_id_len + sizeof(uint16_t) + sizeof(uint32_t) + req_size;
149 :
150 : } else {
151 : /* Copy request_len */
152 6 : libspdm_copy_mem(vendor_request, sizeof(uint16_t), &req_size, sizeof(uint16_t));
153 6 : vendor_request += sizeof(uint16_t);
154 :
155 : /* Copy payload */
156 6 : if (req_size != 0) {
157 6 : libspdm_copy_mem(vendor_request, req_size, req_data, req_size);
158 : }
159 :
160 6 : spdm_request_size = sizeof(spdm_vendor_defined_request_msg_t) +
161 6 : req_vendor_id_len + sizeof(uint16_t) + req_size;
162 : }
163 :
164 : /* -=[Send Request Phase]=- */
165 : status =
166 10 : libspdm_send_spdm_request(spdm_context, session_id, spdm_request_size, spdm_request);
167 10 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
168 1 : libspdm_release_sender_buffer (spdm_context);
169 1 : return LIBSPDM_STATUS_SEND_FAIL;
170 : }
171 9 : libspdm_release_sender_buffer (spdm_context);
172 9 : spdm_request = (void *)spdm_context->last_spdm_request;
173 :
174 : /* -=[Receive Response Phase]=- */
175 9 : status = libspdm_acquire_receiver_buffer (spdm_context, &message_size, (void **)&message);
176 9 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
177 0 : return status;
178 : }
179 9 : LIBSPDM_ASSERT (message_size >= transport_header_size);
180 9 : spdm_response = (void *)(message);
181 9 : spdm_response_size = message_size;
182 :
183 9 : status = libspdm_receive_spdm_response(spdm_context, session_id,
184 : &spdm_response_size,
185 : (void **)&spdm_response);
186 :
187 9 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
188 2 : status = LIBSPDM_STATUS_RECEIVE_FAIL;
189 2 : goto done;
190 : }
191 :
192 : /* -=[Validate Response Phase]=- */
193 7 : if (spdm_response_size < sizeof(spdm_message_header_t)) {
194 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
195 0 : goto done;
196 : }
197 7 : if (spdm_response->header.request_response_code == SPDM_ERROR) {
198 1 : status = libspdm_handle_error_response_main(
199 : spdm_context, session_id,
200 : &spdm_response_size,
201 : (void **)&spdm_response, SPDM_VENDOR_DEFINED_REQUEST,
202 : SPDM_VENDOR_DEFINED_RESPONSE);
203 1 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
204 1 : goto done;
205 : }
206 6 : } else if (spdm_response->header.request_response_code != SPDM_VENDOR_DEFINED_RESPONSE) {
207 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
208 0 : goto done;
209 : }
210 6 : if (spdm_response->header.spdm_version != spdm_request->header.spdm_version) {
211 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
212 0 : goto done;
213 : }
214 :
215 6 : if (spdm_response_size < sizeof(spdm_vendor_defined_response_msg_t)) {
216 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
217 0 : goto done;
218 : }
219 :
220 6 : if ((spdm_response->header.param1 & SPDM_VENDOR_DEFINED_RESONSE_LARGE_RESP) !=
221 6 : (spdm_request->header.param1 & SPDM_VENDOR_DEFINED_REQUEST_LARGE_REQ)) {
222 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
223 1 : goto done;
224 : }
225 :
226 5 : if (use_large_payload) {
227 : /* check response buffer size at least spdm response default header plus
228 : * number of bytes required by vendor id and 2 bytes for reserved and 4 bytes
229 : * for large response payload size */
230 3 : if (spdm_response_size < sizeof(spdm_vendor_defined_response_msg_t) +
231 3 : spdm_response->vendor_id_len + sizeof(uint16_t) + sizeof(uint32_t)) {
232 1 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
233 1 : goto done;
234 : }
235 : } else {
236 : /* check response buffer size at least spdm response default header plus
237 : * number of bytes required by vendor id and 2 bytes for response payload size */
238 2 : if (spdm_response_size < sizeof(spdm_vendor_defined_response_msg_t) +
239 2 : spdm_response->vendor_id_len + sizeof(uint16_t)) {
240 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
241 0 : goto done;
242 : }
243 : }
244 :
245 4 : *resp_standard_id = spdm_response->standard_id;
246 4 : if (*resp_vendor_id_len < spdm_response->vendor_id_len) {
247 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
248 0 : goto done;
249 : }
250 4 : *resp_vendor_id_len = spdm_response->vendor_id_len;
251 4 : if ((*resp_vendor_id_len) != 0) {
252 4 : libspdm_copy_mem(resp_vendor_id, *resp_vendor_id_len, spdm_response->vendor_plus_request,
253 4 : *resp_vendor_id_len);
254 : }
255 :
256 : /* -=[Process Response Phase]=- */
257 4 : response_ptr = spdm_response->vendor_plus_request + spdm_response->vendor_id_len;
258 4 : if (use_large_payload) {
259 2 : response_ptr += sizeof(uint16_t);
260 2 : response_size = *((uint32_t*)response_ptr);
261 2 : if (spdm_response_size < response_size +
262 2 : sizeof(spdm_vendor_defined_response_msg_t) +
263 2 : spdm_response->vendor_id_len + sizeof(uint16_t) + sizeof(uint32_t)) {
264 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
265 1 : goto done;
266 : }
267 1 : response_ptr += sizeof(uint32_t);
268 : } else {
269 2 : response_size = *((uint16_t*)response_ptr);
270 :
271 2 : if (spdm_response_size < response_size +
272 2 : sizeof(spdm_vendor_defined_response_msg_t) +
273 2 : spdm_response->vendor_id_len + sizeof(uint16_t)) {
274 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
275 0 : goto done;
276 : }
277 2 : response_ptr += sizeof(uint16_t);
278 : }
279 :
280 3 : if (*resp_size < response_size) {
281 1 : status = LIBSPDM_STATUS_BUFFER_TOO_SMALL;
282 1 : goto done;
283 : }
284 2 : libspdm_copy_mem(resp_data, *resp_size, response_ptr, response_size);
285 2 : *resp_size = response_size;
286 :
287 : /* -=[Log Message Phase]=- */
288 : #if LIBSPDM_ENABLE_MSG_LOG
289 2 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
290 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
291 :
292 2 : status = LIBSPDM_STATUS_SUCCESS;
293 9 : done:
294 9 : libspdm_release_receiver_buffer (spdm_context); /* this will free up response-message, need to find workaround */
295 9 : return status;
296 : }
297 :
298 10 : libspdm_return_t libspdm_vendor_send_request_receive_response(
299 : void *spdm_context,
300 : const uint32_t *session_id,
301 : uint16_t req_standard_id,
302 : uint8_t req_vendor_id_len,
303 : const void *req_vendor_id,
304 : uint32_t req_size,
305 : const void *req_data,
306 : uint16_t *resp_standard_id,
307 : uint8_t *resp_vendor_id_len,
308 : void *resp_vendor_id,
309 : uint32_t *resp_size,
310 : void *resp_data)
311 : {
312 : libspdm_context_t *context;
313 : size_t retry;
314 : uint64_t retry_delay_time;
315 : libspdm_return_t status;
316 :
317 10 : context = spdm_context;
318 10 : context->crypto_request = true;
319 10 : retry = context->retry_times;
320 10 : retry_delay_time = context->retry_delay_time;
321 : do {
322 10 : status = libspdm_try_vendor_send_request_receive_response(
323 : context,
324 : session_id,
325 : req_standard_id,
326 : req_vendor_id_len,
327 : req_vendor_id,
328 : req_size,
329 : req_data,
330 : resp_standard_id,
331 : resp_vendor_id_len,
332 : resp_vendor_id,
333 : resp_size,
334 : resp_data);
335 10 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
336 10 : return status;
337 : }
338 :
339 0 : libspdm_sleep(retry_delay_time);
340 0 : } while (retry-- != 0);
341 :
342 0 : return status;
343 : }
344 :
345 : #endif /* LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES */
|