void __certificate_properties_fill_cert_ext_BasicConstraints (GtkTreeStore *store, GtkTreeIter *parent, gnutls_x509_crt_t *certificate) { guint critical; gint result; guint ca; gint path_len_constraint; gchar *pathlen_as_string = NULL; GtkTreeIter l; gchar *ca_as_string = NULL; result = gnutls_x509_crt_get_basic_constraints(*certificate, &critical, &ca, &path_len_constraint); if (result < 0) { fprintf(stderr, "Error: %s\n", gnutls_strerror(result)); return; } ca_as_string = ca ? _("TRUE") : _("FALSE"); pathlen_as_string = g_strdup_printf ("%d", path_len_constraint); gtk_tree_store_append(store, &l, parent); gtk_tree_store_set(store, &l, CERTIFICATE_PROPERTIES_COL_NAME, _("CA"), CERTIFICATE_PROPERTIES_COL_VALUE, ca_as_string, -1); gtk_tree_store_append(store, &l, parent); gtk_tree_store_set(store, &l, CERTIFICATE_PROPERTIES_COL_NAME, _("Path Length Constraint"), CERTIFICATE_PROPERTIES_COL_VALUE, pathlen_as_string, -1); g_free (pathlen_as_string); }
/* Returns true if the provided purpose is in accordance with the certificate. */ unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, unsigned no_any) { char oid[MAX_OID_SIZE]; size_t oid_size; int ret; unsigned critical = 0; unsigned check_obsolete_oids = 0; unsigned i; /* The check_obsolete_oids hack is because of certain very old CA certificates * around which instead of having the GNUTLS_KP_TLS_WWW_SERVER have some old * OIDs for that purpose. Assume these OIDs equal GNUTLS_KP_TLS_WWW_SERVER in * CA certs */ if (strcmp(purpose, GNUTLS_KP_TLS_WWW_SERVER) == 0) { unsigned ca_status; ret = gnutls_x509_crt_get_basic_constraints(cert, NULL, &ca_status, NULL); if (ret < 0) ca_status = 0; if (ca_status) check_obsolete_oids = 1; } for (i=0;;i++) { oid_size = sizeof(oid); ret = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid, &oid_size, &critical); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { if (i==0) { /* no key purpose in certificate, assume ANY */ return 1; } else { gnutls_assert(); break; } } else if (ret < 0) { gnutls_assert(); break; } if (check_obsolete_oids) { if (strcmp(oid, PURPOSE_NSSGC) == 0) { return 1; } else if (strcmp(oid, PURPOSE_VSGC) == 0) { return 1; } } if (strcmp(oid, purpose) == 0 || (no_any == 0 && strcmp(oid, GNUTLS_KP_ANY) == 0)) { return 1; } _gnutls_debug_log("looking for key purpose '%s', but have '%s'\n", purpose, oid); } return 0; }
/* * The gnutls_x509_crt_get_basic_constraints function isn't * available in GNUTLS 1.0.x branches. This isn't critical * though, since gnutls_certificate_verify_peers2 will do * pretty much the same check at runtime, so we can just * disable this code */ static int qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds, gnutls_x509_crt_t cert, const char *certFile, bool isServer, bool isCA, Error **errp) { int status; status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); trace_qcrypto_tls_creds_x509_check_basic_constraints( creds, certFile, status); if (status > 0) { /* It is a CA cert */ if (!isCA) { error_setg(errp, isServer ? "The certificate %s basic constraints show a CA, " "but we need one for a server" : "The certificate %s basic constraints show a CA, " "but we need one for a client", certFile); return -1; } } else if (status == 0) { /* It is not a CA cert */ if (isCA) { error_setg(errp, "The certificate %s basic constraints do not " "show a CA", certFile); return -1; } } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { /* Missing basicConstraints */ if (isCA) { error_setg(errp, "The certificate %s is missing basic constraints " "for a CA", certFile); return -1; } } else { /* General error */ error_setg(errp, "Unable to query certificate %s basic constraints: %s", certFile, gnutls_strerror(status)); return -1; } return 0; }
/* Checks if the issuer of a certificate is a * Certificate Authority, or if the certificate is the same * as the issuer (and therefore it doesn't need to be a CA). * * Returns true or false, if the issuer is a CA, * or not. */ static unsigned check_if_ca(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer, unsigned int *max_path, unsigned int flags) { gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t issuer_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_datum_t issuer_signature = { NULL, 0 }; int pathlen = -1, ret; unsigned result; unsigned int ca_status = 0; /* Check if the issuer is the same with the * certificate. This is added in order for trusted * certificates to be able to verify themselves. */ ret = _gnutls_x509_get_signed_data(issuer->cert, &issuer->der, "tbsCertificate", &issuer_signed_data); if (ret < 0) { gnutls_assert(); goto fail; } ret = _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", &cert_signed_data); if (ret < 0) { gnutls_assert(); goto fail; } ret = _gnutls_x509_get_signature(issuer->cert, "signature", &issuer_signature); if (ret < 0) { gnutls_assert(); goto fail; } ret = _gnutls_x509_get_signature(cert->cert, "signature", &cert_signature); if (ret < 0) { gnutls_assert(); goto fail; } /* If the subject certificate is the same as the issuer * return true. */ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) if (cert_signed_data.size == issuer_signed_data.size) { if ((memcmp (cert_signed_data.data, issuer_signed_data.data, cert_signed_data.size) == 0) && (cert_signature.size == issuer_signature.size) && (memcmp (cert_signature.data, issuer_signature.data, cert_signature.size) == 0)) { result = 1; goto cleanup; } } ret = gnutls_x509_crt_get_basic_constraints(issuer, NULL, &ca_status, &pathlen); if (ret < 0) { ca_status = 0; pathlen = -1; } if (ca_status != 0 && pathlen != -1) { if ((unsigned) pathlen < *max_path) *max_path = pathlen; } if (ca_status != 0) { result = 1; goto cleanup; } /* Handle V1 CAs that do not have a basicConstraint, but accept these certs only if the appropriate flags are set. */ else if ((ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) && ((flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT) || (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) && (gnutls_x509_crt_check_issuer(issuer, issuer) != 0)))) { gnutls_assert(); result = 1; goto cleanup; } else { gnutls_assert(); } fail: result = 0; cleanup: _gnutls_free_datum(&cert_signed_data); _gnutls_free_datum(&issuer_signed_data); _gnutls_free_datum(&cert_signature); _gnutls_free_datum(&issuer_signature); return result; }
/** Determines whether one certificate has been issued and signed by another * * @param crt Certificate to check the signature of * @param issuer Issuer's certificate * * @return TRUE if crt was signed and issued by issuer, otherwise FALSE * @TODO Modify this function to return a reason for invalidity? */ static gboolean x509_certificate_signed_by(PurpleCertificate * crt, PurpleCertificate * issuer) { gnutls_x509_crt_t crt_dat; gnutls_x509_crt_t issuer_dat; unsigned int verify; /* used to store result from GnuTLS verifier */ int ret; gchar *crt_id = NULL; gchar *issuer_id = NULL; g_return_val_if_fail(crt, FALSE); g_return_val_if_fail(issuer, FALSE); /* Verify that both certs are the correct scheme */ g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE); g_return_val_if_fail(issuer->scheme == &x509_gnutls, FALSE); /* TODO: check for more nullness? */ crt_dat = X509_GET_GNUTLS_DATA(crt); issuer_dat = X509_GET_GNUTLS_DATA(issuer); /* Ensure crt issuer matches the name on the issuer cert. */ ret = gnutls_x509_crt_check_issuer(crt_dat, issuer_dat); if (ret <= 0) { if (ret < 0) { purple_debug_error("gnutls/x509", "GnuTLS error %d while checking certificate issuer match.", ret); } else { gchar *crt_id, *issuer_id, *crt_issuer_id; crt_id = purple_certificate_get_unique_id(crt); issuer_id = purple_certificate_get_unique_id(issuer); crt_issuer_id = purple_certificate_get_issuer_unique_id(crt); purple_debug_info("gnutls/x509", "Certificate %s is issued by " "%s, which does not match %s.\n", crt_id ? crt_id : "(null)", crt_issuer_id ? crt_issuer_id : "(null)", issuer_id ? issuer_id : "(null)"); g_free(crt_id); g_free(issuer_id); g_free(crt_issuer_id); } /* The issuer is not correct, or there were errors */ return FALSE; } /* Check basic constraints extension (if it exists then the CA flag must be set to true, and it must exist for certs with version 3 or higher. */ ret = gnutls_x509_crt_get_basic_constraints(issuer_dat, NULL, NULL, NULL); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { if (gnutls_x509_crt_get_version(issuer_dat) >= 3) { /* Reject cert (no basic constraints and cert version is >= 3). */ gchar *issuer_id = purple_certificate_get_unique_id(issuer); purple_debug_info("gnutls/x509", "Rejecting cert because the " "basic constraints extension is missing from issuer cert " "for %s. The basic constraints extension is required on " "all version 3 or higher certs (this cert is version %d).", issuer_id ? issuer_id : "(null)", gnutls_x509_crt_get_version(issuer_dat)); g_free(issuer_id); return FALSE; } else { /* Allow cert (no basic constraints and cert version is < 3). */ purple_debug_info("gnutls/x509", "Basic constraint extension is " "missing from issuer cert for %s. Allowing this because " "the cert is version %d and the basic constraints " "extension is only required for version 3 or higher " "certs.", issuer_id ? issuer_id : "(null)", gnutls_x509_crt_get_version(issuer_dat)); } } else if (ret <= 0) { /* Reject cert (CA flag is false in basic constraints). */ gchar *issuer_id = purple_certificate_get_unique_id(issuer); purple_debug_info("gnutls/x509", "Rejecting cert because the CA flag " "is set to false in the basic constraints extension for " "issuer cert %s. ret=%d\n", issuer_id ? issuer_id : "(null)", ret); g_free(issuer_id); return FALSE; } /* Now, check the signature */ /* The second argument is a ptr to an array of "trusted" issuer certs, but we're only using one trusted one */ ret = gnutls_x509_crt_verify(crt_dat, &issuer_dat, 1, /* Permit signings by X.509v1 certs (Verisign and possibly others have root certificates that predate the current standard) */ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &verify); if (ret != 0) { purple_debug_error("gnutls/x509", "Attempted certificate verification caused a GnuTLS error code %d. I will just say the signature is bad, but you should look into this.\n", ret); return FALSE; } #ifdef HAVE_GNUTLS_CERT_INSECURE_ALGORITHM if (verify & GNUTLS_CERT_INSECURE_ALGORITHM) { /* * A certificate in the chain is signed with an insecure * algorithm. Put a warning into the log to make this error * perfectly clear as soon as someone looks at the debug log is * generated. */ crt_id = purple_certificate_get_unique_id(crt); issuer_id = purple_certificate_get_issuer_unique_id(crt); purple_debug_warning("gnutls/x509", "Insecure hash algorithm used by %s to sign %s\n", issuer_id, crt_id); } #endif if (verify & GNUTLS_CERT_INVALID) { /* Signature didn't check out, but at least there were no errors*/ if (!crt_id) crt_id = purple_certificate_get_unique_id(crt); if (!issuer_id) issuer_id = purple_certificate_get_issuer_unique_id(crt); purple_debug_error("gnutls/x509", "Bad signature from %s on %s\n", issuer_id, crt_id); g_free(crt_id); g_free(issuer_id); return FALSE; } /* if (ret, etc.) */ /* If we got here, the signature is good */ return TRUE; }