From: Nikos Mavrogiannopoulos Date: Fri, 8 Aug 2014 18:32:02 +0000 (+0200) Subject: danetool: if the certificate to verify against is not provide it try to obtain it X-Git-Tag: gnutls_3_4_0~1102 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9b0d293f5877596f65eebcb5076c17758acdd417;p=thirdparty%2Fgnutls.git danetool: if the certificate to verify against is not provide it try to obtain it --- diff --git a/src/Makefile.am b/src/Makefile.am index 55bcd5f4f1..78d8cb293b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,7 +155,7 @@ libcmd_certtool_la_LIBADD += $(LIBOPTS) $(LTLIBINTL) libcmd_certtool_la_LIBADD += $(LTLIBREADLINE) gl/libgnu_gpl.la libcmd_certtool_la_LIBADD += $(INET_PTON_LIB) $(LIB_CLOCK_GETTIME) -danetool_SOURCES = danetool.c certtool-common.c certtool-extras.c common.c +danetool_SOURCES = danetool.c certtool-common.c certtool-extras.c common.c socket.c danetool_LDADD = ../lib/libgnutls.la danetool_LDADD += libcmd-danetool.la ../gl/libgnu.la gl/libgnu_gpl.la if ENABLE_DANE diff --git a/src/cli.c b/src/cli.c index 8a76f2c55f..3399531c3b 100644 --- a/src/cli.c +++ b/src/cli.c @@ -374,49 +374,6 @@ static int read_yesno(const char *input_str) return 0; } -/* converts a textual service or port to - * a service. - */ -static const char *port_to_service(const char *sport) -{ - unsigned int port; - struct servent *sr; - - port = atoi(sport); - if (port == 0) - return sport; - - port = htons(port); - - sr = getservbyport(port, udp ? "udp" : "tcp"); - if (sr == NULL) { - fprintf(stderr, - "Warning: getservbyport() failed. Using port number as service.\n"); - return sport; - } - - return sr->s_name; -} - -static int service_to_port(const char *service) -{ - unsigned int port; - struct servent *sr; - - port = atoi(service); - if (port != 0) - return port; - - sr = getservbyname(service, udp ? "udp" : "tcp"); - if (sr == NULL) { - fprintf(stderr, "Warning: getservbyname() failed.\n"); - exit(1); - } - - return ntohs(sr->s_port); -} - - static int cert_verify_callback(gnutls_session_t session) { int rc; @@ -471,7 +428,7 @@ static int cert_verify_callback(gnutls_session_t session) if (ca_verify == 0) vflags |= DANE_VFLAG_ONLY_CHECK_EE_USAGE; - port = service_to_port(service); + port = service_to_port(service, udp?"udp":"tcp"); rc = dane_verify_session_crt(NULL, session, hostname, udp ? "udp" : "tcp", port, sflags, vflags, &status); @@ -512,7 +469,7 @@ static int cert_verify_callback(gnutls_session_t session) return -1; } - txt_service = port_to_service(service); + txt_service = port_to_service(service, udp?"udp":"tcp"); rc = gnutls_verify_stored_pubkey(NULL, NULL, hostname, txt_service, @@ -893,7 +850,7 @@ static int try_resume(socket_st * hd) printf ("\n\n- Connecting again- trying to resume previous session\n"); - socket_open(hd, hostname, service, udp); + socket_open(hd, hostname, service, udp, CONNECT_MSG); hd->session = init_tls_session(hostname); gnutls_session_set_data(hd->session, session_data, @@ -1116,7 +1073,8 @@ int main(int argc, char **argv) init_global_tls_stuff(); - socket_open(&hd, hostname, service, udp); + socket_open(&hd, hostname, service, udp, CONNECT_MSG); + hd.verbose = verbose; hd.session = init_tls_session(hostname); if (starttls) diff --git a/src/danetool.c b/src/danetool.c index 7055b94cc1..dd3cd62dd9 100644 --- a/src/danetool.c +++ b/src/danetool.c @@ -49,7 +49,9 @@ #include #include "danetool-args.h" #include "certtool-common.h" +#include "socket.h" +static const char* obtain_cert(const char *hostname, const char *proto, unsigned int port); static void cmd_parser(int argc, char **argv); static void dane_info(const char *host, const char *proto, unsigned int port, unsigned int ca, @@ -187,6 +189,7 @@ static void dane_check(const char *host, const char *proto, unsigned int usage, type, match; gnutls_datum_t data, file; size_t size; + unsigned del = 0; unsigned vflags = DANE_VFLAG_FAIL_IF_NOT_CHECKED; if (ENABLED_OPT(LOCAL_DNS)) @@ -264,6 +267,8 @@ static void dane_check(const char *host, const char *proto, entries = dane_query_entries(q); for (i = 0; i < entries; i++) { + del = 0; + ret = dane_query_data(q, i, &usage, &type, &match, &data); if (ret < 0) { fprintf(stderr, "dane_query_data: %s\n", @@ -292,7 +297,12 @@ static void dane_check(const char *host, const char *proto, dane_cert_type_name(type), type); printf("Contents: %s (%.2x)\n", dane_match_type_name(match), match); - printf("Data: %s\n", lbuffer); + printf("Data: %s\n\n", lbuffer); + + if (!cinfo->cert) { + cinfo->cert = obtain_cert(host, proto, port); + del = 1; + } /* Verify the DANE data */ if (cinfo->cert) { @@ -375,9 +385,13 @@ static void dane_check(const char *host, const char *proto, } gnutls_free(clist); } + + if (del != 0) { + remove(cinfo->cert); + } } else { fprintf(stderr, - "\nCertificate was not verified. Use --load-certificate.\n"); + "\nCertificate could not be obtained. You can explicitly load the certificate using --load-certificate.\n"); } } @@ -522,3 +536,121 @@ static void dane_info(const char *host, const char *proto, port, proto, host, usage, selector, type, lbuffer); } + + +struct priv_st { + int fd; + int found; +}; + +static int cert_callback(gnutls_session_t session) +{ + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + int ret; + unsigned i; + gnutls_datum_t t; + struct priv_st *priv; + + cert_list = gnutls_certificate_get_peers(session, &cert_list_size); + if (cert_list_size == 0) { + fprintf(stderr, "no certificates sent by server!\n"); + return -1; + } + + priv = gnutls_session_get_ptr(session); + + for (i=0;ifd, t.data, t.size); + gnutls_free(t.data); + } + priv->found = 1; + + return -1; +} + +static int get_cert(socket_st *hd, const char *hostname, unsigned udp, int fd) +{ + gnutls_certificate_credentials_t xcred; + gnutls_session_t session; + int ret; + struct priv_st priv; + + priv.found = 0; + priv.fd = fd; + + ret = gnutls_certificate_allocate_credentials(&xcred); + if (ret < 0) { + fprintf(stderr, "error[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); + exit(1); + } + gnutls_certificate_set_verify_function(xcred, cert_callback); + + ret = gnutls_init(&session, (udp?GNUTLS_DATAGRAM:0)|GNUTLS_CLIENT); + if (ret < 0) { + fprintf(stderr, "error[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); + exit(1); + } + gnutls_session_set_ptr(session, &priv); + gnutls_transport_set_int(session, hd->fd); + + gnutls_set_default_priority(session); + gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname, strlen(hostname)); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + + do { + ret = gnutls_handshake(session); + } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_WARNING_ALERT_RECEIVED); + /* we don't care on the result */ + + gnutls_deinit(session); + gnutls_certificate_free_credentials(xcred); + + if (priv.found == 0) + return -1; + + return 0; +} + +static const char *obtain_cert(const char *hostname, const char *proto, unsigned int port) +{ + socket_st hd; + char txt_port[16]; + unsigned udp = 0; + static char tmpfile[32] = "danetool-certXXXXXX"; + int fd, ret; + + if (strcmp(proto, "udp") == 0) + udp = 1; + + sockets_init(); + snprintf(txt_port, sizeof(txt_port), "%u", port); + socket_open(&hd, hostname, port_to_service(txt_port, proto), udp, "Obtaining certificate from"); + + fd = mkstemp(tmpfile); + if (fd == -1) { + int e = errno; + fprintf(stderr, "error[%d]: %s\n", __LINE__, + strerror(e)); + exit(1); + } + + ret = get_cert(&hd, hostname, udp, fd); + close(fd); + + socket_bye(&hd); + + if (ret == -1) + return NULL; + else + return tmpfile; +} diff --git a/src/ocsptool-common.c b/src/ocsptool-common.c index 2e7a359aeb..5ea2030cbf 100644 --- a/src/ocsptool-common.c +++ b/src/ocsptool-common.c @@ -195,7 +195,7 @@ int send_ocsp_request(const char *server, (unsigned int) req.size); headers_size = strlen(headers); - socket_open(&hd, hostname, service, 0); + socket_open(&hd, hostname, service, 0, CONNECT_MSG); socket_send(&hd, headers, headers_size); socket_send(&hd, req.data, req.size); diff --git a/src/socket.c b/src/socket.c index a91fea7f81..1b2d851bec 100644 --- a/src/socket.c +++ b/src/socket.c @@ -40,7 +40,6 @@ #define MAX_BUF 4096 -extern unsigned int verbose; /* Functions to manipulate sockets */ @@ -105,7 +104,7 @@ socket_send_range(const socket_st * socket, const void *buffer, } while (ret == -1 && errno == EINTR); - if (ret > 0 && ret != buffer_size && verbose) + if (ret > 0 && ret != buffer_size && socket->verbose) fprintf(stderr, "*** Only sent %d bytes instead of %d.\n", ret, buffer_size); @@ -144,7 +143,7 @@ void socket_bye(socket_st * socket) void socket_open(socket_st * hd, const char *hostname, const char *service, - int udp) + int udp, const char *msg) { struct addrinfo hints, *res, *ptr; int sd, err; @@ -194,7 +193,8 @@ socket_open(socket_st * hd, const char *hostname, const char *service, } - printf("Connecting to '%s:%s'...\n", buffer, portname); + if (msg) + printf("%s '%s:%s'...\n", msg, buffer, portname); err = connect(sd, ptr->ai_addr, ptr->ai_addrlen); if (err < 0) { @@ -240,3 +240,45 @@ void sockets_init(void) #endif } + +/* converts a textual service or port to + * a service. + */ +const char *port_to_service(const char *sport, const char *proto) +{ + unsigned int port; + struct servent *sr; + + port = atoi(sport); + if (port == 0) + return sport; + + port = htons(port); + + sr = getservbyport(port, proto); + if (sr == NULL) { + fprintf(stderr, + "Warning: getservbyport() failed. Using port number as service.\n"); + return sport; + } + + return sr->s_name; +} + +int service_to_port(const char *service, const char *proto) +{ + unsigned int port; + struct servent *sr; + + port = atoi(service); + if (port != 0) + return port; + + sr = getservbyname(service, proto); + if (sr == NULL) { + fprintf(stderr, "Warning: getservbyname() failed.\n"); + exit(1); + } + + return ntohs(sr->s_port); +} diff --git a/src/socket.h b/src/socket.h index 00ec616378..d06788ce17 100644 --- a/src/socket.h +++ b/src/socket.h @@ -9,6 +9,7 @@ typedef struct { char *service; struct addrinfo *ptr; struct addrinfo *addr_info; + int verbose; } socket_st; ssize_t socket_recv(const socket_st * socket, void *buffer, @@ -18,7 +19,12 @@ ssize_t socket_send(const socket_st * socket, const void *buffer, ssize_t socket_send_range(const socket_st * socket, const void *buffer, int buffer_size, gnutls_range_st * range); void socket_open(socket_st * hd, const char *hostname, const char *service, - int udp); + int udp, const char *msg); void socket_bye(socket_st * socket); void sockets_init(void); + +int service_to_port(const char *service, const char *proto); +const char *port_to_service(const char *sport, const char *proto); + +#define CONNECT_MSG "Connecting to" diff --git a/tests/suite/testdane b/tests/suite/testdane index eeb283cc30..971b47d87f 100755 --- a/tests/suite/testdane +++ b/tests/suite/testdane @@ -19,7 +19,6 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. srcdir="${srcdir:-.}" -CLI="${CLI:-../../src/gnutls-cli$EXEEXT}" DANETOOL="${DANETOOL:-../../src/danetool$EXEEXT}" unset RETCODE @@ -40,14 +39,8 @@ HOSTS="$HOSTS nohats.ca" # used to work: dane.nox.su torproject.org jhcloos.com for i in $HOSTS;do echo -n "$i: " -rm -f tmp -$CLI $i -p 443 --print-cert --insecure >tmp 2>&1 /dev/null 2>&1 +$DANETOOL --check $i >/dev/null 2>&1 if [ $? != 0 ];then echo "Error checking $i" exit 1 @@ -63,14 +56,7 @@ HOSTS="bad-hash.dane.verisignlabs.com bad-params.dane.verisignlabs.com" HOSTS="$HOSTS bad-sig.dane.verisignlabs.com" for i in $HOSTS;do echo -n "$i: " -rm -f tmp -$CLI $i -p 443 --print-cert --insecure >tmp 2>&1 /dev/null 2>&1 +$DANETOOL --check $i >/dev/null 2>&1 if [ $? = 0 ];then echo "Checking $i should have failed" exit 1