Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cert-pinning on Windows and Mac. #702

Closed
wants to merge 7 commits into from

Conversation

chogorma
Copy link

@chogorma chogorma commented Feb 19, 2018

Pull request for the solution we ended up using from the first PR: #135

Again, this is a preliminary pull request to see if you guys are happy to add this to Casablanca.

Added a callback that will return the certificate_info with the cert chain to the consumer.

The certificate_info will contain:

  1. The encoded data of each certificate in the chain up to the root certificate (if the OS can build the full chain).
  2. The host name of the request.
  3. The error code for the current status of the certificate chain.
  4. The OS verified status of the chain.

Using this approach we leave the validation of the full certificate chain up to the consumer, so Casablanca does not have to support managing different types of certificate lists for the consumer to do cert-pinning.

The consumer can then use OpenSSL (Or whatever they want) to import the certificate chain and they will then decide on whether to accept or reject the connection.

Current changes are for Windows & Mac.

@msftclas
Copy link

msftclas commented Feb 19, 2018

CLA assistant check
All CLA requirements met.

};

return http::client::details::verify_cert_chain_platform_specific(verifyCtx, utility::conversions::to_utf8string(host), chainFunc);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code cosmetics: indentation of closing bracket

p_request_context->report_error(errorCode, build_error_msg(error_result));
break;
}
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This status does not seem to be hit on our system (Windows 7). We get the following notifications:

  • WINHTTP_CALLBACK_STATUS_HANDLE_CREATED
  • WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
  • WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
  • WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
  • WINHTTP_CALLBACK_STATUS_READ_COMPLETE
    ....

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the PR is missing a flag when calling WinHttpSetStatusCallback, we do have integration tests on our network manager but somehow i missed the flag when creating the diff from our repo.

Adding that that now.

Copy link
Contributor

@reneme reneme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for this patch! I really like the API and I would love to have it upstream.

{
CertificateChain certificate_chain;
std::string host_name;
long certificate_error{ 0 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I interpret this error code? I suppose it's platform dependent?
Please add some description in the comments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the error code is platform dependent, we use it for debugging certificate issues.
Mac: https://developer.apple.com/documentation/security/sectrustresulttype
Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/aa377590(v=vs.85).aspx

Will add a comment for that.

return false;
}

auto info = std::make_shared<certificate_info>(host, get_X509_cert_chain_encoded_data(verifyCtx));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does currently not compile on Linux because get_X509_cert_chain_encoded_data() is not defined (due to some #ifdef). @chogorma Please refer to our PR to your private repository.

auto info = std::make_shared<certificate_info>(host, get_X509_cert_chain_encoded_data(verifyCtx));
info->verified = true;

return m_http_client->client_config().invoke_certificate_chain_callback(info);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Linux this is called several times. I'm not exactly sure why, but I suppose it is due to the way OpenSSL handles certificate chains (cf. comments in this method). Do we need a similar workaround as for macOS and Android above?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would need to do the same flow on Linux as per the macOS flow above.

The "is_end_certificate_in_chain" check above should allow the connection until we get the end certificate, once we get that, then we get the OS to build the full cert chain, only after that, then we do the cert-pinning.

If you just wait for the end certificate and don't build the full chain from the OS, you will only have the certs from the SSL handshake, and more than likely that will not be the full chain.

auto info = std::make_shared<http::client::certificate_info>(utility::conversions::to_utf8string(m_uri.host()), get_X509_cert_chain_encoded_data(verifyCtx));
info->verified = true;

return m_config.invoke_certificate_chain_callback(info);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wild guess: I would expect that this would also be invoked more than once per connection on Linux as it seems to be duplicated code referred to my comment above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct as the check "is_end_certificate_in_chain" isn't done on Linux.

@xqp
Copy link
Contributor

xqp commented Apr 5, 2018

Thanks for this PR 👍
@chogorma can you look at our pr in your local repo? :)

linux: invoke the certificate chain callback only once
@BillyONeal
Copy link
Member

We have had a number of requests for something like this -- we would be interested in adding such a feature, but unfortunately this one doesn't merge cleanly and appears to be incompatible with how we implemented Host: domain reassignment :(

Any chance you want to try to resolve the merges or should we close this?

@BillyONeal
Copy link
Member

Given that this doesn't merge cleanly and is missing tests, I'm closing this for now. Anyone feel free to reopen with the conflicts fixed.

(We aren't normally a super stickler about tests being added for each new feature, but this one is security sensitive. Doing it as an "outside test" that pings a server, like we do for our badssl tests, is OK)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants