static void print_resp(gnutls_buffer_st * str, gnutls_ocsp_resp_t resp, gnutls_ocsp_print_formats_t format) { int ret; unsigned indx; ret = gnutls_ocsp_resp_get_status(resp); if (ret < 0) { addf(str, "error: ocsp_resp_get_status: %s\n", gnutls_strerror(ret)); return; } adds(str, "\tResponse Status: "); switch (ret) { case GNUTLS_OCSP_RESP_SUCCESSFUL: adds(str, "Successful\n"); break; case GNUTLS_OCSP_RESP_MALFORMEDREQUEST: adds(str, "malformedRequest\n"); return; case GNUTLS_OCSP_RESP_INTERNALERROR: adds(str, "internalError\n"); return; case GNUTLS_OCSP_RESP_TRYLATER: adds(str, "tryLater\n"); return; case GNUTLS_OCSP_RESP_SIGREQUIRED: adds(str, "sigRequired\n"); return; case GNUTLS_OCSP_RESP_UNAUTHORIZED: adds(str, "unauthorized\n"); return; default: adds(str, "unknown\n"); return; } { gnutls_datum_t oid; ret = gnutls_ocsp_resp_get_response(resp, &oid, NULL); if (ret < 0) { addf(str, "error: get_response: %s\n", gnutls_strerror(ret)); return; } adds(str, "\tResponse Type: "); #define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1" if (oid.size == sizeof(OCSP_BASIC) && memcmp(oid.data, OCSP_BASIC, oid.size) == 0) { adds(str, "Basic OCSP Response\n"); gnutls_free(oid.data); } else { addf(str, "Unknown response type (%.*s)\n", oid.size, oid.data); gnutls_free(oid.data); return; } } /* Version. */ { int version = gnutls_ocsp_resp_get_version(resp); if (version < 0) addf(str, "error: get_version: %s\n", gnutls_strerror(version)); else addf(str, _("\tVersion: %d\n"), version); } /* responderID */ { gnutls_datum_t dn; ret = gnutls_ocsp_resp_get_responder(resp, &dn); if (ret < 0 || dn.data == NULL) { if (dn.data == 0) { ret = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &dn); if (ret >= 0) { addf(str, _("\tResponder Key ID: ")); _gnutls_buffer_hexprint(str, dn.data, dn.size); adds(str, "\n"); } gnutls_free(dn.data); } else { addf(str, "error: get_dn: %s\n", gnutls_strerror(ret)); } } else { if (dn.data != NULL) { addf(str, _("\tResponder ID: %.*s\n"), dn.size, dn.data); gnutls_free(dn.data); } } } { char s[42]; size_t max = sizeof(s); struct tm t; time_t tim = gnutls_ocsp_resp_get_produced(resp); if (tim == (time_t) - 1) addf(str, "error: ocsp_resp_get_produced\n"); else if (gmtime_r(&tim, &t) == NULL) addf(str, "error: gmtime_r (%ld)\n", (unsigned long) tim); else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0) addf(str, "error: strftime (%ld)\n", (unsigned long) tim); else addf(str, _("\tProduced At: %s\n"), s); } addf(str, "\tResponses:\n"); for (indx = 0;; indx++) { gnutls_digest_algorithm_t digest; gnutls_datum_t in, ik, sn; unsigned int cert_status; time_t this_update; time_t next_update; time_t revocation_time; unsigned int revocation_reason; ret = gnutls_ocsp_resp_get_single(resp, indx, &digest, &in, &ik, &sn, &cert_status, &this_update, &next_update, &revocation_time, &revocation_reason); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; addf(str, "\t\tCertificate ID:\n"); if (ret != GNUTLS_E_SUCCESS) { addf(str, "error: get_singleresponse: %s\n", gnutls_strerror(ret)); continue; } addf(str, "\t\t\tHash Algorithm: %s\n", _gnutls_digest_get_name(hash_to_entry(digest))); adds(str, "\t\t\tIssuer Name Hash: "); _gnutls_buffer_hexprint(str, in.data, in.size); adds(str, "\n"); adds(str, "\t\t\tIssuer Key Hash: "); _gnutls_buffer_hexprint(str, ik.data, ik.size); adds(str, "\n"); adds(str, "\t\t\tSerial Number: "); _gnutls_buffer_hexprint(str, sn.data, sn.size); adds(str, "\n"); gnutls_free(in.data); gnutls_free(ik.data); gnutls_free(sn.data); { const char *p = NULL; switch (cert_status) { case GNUTLS_OCSP_CERT_GOOD: p = "good"; break; case GNUTLS_OCSP_CERT_REVOKED: p = "revoked"; break; case GNUTLS_OCSP_CERT_UNKNOWN: p = "unknown"; break; default: addf(str, "\t\tCertificate Status: unexpected value %d\n", cert_status); break; } if (p) addf(str, "\t\tCertificate Status: %s\n", p); } /* XXX revocation reason */ if (cert_status == GNUTLS_OCSP_CERT_REVOKED) { char s[42]; size_t max = sizeof(s); struct tm t; if (revocation_time == (time_t) - 1) addf(str, "error: revocation_time\n"); else if (gmtime_r(&revocation_time, &t) == NULL) addf(str, "error: gmtime_r (%ld)\n", (unsigned long) revocation_time); else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0) addf(str, "error: strftime (%ld)\n", (unsigned long) revocation_time); else addf(str, _("\t\tRevocation time: %s\n"), s); } { char s[42]; size_t max = sizeof(s); struct tm t; if (this_update == (time_t) - 1) addf(str, "error: this_update\n"); else if (gmtime_r(&this_update, &t) == NULL) addf(str, "error: gmtime_r (%ld)\n", (unsigned long) this_update); else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0) addf(str, "error: strftime (%ld)\n", (unsigned long) this_update); else addf(str, _("\t\tThis Update: %s\n"), s); } { char s[42]; size_t max = sizeof(s); struct tm t; if (next_update != (time_t) - 1) { if (gmtime_r(&next_update, &t) == NULL) addf(str, "error: gmtime_r (%ld)\n", (unsigned long) next_update); else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0) addf(str, "error: strftime (%ld)\n", (unsigned long) next_update); else addf(str, _("\t\tNext Update: %s\n"), s); } } /* XXX singleRequestExtensions */ } adds(str, "\tExtensions:\n"); for (indx = 0;; indx++) { gnutls_datum_t oid; unsigned int critical; gnutls_datum_t data; ret = gnutls_ocsp_resp_get_extension(resp, indx, &oid, &critical, &data); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; else if (ret != GNUTLS_E_SUCCESS) { addf(str, "error: get_extension: %s\n", gnutls_strerror(ret)); continue; } if (oid.size == sizeof(GNUTLS_OCSP_NONCE) && memcmp(oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) { gnutls_datum_t nonce; unsigned int ncrit; ret = gnutls_ocsp_resp_get_nonce(resp, &ncrit, &nonce); if (ret != GNUTLS_E_SUCCESS) { addf(str, "error: get_nonce: %s\n", gnutls_strerror(ret)); } else { addf(str, "\t\tNonce%s: ", ncrit ? " (critical)" : ""); _gnutls_buffer_hexprint(str, nonce.data, nonce.size); adds(str, "\n"); gnutls_free(nonce.data); } } else { addf(str, "\t\tUnknown extension %s (%s):\n", oid.data, critical ? "critical" : "not critical"); adds(str, _("\t\t\tASCII: ")); _gnutls_buffer_asciiprint(str, (char *) data.data, data.size); addf(str, "\n"); adds(str, _("\t\t\tHexdump: ")); _gnutls_buffer_hexprint(str, (char *) data.data, data.size); adds(str, "\n"); } gnutls_free(oid.data); gnutls_free(data.data); } /* Signature. */ if (format == GNUTLS_OCSP_PRINT_FULL) { gnutls_datum_t sig; ret = gnutls_ocsp_resp_get_signature_algorithm(resp); if (ret < 0) addf(str, "error: get_signature_algorithm: %s\n", gnutls_strerror(ret)); else { const char *name = gnutls_sign_algorithm_get_name(ret); if (name == NULL) name = _("unknown"); addf(str, _("\tSignature Algorithm: %s\n"), name); } if (ret != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure(ret) == 0) { adds(str, _("warning: signed using a broken signature " "algorithm that can be forged.\n")); } ret = gnutls_ocsp_resp_get_signature(resp, &sig); if (ret < 0) addf(str, "error: get_signature: %s\n", gnutls_strerror(ret)); else { adds(str, _("\tSignature:\n")); _gnutls_buffer_hexdump(str, sig.data, sig.size, "\t\t"); gnutls_free(sig.data); } } /* certs */ if (format == GNUTLS_OCSP_PRINT_FULL) { gnutls_x509_crt_t *certs; size_t ncerts, i; gnutls_datum_t out; ret = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts); if (ret < 0) addf(str, "error: get_certs: %s\n", gnutls_strerror(ret)); else { if (ncerts > 0) addf(str, "\tAdditional certificates:\n"); for (i = 0; i < ncerts; i++) { size_t s = 0; ret = gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL, &out); if (ret < 0) addf(str, "error: crt_print: %s\n", gnutls_strerror(ret)); else { addf(str, "%.*s", out.size, out.data); gnutls_free(out.data); } ret = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, NULL, &s); if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) addf(str, "error: crt_export: %s\n", gnutls_strerror(ret)); else { out.data = gnutls_malloc(s); if (out.data == NULL) addf(str, "error: malloc: %s\n", gnutls_strerror (GNUTLS_E_MEMORY_ERROR)); else { ret = gnutls_x509_crt_export (certs[i], GNUTLS_X509_FMT_PEM, out.data, &s); if (ret < 0) addf(str, "error: crt_export: %s\n", gnutls_strerror (ret)); else { out.size = s; addf(str, "%.*s", out.size, out.data); } gnutls_free(out.data); } } gnutls_x509_crt_deinit(certs[i]); } gnutls_free(certs); } } }
/* * Verifies the given certificate against a certificate list of * trusted CAs. * * Returns only 0 or 1. If 1 it means that the certificate * was successfuly verified. * * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. * * Output will hold some extra information about the verification * procedure. */ static unsigned verify_crt(gnutls_x509_crt_t cert, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, unsigned int *output, verify_state_st *vparams, unsigned end_cert) { gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; int issuer_version, hash_algo; unsigned result = 1; const mac_entry_st * me; unsigned int out = 0, usage; int sigalg, ret; if (output) *output = 0; if (vparams->max_path == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); /* bail immediately, to avoid inconistency */ goto cleanup; } vparams->max_path--; if (tcas_size >= 1) issuer = find_issuer(cert, trusted_cas, tcas_size); ret = _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", &cert_signed_data); if (ret < 0) { MARK_INVALID(0); cert_signed_data.data = NULL; } ret = _gnutls_x509_get_signature(cert->cert, "signature", &cert_signature); if (ret < 0) { MARK_INVALID(0); cert_signature.data = NULL; } ret = _gnutls_x509_get_signature_algorithm(cert->cert, "signatureAlgorithm.algorithm"); if (ret < 0) { MARK_INVALID(0); } sigalg = ret; /* issuer is not in trusted certificate * authorities. */ if (issuer == NULL) { MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_FOUND); } else { if (vparams->nc != NULL) { /* append the issuer's constraints */ ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc, GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL); if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto nc_done; } /* only check name constraints in server certificates, not CAs */ if (end_cert != 0) { ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DNSNAME, cert); if (ret == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto nc_done; } ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_RFC822NAME, cert); if (ret == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto nc_done; } ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DN, cert); if (ret == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto nc_done; } ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_URI, cert); if (ret == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto nc_done; } ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_IPADDRESS, cert); if (ret == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto nc_done; } } } nc_done: if (vparams->tls_feat != NULL) { /* append the issuer's constraints */ ret = gnutls_x509_crt_get_tlsfeatures(issuer, vparams->tls_feat, GNUTLS_EXT_FLAG_APPEND, NULL); if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto feat_done; } ret = gnutls_x509_tlsfeatures_check_crt(vparams->tls_feat, cert); if (ret == 0) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); goto feat_done; } } feat_done: issuer_version = gnutls_x509_crt_get_version(issuer); if (issuer_version < 0) { MARK_INVALID(0); } else if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) && ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) || issuer_version != 1)) { if (check_if_ca(cert, issuer, &vparams->max_path, flags) != 1) { MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_CA); } ret = gnutls_x509_crt_get_key_usage(issuer, &usage, NULL); if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { if (ret < 0) { MARK_INVALID(0); } else if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); } } } if (sigalg >= 0) { hash_algo = gnutls_sign_get_hash_algorithm(sigalg); me = mac_to_entry(hash_algo); } else { me = NULL; } if (me == NULL) { MARK_INVALID(0); } else if (cert_signed_data.data != NULL && cert_signature.data != NULL) { ret = _gnutls_x509_verify_data(me, &cert_signed_data, &cert_signature, issuer); if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) { MARK_INVALID(GNUTLS_CERT_SIGNATURE_FAILURE); } else if (ret < 0) { MARK_INVALID(0); } } } /* we always check the issuer for unsupported critical extensions */ if (issuer && check_for_unknown_exts(issuer) != 0) { if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) { MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS); } } /* we only check the end-certificate for critical extensions; that * way do not perform this check twice on the certificates when * verifying a large list */ if (end_cert && check_for_unknown_exts(cert) != 0) { if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) { MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS); } } if (sigalg >= 0) { if (is_level_acceptable(cert, issuer, sigalg, flags) == 0) { MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM); } /* If the certificate is not self signed check if the algorithms * used are secure. If the certificate is self signed it doesn't * really matter. */ if (gnutls_sign_is_secure(sigalg) == 0 && _gnutls_is_broken_sig_allowed(sigalg, flags) == 0 && is_issuer(cert, cert) == 0) { MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM); } } /* Check activation/expiration times */ if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { /* check the time of the issuer first */ if (issuer != NULL && !(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) { out |= check_time_status(issuer, vparams->now); if (out != 0) { gnutls_assert(); result = 0; } } out |= check_time_status(cert, vparams->now); if (out != 0) { gnutls_assert(); result = 0; } } cleanup: if (output) *output |= out; if (vparams->func) { if (result == 0) { out |= GNUTLS_CERT_INVALID; } vparams->func(cert, issuer, NULL, out); } _gnutls_free_datum(&cert_signed_data); _gnutls_free_datum(&cert_signature); return result; }