aboutsummaryrefslogtreecommitdiff
path: root/crypto/ct
diff options
context:
space:
mode:
authorViktor Dukhovni <openssl-users@dukhovni.org>2016-04-07 14:17:37 -0400
committerViktor Dukhovni <openssl-users@dukhovni.org>2016-04-07 14:41:34 -0400
commit43341433a88a6a2cd38c35359f48653e809b10cd (patch)
tree37b70a38d94f8f9bfa18f633b35df2647d13273a /crypto/ct
parentc636c1c470fd2b4b0cb546e6ee85971375e42ec1 (diff)
downloadopenssl-43341433a88a6a2cd38c35359f48653e809b10cd.zip
openssl-43341433a88a6a2cd38c35359f48653e809b10cd.tar.gz
openssl-43341433a88a6a2cd38c35359f48653e809b10cd.tar.bz2
Suppress CT callback as appropriate
Suppress CT callbacks with aNULL or PSK ciphersuites that involve no certificates. Ditto when the certificate chain is validated via DANE-TA(2) or DANE-EE(3) TLSA records. Also skip SCT processing when the chain is fails verification. Move and consolidate CT callbacks from libcrypto to libssl. We also simplify the interface to SSL_{,CTX_}_enable_ct() which can specify either a permissive mode that just collects information or a strict mode that requires at least one valid SCT or else asks to abort the connection. Simplified SCT processing and options in s_client(1) which now has just a simple pair of "-noct" vs. "-ct" options, the latter enables the permissive callback so that we can complete the handshake and report all relevant information. When printing SCTs, print the validation status if set and not valid. Signed-off-by: Rob Percival <robpercival@google.com> Reviewed-by: Emilia Käsper <emilia@openssl.org>
Diffstat (limited to 'crypto/ct')
-rw-r--r--crypto/ct/ct_oct.c12
-rw-r--r--crypto/ct/ct_prn.c23
-rw-r--r--crypto/ct/ct_sct.c33
-rw-r--r--crypto/ct/ct_vfy.c59
4 files changed, 58 insertions, 69 deletions
diff --git a/crypto/ct/ct_oct.c b/crypto/ct/ct_oct.c
index 620edab..ece353b 100644
--- a/crypto/ct/ct_oct.c
+++ b/crypto/ct/ct_oct.c
@@ -135,10 +135,14 @@ SCT *o2i_SCT(SCT **psct, const unsigned char **in, size_t len)
if (sct->version == SCT_VERSION_V1) {
int sig_len;
size_t len2;
- /*
- * Fixed-length header: struct { (1 byte) Version sct_version; (32
- * bytes) log_id id; (8 bytes) uint64 timestamp; (2 bytes + ?)
- * CtExtensions extensions;
+ /*-
+ * Fixed-length header:
+ * struct {
+ * Version sct_version; (1 byte)
+ * log_id id; (32 bytes)
+ * uint64 timestamp; (8 bytes)
+ * CtExtensions extensions; (2 bytes + ?)
+ * }
*/
if (len < 43) {
CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
diff --git a/crypto/ct/ct_prn.c b/crypto/ct/ct_prn.c
index 0d9d019..5004ae0 100644
--- a/crypto/ct/ct_prn.c
+++ b/crypto/ct/ct_prn.c
@@ -96,6 +96,26 @@ static void timestamp_print(uint64_t timestamp, BIO *out)
ASN1_GENERALIZEDTIME_free(gen);
}
+const char *SCT_validation_status_string(const SCT *sct)
+{
+
+ switch (SCT_get_validation_status(sct)) {
+ case SCT_VALIDATION_STATUS_NOT_SET:
+ return "not set";
+ case SCT_VALIDATION_STATUS_UNKNOWN_VERSION:
+ return "unknown version";
+ case SCT_VALIDATION_STATUS_UNKNOWN_LOG:
+ return "unknown log";
+ case SCT_VALIDATION_STATUS_UNVERIFIED:
+ return "unverified";
+ case SCT_VALIDATION_STATUS_INVALID:
+ return "invalid";
+ case SCT_VALIDATION_STATUS_VALID:
+ return "valid";
+ }
+ return "unknown status";
+}
+
void SCT_print(const SCT *sct, BIO *out, int indent,
const CTLOG_STORE *log_store)
{
@@ -143,9 +163,10 @@ void SCT_print(const SCT *sct, BIO *out, int indent,
void SCT_LIST_print(const STACK_OF(SCT) *sct_list, BIO *out, int indent,
const char *separator, const CTLOG_STORE *log_store)
{
+ int sct_count = sk_SCT_num(sct_list);
int i;
- for (i = 0; i < sk_SCT_num(sct_list); ++i) {
+ for (i = 0; i < sct_count; ++i) {
SCT *sct = sk_SCT_value(sct_list, i);
SCT_print(sct, out, indent, log_store);
diff --git a/crypto/ct/ct_sct.c b/crypto/ct/ct_sct.c
index 9eefa0c..1fc7456 100644
--- a/crypto/ct/ct_sct.c
+++ b/crypto/ct/ct_sct.c
@@ -334,17 +334,22 @@ int SCT_validate(SCT *sct, const CT_POLICY_EVAL_CTX *ctx)
X509_PUBKEY *pub = NULL, *log_pkey = NULL;
const CTLOG *log;
+ /*
+ * With an unrecognized SCT version we don't know what such an SCT means,
+ * let alone validate one. So we return validation failure (0).
+ */
if (sct->version != SCT_VERSION_V1) {
sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_VERSION;
- goto end;
+ return 0;
}
log = CTLOG_STORE_get0_log_by_id(ctx->log_store,
sct->log_id, sct->log_id_len);
+ /* Similarly, an SCT from an unknown log also cannot be validated. */
if (log == NULL) {
sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_LOG;
- goto end;
+ return 0;
}
sctx = SCT_CTX_new();
@@ -372,10 +377,28 @@ int SCT_validate(SCT *sct, const CT_POLICY_EVAL_CTX *ctx)
goto err;
}
+ /*
+ * XXX: Potential for optimization. This repeats some idempotent heavy
+ * lifting on the certificate for each candidate SCT, and appears to not
+ * use any information in the SCT itself, only the certificate is
+ * processed. So it may make more sense to to do this just once, perhaps
+ * associated with the shared (by all SCTs) policy eval ctx.
+ *
+ * XXX: Failure here is global (SCT independent) and represents either an
+ * issue with the certificate (e.g. duplicate extensions) or an out of
+ * memory condition. When the certificate is incompatible with CT, we just
+ * mark the SCTs invalid, rather than report a failure to determine the
+ * validation status. That way, callbacks that want to do "soft" SCT
+ * processing will not abort handshakes with false positive internal
+ * errors. Since the function does not distinguish between certificate
+ * issues (peer's fault) and internal problems (out fault) the safe thing
+ * to do is to report a validation failure and let the callback or
+ * application decide what to do.
+ */
if (SCT_CTX_set1_cert(sctx, ctx->cert, NULL) != 1)
- goto err;
-
- sct->validation_status = SCT_verify(sctx, sct) == 1 ?
+ sct->validation_status = SCT_VALIDATION_STATUS_UNVERIFIED;
+ else
+ sct->validation_status = SCT_verify(sctx, sct) == 1 ?
SCT_VALIDATION_STATUS_VALID : SCT_VALIDATION_STATUS_INVALID;
end:
diff --git a/crypto/ct/ct_vfy.c b/crypto/ct/ct_vfy.c
index 9895231..71c0361 100644
--- a/crypto/ct/ct_vfy.c
+++ b/crypto/ct/ct_vfy.c
@@ -71,65 +71,6 @@ typedef enum sct_signature_type_t {
SIGNATURE_TYPE_TREE_HASH
} SCT_SIGNATURE_TYPE;
-int CT_verify_no_bad_scts(const CT_POLICY_EVAL_CTX *ctx,
- const STACK_OF(SCT) *scts, void *arg)
-{
- int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;
- int i;
-
- for (i = 0; i < sct_count; ++i) {
- SCT *sct = sk_SCT_value(scts, i);
-
- switch (SCT_get_validation_status(sct)) {
- case SCT_VALIDATION_STATUS_INVALID:
- return 0;
- case SCT_VALIDATION_STATUS_NOT_SET:
- CTerr(CT_F_CT_VERIFY_NO_BAD_SCTS,
- CT_R_SCT_VALIDATION_STATUS_NOT_SET);
- return -1;
- default:
- /* Ignore other validation statuses. */
- break;
- }
- }
-
- return 1;
-}
-
-int CT_verify_at_least_one_good_sct(const CT_POLICY_EVAL_CTX *ctx,
- const STACK_OF(SCT) *scts, void *arg)
-{
- int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;
- int valid_scts = 0;
- int i;
-
- for (i = 0; i < sct_count; ++i) {
- SCT *sct = sk_SCT_value(scts, i);
-
- switch (SCT_get_validation_status(sct)) {
- case SCT_VALIDATION_STATUS_VALID:
- ++valid_scts;
- break;
- case SCT_VALIDATION_STATUS_INVALID:
- return 0;
- case SCT_VALIDATION_STATUS_NOT_SET:
- CTerr(CT_F_CT_VERIFY_AT_LEAST_ONE_GOOD_SCT,
- CT_R_SCT_VALIDATION_STATUS_NOT_SET);
- return -1;
- default:
- /* Ignore other validation statuses. */
- break;
- }
- }
-
- if (valid_scts == 0) {
- CTerr(CT_F_CT_VERIFY_AT_LEAST_ONE_GOOD_SCT, CT_R_NOT_ENOUGH_SCTS);
- return 0;
- }
-
- return 1;
-}
-
/*
* Update encoding for SCT signature verification/generation to supplied
* EVP_MD_CTX.