From: Wouter Wijngaards Date: Fri, 24 Sep 2010 17:33:49 +0000 (+0000) Subject: unbound-anchor work X-Git-Tag: release-1.4.7rc1~78 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dcb871a75598cfeff4f8c3d8254b8ce661c867c7;p=thirdparty%2Funbound.git unbound-anchor work git-svn-id: file:///svn/unbound/trunk@2247 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/smallapp/unbound-anchor.c b/smallapp/unbound-anchor.c index 570c5087a..9e80b5350 100644 --- a/smallapp/unbound-anchor.c +++ b/smallapp/unbound-anchor.c @@ -130,6 +130,18 @@ usage() exit(1); } +/** print hex data */ +static void +print_data(char* msg, char* data, int len) +{ + int i; + printf("%s: ", msg); + for(i=0; inext; + free(p); + p = np; + } +} + /** create ip_list entry for a RR record */ static struct ip_list* RR_to_ip(int tp, char* data, int len) @@ -561,20 +638,25 @@ connect_to_ip(struct ip_list* ip) return fd; } -/** initiate TLS on a connection */ -static SSL* -TLS_initiate(int fd) +/** create SSL context */ +static SSL_CTX* +setup_sslctx(void) { - SSL_CTX* sslctx; - SSL* ssl; - X509* x; - int r; - sslctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX* sslctx = SSL_CTX_new(SSLv23_client_method()); if(!sslctx) { if(verb) printf("SSL_CTX_new error\n"); return NULL; } - ssl = SSL_new(sslctx); + return sslctx; +} + +/** initiate TLS on a connection */ +static SSL* +TLS_initiate(SSL_CTX* sslctx, int fd) +{ + X509* x; + int r; + SSL* ssl = SSL_new(sslctx); if(!ssl) { if(verb) printf("SSL_new error\n"); return NULL; @@ -611,13 +693,14 @@ TLS_initiate(int fd) /** perform neat TLS shutdown */ static void -TLS_shutdown(int fd, SSL* ssl) +TLS_shutdown(int fd, SSL* ssl, SSL_CTX* sslctx) { /* shutdown the SSL connection nicely */ if(SSL_shutdown(ssl) == 0) { SSL_shutdown(ssl); } SSL_free(ssl); + SSL_CTX_free(sslctx); fd_close(fd); } @@ -731,22 +814,27 @@ read_http_headers(SSL* ssl, size_t* clen) static char* read_data_chunk(SSL* ssl, size_t len) { + size_t got = 0; int r; char* data = malloc(len+1); if(!data) { if(verb) printf("out of memory\n"); return NULL; } - if((r = SSL_read(ssl, data, len)) <= 0) { - if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { - /* EOF */ - if(verb) printf("could not SSL_read: unexpected EOF\n"); + while(got < len) { + if((r = SSL_read(ssl, data+got, (int)(len-got))) <= 0) { + if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { + /* EOF */ + if(verb) printf("could not SSL_read: unexpected EOF\n"); + free(data); + return NULL; + } + if(verb) printf("could not SSL_read\n"); free(data); return NULL; } - if(verb) printf("could not SSL_read\n"); - free(data); - return NULL; + if(verb >= 2) printf("at %d/%d\n", (int)got, (int)len); + got += r; } if(verb>=2) printf("read %d data\n", (int)len); data[len] = 0; @@ -809,11 +897,13 @@ do_chunked_read(SSL* ssl) BIO_free(mem); return NULL; } - if(BIO_write(mem, body, len) <= 0) { + if(BIO_write(mem, body, (int)len) <= 0) { if(verb) printf("out of memory\n"); + free(body); BIO_free(mem); return NULL; } + free(body); /* skip empty line after data chunk */ if(!read_ssl_line(ssl, buf, sizeof(buf))) { BIO_free(mem); @@ -832,7 +922,9 @@ write_http_get(SSL* ssl, char* pathname, char* urlname) write_ssl_line(ssl, "Host: %s", urlname) && write_ssl_line(ssl, "User-Agent: unbound-anchor/%s", PACKAGE_VERSION) && - /*write_ssl_line(ssl, "Connection: close", NULL) &&*/ + /* We do not really do multiple queries per connection, + * but this header setting is also not needed. + * write_ssl_line(ssl, "Connection: close", NULL) &&*/ write_ssl_line(ssl, "", NULL)) { return 1; } @@ -855,11 +947,14 @@ read_http_result(SSL* ssl) char* d = NULL; long l; l = BIO_get_mem_data(tmp, &d); - if(verb) printf("chunked data is %d\n", (int)l); + if(verb>=2) printf("chunked data is %d\n", (int)l); if(l == 0 || d == NULL) { if(verb) printf("out of memory\n"); return NULL; } + /* the result is zero terminated for robustness, but we + * do not include that in the BIO len (for binary data) */ + len = (size_t)l-1; data = strdup(d); if(data == NULL) { if(verb) printf("out of memory\n"); @@ -869,7 +964,8 @@ read_http_result(SSL* ssl) } else { data = read_data_chunk(ssl, len); } - m = BIO_new_mem_buf(data, -1); + if(verb >= 4) print_data("read data", data, (int)len); + m = BIO_new_mem_buf(data, (int)len); if(!m) { if(verb) printf("out of memory\n"); exit(0); @@ -884,22 +980,30 @@ https_to_ip(struct ip_list* ip, char* pathname, char* urlname) int fd; SSL* ssl; BIO* bio; + SSL_CTX* sslctx = setup_sslctx(); + if(!sslctx) { + return NULL; + } fd = connect_to_ip(ip); - if(fd == -1) + if(fd == -1) { + SSL_CTX_free(sslctx); return NULL; - ssl = TLS_initiate(fd); + } + ssl = TLS_initiate(sslctx, fd); if(!ssl) { + SSL_CTX_free(sslctx); fd_close(fd); return NULL; } if(!write_http_get(ssl, pathname, urlname)) { if(verb) printf("could not write to server\n"); SSL_free(ssl); + SSL_CTX_free(sslctx); fd_close(fd); return NULL; } bio = read_http_result(ssl); - TLS_shutdown(fd, ssl); + TLS_shutdown(fd, ssl, sslctx); return bio; } @@ -925,11 +1029,107 @@ https(struct ip_list* ip_list, char* pathname, char* urlname) if(verb) printf("could not fetch %s\n", pathname); exit(0); } else { - if(verb) printf("fetched %s\n", pathname); + if(verb) printf("fetched %s (%d bytes)\n", + pathname, (int)BIO_ctrl_pending(bio)); } return bio; } +/** free up a downloaded file BIO */ +static void +free_file_bio(BIO* bio) +{ + char* pp = NULL; + BIO_reset(bio); + (void)BIO_get_mem_data(bio, &pp); + free(pp); + BIO_free(bio); +} + +/** verify a PKCS7 signature, false on failure */ +static int +verify_p7sig(BIO* data, BIO* p7s, BIO* pem, STACK_OF(X509)* trust, time_t now) +{ + X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); + PKCS7* p7; + X509_STORE *store = X509_STORE_new(); + STACK_OF(X509) *certs; + int secure = 0; + int i; + + BIO_reset(p7s); + BIO_reset(data); + + if(!param || !store) { + if(verb) printf("out of memory\n"); + X509_VERIFY_PARAM_free(param); + X509_STORE_free(store); + return 0; + } + + /* convert p7s to p7 (the signature) */ + p7 = d2i_PKCS7_bio(p7s, NULL); + if(!p7) { + if(verb) printf("could not parse p7s signature file\n"); + X509_VERIFY_PARAM_free(param); + X509_STORE_free(store); + return 0; + } + if(verb >= 2) printf("parsed the PKCS7 signature\n"); + + /* convert trust to trusted certificate store */ + /* set current time */ + X509_VERIFY_PARAM_set_time(param, now); + /* do the selfcheck on the root certificate */ + X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CHECK_SS_SIGNATURE); + X509_STORE_set1_param(store, param); + for(i=0; i= 2) printf("setup the X509_STORE\n"); + + /* convert pem to (intermediate) certs */ + certs = read_cert_bio(pem); + if(verb >= 2) printf("read the intermediate certificates\n"); + + if(PKCS7_verify(p7, certs, store, data, NULL, 0) == 1) { + secure = 1; + if(verb >= 2) printf("the PKCS7 signature verified\n"); + } + +#ifndef S_SPLINT_S + sk_X509_pop_free(certs, X509_free); +#endif + X509_STORE_free(store); + PKCS7_free(p7); + return secure; +} + +/** Perform the verification and update of the trustanchor file */ +static void +verify_and_update_anchor(char* root_anchor_file, char* debugconf, + struct ub_result* dnskey, BIO* xml, BIO* p7s, BIO* pem, + STACK_OF(X509)* cert) +{ + time_t now = get_time_now(debugconf); + + /* verify xml file */ + if(!verify_p7sig(xml, p7s, pem, cert, now)) { + printf("the PKCS7 signature failed\n"); + exit(0); + } + + /* see if xml file verifies the dnskey that was probed */ + + /* reinstate 5011 tracking */ + +} + /** perform actual certupdate work */ static int do_certupdate(char* root_anchor_file, char* root_cert_file, @@ -956,22 +1156,24 @@ do_certupdate(char* root_anchor_file, char* root_cert_file, /* update the pem file (optional) */ /*do_pem_update(cert, pem, &write_cert);*/ if(write_cert) { - /*write_cert_file(root_cert_file, cert);*/ + (void)write_cert_file(root_cert_file, cert); + if(verb) printf("wrote cert to %s\n", root_cert_file); } /* verify and update the root anchor */ - /* verify xml file */ - /* see if xml file verifies the dnskey that was probed */ - /* reinstate 5011 tracking */ + verify_and_update_anchor(root_anchor_file, debugconf, dnskey, + xml, p7s, pem, cert); if(verb) printf("success: the anchor has been updated " "using the cert\n"); - /* TODO free the data buffers inside them */ - BIO_free(xml); - BIO_free(p7s); - BIO_free(pem); - sk_X509_free(cert); + free_file_bio(xml); + free_file_bio(p7s); + free_file_bio(pem); +#ifndef S_SPLINT_S + sk_X509_pop_free(cert, X509_free); +#endif ub_resolve_free(dnskey); + ip_list_free(ip_list); return 0; } @@ -1134,32 +1336,6 @@ read_last_success_time(char* file) return 0; } -/** - * Get current time. - * @param debugconf: with override time for tests - */ -static int32_t -get_time_now(char* debugconf) -{ - if(debugconf) { - FILE* in = fopen(debugconf, "r"); - char line[1024]; - if(!in) { - if(verb) printf("%s: %s\n", debugconf, strerror(errno)); - return (int32_t)time(NULL); - } - /* must be ^val-override-date: 1234567$ formatted */ - while(fgets(line, (int)sizeof(line), in)) { - if(strncmp(line, "val-override-date: ", 19) == 0) { - fclose(in); - return (int32_t)atoi(line+19); - } - } - fclose(in); - } - return (int32_t)time(NULL); -} - /** * Read autotrust 5011 probe file and see if the date * compared to the current date allows a certupdate. @@ -1172,7 +1348,7 @@ static int probe_date_allows_certupdate(char* root_anchor_file, char* debugconf) { int32_t last_success = read_last_success_time(root_anchor_file); - int32_t now = get_time_now(debugconf); + int32_t now = (int32_t)get_time_now(debugconf); int32_t leeway = 30 * 24 * 3600; /* 30 days leeway */ /* if the date is before 2010-07-15:00.00.00 then the root has not * been signed yet, and thus we refuse to take action. */