Tuesday, November 22, 2011

Revisiting building OpenSSL

$ ./Configure --prefix=/usr/local no-threads shared darwin64-x86_64-cc
$ make

Sunday, November 20, 2011

Getting the certificate in DER and printing it

This hack fetches the certificate from a web server and prints it out in hex format. It's pretty ugly at this point, but note that the certificate bytes match.

#include
#include
#include
#include

BIO *b_err;

int dane_verify(SSL *con, char *s_host, short s_port) {
X509 *dane_peer = NULL;
char buf[BUFSIZ];
if (b_err == NULL)
b_err=BIO_new_fp(stderr,BIO_NOCLOSE);
BIO_printf(b_err, "DANE:%s:%d\n", s_host, s_port);

dane_peer = SSL_get_peer_certificate(con);
if (dane_peer != NULL) {
BIO_printf(b_err, "DANE:Server certificate\n");
X509_NAME_oneline(X509_get_subject_name(dane_peer),
buf,sizeof buf);
BIO_printf(b_err,"DANE:subject=%s\n",buf);
X509_NAME_oneline(X509_get_issuer_name(dane_peer),
buf,sizeof buf);
BIO_printf(b_err,"DANE:issuer=%s\n",buf);
//X509_print(b_err,dane_peer);
int len;
unsigned char *buf, *buf_tmp;

buf = NULL;

len = i2d_X509(dane_peer, &buf);
buf_tmp = buf;
int der_bytes[len+1];
int i;
for (i=0; i BIO_printf(b_err, "%2X", *buf_tmp);
buf_tmp++;
}
BIO_printf(b_err, "\n");
der_bytes[len] = '\0';

BIO_printf(b_err, "DANE: here we go: %X\n", der_bytes);

if (len < 0) {
BIO_printf(b_err, "DANE: ops\n");
} else {
BIO_printf(b_err, "DANE... %d\n", len);
BIO_printf(b_err, "DANE: %u\n", buf);
}



} else
BIO_printf(b_err,"DANE:no peer certificate available\n");

(void)BIO_flush(b_err);
return 0;
}

$ ./openssl s_client -dane -connect www.example.com:443
CONNECTED(00000003)
DANE:www.example.com:443
DANE:no peer certificate available
DANE:www.example.com:443
DANE:no peer certificate available
depth=0 C = US, ST = California, L = Mountain View, O = Default Company Ltd, CN = www.example.com, emailAddress = mathias.samuelson@gmail.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Default Company Ltd, CN = www.example.com, emailAddress = mathias.samuelson@gmail.com
verify return:1
DANE:www.example.com:443
DANE:Server certificate
DANE:subject=/C=US/ST=California/L=Mountain View/O=Default Company Ltd/CN=www.example.com/emailAddress=mathias.samuelson@gmail.com
DANE:issuer=/C=US/ST=California/L=Mountain View/O=Default Company Ltd/CN=www.example.com/emailAddress=mathias.samuelson@gmail.com
3082 2B53082 21E 2 9 087DEC83E4049FDBD30 D 6 92A864886F7 D 1 1 5 5 030819E31 B30 9 6 355 4 613 2555331133011 6 355 4 8 C A43616C69666F726E696131163014 6 355 4 7 C D4D6F756E7461696E2056696577311C301A 6 355 4 A C1344656661756C7420436F6D70616E79204C746431183016 6 355 4 3 C F7777772E6578616D706C652E636F6D312A3028 6 92A864886F7 D 1 9 1161B6D6174686961732E73616D75656C736F6E40676D61696C2E636F6D301E17 D3131313131333232333633395A17 D3132313131323232333633395A30819E31 B30 9 6 355 4 613 2555331133011 6 355 4 8 C A43616C69666F726E696131163014 6 355 4 7 C D4D6F756E7461696E2056696577311C301A 6 355 4 A C1344656661756C7420436F6D70616E79204C746431183016 6 355 4 3 C F7777772E6578616D706C652E636F6D312A3028 6 92A864886F7 D 1 9 1161B6D6174686961732E73616D75656C736F6E40676D61696C2E636F6D30819F30 D 6 92A864886F7 D 1 1 1 5 0 3818D 0308189 28181 0A8148A 27BB0DA5613 5963ABF7092741E443543AA16DEE369ED4E9542298BEF3DFCB16A2D 1C38EC6A89E8CD6 58889BC6E8BBD659F84726AA5E9F2A459BD40 1169AF91479 99CA34747 B75A5D3FC5FE1AF8823D4FC4B 9D7FD92A48244457911B466652E8AC69458E8EA4EA9896A D5AE76A6D 32F5DEB961B24AC61D331 2 3 1 0 130 D 6 92A864886F7 D 1 1 5 5 0 38181 08C2ACCCB27885E42DB35724E3BCE6CD58DA5671CE5AD7B8EC0B5C52421C4AA84BD 3 28D5CFF 19A853DE74ED7 471D3F610489C7FC232522B13623AA4F4B38A 82F28 51016917CE588DA8326156629CF70FFB2663C8A63CE 860CA81E7815F82A4FB3BF7DD385F 38696F5 04254C5BC39451DA220FE24D0578483464F6D58
DANE: here we go: 5FBFE130
DANE... 697
DANE: 3280608
...

