int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc, server_rec *s, conn_rec *c, apr_pool_t *pool) { X509 *cert = X509_STORE_CTX_get_current_cert(ctx); apr_pool_t *vpool; int rv; if (!cert) { /* starting with OpenSSL 1.0, X509_STORE_CTX_get_current_cert() * may yield NULL. Return early, but leave the ctx error as is. */ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "No cert available to check with OCSP"); return 1; } else if (cert->valid && X509_check_issued(cert,cert) == X509_V_OK) { /* don't do OCSP checking for valid self-issued certs */ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "Skipping OCSP check for valid self-issued cert"); X509_STORE_CTX_set_error(ctx, X509_V_OK); return 1; } /* Create a temporary pool to constrain memory use (the passed-in * pool may be e.g. a connection pool). */ apr_pool_create(&vpool, pool); rv = verify_ocsp_status(cert, ctx, c, sc, s, vpool); apr_pool_destroy(vpool); /* Propagate the verification status back to the passed-in * context. */ switch (rv) { case V_OCSP_CERTSTATUS_GOOD: X509_STORE_CTX_set_error(ctx, X509_V_OK); break; case V_OCSP_CERTSTATUS_REVOKED: X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); break; case V_OCSP_CERTSTATUS_UNKNOWN: /* correct error code for application errors? */ X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION); break; } return rv == V_OCSP_CERTSTATUS_GOOD; }
int verify_callback(int ok, X509_STORE_CTX *ctx) { char buf[256]; X509 *err_cert; int err,depth; err_cert=X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256); BIO_printf(bio_err,"depth=%d %s\n",depth,buf); if (!ok) { BIO_printf(bio_err,"verify error:num=%d:%s\n",err, X509_verify_cert_error_string(err)); if (depth < 6) { ok=1; X509_STORE_CTX_set_error(ctx,X509_V_OK); } else { ok=0; X509_STORE_CTX_set_error(ctx,X509_V_ERR_CERT_CHAIN_TOO_LONG); } } switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),buf,256); BIO_printf(bio_err,"issuer= %s\n",buf); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: BIO_printf(bio_err,"notBefore="); ASN1_UTCTIME_print(bio_err,X509_get_notBefore(ctx->current_cert)); BIO_printf(bio_err,"\n"); break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: BIO_printf(bio_err,"notAfter="); ASN1_UTCTIME_print(bio_err,X509_get_notAfter(ctx->current_cert)); BIO_printf(bio_err,"\n"); break; } BIO_printf(bio_err,"verify return:%d\n",ok); return(ok); }
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { printf("*** Verify callback function called\n"); char buf[256]; X509 *err_cert; int err, depth; SSL *ssl; err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); //mydata = SSL_get_ex_data(ssl, mydata_index); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); /* * Catch a too long certificate chain. The depth limit set using * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so * that whenever the "depth>verify_depth" condition is met, we * have violated the limit and want to log this error condition. * We must do it here, because the CHAIN_TOO_LONG error would not * be found explicitly; only errors introduced by cutting off the * additional certificates would be logged. */ #if 0 if (depth > mydata->verify_depth) { preverify_ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(ctx, err); } #endif if (!preverify_ok) { printf("verify error:num=%d:%s:depth=%d:%s\n", err, X509_verify_cert_error_string(err), depth, buf); } printf("depth=%d:%s\n", depth, buf); /* * At this point, err contains the last verification error. We can use * it for something special */ if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) { X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); printf("issuer= %s\n", buf); } #if 0 if (mydata->always_continue) return 1; else #endif return preverify_ok; }
NOEXPORT int cert_check_local(X509_STORE_CTX *callback_ctx) { X509 *cert=X509_STORE_CTX_get_current_cert(callback_ctx); X509_OBJECT obj; #if OPENSSL_VERSION_NUMBER>=0x10000000L STACK_OF(X509) *sk; int i; sk=X509_STORE_get1_certs(callback_ctx, X509_get_subject_name(cert)); if(sk) { for(i=0; i<sk_X509_num(sk); i++) if(compare_pubkeys(cert, sk_X509_value(sk, i))) { sk_X509_pop_free(sk, X509_free); return 1; /* accept */ } sk_X509_pop_free(sk, X509_free); } #endif /* pre-1.0.0 API only returns a single matching certificate */ if(X509_STORE_get_by_subject(callback_ctx, X509_LU_X509, X509_get_subject_name(cert), &obj)==1 && compare_pubkeys(cert, obj.data.x509)) return 1; /* accept */ s_log(LOG_WARNING, "CERT: Certificate not found in local repository"); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_CERT_REJECTED); return 0; /* reject */ }
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { int ret = preverify_ok; /* determine the status for the current cert */ X509_STORE_CTX_get_current_cert(ctx); int err = X509_STORE_CTX_get_error(ctx); int depth = X509_STORE_CTX_get_error_depth(ctx); /* conjure the stream & context to use */ SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); SSLSocket *stream = (SSLSocket*)SSL_get_ex_data(ssl, SSLSocket::GetSSLExDataIndex()); /* if allow_self_signed is set, make sure that verification succeeds */ if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && stream->getContext()["allow_self_signed"].toBoolean()) { ret = 1; } /* check the depth */ Variant vdepth = stream->getContext()["verify_depth"]; if (vdepth.toBoolean() && depth > vdepth.toInt64()) { ret = 0; X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); } return ret; }
int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) { char buf[CCERT_BUFSIZ]; X509 *cert; int err; int depth; int max_depth; SSL *con; TLS_SESS_STATE *TLScontext; /* May be NULL as of OpenSSL 1.0, thanks for the API change! */ cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); TLScontext = SSL_get_ex_data(con, TLScontext_index); depth = X509_STORE_CTX_get_error_depth(ctx); /* Don't log the internal root CA unless there's an unexpected error. */ if (ok && TLScontext->tadepth > 0 && depth > TLScontext->tadepth) return (1); /* * Certificate chain depth limit violations are mis-reported by the * OpenSSL library, from SSL_CTX_set_verify(3): * * The certificate verification depth set with SSL[_CTX]_verify_depth() * stops the verification at a certain depth. The error message produced * will be that of an incomplete certificate chain and not * X509_V_ERR_CERT_CHAIN_TOO_LONG as may be expected. * * We set a limit that is one higher than the user requested limit. If this * higher limit is reached, we raise an error even a trusted root CA is * present at this depth. This disambiguates trust chain truncation from * an incomplete trust chain. */ max_depth = SSL_get_verify_depth(con) - 1; /* * We never terminate the SSL handshake in the verification callback, * rather we allow the TLS handshake to continue, but mark the session as * unverified. The application is responsible for closing any sessions * with unverified credentials. */ if (max_depth >= 0 && depth > max_depth) { X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_CERT_CHAIN_TOO_LONG); ok = 0; } if (ok == 0) update_error_state(TLScontext, depth, cert, err); if (TLScontext->log_mask & TLS_LOG_VERBOSE) { if (cert) X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); else strcpy(buf, "<unknown>"); msg_info("%s: depth=%d verify=%d subject=%s", TLScontext->namaddr, depth, ok, printable(buf, '?')); } return (1); }
int server_verify_cert(X509_STORE_CTX *xs, void *ctx) { log_debug("[X509] [VERIFY] hello !"); X509_STORE_CTX_set_error(xs, X509_V_OK); return 1; }
/** * OpenSSL Certificate verification callback * * Called for each certificate in a chain being verified. OpenSSL * calls this in deepest first order from the certificate authority to * the peer certificate at position 0. * * Each certificate is stored in the fetch context the first time it * is presented. If an error is encountered it is only returned for * the peer certificate at position 0 allowing the enumeration of the * entire chain not stopping early at the depth of the erroring * certificate. * * \param verify_ok 0 if the caller has already determined the chain * has errors else 1 * \param x509_ctx certificate context being verified * \return 1 to indicate verification should continue and 0 to indicate * verification should stop. */ static int fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx) { int depth; struct curl_fetch_info *fetch; depth = X509_STORE_CTX_get_error_depth(x509_ctx); fetch = X509_STORE_CTX_get_app_data(x509_ctx); /* record the max depth */ if (depth > fetch->cert_depth) { fetch->cert_depth = depth; } /* certificate chain is excessively deep so fail verification */ if (depth >= MAX_CERTS) { X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); return 0; } /* save the certificate by incrementing the reference count and * keeping a pointer. */ if (!fetch->cert_data[depth].cert) { fetch->cert_data[depth].cert = X509_STORE_CTX_get_current_cert(x509_ctx); fetch->cert_data[depth].cert->references++; fetch->cert_data[depth].err = X509_STORE_CTX_get_error(x509_ctx); } /* allow certificate chain to be completed */ if (depth > 0) { verify_ok = 1; } else { /* search for deeper certificates in the chain with errors */ for (depth = fetch->cert_depth; depth > 0; depth--) { if (fetch->cert_data[depth].err != 0) { /* error in previous certificate so fail verification */ verify_ok = 0; X509_STORE_CTX_set_error(x509_ctx, fetch->cert_data[depth].err); } } } return verify_ok; }
static int verify_callback(int ok, X509_STORE_CTX *ctx) { verify_context *vctx; X509 *err_cert; int err, depth; vctx = (verify_context *)ctx; err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); X509_NAME_oneline(X509_get_subject_name(err_cert), vctx->certdesc, sizeof(vctx->certdesc)); if (!ok && depth >= 6) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); } switch (ctx->error) { case X509_V_ERR_INVALID_PURPOSE: case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: err = X509_V_OK; X509_STORE_CTX_set_error(ctx, X509_V_OK); ok = 1; break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: /* In this case, don't reset err to X509_V_OK, so that it can be reported, although we do return 1, so that the digest will still be checked */ ok = 1; break; default: break; } if (ok && vctx->err == X509_V_OK) vctx->err = err; return ok; }
int ossl_verify_cb(int ok, X509_STORE_CTX *ctx) { VALUE proc, rctx, ret; struct ossl_verify_cb_args args; int state = 0; proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_verify_cb_idx); if ((void*)proc == 0) proc = (VALUE)X509_STORE_get_ex_data(ctx->ctx, ossl_verify_cb_idx); if ((void*)proc == 0) return ok; if (!NIL_P(proc)) { ret = Qfalse; rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, (VALUE)ctx, &state); if (state) { rb_set_errinfo(Qnil); rb_warn("StoreContext initialization failure"); } else { args.proc = proc; args.preverify_ok = ok ? Qtrue : Qfalse; args.store_ctx = rctx; ret = rb_protect((VALUE(*)(VALUE))ossl_call_verify_cb_proc, (VALUE)&args, &state); if (state) { rb_set_errinfo(Qnil); rb_warn("exception in verify_callback is ignored"); } ossl_x509stctx_clear_ptr(rctx); } if (ret == Qtrue) { X509_STORE_CTX_set_error(ctx, X509_V_OK); ok = 1; } else{ if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); } ok = 0; } } return ok; }
static VALUE ossl_x509stctx_set_error(VALUE self, VALUE err) { X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); X509_STORE_CTX_set_error(ctx, NUM2INT(err)); return err; }
static int _checkExpiration(T C, X509_STORE_CTX *ctx, X509 *certificate) { if (C->minimumValidDays) { // If we have warn-X-days-before-expire condition, check the certificate validity (already expired certificates are catched in preverify => we don't need to handle them here). int deltadays = 0; #ifdef HAVE_ASN1_TIME_DIFF int deltaseconds; if (! ASN1_TIME_diff(&deltadays, &deltaseconds, NULL, X509_get_notAfter(certificate))) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); snprintf(C->error, sizeof(C->error), "invalid time format (in certificate's notAfter field)"); return 0; } #else ASN1_GENERALIZEDTIME *t = ASN1_TIME_to_generalizedtime(X509_get_notAfter(certificate), NULL); if (! t) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); snprintf(C->error, sizeof(C->error), "invalid time format (in certificate's notAfter field)"); return 0; } TRY { deltadays = (double)(Time_toTimestamp((const char *)t->data) - Time_now()) / 86400.; } ELSE { X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); snprintf(C->error, sizeof(C->error), "invalid time format (in certificate's notAfter field) -- %s", t->data); } FINALLY { ASN1_STRING_free(t); } END_TRY; #endif if (deltadays < C->minimumValidDays) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION); snprintf(C->error, sizeof(C->error), "certificate expire in %d days matches check limit [valid > %d days]", deltadays, C->minimumValidDays); return 0; } }
static int verify_cb(int ok, X509_STORE_CTX *ctx) { char *buf = NULL; X509 *err_cert; int err, depth; err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); if (depth > MAX_CERT_DEPTH) { ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(ctx, err); } if (!ok) { if (err_cert) buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0); debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : ""); free(buf); buf = NULL; switch (err) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: if (err_cert) { buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0); if (buf) { debug(DBG_WARN, "\tIssuer=%s", buf); free(buf); buf = NULL; } } break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: debug(DBG_WARN, "\tCertificate not yet valid"); break; case X509_V_ERR_CERT_HAS_EXPIRED: debug(DBG_WARN, "Certificate has expired"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: debug(DBG_WARN, "Certificate no longer valid (after notAfter)"); break; case X509_V_ERR_NO_EXPLICIT_POLICY: debug(DBG_WARN, "No Explicit Certificate Policy"); break; } } return ok; }
int ossl_verify_cb(int ok, X509_STORE_CTX *ctx) { VALUE proc, rctx, ret; struct ossl_verify_cb_args args; int state = 0; proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_verify_cb_idx); if ((void*)proc == 0) proc = (VALUE)X509_STORE_get_ex_data(ctx->ctx, ossl_verify_cb_idx); if ((void*)proc == 0) return ok; if (!NIL_P(proc)) { rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, (VALUE)ctx, &state); ret = Qfalse; if (!state) { args.proc = proc; args.preverify_ok = ok ? Qtrue : Qfalse; args.store_ctx = rctx; ret = rb_ensure(ossl_call_verify_cb_proc, (VALUE)&args, ossl_x509stctx_clear_ptr, rctx); } if (ret == Qtrue) { X509_STORE_CTX_set_error(ctx, X509_V_OK); ok = 1; } else{ if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); } ok = 0; } } return ok; }
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { char buf[256]; X509 *cert; int err, depth; cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); /* * Catch a too long certificate chain. The depth limit set using * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so * that whenever the "depth>verify_depth" condition is met, we * have violated the limit and want to log this error condition. * We must do it here, because the CHAIN_TOO_LONG error would not * be found explicitly; only errors introduced by cutting off the * additional certificates would be logged. */ if (depth > 100) { preverify_ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(ctx, err); } if (!preverify_ok) logit(LOG_ERR, "Certificate verification error:num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf); /* * At this point, err contains the last verification error. We can use * it for something special */ if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) { X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf)); logit(LOG_ERR, "issuer= %s", buf); } if (secure_ssl) return preverify_ok; return 1; }
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */ { void *stream; SSL *ssl; X509 *err_cert; int err, depth, ret; ret = preverify_ok; /* determine the status for the current cert */ err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); /* conjure the stream & context to use */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); stream = SSL_get_ex_data(ssl, ssl_stream_data_index); /* if allow_self_signed is set, make sure that verification succeeds */ if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) { ret = 1; } /* check the depth */ if (GET_VER_OPT("verify_depth")) { convert_to_long_ex(val); if (depth > Z_LVAL_PP(val)) { ret = 0; X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); } } return ret; }
/* * Globally defined verify callback * * Arguments: ok - True everything is OK "so far", false otherwise * x509_ctx - Contains the certificate being checked, the current * error number and depth, and the Connection we're * dealing with * Returns: True if everything is okay, false otherwise */ static int global_verify_callback( int ok, X509_STORE_CTX * x509_ctx ) { PyObject *argv, *ret; SSL *ssl; ssl_ConnectionObj *conn; crypto_X509Obj *cert; X509 *x509; int errnum = X509_STORE_CTX_get_error(x509_ctx); int errdepth = X509_STORE_CTX_get_error_depth(x509_ctx); #ifdef GSI_HANDSHAKE_DEBUG logMsg( 0, "GVC %d errnum %d errdepth", errnum, errdepth ); #endif ssl = (SSL *) X509_STORE_CTX_get_app_data(x509_ctx); conn = (ssl_ConnectionObj *) SSL_get_app_data(ssl); OBJ_END_THREADS( conn ); Py_INCREF( conn ); if ( conn->context->verify_callback != Py_None ) { x509 = X509_STORE_CTX_get_current_cert(x509_ctx); cert = crypto_X509_New(x509, 0); argv = Py_BuildValue("(OOiii)", (PyObject *) conn, (PyObject *) cert, errnum, errdepth, ok); Py_DECREF(cert); /* We need to get back our thread state before calling the callback */ ret = PyEval_CallObject(conn->context->verify_callback, argv); Py_DECREF(argv); if ( ret == NULL ) { ok = 0; } else { if ( PyObject_IsTrue(ret) ) { X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); ok = 1; } else ok = 0; Py_DECREF(ret); } } //Set the remove verification flag in the end if ( errdepth == 0 && ok ) { conn->remoteCertVerified = 1; } Py_DECREF( conn ); OBJ_BEGIN_THREADS( conn ); return ok; }
static int verify_reject_cb(X509_STORE_CTX *ctx, void *arg) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION); return 0; }
/* based on the code of stunnel utility */ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { X509_STORE* store; X509_STORE_CTX store_ctx; X509_OBJECT obj; X509_NAME* subject; X509_NAME* issuer; X509* cert; X509_CRL* crl; X509_REVOKED* revoked; EVP_PKEY* pubkey; int i, n, rc; ASN1_TIME* next_update = NULL; if (!preverify_ok) { return 0; } if ((store = pthread_getspecific(tls_store_key)) == NULL) { ERROR("Failed to get thread-specific X509 store"); return 1; /* fail */ } cert = X509_STORE_CTX_get_current_cert(x509_ctx); subject = X509_get_subject_name(cert); issuer = X509_get_issuer_name(cert); /* try to retrieve a CRL corresponding to the _subject_ of * the current certificate in order to verify it's integrity */ memset((char *)&obj, 0, sizeof obj); X509_STORE_CTX_init(&store_ctx, store, NULL, NULL); rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj); X509_STORE_CTX_cleanup(&store_ctx); crl = obj.data.crl; if (rc > 0 && crl) { next_update = X509_CRL_get_nextUpdate(crl); /* verify the signature on this CRL */ pubkey = X509_get_pubkey(cert); if (X509_CRL_verify(crl, pubkey) <= 0) { X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); X509_OBJECT_free_contents(&obj); if (pubkey) { EVP_PKEY_free(pubkey); } return 0; /* fail */ } if (pubkey) { EVP_PKEY_free(pubkey); } /* check date of CRL to make sure it's not expired */ if (!next_update) { X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); X509_OBJECT_free_contents(&obj); return 0; /* fail */ } if (X509_cmp_current_time(next_update) < 0) { X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED); X509_OBJECT_free_contents(&obj); return 0; /* fail */ } X509_OBJECT_free_contents(&obj); } /* try to retrieve a CRL corresponding to the _issuer_ of * the current certificate in order to check for revocation */ memset((char *)&obj, 0, sizeof obj); X509_STORE_CTX_init(&store_ctx, store, NULL, NULL); rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj); X509_STORE_CTX_cleanup(&store_ctx); crl = obj.data.crl; if (rc > 0 && crl) { /* check if the current certificate is revoked by this CRL */ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); for (i = 0; i < n; i++) { revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) { ERROR("Certificate revoked"); X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED); X509_OBJECT_free_contents(&obj); return 0; /* fail */ } } X509_OBJECT_free_contents(&obj); } return 1; /* success */ }
/* * Before trusting a certificate, you must make sure that the * certificate is 'valid'. There are several steps that your * application can take in determining if a certificate is * valid. Commonly used steps are: * * 1.Verifying the certificate's signature, and verifying that * the certificate has been issued by a trusted Certificate * Authority. * * 2.Verifying that the certificate is valid for the present date * (i.e. it is being presented within its validity dates). * * 3.Verifying that the certificate has not been revoked by its * issuing Certificate Authority, by checking with respect to a * Certificate Revocation List (CRL). * * 4.Verifying that the credentials presented by the certificate * fulfill additional requirements specific to the application, * such as with respect to access control lists or with respect * to OCSP (Online Certificate Status Processing). * * NOTE: This callback will be called multiple times based on the * depth of the root certificate chain */ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) { char subject[1024]; /* Used for the subject name */ char issuer[1024]; /* Used for the issuer name */ char common_name[1024]; char cn_str[1024]; char buf[64]; EAP_HANDLER *handler = NULL; X509 *client_cert; X509 *issuer_cert; SSL *ssl; int err, depth, lookup; EAP_TLS_CONF *conf; int my_ok = ok; REQUEST *request; ASN1_INTEGER *sn = NULL; ASN1_TIME *asn_time = NULL; #ifdef HAVE_OPENSSL_OCSP_H X509_STORE *ocsp_store = NULL; #endif client_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); lookup = depth; /* * Log client/issuing cert. If there's an error, log * issuing cert. */ if ((lookup > 1) && !my_ok) lookup = 1; /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0); request = handler->request; conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); #ifdef HAVE_OPENSSL_OCSP_H ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, 2); #endif /* * Get the Serial Number */ buf[0] = '\0'; sn = X509_get_serialNumber(client_cert); /* * For this next bit, we create the attributes *only* if * we're at the client or issuing certificate. */ if ((lookup <= 1) && sn && (sn->length < (sizeof(buf) / 2))) { char *p = buf; int i; for (i = 0; i < sn->length; i++) { sprintf(p, "%02x", (unsigned int)sn->data[i]); p += 2; } pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_SERIAL][lookup], buf, T_OP_SET)); } /* * Get the Expiration Date */ buf[0] = '\0'; asn_time = X509_get_notAfter(client_cert); if ((lookup <= 1) && asn_time && (asn_time->length < MAX_STRING_LEN)) { memcpy(buf, (char*) asn_time->data, asn_time->length); buf[asn_time->length] = '\0'; pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_EXPIRATION][lookup], buf, T_OP_SET)); } /* * Get the Subject & Issuer */ subject[0] = issuer[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(client_cert), subject, sizeof(subject)); subject[sizeof(subject) - 1] = '\0'; if ((lookup <= 1) && subject[0] && (strlen(subject) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_SUBJECT][lookup], subject, T_OP_SET)); } X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, sizeof(issuer)); issuer[sizeof(issuer) - 1] = '\0'; if ((lookup <= 1) && issuer[0] && (strlen(issuer) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_ISSUER][lookup], issuer, T_OP_SET)); } /* * Get the Common Name */ X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), NID_commonName, common_name, sizeof(common_name)); common_name[sizeof(common_name) - 1] = '\0'; if ((lookup <= 1) && common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_CN][lookup], common_name, T_OP_SET)); } /* * If the CRL has expired, that might still be OK. */ if (!my_ok && (conf->allow_expired_crl) && (err == X509_V_ERR_CRL_HAS_EXPIRED)) { my_ok = 1; X509_STORE_CTX_set_error( ctx, 0 ); } if (!my_ok) { const char *p = X509_verify_cert_error_string(err); radlog(L_ERR,"--> verify error:num=%d:%s\n",err, p); radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", p, T_OP_SET); return my_ok; } switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: radlog(L_ERR, "issuer= %s\n", issuer); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: radlog(L_ERR, "notBefore="); #if 0 ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert)); #endif break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: radlog(L_ERR, "notAfter="); #if 0 ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert)); #endif break; } /* * If we're at the actual client cert, apply additional * checks. */ if (depth == 0) { /* * If the conf tells us to, check cert issuer * against the specified value and fail * verification if they don't match. */ if (conf->check_cert_issuer && (strcmp(issuer, conf->check_cert_issuer) != 0)) { radlog(L_AUTH, "rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer); my_ok = 0; } /* * If the conf tells us to, check the CN in the * cert against xlat'ed value, but only if the * previous checks passed. */ if (my_ok && conf->check_cert_cn) { if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) { radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.", conf->check_cert_cn); /* if this fails, fail the verification */ my_ok = 0; } else { RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str); if (strcmp(cn_str, common_name) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str); my_ok = 0; } } } /* check_cert_cn */ #ifdef HAVE_OPENSSL_OCSP_H if (my_ok && conf->ocsp_enable){ RDEBUG2("--> Starting OCSP Request"); if(X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert)!=1) { radlog(L_ERR, "Error: Couldn't get issuer_cert for %s", common_name); } my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf); } #endif while (conf->verify_client_cert_cmd) { char filename[256]; int fd; FILE *fp; snprintf(filename, sizeof(filename), "%s/%s.client.XXXXXXXX", conf->verify_tmp_dir, progname); fd = mkstemp(filename); if (fd < 0) { RDEBUG("Failed creating file in %s: %s", conf->verify_tmp_dir, strerror(errno)); break; } fp = fdopen(fd, "w"); if (!fp) { RDEBUG("Failed opening file %s: %s", filename, strerror(errno)); break; } if (!PEM_write_X509(fp, client_cert)) { fclose(fp); RDEBUG("Failed writing certificate to file"); goto do_unlink; } fclose(fp); if (!radius_pairmake(request, &request->packet->vps, "TLS-Client-Cert-Filename", filename, T_OP_SET)) { RDEBUG("Failed creating TLS-Client-Cert-Filename"); goto do_unlink; } RDEBUG("Verifying client certificate: %s", conf->verify_client_cert_cmd); if (radius_exec_program(conf->verify_client_cert_cmd, request, 1, NULL, 0, request->packet->vps, NULL, 1) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) fails external verification!", common_name); my_ok = 0; } else { RDEBUG("Client certificate CN %s passed external validation", common_name); } do_unlink: unlink(filename); break; } } /* depth == 0 */ if (debug_flag > 0) { RDEBUG2("chain-depth=%d, ", depth); RDEBUG2("error=%d", err); RDEBUG2("--> User-Name = %s", handler->identity); RDEBUG2("--> BUF-Name = %s", common_name); RDEBUG2("--> subject = %s", subject); RDEBUG2("--> issuer = %s", issuer); RDEBUG2("--> verify return:%d", my_ok); } return my_ok; }
// Certificate chain verification callback: return 1 if verified, // 0 if remote cannot be verified (fail handshake). // static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { if (!preverify_ok || X509_STORE_CTX_get_error_depth(ctx) != 0) // already failed, or not at peer cert in chain return preverify_ok; X509 *cert = X509_STORE_CTX_get_current_cert(ctx); SSL *ssn = (SSL *) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); if (!ssn) { pn_transport_logf(NULL, "Error: unexpected error - SSL session info not available for peer verify!"); return 0; // fail connection } pn_transport_t *transport = (pn_transport_t *)SSL_get_ex_data(ssn, ssl_ex_data_index); if (!transport) { pn_transport_logf(NULL, "Error: unexpected error - SSL context info not available for peer verify!"); return 0; // fail connection } pni_ssl_t *ssl = transport->ssl; if (ssl->domain->verify_mode != PN_SSL_VERIFY_PEER_NAME) return preverify_ok; if (!ssl->peer_hostname) { pn_transport_logf(transport, "Error: configuration error: PN_SSL_VERIFY_PEER_NAME configured, but no peer hostname set!"); return 0; // fail connection } ssl_log(transport, "Checking identifying name in peer cert against '%s'", ssl->peer_hostname); bool matched = false; /* first check any SubjectAltName entries, as per RFC2818 */ GENERAL_NAMES *sans = (GENERAL_NAMES *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (sans) { int name_ct = sk_GENERAL_NAME_num( sans ); int i; for (i = 0; !matched && i < name_ct; ++i) { GENERAL_NAME *name = sk_GENERAL_NAME_value( sans, i ); if (name->type == GEN_DNS) { ASN1_STRING *asn1 = name->d.dNSName; if (asn1 && asn1->data && asn1->length) { unsigned char *str; int len = ASN1_STRING_to_UTF8( &str, asn1 ); if (len >= 0) { ssl_log(transport, "SubjectAltName (dns) from peer cert = '%.*s'", len, str ); matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len ); OPENSSL_free( str ); } } } } GENERAL_NAMES_free( sans ); } /* if no general names match, try the CommonName from the subject */ X509_NAME *name = X509_get_subject_name(cert); int i = -1; while (!matched && (i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i); ASN1_STRING *name_asn1 = X509_NAME_ENTRY_get_data(ne); if (name_asn1) { unsigned char *str; int len = ASN1_STRING_to_UTF8( &str, name_asn1); if (len >= 0) { ssl_log(transport, "commonName from peer cert = '%.*s'", len, str); matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len ); OPENSSL_free(str); } } } if (!matched) { ssl_log(transport, "Error: no name matching %s found in peer cert - rejecting handshake.", ssl->peer_hostname); preverify_ok = 0; #ifdef X509_V_ERR_APPLICATION_VERIFICATION X509_STORE_CTX_set_error( ctx, X509_V_ERR_APPLICATION_VERIFICATION ); #endif } else { ssl_log(transport, "Name from peer cert matched - peer is valid."); } return preverify_ok; }
NOEXPORT int ocsp_check(X509_STORE_CTX *callback_ctx) { SSL *ssl; CLI *c; X509 *cert; OCSP_CERTID *cert_id; STACK_OF(OPENSSL_STRING) *aia; int i, status=V_OCSP_CERTSTATUS_UNKNOWN; /* get the current certificate ID */ cert=X509_STORE_CTX_get_current_cert(callback_ctx); if(!cert) { s_log(LOG_ERR, "OCSP: Failed to get the current certificate"); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_APPLICATION_VERIFICATION); return 0; /* reject */ } if(!X509_NAME_cmp(X509_get_subject_name(cert), X509_get_issuer_name(cert))) { s_log(LOG_DEBUG, "OCSP: Ignoring root certificate"); return 1; /* accept */ } cert_id=OCSP_cert_to_id(NULL, cert, get_current_issuer(callback_ctx)); if(!cert_id) { sslerror("OCSP: OCSP_cert_to_id"); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_APPLICATION_VERIFICATION); return 0; /* reject */ } ssl=X509_STORE_CTX_get_ex_data(callback_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); c=SSL_get_ex_data(ssl, cli_index); /* use the responder specified in the configuration file */ if(c->opt->ocsp_url) { s_log(LOG_DEBUG, "OCSP: Connecting configured responder \"%s\"", c->opt->ocsp_url); if(ocsp_request(c, callback_ctx, cert_id, c->opt->ocsp_url)!= V_OCSP_CERTSTATUS_GOOD) return 0; /* reject */ } /* use the responder from AIA (Authority Information Access) */ if(!c->opt->option.aia) return 1; /* accept */ aia=X509_get1_ocsp(cert); if(!aia) return 1; /* accept */ for(i=0; i<sk_OPENSSL_STRING_num(aia); i++) { s_log(LOG_DEBUG, "OCSP: Connecting AIA responder \"%s\"", sk_OPENSSL_STRING_value(aia, i)); status=ocsp_request(c, callback_ctx, cert_id, sk_OPENSSL_STRING_value(aia, i)); if(status!=V_OCSP_CERTSTATUS_UNKNOWN) break; /* we received a definitive response */ } X509_email_free(aia); if(status==V_OCSP_CERTSTATUS_GOOD) return 1; /* accept */ return 0; /* reject */ }
/* returns one of: * V_OCSP_CERTSTATUS_GOOD * V_OCSP_CERTSTATUS_REVOKED * V_OCSP_CERTSTATUS_UNKNOWN */ NOEXPORT int ocsp_request(CLI *c, X509_STORE_CTX *callback_ctx, OCSP_CERTID *cert_id, char *url) { int status=V_OCSP_CERTSTATUS_UNKNOWN; int reason; int ctx_err=X509_V_ERR_APPLICATION_VERIFICATION; OCSP_REQUEST *request=NULL; OCSP_RESPONSE *response=NULL; OCSP_BASICRESP *basic_response=NULL; ASN1_GENERALIZEDTIME *revoked_at=NULL, *this_update=NULL, *next_update=NULL; /* build request */ request=OCSP_REQUEST_new(); if(!request) { sslerror("OCSP: OCSP_REQUEST_new"); goto cleanup; } if(!OCSP_request_add0_id(request, cert_id)) { sslerror("OCSP: OCSP_request_add0_id"); goto cleanup; } OCSP_request_add1_nonce(request, NULL, -1); /* send the request and get a response */ response=ocsp_get_response(c, request, url); if(!response) goto cleanup; status=OCSP_response_status(response); if(status!=OCSP_RESPONSE_STATUS_SUCCESSFUL) { s_log(LOG_WARNING, "OCSP: Responder error: %d: %s", status, OCSP_response_status_str(status)); goto cleanup; } s_log(LOG_DEBUG, "OCSP: Response received"); /* verify the response */ basic_response=OCSP_response_get1_basic(response); if(!basic_response) { sslerror("OCSP: OCSP_response_get1_basic"); goto cleanup; } if(OCSP_check_nonce(request, basic_response)<=0) { s_log(LOG_WARNING, "OCSP: Invalid nonce"); goto cleanup; } if(OCSP_basic_verify(basic_response, NULL, c->opt->revocation_store, c->opt->ocsp_flags)<=0) { sslerror("OCSP: OCSP_basic_verify"); goto cleanup; } if(!OCSP_resp_find_status(basic_response, cert_id, &status, &reason, &revoked_at, &this_update, &next_update)) { sslerror("OCSP: OCSP_resp_find_status"); goto cleanup; } s_log(LOG_NOTICE, "OCSP: Status: %s", OCSP_cert_status_str(status)); log_time(LOG_INFO, "OCSP: This update", this_update); log_time(LOG_INFO, "OCSP: Next update", next_update); /* check if the response is valid for at least one minute */ if(!OCSP_check_validity(this_update, next_update, 60, -1)) { sslerror("OCSP: OCSP_check_validity"); status=V_OCSP_CERTSTATUS_UNKNOWN; goto cleanup; } switch(status) { case V_OCSP_CERTSTATUS_GOOD: break; case V_OCSP_CERTSTATUS_REVOKED: if(reason==-1) s_log(LOG_WARNING, "OCSP: Certificate revoked"); else s_log(LOG_WARNING, "OCSP: Certificate revoked: %d: %s", reason, OCSP_crl_reason_str(reason)); log_time(LOG_NOTICE, "OCSP: Revoked at", revoked_at); ctx_err=X509_V_ERR_CERT_REVOKED; break; case V_OCSP_CERTSTATUS_UNKNOWN: s_log(LOG_WARNING, "OCSP: Unknown verification status"); } cleanup: if(request) OCSP_REQUEST_free(request); if(response) OCSP_RESPONSE_free(response); if(basic_response) OCSP_BASICRESP_free(basic_response); if(status!=V_OCSP_CERTSTATUS_GOOD) X509_STORE_CTX_set_error(callback_ctx, ctx_err); return status; }
int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) { char buf[CCERT_BUFSIZ]; X509 *cert; int err; int depth; SSL *con; TLS_SESS_STATE *TLScontext; depth = X509_STORE_CTX_get_error_depth(ctx); cert = X509_STORE_CTX_get_current_cert(ctx); con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); TLScontext = SSL_get_ex_data(con, TLScontext_index); /* * The callback function is called repeatedly, first with the root * certificate, and then with each intermediate certificate ending with * the peer certificate. * * With each call, the validity of the current certificate (usage bits, * attributes, expiration, ... checked by the OpenSSL library) is * available in the "ok" argument. Error details are available via * X509_STORE_CTX API. * * We never terminate the SSL handshake in the verification callback, rather * we allow the TLS handshake to continue, but mark the session as * unverified. The application is responsible for closing any sessions * with unverified credentials. * * Certificate chain depth limit violations are mis-reported by the OpenSSL * library, from SSL_CTX_set_verify(3): * * The certificate verification depth set with SSL[_CTX]_verify_depth() * stops the verification at a certain depth. The error message produced * will be that of an incomplete certificate chain and not * X509_V_ERR_CERT_CHAIN_TOO_LONG as may be expected. * * We set a limit that is one higher than the user requested limit. If this * higher limit is reached, we raise an error even a trusted root CA is * present at this depth. This disambiguates trust chain truncation from * an incomplete trust chain. */ if (depth >= SSL_get_verify_depth(con)) { ok = 0; X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); } if (TLScontext->log_level >= 2) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); acl_msg_info("%s: certificate verification depth=%d verify=%d subject=%s", TLScontext->namaddr, depth, ok, printable(buf, '?')); } /* * If no errors, or we are not logging verification errors, we are done. */ if (ok || (TLScontext->peer_status & TLS_CERT_FLAG_LOGGED) != 0) return (1); /* * One counter-example is enough. */ TLScontext->peer_status |= TLS_CERT_FLAG_LOGGED; #define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server") /* * Specific causes for verification failure. */ switch (err = X509_STORE_CTX_get_error(ctx)) { case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: acl_msg_info("certificate verification failed for %s: " "self-signed certificate", TLScontext->namaddr); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: /* * There is no difference between issuing cert not provided and * provided, but not found in CAfile/CApath. Either way, we don't * trust it. */ X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf)); acl_msg_info("certificate verification failed for %s: untrusted issuer %s", TLScontext->namaddr, printable(buf, '?')); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: acl_msg_info("%s certificate verification failed for %s: certificate not" " yet valid", PURPOSE, TLScontext->namaddr); break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: acl_msg_info("%s certificate verification failed for %s: certificate has" " expired", PURPOSE, TLScontext->namaddr); break; case X509_V_ERR_INVALID_PURPOSE: acl_msg_info("certificate verification failed for %s: not designated for " "use as a %s certificate", TLScontext->namaddr, PURPOSE); break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: acl_msg_info("certificate verification failed for %s: " "certificate chain longer than limit(%d)", TLScontext->namaddr, SSL_get_verify_depth(con) - 1); break; default: acl_msg_info("%s certificate verification failed for %s: num=%d:%s", PURPOSE, TLScontext->namaddr, err, X509_verify_cert_error_string(err)); break; } return (1); }
static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg) { sslctxparm * p = (sslctxparm *) arg; int ok, err; fprintf(stderr,"ssl_app_verify_callback sslctxparm=%p ctx=%p\n", (void *)p, (void*)ctx); #if OPENSSL_VERSION_NUMBER<0x00907000L /* not necessary in openssl 0.9.7 or later */ fprintf(stderr,"This version %s of openssl does not support a parm (%p)" ", getting a global static %p \n", OPENSSL_VERSION_TEXT, (void *)p, (void *)globalparm); p = globalparm; #endif /* The following error should not occur. We test this to avoid segfault. */ if (!p || !ctx) { fprintf(stderr,"Internal error in ssl_app_verify_callback " "sslctxparm=%p ctx=%p\n",(void *)p,(void*)ctx); return 0; } ok= X509_verify_cert(ctx); err=X509_STORE_CTX_get_error(ctx); /* The following seems to be a problem in 0.9.7/8 openssl versions */ #if 1 if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) { fprintf(stderr,"X509_verify_cert: repairing self signed\n") ; X509_STORE_CTX_set_error(ctx,X509_V_OK); ok = 1; } #endif if (ok && ctx->cert) { unsigned char * accessinfoURL ; accessinfoURL = my_get_ext(ctx->cert,p->accesstype ,NID_info_access); if (accessinfoURL) { if (strcmp((char *)p->accessinfoURL, (char *)accessinfoURL)) { fprintf(stderr, "Setting URL <%s>, was <%s>\n", (char *)accessinfoURL, (char *)p->accessinfoURL); OPENSSL_free(p->accessinfoURL); p->accessinfoURL = accessinfoURL; /* We need to be able to deal with a custom port number, but the URL in the cert uses a static one. We thus need to create a new URL that uses the currently requested port number which may not be the one this URL uses! */ sprintf(newurl, "https://127.0.0.1:%d/509", portnum); fprintf(stderr, "But *really* Setting URL <%s>\n", newurl); curl_easy_setopt(p->curl, CURLOPT_URL, newurl); } else OPENSSL_free(accessinfoURL); } } return(ok); }
/* based on BSD-style licensed code of mod_ssl */ static int crl_check(CLI *c, X509_STORE_CTX *callback_ctx) { X509_STORE_CTX store_ctx; X509_OBJECT obj; X509_NAME *subject; X509_NAME *issuer; X509 *cert; X509_CRL *crl; X509_REVOKED *revoked; EVP_PKEY *pubkey; long serial; int i, n, rc; char *cp; ASN1_TIME *last_update=NULL, *next_update=NULL; /* determine certificate ingredients in advance */ cert=X509_STORE_CTX_get_current_cert(callback_ctx); subject=X509_get_subject_name(cert); issuer=X509_get_issuer_name(cert); /* try to retrieve a CRL corresponding to the _subject_ of * the current certificate in order to verify it's integrity */ memset((char *)&obj, 0, sizeof obj); X509_STORE_CTX_init(&store_ctx, c->opt->revocation_store, NULL, NULL); rc=X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj); X509_STORE_CTX_cleanup(&store_ctx); crl=obj.data.crl; if(rc>0 && crl) { cp=X509_NAME_oneline(subject, NULL, 0); s_log(LOG_INFO, "CRL: issuer: %s", cp); OPENSSL_free(cp); last_update=X509_CRL_get_lastUpdate(crl); next_update=X509_CRL_get_nextUpdate(crl); log_time(LOG_INFO, "CRL: last update", last_update); log_time(LOG_INFO, "CRL: next update", next_update); /* verify the signature on this CRL */ pubkey=X509_get_pubkey(cert); if(X509_CRL_verify(crl, pubkey)<=0) { s_log(LOG_WARNING, "CRL: Invalid signature"); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); X509_OBJECT_free_contents(&obj); if(pubkey) EVP_PKEY_free(pubkey); return 0; /* reject connection */ } if(pubkey) EVP_PKEY_free(pubkey); /* check date of CRL to make sure it's not expired */ if(!next_update) { s_log(LOG_WARNING, "CRL: Invalid nextUpdate field"); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); X509_OBJECT_free_contents(&obj); return 0; /* reject connection */ } if(X509_cmp_current_time(next_update)<0) { s_log(LOG_WARNING, "CRL: CRL Expired - revoking all certificates"); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_CRL_HAS_EXPIRED); X509_OBJECT_free_contents(&obj); return 0; /* reject connection */ } X509_OBJECT_free_contents(&obj); } /* try to retrieve a CRL corresponding to the _issuer_ of * the current certificate in order to check for revocation */ memset((char *)&obj, 0, sizeof obj); X509_STORE_CTX_init(&store_ctx, c->opt->revocation_store, NULL, NULL); rc=X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj); X509_STORE_CTX_cleanup(&store_ctx); crl=obj.data.crl; if(rc>0 && crl) { /* check if the current certificate is revoked by this CRL */ n=sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); for(i=0; i<n; i++) { revoked=sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); if(ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) { serial=ASN1_INTEGER_get(revoked->serialNumber); cp=X509_NAME_oneline(issuer, NULL, 0); s_log(LOG_WARNING, "CRL: Certificate with serial %ld (0x%lX) " "revoked per CRL from issuer %s", serial, serial, cp); OPENSSL_free(cp); X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_CERT_REVOKED); X509_OBJECT_free_contents(&obj); return 0; /* reject connection */ } } X509_OBJECT_free_contents(&obj); } return 1; /* accept connection */ }
int CryptoManager::verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { int err = X509_STORE_CTX_get_error(ctx); SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); SSLVerifyData* verifyData = (SSLVerifyData*)SSL_get_ex_data(ssl, CryptoManager::idxVerifyData); // TODO: we should make sure that the trusted certificate store never overules KeyPrint, if present, because certificate pinning on an individual certificate is a stronger method of verification. // verifyData is unset only when KeyPrint has been pinned and we are not skipping errors due to incomplete chains // we can fail here f.ex. if the certificate has expired but is still pinned with KeyPrint if (!verifyData) return preverify_ok; bool allowUntrusted = verifyData->first; string keyp = verifyData->second; if (!keyp.empty()) { X509* cert = X509_STORE_CTX_get_current_cert(ctx); if (!cert) return 0; string kp2(keyp); if (kp2.compare(0, 12, "trusted_keyp") == 0) { // Possible follow up errors, after verification of a partial chain if (err == X509_V_ERR_CERT_UNTRUSTED || err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) { X509_STORE_CTX_set_error(ctx, X509_V_OK); return 1; } } else if (kp2.compare(0, 7, "SHA256/") != 0) return allowUntrusted ? 1 : 0; ByteVector kp = ssl::X509_digest(cert, EVP_sha256()); ByteVector kp2v(kp.size()); Encoder::fromBase32(&kp2[7], &kp2v[0], kp2v.size()); if (std::equal(kp.begin(), kp.end(), kp2v.begin())) { // KeyPrint validated, we can get rid of it (to avoid unnecessary passes) SSL_set_ex_data(ssl, CryptoManager::idxVerifyData, NULL); if (err != X509_V_OK) { // This is the right way to get the certificate store, although it is rather roundabout X509_STORE* store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(ssl)); dcassert(store == ctx->ctx); // Hide the potential library error about trying to add a dupe ERR_set_mark(); if (X509_STORE_add_cert(store, cert)) { X509_STORE_CTX_set_error(ctx, X509_V_OK); X509_verify_cert(ctx); err = X509_STORE_CTX_get_error(ctx); } else ERR_pop_to_mark(); // KeyPrint was not root certificate or we don't have the issuer certificate, the best we can do is trust the pinned KeyPrint if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN || err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) { X509_STORE_CTX_set_error(ctx, X509_V_OK); // Set this to allow ignoring any follow up errors caused by the incomplete chain SSL_set_ex_data(ssl, CryptoManager::idxVerifyData, &CryptoManager::trustedKeyprint); return 1; } } return (err == X509_V_OK) ? 1 : 0; } else { if (X509_STORE_CTX_get_error_depth(ctx) > 0) return 1; } } if (allowUntrusted) { /* // We let untrusted certificates through unconditionally, when allowed, but we like to complain if (!preverify_ok && err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) { X509* cert = NULL; if ((cert = X509_STORE_CTX_get_current_cert(ctx)) != NULL) { X509_NAME* subject = X509_get_subject_name(cert); string tmp, line; tmp = getNameEntryByNID(subject, NID_commonName); if (!tmp.empty()) { CID certCID(tmp); if (certCID) tmp = Util::listToString(ClientManager::getInstance()->getNicks(certCID)); line += (!line.empty() ? ", " : "") + tmp; } tmp = getNameEntryByNID(subject, NID_organizationName); if (!tmp.empty()) line += (!line.empty() ? ", " : "") + tmp; ByteVector kp = ssl::X509_digest(cert, EVP_sha256()); string keyp = "SHA256/" + Encoder::toBase32(&kp[0], kp.size()); LogManager::getInstance()->message(STRING_F(VERIFY_CERT_FAILED, line % X509_verify_cert_error_string(err) % keyp), LogManager::LOG_INFO); } }*/ return 1; } return preverify_ok; }