/* * Verifies the given certificate again 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. Issuer will hold the actual issuer from the trusted list. */ static int _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, unsigned int *output, gnutls_x509_crt_t * _issuer, time_t now, gnutls_verify_output_function func) { gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; int issuer_version, result, hash_algo; unsigned int out = 0; if (output) *output = 0; if (tcas_size >= 1) issuer = find_issuer (cert, trusted_cas, tcas_size); else { gnutls_assert (); out = GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; if (output) *output |= out; result = 0; goto cleanup; } /* issuer is not in trusted certificate * authorities. */ if (issuer == NULL) { out = GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; if (output) *output |= out; gnutls_assert (); result = 0; goto cleanup; } if (_issuer != NULL) *_issuer = issuer; issuer_version = gnutls_x509_crt_get_version (issuer); if (issuer_version < 0) { gnutls_assert (); return issuer_version; } 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, flags) == 0) { gnutls_assert (); out = GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID; if (output) *output |= out; result = 0; goto cleanup; } } result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", &cert_signed_data); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature_algorithm(cert->cert, "signatureAlgorithm.algorithm"); if (result < 0) { gnutls_assert (); goto cleanup; } hash_algo = gnutls_sign_get_hash_algorithm(result); result = _gnutls_x509_verify_data (hash_algo, &cert_signed_data, &cert_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { gnutls_assert (); out |= GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNATURE_FAILURE; /* error. ignore it */ if (output) *output |= out; result = 0; } else if (result < 0) { gnutls_assert(); goto cleanup; } /* 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 (is_issuer (cert, cert) == 0) { int sigalg; sigalg = gnutls_x509_crt_get_signature_algorithm (cert); if (((sigalg == GNUTLS_SIGN_RSA_MD2) && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || ((sigalg == GNUTLS_SIGN_RSA_MD5) && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) { out = GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID; if (output) *output |= out; result = 0; } } /* Check activation/expiration times */ if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { /* check the time of the issuer first */ if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) { out |= check_time (issuer, now); if (out != 0) { result = 0; if (output) *output |= out; } } out |= check_time (cert, now); if (out != 0) { result = 0; if (output) *output |= out; } } cleanup: if (result >= 0 && func) func(cert, issuer, NULL, out); _gnutls_free_datum (&cert_signed_data); _gnutls_free_datum (&cert_signature); return result; }
int X509_verify_cert(X509_STORE_CTX *ctx) { X509 *x,*xtmp,*chain_ss=NULL; X509_NAME *xn; int depth,i,ok=0; int num; int (*cb)(); STACK_OF(X509) *sktmp=NULL; if (ctx->cert == NULL) { X509err(X509_F_X509_VERIFY_CERT,X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); return -1; } cb=ctx->verify_cb; /* first we make sure the chain we are going to build is * present and that the first entry is in place */ if (ctx->chain == NULL) { if ( ((ctx->chain=sk_X509_new_null()) == NULL) || (!sk_X509_push(ctx->chain,ctx->cert))) { X509err(X509_F_X509_VERIFY_CERT,ERR_R_MALLOC_FAILURE); goto end; } CRYPTO_add(&ctx->cert->references,1,CRYPTO_LOCK_X509); ctx->last_untrusted=1; } /* We use a temporary STACK so we can chop and hack at it */ if (ctx->untrusted != NULL && (sktmp=sk_X509_dup(ctx->untrusted)) == NULL) { X509err(X509_F_X509_VERIFY_CERT,ERR_R_MALLOC_FAILURE); goto end; } num=sk_X509_num(ctx->chain); x=sk_X509_value(ctx->chain,num-1); depth=ctx->depth; for (;;) { /* If we have enough, we break */ if (depth < num) break; /* FIXME: If this happens, we should take * note of it and, if appropriate, use the * X509_V_ERR_CERT_CHAIN_TOO_LONG error * code later. */ /* If we are self signed, we break */ xn=X509_get_issuer_name(x); if (ctx->check_issued(ctx, x,x)) break; /* If we were passed a cert chain, use it first */ if (ctx->untrusted != NULL) { xtmp=find_issuer(ctx, sktmp,x); if (xtmp != NULL) { if (!sk_X509_push(ctx->chain,xtmp)) { X509err(X509_F_X509_VERIFY_CERT,ERR_R_MALLOC_FAILURE); goto end; } CRYPTO_add(&xtmp->references,1,CRYPTO_LOCK_X509); sk_X509_delete_ptr(sktmp,xtmp); ctx->last_untrusted++; x=xtmp; num++; /* reparse the full chain for * the next one */ continue; } } break; } /* at this point, chain should contain a list of untrusted * certificates. We now need to add at least one trusted one, * if possible, otherwise we complain. */ /* Examine last certificate in chain and see if it * is self signed. */ i=sk_X509_num(ctx->chain); x=sk_X509_value(ctx->chain,i-1); xn = X509_get_subject_name(x); if (ctx->check_issued(ctx, x, x)) { /* we have a self signed certificate */ if (sk_X509_num(ctx->chain) == 1) { /* We have a single self signed certificate: see if * we can find it in the store. We must have an exact * match to avoid possible impersonation. */ ok = ctx->get_issuer(&xtmp, ctx, x); if ((ok <= 0) || X509_cmp(x, xtmp)) { ctx->error=X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; ctx->current_cert=x; ctx->error_depth=i-1; if (ok == 1) X509_free(xtmp); ok=cb(0,ctx); if (!ok) goto end; } else { /* We have a match: replace certificate with store version * so we get any trust settings. */ X509_free(x); x = xtmp; sk_X509_set(ctx->chain, i - 1, x); ctx->last_untrusted=0; } } else { /* extract and save self signed certificate for later use */ chain_ss=sk_X509_pop(ctx->chain); ctx->last_untrusted--; num--; x=sk_X509_value(ctx->chain,num-1); } } /* We now lookup certs from the certificate store */ for (;;) { /* If we have enough, we break */ if (depth < num) break; /* If we are self signed, we break */ xn=X509_get_issuer_name(x); if (ctx->check_issued(ctx,x,x)) break; ok = ctx->get_issuer(&xtmp, ctx, x); if (ok < 0) return ok; if (ok == 0) break; x = xtmp; if (!sk_X509_push(ctx->chain,x)) { X509_free(xtmp); X509err(X509_F_X509_VERIFY_CERT,ERR_R_MALLOC_FAILURE); return 0; } num++; } /* we now have our chain, lets check it... */ xn=X509_get_issuer_name(x); /* Is last certificate looked up self signed? */ if (!ctx->check_issued(ctx,x,x)) { if ((chain_ss == NULL) || !ctx->check_issued(ctx, x, chain_ss)) { if (ctx->last_untrusted >= num) ctx->error=X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; else ctx->error=X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; ctx->current_cert=x; } else { sk_X509_push(ctx->chain,chain_ss); num++; ctx->last_untrusted=num; ctx->current_cert=chain_ss; ctx->error=X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; chain_ss=NULL; } ctx->error_depth=num-1; ok=cb(0,ctx); if (!ok) goto end; } /* We have the chain complete: now we need to check its purpose */ ok = check_chain_extensions(ctx); if (!ok) goto end; /* The chain extensions are OK: check trust */ if (ctx->trust > 0) ok = check_trust(ctx); if (!ok) goto end; /* We may as well copy down any DSA parameters that are required */ X509_get_pubkey_parameters(NULL,ctx->chain); /* Check revocation status: we do this after copying parameters * because they may be needed for CRL signature verification. */ ok = ctx->check_revocation(ctx); if(!ok) goto end; /* At this point, we have a chain and just need to verify it */ if (ctx->verify != NULL) ok=ctx->verify(ctx); else ok=internal_verify(ctx); if (0) { end: X509_get_pubkey_parameters(NULL,ctx->chain); } if (sktmp != NULL) sk_X509_free(sktmp); if (chain_ss != NULL) X509_free(chain_ss); return ok; }
/* * Verifies the given certificate again 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. Issuer will hold the actual issuer from the trusted list. */ static int _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, unsigned int *output, gnutls_x509_crt_t * _issuer) { gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; int issuer_version, result; if (output) *output = 0; if (tcas_size >= 1) issuer = find_issuer (cert, trusted_cas, tcas_size); else { gnutls_assert (); if (output) *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; return 0; } /* issuer is not in trusted certificate * authorities. */ if (issuer == NULL) { if (output) *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; gnutls_assert (); return 0; } if (_issuer != NULL) *_issuer = issuer; issuer_version = gnutls_x509_crt_get_version (issuer); if (issuer_version < 0) { gnutls_assert (); return issuer_version; } 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, flags) == 0) { gnutls_assert (); if (output) *output |= GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID; return 0; } } result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", &cert_signed_data); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_verify_signature (&cert_signed_data, NULL, &cert_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { gnutls_assert (); /* error. ignore it */ if (output) *output |= GNUTLS_CERT_INVALID; result = 0; } else if (result < 0) { gnutls_assert(); goto cleanup; } /* 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 (is_issuer (cert, cert) == 0) { int sigalg; sigalg = gnutls_x509_crt_get_signature_algorithm (cert); if (((sigalg == GNUTLS_SIGN_RSA_MD2) && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || ((sigalg == GNUTLS_SIGN_RSA_MD5) && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) { if (output) *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID; result = 0; } } cleanup: _gnutls_free_datum (&cert_signed_data); _gnutls_free_datum (&cert_signature); return result; }
/* * 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; }
/* Return the certificate chain sent by the peer, or NULL on error. */ static ne_ssl_certificate *make_peers_chain(gnutls_session sock, gnutls_certificate_credentials crd) { ne_ssl_certificate *current = NULL, *top = NULL; const gnutls_datum *certs; unsigned int n, count; ne_ssl_certificate *cert; certs = gnutls_certificate_get_peers(sock, &count); if (!certs) { return NULL; } NE_DEBUG(NE_DBG_SSL, "ssl: Got %u certs in peer chain.\n", count); for (n = 0; n < count; n++) { gnutls_x509_crt x5; if (gnutls_x509_crt_init(&x5) || gnutls_x509_crt_import(x5, &certs[n], GNUTLS_X509_FMT_DER)) { if (top) { ne_ssl_cert_free(top); } return NULL; } cert = populate_cert(ne_calloc(sizeof *cert), x5); if (top == NULL) { current = top = cert; } else { current->issuer = cert; current = cert; } } #ifdef HAVE_GNUTLS_CERTIFICATE_GET_X509_CAS /* GnuTLS only returns the peers which were *sent* by the server * in the Certificate list during the handshake. Fill in the * complete chain manually against the certs we trust: */ if (current->issuer == NULL) { gnutls_x509_crt issuer; gnutls_x509_crt *ca_list; unsigned int num_cas; gnutls_certificate_get_x509_cas(crd, &ca_list, &num_cas); do { /* Look up the issuer. */ issuer = find_issuer(ca_list, num_cas, current->subject); if (issuer) { issuer = x509_crt_copy(issuer); cert = populate_cert(ne_calloc(sizeof *cert), issuer); /* Check that the issuer does not match the current * cert. */ if (ne_ssl_cert_cmp(current, cert)) { current = current->issuer = cert; } else { ne_ssl_cert_free(cert); issuer = NULL; } } } while (issuer); } #endif return top; }