The certificate starts with the bytes 0x3082 ... ends with 0x6D58.

We see that the number of bytes is 697, whereas the record in DNS is 699 bytes. The record should have three one octet fields and then the certificate bytes, but I'm actually only seeing two octets before the certificate. Need to look into why that is.

So far so good though.

Fetching the TLSA record with libunbound

Here's the modified code that fetches a TLSA record and prints the bytes in hex:

#include
#include
#include
#include
#include

int main(void)
{
struct ub_ctx* ctx;
struct ub_result* result;
int retval;

/* create context */
ctx = ub_ctx_create();
if(!ctx) {
printf("error: could not create unbound context\n");
return 1;
}
/* read /etc/resolv.conf for DNS proxy settings (from DHCP) */
if( (retval=ub_ctx_resolvconf(ctx, "/etc/resolv.conf")) != 0) {
printf("error reading resolv.conf: %s. errno says: %s\n",
ub_strerror(retval), strerror(errno));
return 1;
}
/* read /etc/hosts for locally supplied host addresses */
if( (retval=ub_ctx_hosts(ctx, "/etc/hosts")) != 0) {
printf("error reading hosts: %s. errno says: %s\n",
ub_strerror(retval), strerror(errno));
return 1;
}

/* read public keys for DNSSEC verification */
if( (retval=ub_ctx_add_ta_file(ctx, "keys")) != 0) {
printf("error adding keys: %s\n", ub_strerror(retval));
return 1;
}

/* query for webserver */
retval = ub_resolve(ctx, "_443._tcp.www.example.com",
65468 /* TYPE A (IPv4 address) */,
1 /* CLASS IN (internet) */, &result);
if(retval != 0) {
printf("resolve error: %s\n", ub_strerror(retval));
return 1;
}

/* show first result */
if(result->havedata) {
unsigned char *buf = (char*)result->data[0];
int i;
printf("The record length is %d\n", result->len[0]);
for (i = 0; i < result->len[0]; i++) {
printf("%2X", *buf);
buf++;
}
printf("\n");
}
/* show security status */
if(result->secure)
printf("Result is secure\n");
else if(result->bogus)
printf("Result is bogus: %s\n", result->why_bogus);
else printf("Result is insecure\n");

ub_resolve_free(result);
ub_ctx_delete(ctx);
return 0;
}

