Read AIK certificate/ AIK public key from file
[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
18 #include <imv/imv_agent.h>
19 #include <pa_tnc/pa_tnc_msg.h>
20 #include <ietf/ietf_attr.h>
21 #include <ietf/ietf_attr_pa_tnc_error.h>
22
23 #include <tcg/pts/pts_database.h>
24 #include <tcg/pts/pts_error.h>
25
26 #include <tcg/tcg_attr.h>
27 #include <tcg/tcg_pts_attr_proto_caps.h>
28 #include <tcg/tcg_pts_attr_meas_algo.h>
29 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
30 #include <tcg/tcg_pts_attr_tpm_version_info.h>
31 #include <tcg/tcg_pts_attr_get_aik.h>
32 #include <tcg/tcg_pts_attr_aik.h>
33 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
34 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
35 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
36 #include <tcg/tcg_pts_attr_simple_evid_final.h>
37 #include <tcg/tcg_pts_attr_req_file_meas.h>
38 #include <tcg/tcg_pts_attr_file_meas.h>
39
40 #include <tncif_pa_subtypes.h>
41
42 #include <pen/pen.h>
43 #include <debug.h>
44 #include <utils/linked_list.h>
45
46 /* IMV definitions */
47
48 static const char imv_name[] = "Attestation";
49
50 #define IMV_VENDOR_ID PEN_TCG
51 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
52
53 /**
54 * UTF-8 encoding of the character used to delimiter the filename
55 */
56 #define SOLIDUS_UTF 0x002F
57 #define REVERSE_SOLIDUS_UTF 0x005C
58
59 static imv_agent_t *imv_attestation;
60
61 /**
62 * Supported PTS measurement algorithms
63 */
64 static pts_meas_algorithms_t supported_algorithms = 0;
65
66 /**
67 * PTS file measurement database
68 */
69 static pts_database_t *pts_db;
70
71 /**
72 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
73 */
74 TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id,
75 TNC_Version min_version,
76 TNC_Version max_version,
77 TNC_Version *actual_version)
78 {
79 char *hash_alg, *uri;
80
81 if (imv_attestation)
82 {
83 DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name);
84 return TNC_RESULT_ALREADY_INITIALIZED;
85 }
86 imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE,
87 imv_id, actual_version);
88 if (!imv_attestation || !pts_meas_probe_algorithms(&supported_algorithms))
89 {
90 return TNC_RESULT_FATAL;
91 }
92 if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1)
93 {
94 DBG1(DBG_IMV, "no common IF-IMV version");
95 return TNC_RESULT_NO_COMMON_VERSION;
96 }
97
98 /**
99 * Specify supported PTS measurement algorithms
100 *
101 * sha1 : PTS_MEAS_ALGO_SHA1
102 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
103 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
104 *
105 * we expect the PTS-IMC to select the strongest supported algorithm
106 */
107 hash_alg = lib->settings->get_str(lib->settings,
108 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
109 if (!strcaseeq(hash_alg, "sha384") && !strcaseeq(hash_alg, "sha2_384"))
110 {
111 /* remove SHA384 algorithm */
112 supported_algorithms &= ~PTS_MEAS_ALGO_SHA384;
113 }
114 if (strcaseeq(hash_alg, "sha1"))
115 {
116 /* remove SHA256 algorithm */
117 supported_algorithms &= ~PTS_MEAS_ALGO_SHA256;
118 }
119
120 /* attach file measurement database */
121 uri = lib->settings->get_str(lib->settings,
122 "libimcv.plugins.imv-attestation.database", NULL);
123 pts_db = pts_database_create(uri);
124
125 return TNC_RESULT_SUCCESS;
126 }
127
128 /**
129 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
130 */
131 TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id,
132 TNC_ConnectionID connection_id,
133 TNC_ConnectionState new_state)
134 {
135 imv_state_t *state;
136 imv_attestation_state_t *attestation_state;
137 TNC_Result result;
138
139 if (!imv_attestation)
140 {
141 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
142 return TNC_RESULT_NOT_INITIALIZED;
143 }
144 switch (new_state)
145 {
146 case TNC_CONNECTION_STATE_CREATE:
147 state = imv_attestation_state_create(connection_id);
148 return imv_attestation->create_state(imv_attestation, state);
149 case TNC_CONNECTION_STATE_DELETE:
150 return imv_attestation->delete_state(imv_attestation, connection_id);
151 case TNC_CONNECTION_STATE_HANDSHAKE:
152 result = imv_attestation->change_state(imv_attestation, connection_id,
153 new_state, &state);
154 if (result != TNC_RESULT_SUCCESS)
155 {
156 return result;
157 }
158 attestation_state = (imv_attestation_state_t*)state;
159
160 /* TODO: Get some configurations */
161
162 return TNC_RESULT_SUCCESS;
163 default:
164 return imv_attestation->change_state(imv_attestation, connection_id,
165 new_state, NULL);
166 }
167 }
168
169 static TNC_Result send_message(TNC_ConnectionID connection_id)
170 {
171 pa_tnc_msg_t *msg;
172 pa_tnc_attr_t *attr;
173 pts_t *pts;
174 imv_state_t *state;
175 imv_attestation_state_t *attestation_state;
176 imv_attestation_handshake_state_t handshake_state;
177 TNC_Result result;
178
179 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
180 {
181 return TNC_RESULT_FATAL;
182 }
183 attestation_state = (imv_attestation_state_t*)state;
184 handshake_state = attestation_state->get_handshake_state(attestation_state);
185 pts = attestation_state->get_pts(attestation_state);
186
187 msg = pa_tnc_msg_create();
188
189
190 /* Switch on the attribute type IMV has received */
191 switch (handshake_state)
192 {
193 case IMV_ATTESTATION_STATE_INIT:
194 {
195 pts_proto_caps_flag_t flags;
196
197 /* Send Request Protocol Capabilities attribute */
198 flags = pts->get_proto_caps(pts);
199 attr = tcg_pts_attr_proto_caps_create(flags, TRUE);
200 attr->set_noskip_flag(attr, TRUE);
201 msg->add_attribute(msg, attr);
202
203 /* Send Measurement Algorithms attribute */
204 attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE);
205 attr->set_noskip_flag(attr, TRUE);
206 msg->add_attribute(msg, attr);
207 break;
208 }
209
210 case IMV_ATTESTATION_STATE_MEAS:
211 {
212 enumerator_t *enumerator;
213 u_int32_t delimiter = SOLIDUS_UTF;
214 char *platform_info, *pathname;
215 int id, type;
216 bool is_directory;
217
218 /* Does the PTS-IMC have TPM support? */
219 if (pts->get_proto_caps(pts) & PTS_PROTO_CAPS_T)
220 {
221 /* Send Get TPM Version attribute */
222 attr = tcg_pts_attr_get_tpm_version_info_create();
223 attr->set_noskip_flag(attr, TRUE);
224 msg->add_attribute(msg, attr);
225
226 /* Send Get AIK attribute */
227 attr = tcg_pts_attr_get_aik_create();
228 attr->set_noskip_flag(attr, TRUE);
229 msg->add_attribute(msg, attr);
230 }
231
232 /* Get Platform and OS of the PTS-IMC */
233 platform_info = pts->get_platform_info(pts);
234
235 if (!pts_db || !platform_info)
236 {
237 DBG1(DBG_IMV, "%s%s%s not available",
238 (pts_db) ? "" : "pts database",
239 (!pts_db && !platform_info) ? "and" : "",
240 (platform_info) ? "" : "platform info");
241 break;
242 }
243 DBG1(DBG_IMV, "platform is '%s'", platform_info);
244
245 /* Send Request File Measurement attribute */
246 enumerator = pts_db->create_file_enumerator(pts_db, platform_info);
247 if (!enumerator)
248 {
249 break;
250 }
251 while (enumerator->enumerate(enumerator, &id, &type, &pathname))
252 {
253 is_directory = (type != 0);
254 DBG2(DBG_IMV, "measurement request %d for %s '%s'",
255 id, is_directory ? "directory" : "file", pathname);
256 attr = tcg_pts_attr_req_file_meas_create(is_directory, id,
257 delimiter, pathname);
258 attr->set_noskip_flag(attr, TRUE);
259 msg->add_attribute(msg, attr);
260 }
261 enumerator->destroy(enumerator);
262 break;
263 }
264 case IMV_ATTESTATION_STATE_COMP_EVID:
265 case IMV_ATTESTATION_STATE_IML:
266 DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"",
267 handshake_state);
268 return TNC_RESULT_FATAL;
269 default:
270 DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"",
271 handshake_state);
272 return TNC_RESULT_FATAL;
273 }
274
275 msg->build(msg);
276 result = imv_attestation->send_message(imv_attestation, connection_id,
277 msg->get_encoding(msg));
278 msg->destroy(msg);
279
280 return result;
281 }
282
283 /**
284 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
285 */
286 TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id,
287 TNC_ConnectionID connection_id,
288 TNC_BufferReference msg,
289 TNC_UInt32 msg_len,
290 TNC_MessageType msg_type)
291 {
292 pa_tnc_msg_t *pa_tnc_msg;
293 pa_tnc_attr_t *attr;
294 imv_state_t *state;
295 imv_attestation_state_t *attestation_state;
296 pts_t *pts;
297 enumerator_t *enumerator;
298 TNC_Result result;
299 bool fatal_error = FALSE;
300 bool measurement_error = FALSE;
301
302 if (!imv_attestation)
303 {
304 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
305 return TNC_RESULT_NOT_INITIALIZED;
306 }
307
308 /* get current IMV state */
309 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
310 {
311 return TNC_RESULT_FATAL;
312 }
313 attestation_state = (imv_attestation_state_t*)state;
314 pts = attestation_state->get_pts(attestation_state);
315
316 /* parse received PA-TNC message and automatically handle any errors */
317 result = imv_attestation->receive_message(imv_attestation, connection_id,
318 chunk_create(msg, msg_len), msg_type,
319 &pa_tnc_msg);
320
321 /* no parsed PA-TNC attributes available if an error occurred */
322 if (!pa_tnc_msg)
323 {
324 return result;
325 }
326
327 /* analyze PA-TNC attributes */
328 enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg);
329 while (enumerator->enumerate(enumerator, &attr))
330 {
331 if (attr->get_vendor_id(attr) == PEN_IETF &&
332 attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR)
333 {
334 ietf_attr_pa_tnc_error_t *error_attr;
335 pen_t error_vendor_id;
336 pa_tnc_error_code_t error_code;
337 chunk_t msg_info, attr_info;
338 u_int32_t offset;
339
340 error_attr = (ietf_attr_pa_tnc_error_t*)attr;
341 error_vendor_id = error_attr->get_vendor_id(error_attr);
342 error_code = error_attr->get_error_code(error_attr);
343 msg_info = error_attr->get_msg_info(error_attr);
344
345 if (error_vendor_id == PEN_IETF)
346 {
347 DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B",
348 pa_tnc_error_code_names, error_code, &msg_info);
349
350 switch (error_code)
351 {
352 case PA_ERROR_INVALID_PARAMETER:
353 offset = error_attr->get_offset(error_attr);
354 DBG1(DBG_IMV, " occurred at offset of %u bytes", offset);
355 break;
356 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED:
357 attr_info = error_attr->get_attr_info(error_attr);
358 DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info);
359 break;
360 default:
361 break;
362 }
363 }
364 else if (error_vendor_id == PEN_TCG)
365 {
366 DBG1(DBG_IMV, "received TCG-PTS error '%N'",
367 pts_error_code_names, error_code);
368 DBG1(DBG_IMV, "error information: %B", &msg_info);
369 }
370 fatal_error = TRUE;
371 }
372 else if (attr->get_vendor_id(attr) == PEN_TCG)
373 {
374 switch (attr->get_type(attr))
375 {
376 case TCG_PTS_PROTO_CAPS:
377 {
378 tcg_pts_attr_proto_caps_t *attr_cast;
379 pts_proto_caps_flag_t flags;
380
381 attr_cast = (tcg_pts_attr_proto_caps_t*)attr;
382 flags = attr_cast->get_flags(attr_cast);
383 pts->set_proto_caps(pts, flags);
384
385 attestation_state->set_handshake_state(attestation_state,
386 IMV_ATTESTATION_STATE_MEAS);
387 break;
388 }
389 case TCG_PTS_MEAS_ALGO_SELECTION:
390 {
391 tcg_pts_attr_meas_algo_t *attr_cast;
392 pts_meas_algorithms_t selected_algorithm;
393
394 attr_cast = (tcg_pts_attr_meas_algo_t*)attr;
395 selected_algorithm = attr_cast->get_algorithms(attr_cast);
396 pts->set_meas_algorithm(pts, selected_algorithm);
397
398 attestation_state->set_handshake_state(attestation_state,
399 IMV_ATTESTATION_STATE_MEAS);
400 break;
401 }
402 case TCG_PTS_TPM_VERSION_INFO:
403 {
404 tcg_pts_attr_tpm_version_info_t *attr_cast;
405 chunk_t tpm_version_info;
406
407 attr_cast = (tcg_pts_attr_tpm_version_info_t*)attr;
408 tpm_version_info = attr_cast->get_tpm_version_info(attr_cast);
409 pts->set_tpm_version_info(pts, tpm_version_info);
410
411 attestation_state->set_handshake_state(attestation_state,
412 IMV_ATTESTATION_STATE_END);
413 break;
414 }
415 case TCG_PTS_AIK:
416 {
417 tcg_pts_attr_aik_t *attr_cast;
418 chunk_t aik;
419 bool is_naked_key;
420
421 attr_cast = (tcg_pts_attr_aik_t*)attr;
422 aik = attr_cast->get_aik(attr_cast);
423 is_naked_key = attr_cast->get_naked_flag(attr_cast);
424
425 if(!is_naked_key)
426 {
427 certificate_t *aik_cert;
428
429 aik_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
430 BUILD_BLOB_PEM, aik,
431 BUILD_END);
432 pts->set_aik_cert(pts, aik_cert);
433 }
434 else
435 {
436 public_key_t *aik_key;
437
438 aik_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
439 BUILD_BLOB_PEM, aik,
440 BUILD_END);
441 pts->set_aik_key(pts, aik_key);
442 }
443
444 attestation_state->set_handshake_state(attestation_state,
445 IMV_ATTESTATION_STATE_END);
446 break;
447 }
448
449 /* PTS-based Attestation Evidence */
450 case TCG_PTS_SIMPLE_COMP_EVID:
451 break;
452 case TCG_PTS_SIMPLE_EVID_FINAL:
453 break;
454 case TCG_PTS_FILE_MEAS:
455 {
456 tcg_pts_attr_file_meas_t *attr_cast;
457 u_int16_t request_id;
458 int file_count;
459 pts_meas_algorithms_t algo;
460 pts_file_meas_t *measurements;
461 chunk_t measurement;
462 char *platform_info, *filename;
463 enumerator_t *e_meas;
464
465 platform_info = pts->get_platform_info(pts);
466 if (!pts_db || !platform_info)
467 {
468 break;
469 }
470
471 attr_cast = (tcg_pts_attr_file_meas_t*)attr;
472 measurements = attr_cast->get_measurements(attr_cast);
473 algo = pts->get_meas_algorithm(pts);
474 request_id = measurements->get_request_id(measurements);
475 file_count = measurements->get_file_count(measurements);
476
477 DBG1(DBG_IMV, "measurement request %d returned %d file%s:",
478 request_id, file_count, (file_count == 1) ? "":"s");
479
480 e_meas = measurements->create_enumerator(measurements);
481 while (e_meas->enumerate(e_meas, &filename, &measurement))
482 {
483 enumerator_t *e;
484 chunk_t db_measurement;
485
486 e = pts_db->create_meas_enumerator(pts_db,
487 platform_info, request_id, algo);
488 if (!e)
489 {
490 DBG1(DBG_IMV, " database enumerator failed");
491 continue;
492 }
493 if (!e->enumerate(e, &db_measurement))
494 {
495 DBG1(DBG_IMV, " measurement for '%s' not found"
496 " in database", filename);
497 e->destroy(e);
498 continue;
499 }
500 if (chunk_equals(db_measurement, measurement))
501 {
502 DBG2(DBG_IMV, " %#B for '%s' is ok",
503 &measurement, filename);
504 }
505 else
506 {
507 DBG1(DBG_IMV, " %#B for '%s' does not match %#B",
508 &measurement, filename, &db_measurement);
509 measurement_error = TRUE;
510 }
511 e->destroy(e);
512 }
513 e_meas->destroy(e_meas);
514 attestation_state->set_handshake_state(attestation_state,
515 IMV_ATTESTATION_STATE_END);
516 break;
517 }
518
519 /* TODO: Not implemented yet */
520 case TCG_PTS_DH_NONCE_PARAMS_RESP:
521 case TCG_PTS_UNIX_FILE_META:
522 case TCG_PTS_INTEG_MEAS_LOG:
523 /* Attributes using XML */
524 case TCG_PTS_TEMPL_REF_MANI_SET_META:
525 case TCG_PTS_VERIFICATION_RESULT:
526 case TCG_PTS_INTEG_REPORT:
527 /* On Windows only*/
528 case TCG_PTS_WIN_FILE_META:
529 case TCG_PTS_REGISTRY_VALUE:
530 /* Received on IMC side only*/
531 case TCG_PTS_REQ_PROTO_CAPS:
532 case TCG_PTS_DH_NONCE_PARAMS_REQ:
533 case TCG_PTS_DH_NONCE_FINISH:
534 case TCG_PTS_MEAS_ALGO:
535 case TCG_PTS_GET_TPM_VERSION_INFO:
536 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META:
537 case TCG_PTS_UPDATE_TEMPL_REF_MANI:
538 case TCG_PTS_GET_AIK:
539 case TCG_PTS_REQ_FUNCT_COMP_EVID:
540 case TCG_PTS_GEN_ATTEST_EVID:
541 case TCG_PTS_REQ_FILE_META:
542 case TCG_PTS_REQ_FILE_MEAS:
543 case TCG_PTS_REQ_INTEG_MEAS_LOG:
544 default:
545 DBG1(DBG_IMV, "received unsupported attribute '%N'",
546 tcg_attr_names, attr->get_type(attr));
547 break;
548 }
549 }
550 }
551 enumerator->destroy(enumerator);
552 pa_tnc_msg->destroy(pa_tnc_msg);
553
554
555
556 if (fatal_error)
557 {
558 state->set_recommendation(state,
559 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION,
560 TNC_IMV_EVALUATION_RESULT_ERROR);
561 return imv_attestation->provide_recommendation(imv_attestation,
562 connection_id);
563 }
564
565 if (attestation_state->get_handshake_state(attestation_state) &
566 IMV_ATTESTATION_STATE_END)
567 {
568 if (measurement_error)
569 {
570 state->set_recommendation(state,
571 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
572 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR);
573 }
574 else
575 {
576 state->set_recommendation(state,
577 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
578 TNC_IMV_EVALUATION_RESULT_COMPLIANT);
579 }
580 return imv_attestation->provide_recommendation(imv_attestation,
581 connection_id);
582 }
583
584 return send_message(connection_id);
585 }
586
587 /**
588 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
589 */
590 TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id,
591 TNC_ConnectionID connection_id)
592 {
593 if (!imv_attestation)
594 {
595 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
596 return TNC_RESULT_NOT_INITIALIZED;
597 }
598 return imv_attestation->provide_recommendation(imv_attestation, connection_id);
599 }
600
601 /**
602 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
603 */
604 TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id,
605 TNC_ConnectionID connection_id)
606 {
607 imv_state_t *state;
608 imv_attestation_state_t *attestation_state;
609
610 if (!imv_attestation)
611 {
612 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
613 return TNC_RESULT_NOT_INITIALIZED;
614 }
615 /* get current IMV state */
616 if (!imv_attestation->get_state(imv_attestation, connection_id, &state))
617 {
618 return TNC_RESULT_FATAL;
619 }
620 attestation_state = (imv_attestation_state_t*)state;
621
622 /* Check if IMV has to initiate the PA-TNC exchange */
623 if (attestation_state->get_handshake_state(attestation_state) ==
624 IMV_ATTESTATION_STATE_INIT)
625 {
626 return send_message(connection_id);
627 }
628 return TNC_RESULT_SUCCESS;
629 }
630
631 /**
632 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
633 */
634 TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id)
635 {
636 if (!imv_attestation)
637 {
638 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
639 return TNC_RESULT_NOT_INITIALIZED;
640 }
641 DESTROY_IF(pts_db);
642 imv_attestation->destroy(imv_attestation);
643 imv_attestation = NULL;
644
645 return TNC_RESULT_SUCCESS;
646 }
647
648 /**
649 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
650 */
651 TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id,
652 TNC_TNCS_BindFunctionPointer bind_function)
653 {
654 if (!imv_attestation)
655 {
656 DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name);
657 return TNC_RESULT_NOT_INITIALIZED;
658 }
659 return imv_attestation->bind_functions(imv_attestation, bind_function);
660 }