/src/libssh/src/pki_context.c
Line | Count | Source |
1 | | /* |
2 | | * This file is part of the SSH Library |
3 | | * |
4 | | * Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.com> |
5 | | * |
6 | | * The SSH Library is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, version 2.1 of the License. |
9 | | * |
10 | | * The SSH Library is distributed in the hope that it will be useful, but |
11 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
12 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
13 | | * License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Lesser General Public License |
16 | | * along with the SSH Library; see the file COPYING. If not, write to |
17 | | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
18 | | * MA 02111-1307, USA. |
19 | | */ |
20 | | |
21 | | #include "config.h" |
22 | | |
23 | | #include "libssh/libssh.h" |
24 | | #include "libssh/pki.h" |
25 | | #include "libssh/pki_context.h" |
26 | | #include "libssh/priv.h" |
27 | | #include "libssh/sk_common.h" |
28 | | |
29 | | #ifdef WITH_FIDO2 |
30 | | #include "libssh/buffer.h" |
31 | | #include "libssh/callbacks.h" |
32 | | #include "libssh/sk_api.h" |
33 | | #endif /* WITH_FIDO2 */ |
34 | | |
35 | | /** |
36 | | * @addtogroup libssh_pki |
37 | | * @{ |
38 | | */ |
39 | | |
40 | | /** |
41 | | * @brief Allocate a new generic PKI context container. |
42 | | * |
43 | | * Allocates and default-initializes a new ssh_pki_ctx instance. |
44 | | * |
45 | | * @return Newly allocated context on success, or NULL on allocation failure. |
46 | | * @see ssh_pki_ctx_free() |
47 | | */ |
48 | | ssh_pki_ctx ssh_pki_ctx_new(void) |
49 | 13.2k | { |
50 | 13.2k | struct ssh_pki_ctx_struct *ctx = NULL; |
51 | | |
52 | 13.2k | ctx = calloc(1, sizeof(struct ssh_pki_ctx_struct)); |
53 | 13.2k | if (ctx == NULL) { |
54 | 0 | return NULL; |
55 | 0 | } |
56 | | |
57 | | #ifdef WITH_FIDO2 |
58 | | /* Initialize SK fields with default, if available. */ |
59 | | ctx->sk_callbacks = ssh_sk_get_default_callbacks(); |
60 | | |
61 | | /* |
62 | | * Both OpenSSH security key enrollment and server authentication require |
63 | | * user presence by default, so we replicate that for consistency. |
64 | | */ |
65 | | ctx->sk_flags = SSH_SK_USER_PRESENCE_REQD; |
66 | | |
67 | | ctx->sk_application = strdup("ssh:"); |
68 | | if (ctx->sk_application == NULL) { |
69 | | SSH_LOG(SSH_LOG_WARN, |
70 | | "Failed to allocate memory for default application"); |
71 | | SAFE_FREE(ctx); |
72 | | return NULL; |
73 | | } |
74 | | #endif /* WITH_FIDO2 */ |
75 | | |
76 | 13.2k | return ctx; |
77 | 13.2k | } |
78 | | |
79 | | /** |
80 | | * @brief Free a generic PKI context container. |
81 | | * |
82 | | * @param[in] context The PKI context to free (may be NULL). |
83 | | * @see ssh_pki_ctx_new() |
84 | | */ |
85 | | void ssh_pki_ctx_free(ssh_pki_ctx context) |
86 | 13.2k | { |
87 | 13.2k | if (context == NULL) { |
88 | 0 | return; |
89 | 0 | } |
90 | | |
91 | | #ifdef WITH_FIDO2 |
92 | | SAFE_FREE(context->sk_application); |
93 | | SSH_BUFFER_FREE(context->sk_challenge_buffer); |
94 | | SSH_BUFFER_FREE(context->sk_attestation_buffer); |
95 | | SK_OPTIONS_FREE(context->sk_callbacks_options); |
96 | | #endif /* WITH_FIDO2 */ |
97 | | |
98 | 13.2k | SAFE_FREE(context); |
99 | 13.2k | } |
100 | | |
101 | | /** |
102 | | * @brief Set various options for a PKI context. |
103 | | * |
104 | | * This function can set all possible PKI context options. |
105 | | * |
106 | | * @param[in] context Target PKI context. |
107 | | * @param option The option type to set. This could be one of the following: |
108 | | * |
109 | | * - SSH_PKI_OPTION_RSA_KEY_SIZE (int): |
110 | | * Set the RSA key size in bits for key generation. |
111 | | * Typically 2048, 3072, or 4096 bits. Must be greater |
112 | | * than or equal to 1024, as anything below is considered |
113 | | * insecure. |
114 | | * |
115 | | * - SSH_PKI_OPTION_SK_APPLICATION (const char *): |
116 | | * The Relying Party identifier (application string) that |
117 | | * determines which service/domain this security key |
118 | | * credential will be associated with. This is a required |
119 | | * field for all security key generation operations. |
120 | | * The application string typically starts with "ssh:" for |
121 | | * SSH keys. It is copied internally and can be freed |
122 | | * after setting. |
123 | | * |
124 | | * - SSH_PKI_SK_OPTION_FLAGS (uint8_t): |
125 | | * Set FIDO2/U2F operation flags that control how the FIDO2/U2F |
126 | | * authenticator behaves during generation operations. Multiple |
127 | | * flags can be combined using bitwise OR operations. The |
128 | | * pointer must not be NULL. |
129 | | * |
130 | | * Available flags: |
131 | | * |
132 | | * SSH_SK_USER_PRESENCE_REQD: Requires user presence |
133 | | * |
134 | | * SSH_SK_USER_VERIFICATION_REQD: Requires user verification |
135 | | * |
136 | | * SSH_SK_FORCE_OPERATION: Forces generation even if a |
137 | | * resident key already exists. |
138 | | * |
139 | | * SSH_SK_RESIDENT_KEY: Creates a resident |
140 | | * key stored on the authenticator. |
141 | | * |
142 | | * - SSH_PKI_OPTION_SK_USER_ID (const char *): |
143 | | * Sets the user identifier to associate with a resident |
144 | | * credential during enrollment. When a resident key is |
145 | | * requested (SSH_SK_RESIDENT_KEY), this ID is stored on the |
146 | | * authenticator and later used to look up or prevent duplicate |
147 | | * credentials. Maximum length is SK_MAX_USER_ID_LEN bytes; |
148 | | * longer values will cause the operation to fail. |
149 | | * |
150 | | * - SSH_PKI_OPTION_SK_CHALLENGE (ssh_buffer): |
151 | | * Set custom cryptographic challenge data to be included in |
152 | | * the generation operation. The challenge is signed by the |
153 | | * authenticator during key generation. If not provided, |
154 | | * a random 32-byte challenge will be automatically generated. |
155 | | * The challenge data is copied internally and the caller |
156 | | * retains ownership of the provided buffer. |
157 | | * |
158 | | * - SSH_PKI_OPTION_SK_CALLBACKS (ssh_sk_callbacks): |
159 | | * Set the security key callback structure to use custom |
160 | | * callback functions for FIDO2/U2F operations like enrollment, |
161 | | * signing, and loading resident keys. The structure is not |
162 | | * copied so it needs to be valid for the whole context |
163 | | * lifetime or until replaced. |
164 | | * |
165 | | * @param value The value to set. This is a generic pointer and the |
166 | | * datatype which is used should be set according to the |
167 | | * option type. |
168 | | * |
169 | | * @return SSH_OK on success, SSH_ERROR on error. |
170 | | * |
171 | | * @warning When the option value to set is represented via a pointer |
172 | | * (e.g const char *, ssh_buffer), the value parameter |
173 | | * should be that pointer. Do NOT pass a pointer to a |
174 | | * pointer. |
175 | | * |
176 | | * @warning When the option value to set is not a pointer (e.g int, |
177 | | * uint8_t), the value parameter should be a pointer to the |
178 | | * location storing the value to set (int *, uint8_t *). |
179 | | */ |
180 | | int ssh_pki_ctx_options_set(ssh_pki_ctx context, |
181 | | enum ssh_pki_options_e option, |
182 | | const void *value) |
183 | 0 | { |
184 | 0 | if (context == NULL) { |
185 | 0 | SSH_LOG(SSH_LOG_WARN, "Invalid PKI context passed"); |
186 | 0 | return SSH_ERROR; |
187 | 0 | } |
188 | | |
189 | 0 | switch (option) { |
190 | 0 | case SSH_PKI_OPTION_RSA_KEY_SIZE: |
191 | 0 | if (value == NULL) { |
192 | 0 | SSH_LOG(SSH_LOG_WARN, "RSA key size pointer must not be NULL"); |
193 | 0 | return SSH_ERROR; |
194 | 0 | } else if (*(int *)value != 0 && *(int *)value <= RSA_MIN_KEY_SIZE) { |
195 | 0 | SSH_LOG( |
196 | 0 | SSH_LOG_WARN, |
197 | 0 | "RSA key size must be greater than %d bits or 0 for default", |
198 | 0 | RSA_MIN_KEY_SIZE); |
199 | 0 | return SSH_ERROR; |
200 | 0 | } |
201 | 0 | context->rsa_key_size = *(int *)value; |
202 | 0 | break; |
203 | | |
204 | | #ifdef WITH_FIDO2 |
205 | | case SSH_PKI_OPTION_SK_APPLICATION: |
206 | | SAFE_FREE(context->sk_application); |
207 | | if (value != NULL) { |
208 | | context->sk_application = strdup((char *)value); |
209 | | if (context->sk_application == NULL) { |
210 | | SSH_LOG(SSH_LOG_WARN, |
211 | | "Failed to allocate memory for application"); |
212 | | return SSH_ERROR; |
213 | | } |
214 | | } |
215 | | break; |
216 | | |
217 | | case SSH_PKI_OPTION_SK_FLAGS: |
218 | | if (value == NULL) { |
219 | | return SSH_ERROR; |
220 | | } else { |
221 | | context->sk_flags = *(uint8_t *)value; |
222 | | } |
223 | | break; |
224 | | |
225 | | case SSH_PKI_OPTION_SK_USER_ID: { |
226 | | int rc; |
227 | | |
228 | | /* |
229 | | * Set required to false, because only the enrollment callback supports |
230 | | * the user ID option, and if this context is used for any other |
231 | | * operation, it would fail unnecessarily. |
232 | | */ |
233 | | rc = ssh_pki_ctx_sk_callbacks_option_set(context, |
234 | | SSH_SK_OPTION_NAME_USER_ID, |
235 | | value, |
236 | | false); |
237 | | if (rc != SSH_OK) { |
238 | | return SSH_ERROR; |
239 | | } |
240 | | break; |
241 | | } |
242 | | |
243 | | case SSH_PKI_OPTION_SK_CHALLENGE: { |
244 | | SSH_BUFFER_FREE(context->sk_challenge_buffer); |
245 | | if (value == NULL) { |
246 | | break; |
247 | | } |
248 | | |
249 | | context->sk_challenge_buffer = ssh_buffer_dup((ssh_buffer)value); |
250 | | if (context->sk_challenge_buffer == NULL) { |
251 | | SSH_LOG(SSH_LOG_WARN, "Failed to duplicate challenge buffer"); |
252 | | return SSH_ERROR; |
253 | | } |
254 | | ssh_buffer_set_secure(context->sk_challenge_buffer); |
255 | | break; |
256 | | } |
257 | | |
258 | | case SSH_PKI_OPTION_SK_CALLBACKS: { |
259 | | bool is_compatible = sk_callbacks_check_compatibility(value); |
260 | | if (!is_compatible) { |
261 | | return SSH_ERROR; |
262 | | } |
263 | | context->sk_callbacks = value; |
264 | | break; |
265 | | } |
266 | | #else /* WITH_FIDO2 */ |
267 | 0 | case SSH_PKI_OPTION_SK_APPLICATION: |
268 | 0 | case SSH_PKI_OPTION_SK_FLAGS: |
269 | 0 | case SSH_PKI_OPTION_SK_USER_ID: |
270 | 0 | case SSH_PKI_OPTION_SK_CHALLENGE: |
271 | 0 | case SSH_PKI_OPTION_SK_CALLBACKS: |
272 | 0 | SSH_LOG(SSH_LOG_WARN, SK_NOT_SUPPORTED_MSG); |
273 | 0 | return SSH_ERROR; |
274 | 0 | #endif /* WITH_FIDO2 */ |
275 | | |
276 | 0 | default: |
277 | 0 | SSH_LOG(SSH_LOG_WARN, "Unknown PKI context option: %d", option); |
278 | 0 | return SSH_ERROR; |
279 | 0 | } |
280 | | |
281 | 0 | return SSH_OK; |
282 | 0 | } |
283 | | |
284 | | /** |
285 | | * @brief Set the PIN callback function to get the PIN for security |
286 | | * key authenticator access. |
287 | | * |
288 | | * @param context The PKI context to modify. |
289 | | * @param pin_callback The callback used when the authenticator requires PIN |
290 | | * entry for verification. |
291 | | * @param userdata A generic pointer that is passed as the userdata |
292 | | * argument to the callback function. Can be NULL. |
293 | | * |
294 | | * @return SSH_OK on success, SSH_ERROR if context is NULL. |
295 | | * |
296 | | * @note The callback and userdata are stored internally in the context |
297 | | * structure and must remain valid until the context is freed or |
298 | | * replaced. |
299 | | * |
300 | | * @see ssh_auth_callback |
301 | | */ |
302 | | int ssh_pki_ctx_set_sk_pin_callback(ssh_pki_ctx context, |
303 | | ssh_auth_callback pin_callback, |
304 | | void *userdata) |
305 | 0 | { |
306 | | #ifdef WITH_FIDO2 |
307 | | if (context == NULL) { |
308 | | SSH_LOG(SSH_LOG_WARN, "Context should not be NULL"); |
309 | | return SSH_ERROR; |
310 | | } |
311 | | |
312 | | context->sk_pin_callback = pin_callback; |
313 | | context->sk_userdata = userdata; |
314 | | |
315 | | return SSH_OK; |
316 | | |
317 | | #else |
318 | 0 | (void)context; |
319 | 0 | (void)pin_callback; |
320 | 0 | (void)userdata; |
321 | |
|
322 | 0 | SSH_LOG(SSH_LOG_WARN, SK_NOT_SUPPORTED_MSG); |
323 | 0 | return SSH_ERROR; |
324 | 0 | #endif /* WITH_FIDO2 */ |
325 | 0 | } |
326 | | |
327 | | /** |
328 | | * @brief Set a security key (FIDO2/U2F) callback option in the |
329 | | * context. These options are passed to the sk_callbacks during |
330 | | * enroll/sign/load_resident_keys operations. |
331 | | * |
332 | | * Both the name and value strings are duplicated internally so the caller |
333 | | * retains ownership of the original pointers. |
334 | | * |
335 | | * @param[in] context The PKI context. Must not be NULL. |
336 | | * @param[in] name option name string. Must not be NULL. |
337 | | * @param[in] value option value string. Must not be NULL. |
338 | | * @param[in] required Set to true if the option is mandatory. If set and the |
339 | | * ssh_sk_callbacks do not recognize the option, |
340 | | * the operation should fail. |
341 | | * |
342 | | * @return SSH_OK on success, SSH_ERROR on allocation failure or invalid args. |
343 | | * |
344 | | * @note The option objects are freed automatically when the context is freed |
345 | | * via ssh_pki_sk_ctx_free(). |
346 | | * |
347 | | * @see ssh_sk_callbacks_struct |
348 | | */ |
349 | | int ssh_pki_ctx_sk_callbacks_option_set(ssh_pki_ctx context, |
350 | | const char *name, |
351 | | const char *value, |
352 | | bool required) |
353 | 0 | { |
354 | | #ifdef WITH_FIDO2 |
355 | | struct sk_option *new_option = NULL; |
356 | | struct sk_option **temp = NULL; |
357 | | size_t count = 0; |
358 | | |
359 | | if (context == NULL || name == NULL || value == NULL) { |
360 | | SSH_LOG(SSH_LOG_WARN, "Invalid parameters passed"); |
361 | | return SSH_ERROR; |
362 | | } |
363 | | |
364 | | /* Count existing options */ |
365 | | if (context->sk_callbacks_options != NULL) { |
366 | | while (context->sk_callbacks_options[count] != NULL) { |
367 | | count++; |
368 | | } |
369 | | } |
370 | | |
371 | | /* Allocate new option */ |
372 | | new_option = calloc(1, sizeof(struct sk_option)); |
373 | | if (new_option == NULL) { |
374 | | SSH_LOG(SSH_LOG_WARN, "Failed to allocate memory for new option"); |
375 | | return SSH_ERROR; |
376 | | } |
377 | | |
378 | | new_option->name = strdup(name); |
379 | | if (new_option->name == NULL) { |
380 | | SSH_LOG(SSH_LOG_WARN, "Failed to allocate memory for option name"); |
381 | | SAFE_FREE(new_option); |
382 | | return SSH_ERROR; |
383 | | } |
384 | | |
385 | | new_option->value = strdup(value); |
386 | | if (new_option->value == NULL) { |
387 | | SSH_LOG(SSH_LOG_WARN, "Failed to allocate memory for option value"); |
388 | | SAFE_FREE(new_option->name); |
389 | | SAFE_FREE(new_option); |
390 | | return SSH_ERROR; |
391 | | } |
392 | | |
393 | | new_option->required = required; |
394 | | |
395 | | /* Reallocate array to accommodate new option */ |
396 | | temp = realloc(context->sk_callbacks_options, |
397 | | (count + 2) * sizeof(struct sk_option *)); |
398 | | if (temp == NULL) { |
399 | | SSH_LOG(SSH_LOG_WARN, "Failed to reallocate options array"); |
400 | | SAFE_FREE(new_option->name); |
401 | | SAFE_FREE(new_option->value); |
402 | | SAFE_FREE(new_option); |
403 | | return SSH_ERROR; |
404 | | } |
405 | | |
406 | | context->sk_callbacks_options = temp; |
407 | | context->sk_callbacks_options[count] = new_option; |
408 | | context->sk_callbacks_options[count + 1] = NULL; |
409 | | |
410 | | return SSH_OK; |
411 | | #else |
412 | 0 | (void)context; |
413 | 0 | (void)name; |
414 | 0 | (void)value; |
415 | 0 | (void)required; |
416 | |
|
417 | 0 | SSH_LOG(SSH_LOG_WARN, SK_NOT_SUPPORTED_MSG); |
418 | 0 | return SSH_ERROR; |
419 | 0 | #endif /* WITH_FIDO2 */ |
420 | 0 | } |
421 | | |
422 | | /** |
423 | | * @brief Clear all sk_callbacks options. |
424 | | * |
425 | | * Removes and frees all previously set sk_callbacks options from the context. |
426 | | * |
427 | | * @param[in] context The PKI context to modify. |
428 | | * |
429 | | * @return SSH_OK on success, SSH_ERROR if context is NULL. |
430 | | */ |
431 | | int ssh_pki_ctx_sk_callbacks_options_clear(ssh_pki_ctx context) |
432 | 0 | { |
433 | | #ifdef WITH_FIDO2 |
434 | | if (context == NULL) { |
435 | | SSH_LOG(SSH_LOG_WARN, "Context should not be NULL"); |
436 | | return SSH_ERROR; |
437 | | } |
438 | | |
439 | | SK_OPTIONS_FREE(context->sk_callbacks_options); |
440 | | return SSH_OK; |
441 | | #else |
442 | 0 | (void)context; |
443 | |
|
444 | 0 | SSH_LOG(SSH_LOG_WARN, SK_NOT_SUPPORTED_MSG); |
445 | 0 | return SSH_ERROR; |
446 | 0 | #endif /* WITH_FIDO2 */ |
447 | 0 | } |
448 | | |
449 | | /** |
450 | | * @brief Get a copy of the attestation buffer from a PKI context. |
451 | | * |
452 | | * Retrieves a copy of the attestation buffer stored in the context after a key |
453 | | * enrollment operation. The attestation buffer contains serialized attestation |
454 | | * information in the "ssh-sk-attest-v01" format. |
455 | | * |
456 | | * @param[in] context The PKI context. Must not be NULL. |
457 | | * @param[out] attestation_buffer Pointer to store a copy of the attestation |
458 | | * buffer. Will be set to NULL if no attestation |
459 | | * data is available (e.g., authenticator doesn't |
460 | | * support attestation, or attestation data |
461 | | * was invalid/incomplete). |
462 | | * |
463 | | * @return SSH_OK on success, SSH_ERROR if context or attestation_buffer is |
464 | | * NULL, or if buffer duplication fails. |
465 | | * |
466 | | * @note The caller is responsible for freeing the returned buffer using |
467 | | * SSH_BUFFER_FREE(). |
468 | | */ |
469 | | int ssh_pki_ctx_get_sk_attestation_buffer( |
470 | | const struct ssh_pki_ctx_struct *context, |
471 | | ssh_buffer *attestation_buffer) |
472 | 0 | { |
473 | | #ifdef WITH_FIDO2 |
474 | | if (context == NULL) { |
475 | | SSH_LOG(SSH_LOG_WARN, "Context should not be NULL"); |
476 | | return SSH_ERROR; |
477 | | } |
478 | | |
479 | | if (attestation_buffer == NULL) { |
480 | | SSH_LOG(SSH_LOG_WARN, "attestation_buffer pointer should not be NULL"); |
481 | | return SSH_ERROR; |
482 | | } |
483 | | |
484 | | *attestation_buffer = ssh_buffer_dup(context->sk_attestation_buffer); |
485 | | if (*attestation_buffer == NULL) { |
486 | | SSH_LOG(SSH_LOG_WARN, "Failed to duplicate attestation buffer"); |
487 | | return SSH_ERROR; |
488 | | } |
489 | | |
490 | | return SSH_OK; |
491 | | #else |
492 | 0 | (void)context; |
493 | 0 | (void)attestation_buffer; |
494 | |
|
495 | 0 | SSH_LOG(SSH_LOG_WARN, SK_NOT_SUPPORTED_MSG); |
496 | 0 | return SSH_ERROR; |
497 | 0 | #endif /* WITH_FIDO2 */ |
498 | 0 | } |
499 | | |
500 | | /** |
501 | | * @brief Duplicate an existing PKI context |
502 | | * |
503 | | * Creates a new PKI context and copies all fields from the source context. |
504 | | * This function performs deep copying for all dynamically allocated fields |
505 | | * to ensure independent ownership between source and destination contexts. |
506 | | * |
507 | | * @param[in] context The PKI context to copy from |
508 | | * |
509 | | * @return New PKI context with copied data on success, |
510 | | * NULL on failure or if src_context is NULL |
511 | | */ |
512 | | ssh_pki_ctx ssh_pki_ctx_dup(const ssh_pki_ctx context) |
513 | 0 | { |
514 | 0 | ssh_pki_ctx new_context = NULL; |
515 | |
|
516 | 0 | if (context == NULL) { |
517 | 0 | return NULL; |
518 | 0 | } |
519 | | |
520 | 0 | new_context = ssh_pki_ctx_new(); |
521 | 0 | if (new_context == NULL) { |
522 | 0 | goto error; |
523 | 0 | } |
524 | | |
525 | 0 | new_context->rsa_key_size = context->rsa_key_size; |
526 | |
|
527 | | #ifdef WITH_FIDO2 |
528 | | new_context->sk_callbacks = context->sk_callbacks; |
529 | | |
530 | | // Free the default application string before copying |
531 | | SAFE_FREE(new_context->sk_application); |
532 | | |
533 | | if (context->sk_application != NULL) { |
534 | | new_context->sk_application = strdup(context->sk_application); |
535 | | if (new_context->sk_application == NULL) { |
536 | | SSH_LOG(SSH_LOG_WARN, "Failed to copy SK application string"); |
537 | | goto error; |
538 | | } |
539 | | } |
540 | | |
541 | | new_context->sk_flags = context->sk_flags; |
542 | | |
543 | | new_context->sk_pin_callback = context->sk_pin_callback; |
544 | | new_context->sk_userdata = context->sk_userdata; |
545 | | |
546 | | if (context->sk_challenge_buffer != NULL) { |
547 | | new_context->sk_challenge_buffer = |
548 | | ssh_buffer_dup(context->sk_challenge_buffer); |
549 | | if (new_context->sk_challenge_buffer == NULL) { |
550 | | SSH_LOG(SSH_LOG_WARN, "Failed to copy SK challenge buffer"); |
551 | | goto error; |
552 | | } |
553 | | } |
554 | | |
555 | | if (context->sk_callbacks_options != NULL) { |
556 | | new_context->sk_callbacks_options = sk_options_dup( |
557 | | (const struct sk_option **)context->sk_callbacks_options); |
558 | | if (new_context->sk_callbacks_options == NULL) { |
559 | | SSH_LOG(SSH_LOG_WARN, "Failed to copy SK callbacks options"); |
560 | | goto error; |
561 | | } |
562 | | } |
563 | | |
564 | | if (context->sk_attestation_buffer != NULL) { |
565 | | new_context->sk_attestation_buffer = |
566 | | ssh_buffer_dup(context->sk_attestation_buffer); |
567 | | if (new_context->sk_attestation_buffer == NULL) { |
568 | | SSH_LOG(SSH_LOG_WARN, "Failed to copy SK attestation buffer"); |
569 | | goto error; |
570 | | } |
571 | | } |
572 | | #endif /* WITH_FIDO2 */ |
573 | |
|
574 | 0 | return new_context; |
575 | | |
576 | 0 | error: |
577 | 0 | SSH_PKI_CTX_FREE(new_context); |
578 | | return NULL; |
579 | 0 | } |
580 | | |
581 | | /** @} */ |