1 /* 2 * QEMU crypto TLS x509 credential support 3 * 4 * Copyright (c) 2015 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "crypto/tlscredsx509.h" 22 #include "crypto/tlscredspriv.h" 23 #include "qom/object_interfaces.h" 24 #include "trace.h" 25 26 27 #ifdef CONFIG_GNUTLS 28 29 #include <gnutls/x509.h> 30 31 32 static int 33 qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert, 34 const char *certFile, 35 bool isServer, 36 bool isCA, 37 Error **errp) 38 { 39 time_t now = time(NULL); 40 41 if (now == ((time_t)-1)) { 42 error_setg_errno(errp, errno, "cannot get current time"); 43 return -1; 44 } 45 46 if (gnutls_x509_crt_get_expiration_time(cert) < now) { 47 error_setg(errp, 48 (isCA ? 49 "The CA certificate %s has expired" : 50 (isServer ? 51 "The server certificate %s has expired" : 52 "The client certificate %s has expired")), 53 certFile); 54 return -1; 55 } 56 57 if (gnutls_x509_crt_get_activation_time(cert) > now) { 58 error_setg(errp, 59 (isCA ? 60 "The CA certificate %s is not yet active" : 61 (isServer ? 62 "The server certificate %s is not yet active" : 63 "The client certificate %s is not yet active")), 64 certFile); 65 return -1; 66 } 67 68 return 0; 69 } 70 71 72 #if LIBGNUTLS_VERSION_NUMBER >= 2 73 /* 74 * The gnutls_x509_crt_get_basic_constraints function isn't 75 * available in GNUTLS 1.0.x branches. This isn't critical 76 * though, since gnutls_certificate_verify_peers2 will do 77 * pretty much the same check at runtime, so we can just 78 * disable this code 79 */ 80 static int 81 qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds, 82 gnutls_x509_crt_t cert, 83 const char *certFile, 84 bool isServer, 85 bool isCA, 86 Error **errp) 87 { 88 int status; 89 90 status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); 91 trace_qcrypto_tls_creds_x509_check_basic_constraints( 92 creds, certFile, status); 93 94 if (status > 0) { /* It is a CA cert */ 95 if (!isCA) { 96 error_setg(errp, isServer ? 97 "The certificate %s basic constraints show a CA, " 98 "but we need one for a server" : 99 "The certificate %s basic constraints show a CA, " 100 "but we need one for a client", 101 certFile); 102 return -1; 103 } 104 } else if (status == 0) { /* It is not a CA cert */ 105 if (isCA) { 106 error_setg(errp, 107 "The certificate %s basic constraints do not " 108 "show a CA", 109 certFile); 110 return -1; 111 } 112 } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { 113 /* Missing basicConstraints */ 114 if (isCA) { 115 error_setg(errp, 116 "The certificate %s is missing basic constraints " 117 "for a CA", 118 certFile); 119 return -1; 120 } 121 } else { /* General error */ 122 error_setg(errp, 123 "Unable to query certificate %s basic constraints: %s", 124 certFile, gnutls_strerror(status)); 125 return -1; 126 } 127 128 return 0; 129 } 130 #endif 131 132 133 static int 134 qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds, 135 gnutls_x509_crt_t cert, 136 const char *certFile, 137 bool isCA, 138 Error **errp) 139 { 140 int status; 141 unsigned int usage = 0; 142 unsigned int critical = 0; 143 144 status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); 145 trace_qcrypto_tls_creds_x509_check_key_usage( 146 creds, certFile, status, usage, critical); 147 148 if (status < 0) { 149 if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { 150 usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : 151 GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; 152 } else { 153 error_setg(errp, 154 "Unable to query certificate %s key usage: %s", 155 certFile, gnutls_strerror(status)); 156 return -1; 157 } 158 } 159 160 if (isCA) { 161 if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { 162 if (critical) { 163 error_setg(errp, 164 "Certificate %s usage does not permit " 165 "certificate signing", certFile); 166 return -1; 167 } 168 } 169 } else { 170 if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { 171 if (critical) { 172 error_setg(errp, 173 "Certificate %s usage does not permit digital " 174 "signature", certFile); 175 return -1; 176 } 177 } 178 if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { 179 if (critical) { 180 error_setg(errp, 181 "Certificate %s usage does not permit key " 182 "encipherment", certFile); 183 return -1; 184 } 185 } 186 } 187 188 return 0; 189 } 190 191 192 static int 193 qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds, 194 gnutls_x509_crt_t cert, 195 const char *certFile, 196 bool isServer, 197 Error **errp) 198 { 199 int status; 200 size_t i; 201 unsigned int purposeCritical; 202 unsigned int critical; 203 char *buffer = NULL; 204 size_t size; 205 bool allowClient = false, allowServer = false; 206 207 critical = 0; 208 for (i = 0; ; i++) { 209 size = 0; 210 status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, 211 &size, NULL); 212 213 if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { 214 215 /* If there is no data at all, then we must allow 216 client/server to pass */ 217 if (i == 0) { 218 allowServer = allowClient = true; 219 } 220 break; 221 } 222 if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { 223 error_setg(errp, 224 "Unable to query certificate %s key purpose: %s", 225 certFile, gnutls_strerror(status)); 226 return -1; 227 } 228 229 buffer = g_new0(char, size); 230 231 status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, 232 &size, &purposeCritical); 233 234 if (status < 0) { 235 trace_qcrypto_tls_creds_x509_check_key_purpose( 236 creds, certFile, status, "<none>", purposeCritical); 237 g_free(buffer); 238 error_setg(errp, 239 "Unable to query certificate %s key purpose: %s", 240 certFile, gnutls_strerror(status)); 241 return -1; 242 } 243 trace_qcrypto_tls_creds_x509_check_key_purpose( 244 creds, certFile, status, buffer, purposeCritical); 245 if (purposeCritical) { 246 critical = true; 247 } 248 249 if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { 250 allowServer = true; 251 } else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { 252 allowClient = true; 253 } else if (g_str_equal(buffer, GNUTLS_KP_ANY)) { 254 allowServer = allowClient = true; 255 } 256 257 g_free(buffer); 258 } 259 260 if (isServer) { 261 if (!allowServer) { 262 if (critical) { 263 error_setg(errp, 264 "Certificate %s purpose does not allow " 265 "use with a TLS server", certFile); 266 return -1; 267 } 268 } 269 } else { 270 if (!allowClient) { 271 if (critical) { 272 error_setg(errp, 273 "Certificate %s purpose does not allow use " 274 "with a TLS client", certFile); 275 return -1; 276 } 277 } 278 } 279 280 return 0; 281 } 282 283 284 static int 285 qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds, 286 gnutls_x509_crt_t cert, 287 const char *certFile, 288 bool isServer, 289 bool isCA, 290 Error **errp) 291 { 292 if (qcrypto_tls_creds_check_cert_times(cert, certFile, 293 isServer, isCA, 294 errp) < 0) { 295 return -1; 296 } 297 298 #if LIBGNUTLS_VERSION_NUMBER >= 2 299 if (qcrypto_tls_creds_check_cert_basic_constraints(creds, 300 cert, certFile, 301 isServer, isCA, 302 errp) < 0) { 303 return -1; 304 } 305 #endif 306 307 if (qcrypto_tls_creds_check_cert_key_usage(creds, 308 cert, certFile, 309 isCA, errp) < 0) { 310 return -1; 311 } 312 313 if (!isCA && 314 qcrypto_tls_creds_check_cert_key_purpose(creds, 315 cert, certFile, 316 isServer, errp) < 0) { 317 return -1; 318 } 319 320 return 0; 321 } 322 323 324 static int 325 qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert, 326 const char *certFile, 327 gnutls_x509_crt_t *cacerts, 328 size_t ncacerts, 329 const char *cacertFile, 330 bool isServer, 331 Error **errp) 332 { 333 unsigned int status; 334 335 if (gnutls_x509_crt_list_verify(&cert, 1, 336 cacerts, ncacerts, 337 NULL, 0, 338 0, &status) < 0) { 339 error_setg(errp, isServer ? 340 "Unable to verify server certificate %s against " 341 "CA certificate %s" : 342 "Unable to verify client certificate %s against " 343 "CA certificate %s", 344 certFile, cacertFile); 345 return -1; 346 } 347 348 if (status != 0) { 349 const char *reason = "Invalid certificate"; 350 351 if (status & GNUTLS_CERT_INVALID) { 352 reason = "The certificate is not trusted"; 353 } 354 355 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 356 reason = "The certificate hasn't got a known issuer"; 357 } 358 359 if (status & GNUTLS_CERT_REVOKED) { 360 reason = "The certificate has been revoked"; 361 } 362 363 #ifndef GNUTLS_1_0_COMPAT 364 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 365 reason = "The certificate uses an insecure algorithm"; 366 } 367 #endif 368 369 error_setg(errp, 370 "Our own certificate %s failed validation against %s: %s", 371 certFile, cacertFile, reason); 372 return -1; 373 } 374 375 return 0; 376 } 377 378 379 static gnutls_x509_crt_t 380 qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, 381 const char *certFile, 382 bool isServer, 383 Error **errp) 384 { 385 gnutls_datum_t data; 386 gnutls_x509_crt_t cert = NULL; 387 char *buf = NULL; 388 gsize buflen; 389 GError *gerr; 390 int ret = -1; 391 392 trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile); 393 394 if (gnutls_x509_crt_init(&cert) < 0) { 395 error_setg(errp, "Unable to initialize certificate"); 396 goto cleanup; 397 } 398 399 if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { 400 error_setg(errp, "Cannot load CA cert list %s: %s", 401 certFile, gerr->message); 402 g_error_free(gerr); 403 goto cleanup; 404 } 405 406 data.data = (unsigned char *)buf; 407 data.size = strlen(buf); 408 409 if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { 410 error_setg(errp, isServer ? 411 "Unable to import server certificate %s" : 412 "Unable to import client certificate %s", 413 certFile); 414 goto cleanup; 415 } 416 417 ret = 0; 418 419 cleanup: 420 if (ret != 0) { 421 gnutls_x509_crt_deinit(cert); 422 cert = NULL; 423 } 424 g_free(buf); 425 return cert; 426 } 427 428 429 static int 430 qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, 431 const char *certFile, 432 gnutls_x509_crt_t *certs, 433 unsigned int certMax, 434 size_t *ncerts, 435 Error **errp) 436 { 437 gnutls_datum_t data; 438 char *buf = NULL; 439 gsize buflen; 440 int ret = -1; 441 GError *gerr = NULL; 442 443 *ncerts = 0; 444 trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile); 445 446 if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { 447 error_setg(errp, "Cannot load CA cert list %s: %s", 448 certFile, gerr->message); 449 g_error_free(gerr); 450 goto cleanup; 451 } 452 453 data.data = (unsigned char *)buf; 454 data.size = strlen(buf); 455 456 if (gnutls_x509_crt_list_import(certs, &certMax, &data, 457 GNUTLS_X509_FMT_PEM, 0) < 0) { 458 error_setg(errp, 459 "Unable to import CA certificate list %s", 460 certFile); 461 goto cleanup; 462 } 463 *ncerts = certMax; 464 465 ret = 0; 466 467 cleanup: 468 g_free(buf); 469 return ret; 470 } 471 472 473 #define MAX_CERTS 16 474 static int 475 qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, 476 bool isServer, 477 const char *cacertFile, 478 const char *certFile, 479 Error **errp) 480 { 481 gnutls_x509_crt_t cert = NULL; 482 gnutls_x509_crt_t cacerts[MAX_CERTS]; 483 size_t ncacerts = 0; 484 size_t i; 485 int ret = -1; 486 487 memset(cacerts, 0, sizeof(cacerts)); 488 if (certFile && 489 access(certFile, R_OK) == 0) { 490 cert = qcrypto_tls_creds_load_cert(creds, 491 certFile, isServer, 492 errp); 493 if (!cert) { 494 goto cleanup; 495 } 496 } 497 if (access(cacertFile, R_OK) == 0) { 498 if (qcrypto_tls_creds_load_ca_cert_list(creds, 499 cacertFile, cacerts, 500 MAX_CERTS, &ncacerts, 501 errp) < 0) { 502 goto cleanup; 503 } 504 } 505 506 if (cert && 507 qcrypto_tls_creds_check_cert(creds, 508 cert, certFile, isServer, 509 false, errp) < 0) { 510 goto cleanup; 511 } 512 513 for (i = 0; i < ncacerts; i++) { 514 if (qcrypto_tls_creds_check_cert(creds, 515 cacerts[i], cacertFile, 516 isServer, true, errp) < 0) { 517 goto cleanup; 518 } 519 } 520 521 if (cert && ncacerts && 522 qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts, 523 ncacerts, cacertFile, 524 isServer, errp) < 0) { 525 goto cleanup; 526 } 527 528 ret = 0; 529 530 cleanup: 531 if (cert) { 532 gnutls_x509_crt_deinit(cert); 533 } 534 for (i = 0; i < ncacerts; i++) { 535 gnutls_x509_crt_deinit(cacerts[i]); 536 } 537 return ret; 538 } 539 540 541 static int 542 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, 543 Error **errp) 544 { 545 char *cacert = NULL, *cacrl = NULL, *cert = NULL, 546 *key = NULL, *dhparams = NULL; 547 int ret; 548 int rv = -1; 549 550 trace_qcrypto_tls_creds_x509_load(creds, 551 creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); 552 553 if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 554 if (qcrypto_tls_creds_get_path(&creds->parent_obj, 555 QCRYPTO_TLS_CREDS_X509_CA_CERT, 556 true, &cacert, errp) < 0 || 557 qcrypto_tls_creds_get_path(&creds->parent_obj, 558 QCRYPTO_TLS_CREDS_X509_CA_CRL, 559 false, &cacrl, errp) < 0 || 560 qcrypto_tls_creds_get_path(&creds->parent_obj, 561 QCRYPTO_TLS_CREDS_X509_SERVER_CERT, 562 true, &cert, errp) < 0 || 563 qcrypto_tls_creds_get_path(&creds->parent_obj, 564 QCRYPTO_TLS_CREDS_X509_SERVER_KEY, 565 true, &key, errp) < 0 || 566 qcrypto_tls_creds_get_path(&creds->parent_obj, 567 QCRYPTO_TLS_CREDS_DH_PARAMS, 568 false, &dhparams, errp) < 0) { 569 goto cleanup; 570 } 571 } else { 572 if (qcrypto_tls_creds_get_path(&creds->parent_obj, 573 QCRYPTO_TLS_CREDS_X509_CA_CERT, 574 true, &cacert, errp) < 0 || 575 qcrypto_tls_creds_get_path(&creds->parent_obj, 576 QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, 577 false, &cert, errp) < 0 || 578 qcrypto_tls_creds_get_path(&creds->parent_obj, 579 QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, 580 false, &key, errp) < 0) { 581 goto cleanup; 582 } 583 } 584 585 if (creds->sanityCheck && 586 qcrypto_tls_creds_x509_sanity_check(creds, 587 creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, 588 cacert, cert, errp) < 0) { 589 goto cleanup; 590 } 591 592 ret = gnutls_certificate_allocate_credentials(&creds->data); 593 if (ret < 0) { 594 error_setg(errp, "Cannot allocate credentials: '%s'", 595 gnutls_strerror(ret)); 596 goto cleanup; 597 } 598 599 ret = gnutls_certificate_set_x509_trust_file(creds->data, 600 cacert, 601 GNUTLS_X509_FMT_PEM); 602 if (ret < 0) { 603 error_setg(errp, "Cannot load CA certificate '%s': %s", 604 cacert, gnutls_strerror(ret)); 605 goto cleanup; 606 } 607 608 if (cert != NULL && key != NULL) { 609 ret = gnutls_certificate_set_x509_key_file(creds->data, 610 cert, key, 611 GNUTLS_X509_FMT_PEM); 612 if (ret < 0) { 613 error_setg(errp, "Cannot load certificate '%s' & key '%s': %s", 614 cert, key, gnutls_strerror(ret)); 615 goto cleanup; 616 } 617 } 618 619 if (cacrl != NULL) { 620 ret = gnutls_certificate_set_x509_crl_file(creds->data, 621 cacrl, 622 GNUTLS_X509_FMT_PEM); 623 if (ret < 0) { 624 error_setg(errp, "Cannot load CRL '%s': %s", 625 cacrl, gnutls_strerror(ret)); 626 goto cleanup; 627 } 628 } 629 630 if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 631 if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, 632 &creds->parent_obj.dh_params, 633 errp) < 0) { 634 goto cleanup; 635 } 636 gnutls_certificate_set_dh_params(creds->data, 637 creds->parent_obj.dh_params); 638 } 639 640 rv = 0; 641 cleanup: 642 g_free(cacert); 643 g_free(cacrl); 644 g_free(cert); 645 g_free(key); 646 g_free(dhparams); 647 return rv; 648 } 649 650 651 static void 652 qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds) 653 { 654 if (creds->data) { 655 gnutls_certificate_free_credentials(creds->data); 656 creds->data = NULL; 657 } 658 if (creds->parent_obj.dh_params) { 659 gnutls_dh_params_deinit(creds->parent_obj.dh_params); 660 creds->parent_obj.dh_params = NULL; 661 } 662 } 663 664 665 #else /* ! CONFIG_GNUTLS */ 666 667 668 static void 669 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED, 670 Error **errp) 671 { 672 error_setg(errp, "TLS credentials support requires GNUTLS"); 673 } 674 675 676 static void 677 qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED) 678 { 679 /* nada */ 680 } 681 682 683 #endif /* ! CONFIG_GNUTLS */ 684 685 686 static void 687 qcrypto_tls_creds_x509_prop_set_loaded(Object *obj, 688 bool value, 689 Error **errp) 690 { 691 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 692 693 if (value) { 694 qcrypto_tls_creds_x509_load(creds, errp); 695 } else { 696 qcrypto_tls_creds_x509_unload(creds); 697 } 698 } 699 700 701 #ifdef CONFIG_GNUTLS 702 703 704 static bool 705 qcrypto_tls_creds_x509_prop_get_loaded(Object *obj, 706 Error **errp G_GNUC_UNUSED) 707 { 708 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 709 710 return creds->data != NULL; 711 } 712 713 714 #else /* ! CONFIG_GNUTLS */ 715 716 717 static bool 718 qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED, 719 Error **errp G_GNUC_UNUSED) 720 { 721 return false; 722 } 723 724 725 #endif /* ! CONFIG_GNUTLS */ 726 727 728 static void 729 qcrypto_tls_creds_x509_prop_set_sanity(Object *obj, 730 bool value, 731 Error **errp G_GNUC_UNUSED) 732 { 733 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 734 735 creds->sanityCheck = value; 736 } 737 738 739 static bool 740 qcrypto_tls_creds_x509_prop_get_sanity(Object *obj, 741 Error **errp G_GNUC_UNUSED) 742 { 743 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 744 745 return creds->sanityCheck; 746 } 747 748 749 static void 750 qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) 751 { 752 object_property_set_bool(OBJECT(uc), true, "loaded", errp); 753 } 754 755 756 static void 757 qcrypto_tls_creds_x509_init(Object *obj) 758 { 759 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 760 761 creds->sanityCheck = true; 762 763 object_property_add_bool(obj, "loaded", 764 qcrypto_tls_creds_x509_prop_get_loaded, 765 qcrypto_tls_creds_x509_prop_set_loaded, 766 NULL); 767 object_property_add_bool(obj, "sanity-check", 768 qcrypto_tls_creds_x509_prop_get_sanity, 769 qcrypto_tls_creds_x509_prop_set_sanity, 770 NULL); 771 } 772 773 774 static void 775 qcrypto_tls_creds_x509_finalize(Object *obj) 776 { 777 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 778 779 qcrypto_tls_creds_x509_unload(creds); 780 } 781 782 783 static void 784 qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) 785 { 786 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 787 788 ucc->complete = qcrypto_tls_creds_x509_complete; 789 } 790 791 792 static const TypeInfo qcrypto_tls_creds_x509_info = { 793 .parent = TYPE_QCRYPTO_TLS_CREDS, 794 .name = TYPE_QCRYPTO_TLS_CREDS_X509, 795 .instance_size = sizeof(QCryptoTLSCredsX509), 796 .instance_init = qcrypto_tls_creds_x509_init, 797 .instance_finalize = qcrypto_tls_creds_x509_finalize, 798 .class_size = sizeof(QCryptoTLSCredsX509Class), 799 .class_init = qcrypto_tls_creds_x509_class_init, 800 .interfaces = (InterfaceInfo[]) { 801 { TYPE_USER_CREATABLE }, 802 { } 803 } 804 }; 805 806 807 static void 808 qcrypto_tls_creds_x509_register_types(void) 809 { 810 type_register_static(&qcrypto_tls_creds_x509_info); 811 } 812 813 814 type_init(qcrypto_tls_creds_x509_register_types); 815