Skip to content

C++: New query for SSL certificates not checked #7243

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

Merged
merged 11 commits into from
Dec 1, 2021

Conversation

geoffw0
Copy link
Contributor

@geoffw0 geoffw0 commented Nov 25, 2021

This is the second of two new queries for CWE-295: Improper Certificate Validation.

Results:

  • on 132 LGTM projects; there are quite a few results.
  • there's one obvious FP where the code is simply wrapping SSL_get_peer_certificate.
  • the other results look like plausible TPs to me, but I may need to improve my familiarity with the library and figure out if some of these are in fact OK / safe? --- update: results LGTM on a second look as well.

Precision:

  • provisionally set to @medium, for review after looking at the LGTM results in more depth.

Performance:

  • tested locally on kamailio_kamailio and chakra-core (performance is great with a warmed up cache).
  • no timeouts on the above LGTM run.

@geoffw0 geoffw0 added the C++ label Nov 25, 2021
@geoffw0 geoffw0 requested a review from a team as a code owner November 25, 2021 15:54
Copy link
Contributor

@MathiasVP MathiasVP left a comment

Choose a reason for hiding this comment

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

I haven't looked at any of the LGTM results yet. But I have a couple of code comments.

* `SSL_get_verify_result` at `node`. Note that this is only computed at the call to
* `SSL_get_peer_certificate` and at the start and end of `BasicBlock`s.
*/
predicate certNotChecked(SSLGetPeerCertificateCall getCertCall, ControlFlowNode node) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Intead of rolling our own control-flow analysis here, could we instead use StackVariableReachability? Then isSource could be the call to SSL_get_peer_certificate, isSink could be the exit of the function, and isBarrier could be a call to SSL_get_verify_result.

Would that work?

It may be difficult to fit the certIsZero barrier into SSL_get_peer_certificate, but I think it's doable by having one StackVariableReachability for each call to SSL_get_peer_certificate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried a couple of libraries that didn't quite fit the problem, I may have overlooked StackVariableReachability.. Trying it now...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It may be difficult to fit the certIsZero barrier into SSL_get_peer_certificate, but I think it's doable by having one StackVariableReachability for each call to SSL_get_peer_certificate.

This turns out not to be a major issue, the existing GVN logic finds appropriate guards on certificates returned by SSL_get_peer_certificate, and I've never seen more than one certificate being used at once (in which case we'd have to worry about which certificate it is).

What is an issue is that the certIsZero barrier needs to be a barrier edge, not a barrier node. StackVariableReachability does not appear to support barrier edges and I haven't found a sufficient workaround for this yet. I suppose I could add it as a new feature, but that will then require a lot of testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough. I appreciate that you looked into it! Let's postpone converting the logic to StackVariableReachability to a later point, then.

Comment on lines 75 to 82
// if (cert) { }
guard = cert and
node1 = guard and
node2 = guard.getAFalseSuccessor()
or
// if (!cert) {
node1 = guard.getParent() and
node2 = guard.getParent().(NotExpr).getATrueSuccessor()
Copy link
Contributor

Choose a reason for hiding this comment

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

Could these cases not be covered by using guard.controls? That might be able to solve the FP in test2_11 (and in principle the one in test2_9 as well, but IIRC the GuardConditions class specifically doesn't handle this case very well).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've pushed an improved version using guard.controls (and fixing some other issues). It's a bit tricky because guards gives the basic blocks that may be entered only if the guard goes a particular way, whereas we want to be able to handle blocks that have more than one route in, e.g.

cert = SSL_get_peer_certificate(ssl);
if (cert)
{
  result = SSL_get_verify_result(ssl);
  ...
}
// here

In the case above here is good because one path to it has cert = 0, the other has a call to SSL_get_verify_result - not because of either fact on its own. As such the code is still somewhat special-cased rather than just letting guards do its thing.

@geoffw0
Copy link
Contributor Author

geoffw0 commented Nov 30, 2021

Pushed some improvements:

  • corrected test annotations.
  • now using GuardCondition.controls, though sadly we still rely on special casing NotExpr somewhat rather than being fully general.
  • in one path cert wasn't checked properly, that is now fixed as well.

Copy link
Contributor

@MathiasVP MathiasVP left a comment

Choose a reason for hiding this comment

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

LGTM!

@MathiasVP MathiasVP merged commit 9f8326a into github:main Dec 1, 2021
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.

2 participants