The Story of the GnuTLS Bug

http://blog.existentialize.com/the-story-of-the-gnutls-bug.html


You might have heard about the critical GnuTLS bug that was recently fixed recently. What's the deal with it? Why is it a big deal? What happened?

Here's the bug, in essence, in lib/x509/verify.c:

/* Checks if the issuer of a certificate is a
 * Certificate Authority, or if the certificate is the same
 * as the issuer (and therefore it doesn't need to be a CA).
 *
 * Returns true or false, if the issuer is a CA,
 * or not.
 */
static int
check_if_ca (gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
            unsigned int flags)
{
    int result;
    result =
        _gnutls_x509_get_signed_data (issuer->cert, "tbsCertificate",
                                    &issuer_signed_data);
    if (result < 0)
        {
        gnutls_assert ();
        goto cleanup;
        }

    result =
        _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate",
                                    &cert_signed_data);
    if (result < 0)
        {
        gnutls_assert ();
        goto cleanup;
        }

    // snip

    result = 0;

cleanup:
    // cleanup type stuff
    return result;
}

Can you spot the bug?

Here's the (abridged) fixed version:


    int result;
    result =
        _gnutls_x509_get_signed_data (issuer->cert, "tbsCertificate",
                                    &issuer_signed_data);
    if (result < 0)
        {
        gnutls_assert ();
        goto fail; // CHANGED
        }

    // snip

fail:  // ADDED
    result = 0;

cleanup:
    // cleanup type stuff
    return result;
}


The bug was a disagreement between return value meanings. The function check_if_ca returns "true" or rather 1, when the certificate is a CA, and zero otherwise. However the other functions used return negative when they fail. In C, any integer value other than zero is regarded as a true value. So if the certificate is invalid, it's actually marked as a CA certificate.

So what's the implication of this? This function is used by gnutls_x509_crt_verify, which verifies x509 certificates. Invalid certificates can be passed off as genuine, even though they're invalid.

Based on this and the previous Apple bug, I don't think we've seen the last serious TLS stack bug. Testing TLS is notoriously difficult, as it's pretty complicated. Of course, GnuTLS doesn't have a great track record for correctness in general, so this specifically isn't that surprising.

Return values in C

The bug is the disagreement about return values and true and false. In C, the situation about what to return for success verses failure is sort of complicated.

Let's take socket(2) and connect(2) as examples. To get an IPv4 socket in C, you need to do this:

#include <sys/types.h>
#include <sys/socket.h>

// in some function
int s = socket(AF_INET, SOCK_STREAM, 0);


How do you know if this socket is valid? That is, you successfully got a valid socket that you can call connect with?

You test s. The man page says that on error, -1 is returned. So a common way to test this would be:

if (s < 0) {
    perror("Couldn't get socket");
    exit(EXIT_FAILURE); // or whatever you want to do
}

Easy enough. Let's connect with our socket now:
struct sockaddr_in addr; // set this up somehow
if (connect(s, &addr, sizeof(addr)) {
    perror("Couldn't connect"); 
    exit(EXIT_FAILURE);
}

Because connect returns zero on success, the error check for connect looks backwards. If we've connected, connect will return 0, which evaluates to false. So to avoid this confusion, most C programmers would add an explicit check for less than zero.

Due to this historical standard, you have essentially two options:

  1. Follow the C tradition and return zero for success and non-zero (or less than zero, it depends) for failure.
  2. Return explicit error codes that should be checked.

GnuTLS used a third option, which is the opposite of the first one, returning 1 for success and 0 for failure, and then mixed that with code that used the C traditional method.

If you think this is all just nonsense and that you should use exceptions instead, it's not really clear that that's better in all cases. Martin Sústrik, the author of ZeroMQ, wishes he wrote ZeroMQ in C rather than C++ with exceptions.

git blame

Let's find out how this bug got in here in the first place.

$ git clone https://git.gitorious.org/gnutls/gnutls.git
$ git checkout 6aa26f78150ccbdf^

Now we're at the commit before the bug fix. Let's run git blame and see who edited what when:
$ git blame lib/x509/verify.c

The problem lines around 141-145 were edited by Simon Josefsson, one of the two maintainers of GnuTLS, in 2005. Wow this bug is old!

If you keep going, to a5891d7^, then to 802e1ed^ you'll finally arrive at 0fba2d9^ the first version without the bug.

So it was 0fba2d9 that caused the issue. Why?

This commit was a large refactor of several parts of the certificate code. Many new functions were written which follow the old C-style error handling (return less than zero for failure, zero for success), such as _gnutls_x509_get_signature.

In the same commit, Nikos refactors check_if_ca and it looks remarkably similar to the traditional C-style error handling the other methods he was adding and refactoring. It looks like he just forgot that this wasn't a C-style error handling method at all, but a true-means-true one.

Lessons

What can we learn from this?

If you're writing in C, you need to pick one of the two C error handling model and stick with it. Aggressively refactor any code that doesn't match your error handling model choice.

Use smaller commits. It's more likely that Nikos or someone reviewing his commit would have seen the error in his diff if it was smaller. He might even have not made the mistake in the first place, as the refactor would have been different for check_if_ca.

Perhaps most important of all, test your crypto code. It's the biggest win for quality. The public facing method, gnutls_x509_crt_verify actually still doesn't have any unit tests for it. This should change.

C can be hard to get right, so it's important to be strict in how you write it and it's important to test it well. We haven't seen the last critical TLS stack bug, so don't be surprised when the next one comes. Maybe we should pool our money and pay some security auditors to audit common TLS implementations that we all use daily.

Sean is a software engineer who is passionate about doing things right. He is currently working on Squadron: an awesome configuration and release management tool for SaaS applications.

related posts


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、资源1项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值