To compile and run:
$ gcc -o unb_secure_resolve unb_secure_resolve.c -I/usr/local/include -L/usr/local/lib -lunbound
$ ./unb_secure_resolve The record length is 699
1 03082 2B53082 21E 2 9 087DEC83E4049FDBD30 D 6 92A864886F7 D 1 1 5 5 030819E31 B30 9 6 355 4 613 2555331133011 6 355 4 8 C A43616C69666F726E696131163014 6 355 4 7 C D4D6F756E7461696E2056696577311C301A 6 355 4 A C1344656661756C7420436F6D70616E79204C746431183016 6 355 4 3 C F7777772E6578616D706C652E636F6D312A3028 6 92A864886F7 D 1 9 1161B6D6174686961732E73616D75656C736F6E40676D61696C2E636F6D301E17 D3131313131333232333633395A17 D3132313131323232333633395A30819E31 B30 9 6 355 4 613 2555331133011 6 355 4 8 C A43616C69666F726E696131163014 6 355 4 7 C D4D6F756E7461696E2056696577311C301A 6 355 4 A C1344656661756C7420436F6D70616E79204C746431183016 6 355 4 3 C F7777772E6578616D706C652E636F6D312A3028 6 92A864886F7 D 1 9 1161B6D6174686961732E73616D75656C736F6E40676D61696C2E636F6D30819F30 D 6 92A864886F7 D 1 1 1 5 0 3818D 0308189 28181 0A8148A 27BB0DA5613 5963ABF7092741E443543AA16DEE369ED4E9542298BEF3DFCB16A2D 1C38EC6A89E8CD6 58889BC6E8BBD659F84726AA5E9F2A459BD40 1169AF91479 99CA34747 B75A5D3FC5FE1AF8823D4FC4B 9D7FD92A48244457911B466652E8AC69458E8EA4EA9896A D5AE76A6D 32F5DEB961B24AC61D331 2 3 1 0 130 D 6 92A864886F7 D 1 1 5 5 0 38181 08C2ACCCB27885E42DB35724E3BCE6CD58DA5671CE5AD7B8EC0B5C52421C4AA84BD 3 28D5CFF 19A853DE74ED7 471D3F610489C7FC232522B13623AA4F4B38A 82F28 51016917CE588DA8326156629CF70FFB2663C8A63CE 860CA81E7815F82A4FB3BF7DD385F 38696F5 04254C5BC39451DA220FE24D0578483464F6D58
Result is insecure

Obviously the output leaves something to be desired, but that's not the point. Next post will show the equivalent output from OpenSSL.

Installing a validating DNS stub resolver

So a quick look at the alternatives available, primarily ldns and libunbound indicated that the latter has significantly more useful documentation. Documentation is king, so that's what I'm going to favor.

Installing libunbound on the Mac:

1. Download
2. ./configure --with-ldns=/usr/local/ --with-ssl=/usr/local/ssl/
3. make && make install

I then grabbed one of the tutorial examples from their website and compiled it:

$ gcc -o unb_secure_resolve unb_secure_resolve.c -I/usr/local/include -L/usr/local/lib -lunbound

BIND 9.7 configuration

[root@localhost ~]# cat /etc/named.conf
//
// named.conf
//
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only nameserver (as a localhost DNS resolver only).
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//

options {
listen-on port 53 { 127.0.0.1; 172.16.52.182; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
allow-query { any; };
recursion yes;

dnssec-enable yes;
dnssec-validation yes;
dnssec-lookaside auto;

/* Path to ISC DLV key */
bindkeys-file "/etc/named.iscdlv.key";
};

logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
};

zone "example.com" IN {
type master;
file "example.com";
};

[root@localhost ~]# cat /var/named/example.com
$TTL 1D
$ORIGIN example.com.
@ IN SOA master rname.invalid. (
1 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS master
master A 172.16.52.182
www A 172.16.52.182
_443._tcp.www IN TYPE65468 \# 34 0101D1A0F378F90614277D1677F3ADF67FD56390009BDFFB6A3429C421A26E37FE22

The record at _443._tcp.www was generated by the dane tool and uses a temporary record type waiting for the TLSA to get an official number assigned.

Sunday, November 13, 2011

Setting up a poc environment

Long time since the last post, been very busy with work and the last couple of weeks also struggling with a flu.

Anyway, I will now start building a local poc environment running as a VM on my laptop. It will consist of the following components:

  • Centos 6
  • BIND 9.something
  • OpenSSL
  • Apache

Just finished installing a fresh VM.

"dane" command

This command generates the TLSA record based on a TLS interaction with a webserver. Written Poul Wouters, it can be found as part of sshfp which is here.

Running 'make install' copies the files into the right places, but when trying to use it we now need to deal with all the dependencies.

First, since I'm using Python 2.6 I need to get argparse. Can be found here. Installing setuptools (yum install python-setuptools) made that easy, all I needed to do was run easy_install argparse.

Next stop, ldns-python. Can't find it in yum, so I'll go to a repository for it. Download rpm, install. Not. Need ldns. Install ldns, and then install ldns-python. Done.

Will leave it like that for now.