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 10 : status = libspdm_send_spdm_request(spdm_context, session_id, spdm_request_size, spdm_request);
166 10 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
167 1 : libspdm_release_sender_buffer (spdm_context);
168 1 : return LIBSPDM_STATUS_SEND_FAIL;
169 : }
170 9 : libspdm_zero_mem(message, message_size);
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 (!libspdm_validate_svh_vendor_id_len(spdm_response->standard_id,
221 6 : spdm_response->vendor_id_len)) {
222 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
223 0 : goto done;
224 : }
225 :
226 6 : if ((spdm_response->header.param1 & SPDM_VENDOR_DEFINED_RESPONSE_LARGE_RESP) !=
227 6 : (spdm_request->header.param1 & SPDM_VENDOR_DEFINED_REQUEST_LARGE_REQ)) {
228 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
229 1 : goto done;
230 : }
231 :
232 5 : if (use_large_payload) {
233 : /* check response buffer size at least spdm response default header plus
234 : * number of bytes required by vendor id and 2 bytes for reserved and 4 bytes
235 : * for large response payload size */
236 3 : if (spdm_response_size < sizeof(spdm_vendor_defined_response_msg_t) +
237 3 : spdm_response->vendor_id_len + sizeof(uint16_t) + sizeof(uint32_t)) {
238 1 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
239 1 : goto done;
240 : }
241 : } else {
242 : /* check response buffer size at least spdm response default header plus
243 : * number of bytes required by vendor id and 2 bytes for response payload size */
244 2 : if (spdm_response_size < sizeof(spdm_vendor_defined_response_msg_t) +
245 2 : spdm_response->vendor_id_len + sizeof(uint16_t)) {
246 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
247 0 : goto done;
248 : }
249 : }
250 :
251 4 : *resp_standard_id = spdm_response->standard_id;
252 4 : if (*resp_vendor_id_len < spdm_response->vendor_id_len) {
253 0 : status = LIBSPDM_STATUS_INVALID_MSG_SIZE;
254 0 : goto done;
255 : }
256 4 : *resp_vendor_id_len = spdm_response->vendor_id_len;
257 4 : if ((*resp_vendor_id_len) != 0) {
258 4 : libspdm_copy_mem(resp_vendor_id, *resp_vendor_id_len, spdm_response->vendor_plus_request,
259 4 : *resp_vendor_id_len);
260 : }
261 :
262 : /* -=[Process Response Phase]=- */
263 4 : response_ptr = spdm_response->vendor_plus_request + spdm_response->vendor_id_len;
264 4 : if (use_large_payload) {
265 2 : response_ptr += sizeof(uint16_t);
266 2 : response_size = *((uint32_t*)response_ptr);
267 2 : if (spdm_response_size < response_size +
268 2 : sizeof(spdm_vendor_defined_response_msg_t) +
269 2 : spdm_response->vendor_id_len + sizeof(uint16_t) + sizeof(uint32_t)) {
270 1 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
271 1 : goto done;
272 : }
273 1 : response_ptr += sizeof(uint32_t);
274 : } else {
275 2 : response_size = *((uint16_t*)response_ptr);
276 :
277 2 : if (spdm_response_size < response_size +
278 2 : sizeof(spdm_vendor_defined_response_msg_t) +
279 2 : spdm_response->vendor_id_len + sizeof(uint16_t)) {
280 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
281 0 : goto done;
282 : }
283 2 : response_ptr += sizeof(uint16_t);
284 : }
285 :
286 3 : if (*resp_size < response_size) {
287 1 : status = LIBSPDM_STATUS_BUFFER_TOO_SMALL;
288 1 : goto done;
289 : }
290 2 : libspdm_copy_mem(resp_data, *resp_size, response_ptr, response_size);
291 2 : *resp_size = response_size;
292 :
293 : /* -=[Log Message Phase]=- */
294 : #if LIBSPDM_ENABLE_MSG_LOG
295 2 : libspdm_append_msg_log(spdm_context, spdm_response, spdm_response_size);
296 : #endif /* LIBSPDM_ENABLE_MSG_LOG */
297 :
298 2 : status = LIBSPDM_STATUS_SUCCESS;
299 9 : done:
300 9 : libspdm_zero_mem(spdm_context->last_spdm_request,
301 9 : libspdm_get_scratch_buffer_last_spdm_request_capacity(spdm_context));
302 9 : spdm_context->last_spdm_request_size = 0;
303 : /*
304 : * reciver buffer "message" contains crypted message
305 : * "spdm_response" contains the plain-text VDM message
306 : */
307 9 : libspdm_zero_mem(spdm_response, spdm_response_size);
308 9 : libspdm_release_receiver_buffer (spdm_context); /* this will free up response-message, need to find workaround */
309 9 : return status;
310 : }
311 :
312 10 : libspdm_return_t libspdm_vendor_send_request_receive_response(
313 : void *spdm_context,
314 : const uint32_t *session_id,
315 : uint16_t req_standard_id,
316 : uint8_t req_vendor_id_len,
317 : const void *req_vendor_id,
318 : uint32_t req_size,
319 : const void *req_data,
320 : uint16_t *resp_standard_id,
321 : uint8_t *resp_vendor_id_len,
322 : void *resp_vendor_id,
323 : uint32_t *resp_size,
324 : void *resp_data)
325 : {
326 : libspdm_context_t *context;
327 : size_t retry;
328 : uint64_t retry_delay_time;
329 : libspdm_return_t status;
330 :
331 10 : context = spdm_context;
332 10 : context->crypto_request = true;
333 10 : retry = context->retry_times;
334 10 : retry_delay_time = context->retry_delay_time;
335 : do {
336 10 : status = libspdm_try_vendor_send_request_receive_response(
337 : context,
338 : session_id,
339 : req_standard_id,
340 : req_vendor_id_len,
341 : req_vendor_id,
342 : req_size,
343 : req_data,
344 : resp_standard_id,
345 : resp_vendor_id_len,
346 : resp_vendor_id,
347 : resp_size,
348 : resp_data);
349 10 : if (status != LIBSPDM_STATUS_BUSY_PEER) {
350 10 : return status;
351 : }
352 :
353 0 : libspdm_sleep(retry_delay_time);
354 0 : } while (retry-- != 0);
355 :
356 0 : return status;
357 : }
358 :
359 : #endif /* LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES */
|