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 buffer = NULL; 259 } 260 261 if (isServer) { 262 if (!allowServer) { 263 if (critical) { 264 error_setg(errp, 265 "Certificate %s purpose does not allow " 266 "use with a TLS server", certFile); 267 return -1; 268 } 269 } 270 } else { 271 if (!allowClient) { 272 if (critical) { 273 error_setg(errp, 274 "Certificate %s purpose does not allow use " 275 "with a TLS client", certFile); 276 return -1; 277 } 278 } 279 } 280 281 return 0; 282 } 283 284 285 static int 286 qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds, 287 gnutls_x509_crt_t cert, 288 const char *certFile, 289 bool isServer, 290 bool isCA, 291 Error **errp) 292 { 293 if (qcrypto_tls_creds_check_cert_times(cert, certFile, 294 isServer, isCA, 295 errp) < 0) { 296 return -1; 297 } 298 299 #if LIBGNUTLS_VERSION_NUMBER >= 2 300 if (qcrypto_tls_creds_check_cert_basic_constraints(creds, 301 cert, certFile, 302 isServer, isCA, 303 errp) < 0) { 304 return -1; 305 } 306 #endif 307 308 if (qcrypto_tls_creds_check_cert_key_usage(creds, 309 cert, certFile, 310 isCA, errp) < 0) { 311 return -1; 312 } 313 314 if (!isCA && 315 qcrypto_tls_creds_check_cert_key_purpose(creds, 316 cert, certFile, 317 isServer, errp) < 0) { 318 return -1; 319 } 320 321 return 0; 322 } 323 324 325 static int 326 qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert, 327 const char *certFile, 328 gnutls_x509_crt_t *cacerts, 329 size_t ncacerts, 330 const char *cacertFile, 331 bool isServer, 332 Error **errp) 333 { 334 unsigned int status; 335 336 if (gnutls_x509_crt_list_verify(&cert, 1, 337 cacerts, ncacerts, 338 NULL, 0, 339 0, &status) < 0) { 340 error_setg(errp, isServer ? 341 "Unable to verify server certificate %s against " 342 "CA certificate %s" : 343 "Unable to verify client certificate %s against " 344 "CA certificate %s", 345 certFile, cacertFile); 346 return -1; 347 } 348 349 if (status != 0) { 350 const char *reason = "Invalid certificate"; 351 352 if (status & GNUTLS_CERT_INVALID) { 353 reason = "The certificate is not trusted"; 354 } 355 356 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 357 reason = "The certificate hasn't got a known issuer"; 358 } 359 360 if (status & GNUTLS_CERT_REVOKED) { 361 reason = "The certificate has been revoked"; 362 } 363 364 #ifndef GNUTLS_1_0_COMPAT 365 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 366 reason = "The certificate uses an insecure algorithm"; 367 } 368 #endif 369 370 error_setg(errp, 371 "Our own certificate %s failed validation against %s: %s", 372 certFile, cacertFile, reason); 373 return -1; 374 } 375 376 return 0; 377 } 378 379 380 static gnutls_x509_crt_t 381 qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, 382 const char *certFile, 383 bool isServer, 384 Error **errp) 385 { 386 gnutls_datum_t data; 387 gnutls_x509_crt_t cert = NULL; 388 char *buf = NULL; 389 gsize buflen; 390 GError *gerr; 391 int ret = -1; 392 393 trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile); 394 395 if (gnutls_x509_crt_init(&cert) < 0) { 396 error_setg(errp, "Unable to initialize certificate"); 397 goto cleanup; 398 } 399 400 if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { 401 error_setg(errp, "Cannot load CA cert list %s: %s", 402 certFile, gerr->message); 403 g_error_free(gerr); 404 goto cleanup; 405 } 406 407 data.data = (unsigned char *)buf; 408 data.size = strlen(buf); 409 410 if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { 411 error_setg(errp, isServer ? 412 "Unable to import server certificate %s" : 413 "Unable to import client certificate %s", 414 certFile); 415 goto cleanup; 416 } 417 418 ret = 0; 419 420 cleanup: 421 if (ret != 0) { 422 gnutls_x509_crt_deinit(cert); 423 cert = NULL; 424 } 425 g_free(buf); 426 return cert; 427 } 428 429 430 static int 431 qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, 432 const char *certFile, 433 gnutls_x509_crt_t *certs, 434 unsigned int certMax, 435 size_t *ncerts, 436 Error **errp) 437 { 438 gnutls_datum_t data; 439 char *buf = NULL; 440 gsize buflen; 441 int ret = -1; 442 GError *gerr = NULL; 443 444 *ncerts = 0; 445 trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile); 446 447 if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { 448 error_setg(errp, "Cannot load CA cert list %s: %s", 449 certFile, gerr->message); 450 g_error_free(gerr); 451 goto cleanup; 452 } 453 454 data.data = (unsigned char *)buf; 455 data.size = strlen(buf); 456 457 if (gnutls_x509_crt_list_import(certs, &certMax, &data, 458 GNUTLS_X509_FMT_PEM, 0) < 0) { 459 error_setg(errp, 460 "Unable to import CA certificate list %s", 461 certFile); 462 goto cleanup; 463 } 464 *ncerts = certMax; 465 466 ret = 0; 467 468 cleanup: 469 g_free(buf); 470 return ret; 471 } 472 473 474 #define MAX_CERTS 16 475 static int 476 qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, 477 bool isServer, 478 const char *cacertFile, 479 const char *certFile, 480 Error **errp) 481 { 482 gnutls_x509_crt_t cert = NULL; 483 gnutls_x509_crt_t cacerts[MAX_CERTS]; 484 size_t ncacerts = 0; 485 size_t i; 486 int ret = -1; 487 488 memset(cacerts, 0, sizeof(cacerts)); 489 if (certFile && 490 access(certFile, R_OK) == 0) { 491 cert = qcrypto_tls_creds_load_cert(creds, 492 certFile, isServer, 493 errp); 494 if (!cert) { 495 goto cleanup; 496 } 497 } 498 if (access(cacertFile, R_OK) == 0) { 499 if (qcrypto_tls_creds_load_ca_cert_list(creds, 500 cacertFile, cacerts, 501 MAX_CERTS, &ncacerts, 502 errp) < 0) { 503 goto cleanup; 504 } 505 } 506 507 if (cert && 508 qcrypto_tls_creds_check_cert(creds, 509 cert, certFile, isServer, 510 false, errp) < 0) { 511 goto cleanup; 512 } 513 514 for (i = 0; i < ncacerts; i++) { 515 if (qcrypto_tls_creds_check_cert(creds, 516 cacerts[i], cacertFile, 517 isServer, true, errp) < 0) { 518 goto cleanup; 519 } 520 } 521 522 if (cert && ncacerts && 523 qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts, 524 ncacerts, cacertFile, 525 isServer, errp) < 0) { 526 goto cleanup; 527 } 528 529 ret = 0; 530 531 cleanup: 532 if (cert) { 533 gnutls_x509_crt_deinit(cert); 534 } 535 for (i = 0; i < ncacerts; i++) { 536 gnutls_x509_crt_deinit(cacerts[i]); 537 } 538 return ret; 539 } 540 541 542 static int 543 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, 544 Error **errp) 545 { 546 char *cacert = NULL, *cacrl = NULL, *cert = NULL, 547 *key = NULL, *dhparams = NULL; 548 int ret; 549 int rv = -1; 550 551 trace_qcrypto_tls_creds_x509_load(creds, 552 creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); 553 554 if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 555 if (qcrypto_tls_creds_get_path(&creds->parent_obj, 556 QCRYPTO_TLS_CREDS_X509_CA_CERT, 557 true, &cacert, errp) < 0 || 558 qcrypto_tls_creds_get_path(&creds->parent_obj, 559 QCRYPTO_TLS_CREDS_X509_CA_CRL, 560 false, &cacrl, errp) < 0 || 561 qcrypto_tls_creds_get_path(&creds->parent_obj, 562 QCRYPTO_TLS_CREDS_X509_SERVER_CERT, 563 true, &cert, errp) < 0 || 564 qcrypto_tls_creds_get_path(&creds->parent_obj, 565 QCRYPTO_TLS_CREDS_X509_SERVER_KEY, 566 true, &key, errp) < 0 || 567 qcrypto_tls_creds_get_path(&creds->parent_obj, 568 QCRYPTO_TLS_CREDS_DH_PARAMS, 569 false, &dhparams, errp) < 0) { 570 goto cleanup; 571 } 572 } else { 573 if (qcrypto_tls_creds_get_path(&creds->parent_obj, 574 QCRYPTO_TLS_CREDS_X509_CA_CERT, 575 true, &cacert, errp) < 0 || 576 qcrypto_tls_creds_get_path(&creds->parent_obj, 577 QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, 578 false, &cert, errp) < 0 || 579 qcrypto_tls_creds_get_path(&creds->parent_obj, 580 QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, 581 false, &key, errp) < 0) { 582 goto cleanup; 583 } 584 } 585 586 if (creds->sanityCheck && 587 qcrypto_tls_creds_x509_sanity_check(creds, 588 creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, 589 cacert, cert, errp) < 0) { 590 goto cleanup; 591 } 592 593 ret = gnutls_certificate_allocate_credentials(&creds->data); 594 if (ret < 0) { 595 error_setg(errp, "Cannot allocate credentials: '%s'", 596 gnutls_strerror(ret)); 597 goto cleanup; 598 } 599 600 ret = gnutls_certificate_set_x509_trust_file(creds->data, 601 cacert, 602 GNUTLS_X509_FMT_PEM); 603 if (ret < 0) { 604 error_setg(errp, "Cannot load CA certificate '%s': %s", 605 cacert, gnutls_strerror(ret)); 606 goto cleanup; 607 } 608 609 if (cert != NULL && key != NULL) { 610 ret = gnutls_certificate_set_x509_key_file(creds->data, 611 cert, key, 612 GNUTLS_X509_FMT_PEM); 613 if (ret < 0) { 614 error_setg(errp, "Cannot load certificate '%s' & key '%s': %s", 615 cert, key, gnutls_strerror(ret)); 616 goto cleanup; 617 } 618 } 619 620 if (cacrl != NULL) { 621 ret = gnutls_certificate_set_x509_crl_file(creds->data, 622 cacrl, 623 GNUTLS_X509_FMT_PEM); 624 if (ret < 0) { 625 error_setg(errp, "Cannot load CRL '%s': %s", 626 cacrl, gnutls_strerror(ret)); 627 goto cleanup; 628 } 629 } 630 631 if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 632 if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, 633 &creds->parent_obj.dh_params, 634 errp) < 0) { 635 goto cleanup; 636 } 637 gnutls_certificate_set_dh_params(creds->data, 638 creds->parent_obj.dh_params); 639 } 640 641 rv = 0; 642 cleanup: 643 g_free(cacert); 644 g_free(cacrl); 645 g_free(cert); 646 g_free(key); 647 g_free(dhparams); 648 return rv; 649 } 650 651 652 static void 653 qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds) 654 { 655 if (creds->data) { 656 gnutls_certificate_free_credentials(creds->data); 657 creds->data = NULL; 658 } 659 if (creds->parent_obj.dh_params) { 660 gnutls_dh_params_deinit(creds->parent_obj.dh_params); 661 creds->parent_obj.dh_params = NULL; 662 } 663 } 664 665 666 #else /* ! CONFIG_GNUTLS */ 667 668 669 static void 670 qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED, 671 Error **errp) 672 { 673 error_setg(errp, "TLS credentials support requires GNUTLS"); 674 } 675 676 677 static void 678 qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED) 679 { 680 /* nada */ 681 } 682 683 684 #endif /* ! CONFIG_GNUTLS */ 685 686 687 static void 688 qcrypto_tls_creds_x509_prop_set_loaded(Object *obj, 689 bool value, 690 Error **errp) 691 { 692 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 693 694 if (value) { 695 qcrypto_tls_creds_x509_load(creds, errp); 696 } else { 697 qcrypto_tls_creds_x509_unload(creds); 698 } 699 } 700 701 702 #ifdef CONFIG_GNUTLS 703 704 705 static bool 706 qcrypto_tls_creds_x509_prop_get_loaded(Object *obj, 707 Error **errp G_GNUC_UNUSED) 708 { 709 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 710 711 return creds->data != NULL; 712 } 713 714 715 #else /* ! CONFIG_GNUTLS */ 716 717 718 static bool 719 qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED, 720 Error **errp G_GNUC_UNUSED) 721 { 722 return false; 723 } 724 725 726 #endif /* ! CONFIG_GNUTLS */ 727 728 729 static void 730 qcrypto_tls_creds_x509_prop_set_sanity(Object *obj, 731 bool value, 732 Error **errp G_GNUC_UNUSED) 733 { 734 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 735 736 creds->sanityCheck = value; 737 } 738 739 740 static bool 741 qcrypto_tls_creds_x509_prop_get_sanity(Object *obj, 742 Error **errp G_GNUC_UNUSED) 743 { 744 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 745 746 return creds->sanityCheck; 747 } 748 749 750 static void 751 qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) 752 { 753 object_property_set_bool(OBJECT(uc), true, "loaded", errp); 754 } 755 756 757 static void 758 qcrypto_tls_creds_x509_init(Object *obj) 759 { 760 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 761 762 creds->sanityCheck = true; 763 764 object_property_add_bool(obj, "loaded", 765 qcrypto_tls_creds_x509_prop_get_loaded, 766 qcrypto_tls_creds_x509_prop_set_loaded, 767 NULL); 768 object_property_add_bool(obj, "sanity-check", 769 qcrypto_tls_creds_x509_prop_get_sanity, 770 qcrypto_tls_creds_x509_prop_set_sanity, 771 NULL); 772 } 773 774 775 static void 776 qcrypto_tls_creds_x509_finalize(Object *obj) 777 { 778 QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); 779 780 qcrypto_tls_creds_x509_unload(creds); 781 } 782 783 784 static void 785 qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) 786 { 787 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 788 789 ucc->complete = qcrypto_tls_creds_x509_complete; 790 } 791 792 793 static const TypeInfo qcrypto_tls_creds_x509_info = { 794 .parent = TYPE_QCRYPTO_TLS_CREDS, 795 .name = TYPE_QCRYPTO_TLS_CREDS_X509, 796 .instance_size = sizeof(QCryptoTLSCredsX509), 797 .instance_init = qcrypto_tls_creds_x509_init, 798 .instance_finalize = qcrypto_tls_creds_x509_finalize, 799 .class_size = sizeof(QCryptoTLSCredsX509Class), 800 .class_init = qcrypto_tls_creds_x509_class_init, 801 .interfaces = (InterfaceInfo[]) { 802 { TYPE_USER_CREATABLE }, 803 { } 804 } 805 }; 806 807 808 static void 809 qcrypto_tls_creds_x509_register_types(void) 810 { 811 type_register_static(&qcrypto_tls_creds_x509_info); 812 } 813 814 815 type_init(qcrypto_tls_creds_x509_register_types); 816