added the IMV_ATTESTATION_STATE_NONCE_REQ state
[strongswan.git] / src / libimcv / plugins / imv_attestation / imv_attestation.c
1 /*
2 * Copyright (C) 2011 Sansar Choinyambuu
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program 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 General Public License
13 * for more details.
14 */
15
16 #include "imv_attestation_state.h"
17 #include "imv_attestation_process.h"
18 #include "imv_attestation_build.h"
19
20 #include <imv/imv_agent.h>
21 #include <pa_tnc/pa_tnc_msg.h>
22 #include <ietf/ietf_attr.h>
23 #include <ietf/ietf_attr_pa_tnc_error.h>
24 #include <ietf/ietf_attr_product_info.h>
25
26 #include <libpts.h>
27
28 #include <pts/pts.h>
29 #include <pts/pts_database.h>
30 #include <pts/pts_creds.h>
31
32 #include <tcg/tcg_attr.h>
33
34 #include <tncif_pa_subtypes.h>
35
36 #include <pen/pen.h>
37 #include <debug.h>
38 #include <credentials/credential_manager.h>
39 #include <utils/linked_list.h>
40
41 /* IMV definitions */
42
43 static const char imv_name[] = "Attestation";
44
45 #define IMV_VENDOR_ID PEN_TCG
46 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
47
48 static imv_agent_t *imv_attestation;
49
50 /**
51 * Supported PTS measurement algorithms
52 */
53 static pts_meas_algorithms_t supported_algorithms = PTS_MEAS_ALGO_NONE;
54
55 /**
56 * Supported PTS Diffie Hellman Groups
57 */
58 static pts_dh_group_t supported_dh_groups = PTS_DH_GROUP_NONE;
59
60 /**
61 * Supported PTS Diffie Hellman Groups
62 */
63 static pts_dh_group_t supported_dh_groups = PTS_DH_GROUP_NONE;
64
65 /**
66 * PTS file measurement database
67 */
68 static pts_database_t *pts_db;
69
70 /**
71 * PTS credentials
72 */
73 static pts_creds_t *pts_creds;
74
75 /**
76 * PTS credential manager
77 */
78 static credential_manager_t *pts_credmgr;
79
80 /**
81 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
82 */
83 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
84 TNC_Version min_version,
85 TNC_Version max_version,
86 TNC_Version *actual_version)
87 {
88 char *hash_alg, *dh_group, *uri, *cadir;
89
90 if (imv_attestation)
91 {
92 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
93 return TNC_RESULT_ALREADY_INITIALIZED;
94 }
95 if (!pts_meas_algo_probe(&supported_algorithms) ||
96 !pts_dh_group_probe(&supported_dh_groups))
97 {
98 return TNC_RESULT_FATAL;
99 }
100 if (!pts_probe_dh_groups(&supported_dh_groups))
101 {
102 return TNC_RESULT_FATAL;
103 }
104 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
105 imv_id, actual_version);
106 if (!imv_attestation)
107 {
108 return TNC_RESULT_FATAL;
109 }
110
111 libpts_init();
112
113 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
114 {
115 DBG1(DBG_IMV, "no common IF-IMV version");
116 return TNC_RESULT_NO_COMMON_VERSION;
117 }
118
119 hash_alg = lib->settings->get_str(lib->settings,
120 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
121 dh_group = lib->settings->get_str(lib->settings,
122 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
123
124 if (!pts_meas_algo_update(hash_alg, &supported_algorithms) ||
125 !pts_dh_group_update(dh_group, &supported_dh_groups))
126 {
127 return TNC_RESULT_FATAL;
128 }
129
130 /**
131 * Specify supported PTS Diffie-Hellman groups
132 *
133 * modp1024: PTS_DH_GROUP_IKE2
134 * modp1536: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5
135 * modp2048: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14
136 * ecp256: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 |
137 * PTS_DH_GROUP_IKE19
138 * ecp384: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 |
139 * PTS_DH_GROUP_IKE19 | PTS_DH_GROUP_IKE20
140 *
141 * we expect the PTS-IMC to select the strongest supported group
142 */
143 dh_group = lib->settings->get_str(lib->settings,
144 "libimcv.plugins.imv-attestation.dh_group", "ecp256");
145
146 if (!pts_meas_algo_update(hash_alg, &supported_algorithms) ||
147 !pts_dh_group_update(dh_group, &supported_dh_groups))
148 {
149 return TNC_RESULT_FATAL;
150 }
151
152 /* create a PTS credential manager */
153 pts_credmgr = credential_manager_create();
154
155 /* create PTS credential set */
156 cadir = lib->settings->get_str(lib->settings,
157 "libimcv.plugins.imv-attestation.cadir", NULL);
158 pts_creds = pts_creds_create(cadir);
159 if (pts_creds)
160 {
161 pts_credmgr->add_set(pts_credmgr, pts_creds->get_set(pts_creds));
162 }
163
164 /* attach file measurement database */
165 uri = lib->settings->get_str(lib->settings,
166 "libimcv.plugins.imv-attestation.database", NULL);
167 pts_db = pts_database_create(uri);
168
169 return TNC_RESULT_SUCCESS;
170 }
171
172 /**
173 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
174 */
175 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
176 TNC_ConnectionID connection_id,
177 TNC_ConnectionState new_state)
178 {
179 imv_state_t *state;
180 imv_attestation_state_t *attestation_state;
181 TNC_Result result;
182
183 if (!imv_attestation)
184 {
185 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
186 return TNC_RESULT_NOT_INITIALIZED;
187 }
188 switch (new_state)
189 {
190 case TNC_CONNECTION_STATE_CREATE:
191 state = imv_attestation_state_create(connection_id);
192 return imv_attestation->create_state(imv_attestation, state);
193 case TNC_CONNECTION_STATE_DELETE:
194 return imv_attestation->delete_state(imv_attestation, connection_id);
195 case TNC_CONNECTION_STATE_HANDSHAKE:
196 result = imv_attestation->change_state(imv_attestation,
197 connection_id, new_state, &state);
198 if (result != TNC_RESULT_SUCCESS)
199 {
200 return result;
201 }
202 attestation_state = (imv_attestation_state_t*)state;
203
204 return TNC_RESULT_SUCCESS;
205 default:
206 return imv_attestation->change_state(imv_attestation, connection_id,
207 new_state, NULL);
208 }
209 }
210
211 static TNC_Result send_message(TNC_ConnectionID connection_id)
212 {
213 pa_tnc_msg_t *msg;
214 imv_state_t *state;
215 imv_attestation_state_t *attestation_state;
216 TNC_Result result;
217
218 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
219 {
220 return TNC_RESULT_FATAL;
221 }
222 attestation_state = (imv_attestation_state_t*)state;
223 msg = pa_tnc_msg_create();
224
225 <<<<<<< HEAD
226 if (imv_attestation_build(msg, attestation_state, supported_algorithms,
227 supported_dh_groups, pts_db))
228 {
229 msg->build(msg);
230 result = imv_attestation->send_message(imv_attestation, connection_id,
231 msg->get_encoding(msg));
232 =======
233 if (handshake_state == IMV_ATTESTATION_STATE_NONCE_REQ &&
234 !(pts->get_proto_caps(pts) & PTS_PROTO_CAPS_T))
235 {
236 DBG1(DBG_IMV, "PTS-IMC has no TPM capability - "
237 "advancing to PTS measurement phase");
238 handshake_state = IMV_ATTESTATION_STATE_MEAS;
239 >>>>>>> added the IMV_ATTESTATION_STATE_NONCE_REQ state
240 }
241 else
242 {
243 <<<<<<< HEAD
244 result = TNC_RESULT_FATAL;
245 =======
246 case IMV_ATTESTATION_STATE_INIT:
247 {
248 pts_proto_caps_flag_t flags;
249
250 /* Send Request Protocol Capabilities attribute */
251 flags = pts->get_proto_caps(pts);
252 attr = tcg_pts_attr_proto_caps_create(flags, TRUE);
253 attr->set_noskip_flag(attr, TRUE);
254 msg->add_attribute(msg, attr);
255
256 /* Send Measurement Algorithms attribute */
257 attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
258 attr->set_noskip_flag(attr, TRUE);
259 msg->add_attribute(msg, attr);
260
261 attestation_state->set_handshake_state(attestation_state,
262 IMV_ATTESTATION_STATE_NONCE_REQ);
263 break;
264 }
265 case IMV_ATTESTATION_STATE_NONCE_REQ:
266 {
267 int min_nonce_len;
268
269 /* Send DH nonce parameters request attribute */
270 min_nonce_len = lib->settings->get_int(lib->settings,
271 "libimcv.plugins.imv-attestation.min_nonce_len", 0);
272 attr = tcg_pts_attr_dh_nonce_params_req_create(min_nonce_len,
273 supported_dh_groups);
274 attr->set_noskip_flag(attr, TRUE);
275 msg->add_attribute(msg, attr);
276
277 attestation_state->set_handshake_state(attestation_state,
278 IMV_ATTESTATION_STATE_TPM_INIT);
279 break;
280 }
281 case IMV_ATTESTATION_STATE_TPM_INIT:
282 {
283 pts_meas_algorithms_t selected_algorithm;
284 chunk_t initiator_value, initiator_nonce;
285
286 /* Send DH nonce finish attribute */
287 selected_algorithm = pts->get_meas_algorithm(pts);
288 pts->get_my_public_value(pts, &initiator_value, &initiator_nonce);
289 attr = tcg_pts_attr_dh_nonce_finish_create(selected_algorithm,
290 initiator_value, initiator_nonce);
291 attr->set_noskip_flag(attr, TRUE);
292 msg->add_attribute(msg, attr);
293
294 /* Send Get TPM Version attribute */
295 attr = tcg_pts_attr_get_tpm_version_info_create();
296 attr->set_noskip_flag(attr, TRUE);
297 msg->add_attribute(msg, attr);
298
299 /* Send Get AIK attribute */
300 attr = tcg_pts_attr_get_aik_create();
301 attr->set_noskip_flag(attr, TRUE);
302 msg->add_attribute(msg, attr);
303
304 attestation_state->set_handshake_state(attestation_state,
305 IMV_ATTESTATION_STATE_MEAS);
306 break;
307 }
308 case IMV_ATTESTATION_STATE_MEAS:
309 {
310
311 enumerator_t *enumerator;
312 u_int32_t delimiter = SOLIDUS_UTF;
313 char *platform_info, *pathname;
314 u_int16_t request_id;
315 int id, type;
316 bool is_dir;
317
318 attestation_state->set_handshake_state(attestation_state,
319 IMV_ATTESTATION_STATE_COMP_EVID);
320
321 /* Get Platform and OS of the PTS-IMC */
322 platform_info = pts->get_platform_info(pts);
323
324 if (!pts_db || !platform_info)
325 {
326 DBG1(DBG_IMV, "%s%s%s not available",
327 (pts_db) ? "" : "pts database",
328 (!pts_db && !platform_info) ? "and" : "",
329 (platform_info) ? "" : "platform info");
330 break;
331 }
332 DBG1(DBG_IMV, "platform is '%s'", platform_info);
333
334 /* Send Request File Metadata attribute */
335 attr = tcg_pts_attr_req_file_meta_create(FALSE, SOLIDUS_UTF, "/etc/tnc_config");
336 attr->set_noskip_flag(attr, TRUE);
337 msg->add_attribute(msg, attr);
338
339 /* Send Request File Measurement attribute */
340 enumerator = pts_db->create_file_enumerator(pts_db, platform_info);
341 if (!enumerator)
342 {
343 break;
344 }
345 while (enumerator->enumerate(enumerator, &id, &type, &pathname))
346 {
347 is_dir = (type != 0);
348 request_id = attestation_state->add_request(attestation_state,
349 id, is_dir);
350 DBG2(DBG_IMV, "measurement request %d for %s '%s'",
351 request_id, is_dir ? "directory" : "file", pathname);
352 attr = tcg_pts_attr_req_file_meas_create(is_dir, request_id,
353 delimiter, pathname);
354 attr->set_noskip_flag(attr, TRUE);
355 msg->add_attribute(msg, attr);
356 }
357 enumerator->destroy(enumerator);
358 break;
359 }
360 case IMV_ATTESTATION_STATE_COMP_EVID:
361 {
362 pts_attr_req_funct_comp_evid_flag_t flags;
363 u_int32_t sub_comp_depth;
364 pts_qualifier_t qualifier;
365 pts_funct_comp_name_t name;
366
367 attestation_state->set_handshake_state(attestation_state,
368 IMV_ATTESTATION_STATE_END);
369
370 flags = PTS_REQ_FUNC_COMP_FLAG_PCR;
371 sub_comp_depth = 0;
372 qualifier.kernel = FALSE;
373 qualifier.sub_component = FALSE;
374 qualifier.type = PTS_FUNC_COMP_TYPE_ALL;
375 name = PTS_FUNC_COMP_NAME_BIOS;
376
377 /* Send Request Functional Component Evidence attribute */
378 attr = tcg_pts_attr_req_funct_comp_evid_create(flags, sub_comp_depth,
379 PEN_TCG, qualifier, name);
380 attr->set_noskip_flag(attr, TRUE);
381 msg->add_attribute(msg, attr);
382 /* Send Generate Attestation Evidence attribute */
383 attr = tcg_pts_attr_gen_attest_evid_create();
384 attr->set_noskip_flag(attr, TRUE);
385 msg->add_attribute(msg, attr);
386
387 break;
388 }
389 default:
390 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"",
391 handshake_state);
392 return TNC_RESULT_FATAL;
393 >>>>>>> added the IMV_ATTESTATION_STATE_NONCE_REQ state
394 }
395 msg->destroy(msg);
396
397 return result;
398 }
399
400 /**
401 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
402 */
403 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
404 TNC_ConnectionID connection_id,
405 TNC_BufferReference msg,
406 TNC_UInt32 msg_len,
407 TNC_MessageType msg_type)
408 {
409 pa_tnc_msg_t *pa_tnc_msg;
410 pa_tnc_attr_t *attr;
411 linked_list_t *attr_list;
412 imv_state_t *state;
413 imv_attestation_state_t *attestation_state;
414 pts_t *pts;
415 enumerator_t *enumerator;
416 TNC_Result result;
417
418 if (!imv_attestation)
419 {
420 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
421 return TNC_RESULT_NOT_INITIALIZED;
422 }
423
424 /* get current IMV state */
425 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
426 {
427 return TNC_RESULT_FATAL;
428 }
429 attestation_state = (imv_attestation_state_t*)state;
430 pts = attestation_state->get_pts(attestation_state);
431
432 /* parse received PA-TNC message and automatically handle any errors */
433 result = imv_attestation->receive_message(imv_attestation, connection_id,
434 chunk_create(msg, msg_len), msg_type,
435 &pa_tnc_msg);
436
437 /* no parsed PA-TNC attributes available if an error occurred */
438 if (!pa_tnc_msg)
439 {
440 return result;
441 }
442
443 attr_list = linked_list_create();
444 result = TNC_RESULT_SUCCESS;
445
446 /* analyze PA-TNC attributes */
447 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
448 while (enumerator->enumerate(enumerator, &attr))
449 {
450 if (attr->get_vendor_id(attr) == PEN_IETF)
451 {
452 if (attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
453 {
454 ietf_attr_pa_tnc_error_t *error_attr;
455 pen_t error_vendor_id;
456 pa_tnc_error_code_t error_code;
457 chunk_t msg_info, attr_info;
458 u_int32_t offset;
459
460 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
461 error_vendor_id = error_attr->get_vendor_id(error_attr);
462 error_code = error_attr->get_error_code(error_attr);
463 msg_info = error_attr->get_msg_info(error_attr);
464
465 if (error_vendor_id == PEN_IETF)
466 {
467 DBG1(DBG_IMV, "received PA-TNC error '%N' "
468 "concerning message %#B",
469 pa_tnc_error_code_names, error_code, &msg_info);
470
471 switch (error_code)
472 {
473 case PA_ERROR_INVALID_PARAMETER:
474 offset = error_attr->get_offset(error_attr);
475 DBG1(DBG_IMV, " occurred at offset of %u bytes",
476 offset);
477 break;
478 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
479 attr_info = error_attr->get_attr_info(error_attr);
480 DBG1(DBG_IMV, " unsupported attribute %#B",
481 &attr_info);
482 break;
483 default:
484 break;
485 }
486 }
487 else if (error_vendor_id == PEN_TCG)
488 {
489 DBG1(DBG_IMV, "received TCG-PTS error '%N'",
490 pts_error_code_names, error_code);
491 DBG1(DBG_IMV, "error information: %B", &msg_info);
492 }
493 result = TNC_RESULT_FATAL;
494 }
495 else if (attr->get_type(attr) == IETF_ATTR_PRODUCT_INFORMATION)
496 {
497 ietf_attr_product_info_t *attr_cast;
498 char *platform_info;
499
500 attr_cast = (ietf_attr_product_info_t*)attr;
501 platform_info = attr_cast->get_info(attr_cast, NULL, NULL);
502 pts->set_platform_info(pts, platform_info);
503 }
504 }
505 else if (attr->get_vendor_id(attr) == PEN_TCG)
506 {
507 if (!imv_attestation_process(attr, attr_list, attestation_state,
508 supported_algorithms,supported_dh_groups, pts_db, pts_credmgr))
509 {
510 result = TNC_RESULT_FATAL;
511 break;
512 }
513 }
514 }
515 enumerator->destroy(enumerator);
516 pa_tnc_msg->destroy(pa_tnc_msg);
517
518 if (result != TNC_RESULT_SUCCESS)
519 {
520 attr_list->destroy(attr_list);
521 state->set_recommendation(state,
522 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
523 TNC_IMV_EVALUATION_RESULT_ERROR);
524 return imv_attestation->provide_recommendation(imv_attestation,
525 connection_id);
526 }
527
528 if (attr_list->get_count(attr_list))
529 {
530 pa_tnc_msg = pa_tnc_msg_create();
531
532 enumerator = attr_list->create_enumerator(attr_list);
533 while (enumerator->enumerate(enumerator, &attr))
534 {
535 pa_tnc_msg->add_attribute(pa_tnc_msg, attr);
536 }
537 enumerator->destroy(enumerator);
538
539 pa_tnc_msg->build(pa_tnc_msg);
540 result = imv_attestation->send_message(imv_attestation, connection_id,
541 pa_tnc_msg->get_encoding(pa_tnc_msg));
542
543 pa_tnc_msg->destroy(pa_tnc_msg);
544 attr_list->destroy(attr_list);
545
546 return result;
547 }
548 attr_list->destroy(attr_list);
549
550 if (attestation_state->get_handshake_state(attestation_state) ==
551 IMV_ATTESTATION_STATE_END)
552 {
553 if (attestation_state->get_request_count(attestation_state))
554 {
555 DBG1(DBG_IMV, "failure due to %d pending file measurements",
556 attestation_state->get_request_count(attestation_state));
557 attestation_state->set_measurement_error(attestation_state);
558 }
559 if (attestation_state->get_measurement_error(attestation_state))
560 {
561 state->set_recommendation(state,
562 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
563 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR);
564 }
565 else
566 {
567 state->set_recommendation(state,
568 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
569 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
570 }
571 return imv_attestation->provide_recommendation(imv_attestation,
572 connection_id);
573 }
574
575 return send_message(connection_id);
576 }
577
578 /**
579 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
580 */
581 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
582 TNC_ConnectionID connection_id)
583 {
584 if (!imv_attestation)
585 {
586 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
587 return TNC_RESULT_NOT_INITIALIZED;
588 }
589 return imv_attestation->provide_recommendation(imv_attestation,
590 connection_id);
591 }
592
593 /**
594 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
595 */
596 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
597 TNC_ConnectionID connection_id)
598 {
599 imv_state_t *state;
600 imv_attestation_state_t *attestation_state;
601
602 if (!imv_attestation)
603 {
604 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
605 return TNC_RESULT_NOT_INITIALIZED;
606 }
607 /* get current IMV state */
608 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
609 {
610 return TNC_RESULT_FATAL;
611 }
612 attestation_state = (imv_attestation_state_t*)state;
613
614 /* Check if IMV has to initiate the PA-TNC exchange */
615 if (attestation_state->get_handshake_state(attestation_state) ==
616 IMV_ATTESTATION_STATE_INIT)
617 {
618 return send_message(connection_id);
619 }
620 return TNC_RESULT_SUCCESS;
621 }
622
623 /**
624 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
625 */
626 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
627 {
628 if (!imv_attestation)
629 {
630 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
631 return TNC_RESULT_NOT_INITIALIZED;
632 }
633 if (pts_creds)
634 {
635 pts_credmgr->remove_set(pts_credmgr, pts_creds->get_set(pts_creds));
636 pts_creds->destroy(pts_creds);
637 }
638 DESTROY_IF(pts_db);
639 DESTROY_IF(pts_credmgr);
640
641 libpts_deinit();
642
643 imv_attestation->destroy(imv_attestation);
644 imv_attestation = NULL;
645
646 return TNC_RESULT_SUCCESS;
647 }
648
649 /**
650 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
651 */
652 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
653 TNC_TNCS_BindFunctionPointer bind_function)
654 {
655 if (!imv_attestation)
656 {
657 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
658 return TNC_RESULT_NOT_INITIALIZED;
659 }
660 return imv_attestation->bind_functions(imv_attestation, bind_function);
661 }