Skip to content

Commit

Permalink
Suppress CT callback as appropriate
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Viktor Dukhovni committed Apr 7, 2016
1 parent c636c1c commit 4334143
Show file tree
Hide file tree
Showing 17 changed files with 382 additions and 237 deletions.
67 changes: 39 additions & 28 deletions apps/s_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ typedef enum OPTION_choice {
OPT_S_ENUM,
OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_PROXY, OPT_DANE_TLSA_DOMAIN,
#ifndef OPENSSL_NO_CT
OPT_NOCT, OPT_REQUESTCT, OPT_REQUIRECT, OPT_CTLOG_FILE,
OPT_CT, OPT_NOCT, OPT_CTLOG_FILE,
#endif
OPT_DANE_TLSA_RRDATA
} OPTION_CHOICE;
Expand Down Expand Up @@ -831,9 +831,8 @@ OPTIONS s_client_options[] = {
"Specify engine to be used for client certificate operations"},
#endif
#ifndef OPENSSL_NO_CT
{"ct", OPT_CT, '-', "Request and parse SCTs (also enables OCSP stapling)"},
{"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"},
{"requestct", OPT_REQUESTCT, '-', "Request SCTs (enables OCSP stapling)"},
{"requirect", OPT_REQUIRECT, '-', "Require at least 1 SCT (enables OCSP stapling)"},
{"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"},
#endif
{NULL}
Expand Down Expand Up @@ -935,7 +934,7 @@ int s_client_main(int argc, char **argv)
#endif
#ifndef OPENSSL_NO_CT
char *ctlog_file = NULL;
ct_validation_cb ct_validation = NULL;
int ct_validation = 0;
#endif
int min_version = 0, max_version = 0;

Expand Down Expand Up @@ -1335,13 +1334,10 @@ int s_client_main(int argc, char **argv)
break;
#ifndef OPENSSL_NO_CT
case OPT_NOCT:
ct_validation = NULL;
ct_validation = 0;
break;
case OPT_REQUESTCT:
ct_validation = CT_verify_no_bad_scts;
break;
case OPT_REQUIRECT:
ct_validation = CT_verify_at_least_one_good_sct;
case OPT_CT:
ct_validation = 1;
break;
case OPT_CTLOG_FILE:
ctlog_file = opt_arg();
Expand Down Expand Up @@ -1684,13 +1680,15 @@ int s_client_main(int argc, char **argv)
SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);

#ifndef OPENSSL_NO_CT
if (!SSL_CTX_set_ct_validation_callback(ctx, ct_validation, NULL)) {
/* Enable SCT processing, without early connection termination */
if (ct_validation &&
!SSL_CTX_enable_ct(ctx, SSL_CT_VALIDATION_PERMISSIVE)) {
ERR_print_errors(bio_err);
goto end;
}

if (!ctx_set_ctlog_list_file(ctx, ctlog_file)) {
if (ct_validation != NULL) {
if (ct_validation) {
ERR_print_errors(bio_err);
goto end;
}
Expand Down Expand Up @@ -2570,7 +2568,6 @@ static void print_stuff(BIO *bio, SSL *s, int full)
#endif
unsigned char *exportedkeymat;
#ifndef OPENSSL_NO_CT
const STACK_OF(SCT) *scts;
const SSL_CTX *ctx = SSL_get_SSL_CTX(s);
#endif

Expand Down Expand Up @@ -2626,21 +2623,35 @@ static void print_stuff(BIO *bio, SSL *s, int full)
ssl_print_tmp_key(bio, s);

#ifndef OPENSSL_NO_CT
scts = SSL_get0_peer_scts(s);
BIO_printf(bio, "---\nSCTs present (%i)\n",
scts != NULL ? sk_SCT_num(scts) : 0);

if (SSL_get_ct_validation_callback(s) == NULL) {
BIO_printf(bio, "Warning: CT validation is disabled, so not all "
"SCTs may be displayed. Re-run with \"-requestct\".\n");
}

if (scts != NULL && sk_SCT_num(scts) > 0) {
const CTLOG_STORE *log_store = SSL_CTX_get0_ctlog_store(ctx);

BIO_printf(bio, "---\n");
SCT_LIST_print(scts, bio, 0, "\n---\n", log_store);
BIO_printf(bio, "\n");
/*
* When the SSL session is anonymous, or resumed via an abbreviated
* handshake, no SCTs are provided as part of the handshake. While in
* a resumed session SCTs may be present in the session's certificate,
* no callbacks are invoked to revalidate these, and in any case that
* set of SCTs may be incomplete. Thus it makes little sense to
* attempt to display SCTs from a resumed session's certificate, and of
* course none are associated with an anonymous peer.
*/
if (peer != NULL && !SSL_session_reused(s) && SSL_ct_is_enabled(s)) {
const STACK_OF(SCT) *scts = SSL_get0_peer_scts(s);
int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;

BIO_printf(bio, "---\nSCTs present (%i)\n", sct_count);
if (sct_count > 0) {
const CTLOG_STORE *log_store = SSL_CTX_get0_ctlog_store(ctx);

BIO_printf(bio, "---\n");
for (i = 0; i < sct_count; ++i) {
SCT *sct = sk_SCT_value(scts, i);

BIO_printf(bio, "SCT validation status: %s\n",
SCT_validation_status_string(sct));
SCT_print(sct, bio, 0, log_store);
if (i < sct_count - 1)
BIO_printf(bio, "\n---\n");
}
BIO_printf(bio, "\n");
}
}
#endif

Expand Down
12 changes: 8 additions & 4 deletions crypto/ct/ct_oct.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
23 changes: 22 additions & 1 deletion crypto/ct/ct_prn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
33 changes: 28 additions & 5 deletions crypto/ct/ct_sct.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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:
Expand Down
59 changes: 0 additions & 59 deletions crypto/ct/ct_vfy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 6 additions & 8 deletions doc/apps/s_client.pod
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ B<openssl> B<s_client>
[B<-serverinfo types>]
[B<-status>]
[B<-nextprotoneg protocols>]
[B<-noct|requestct|requirect>]
[B<-ct|noct>]
[B<-ctlogfile>]

=head1 DESCRIPTION
Expand Down Expand Up @@ -464,14 +464,12 @@ Empty list of protocols is treated specially and will cause the client to
advertise support for the TLS extension but disconnect just after
receiving ServerHello with a list of server supported protocols.

=item B<-noct|requestct|requirect>
=item B<-ct|noct>

Use one of these three options to control whether Certificate Transparency (CT)
is disabled (-noct), enabled but not enforced (-requestct), or enabled and
enforced (-requirect). If CT is enabled, signed certificate timestamps (SCTs)
will be requested from the server and invalid SCTs will cause the connection to
be aborted. If CT is enforced, at least one valid SCT from a recognised CT log
(see B<-ctlogfile>) will be required or the connection will be aborted.
Use one of these two options to control whether Certificate Transparency (CT)
is enabled (B<-ct>) or disabled (B<-noct>).
If CT is enabled, signed certificate timestamps (SCTs) will be requested from
the server and reported at handshake completion.

Enabling CT also enables OCSP stapling, as this is one possible delivery method
for SCTs.
Expand Down

0 comments on commit 4334143

Please sign in to comment.