diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2021-02-09 16:00:23 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2021-02-09 16:00:23 +0900 |
commit | 5ce840383da7cf82ffa7dfaeda187f3fe3d591a7 (patch) | |
tree | f93fb33cde2a62aa414b61dca085f3fd0613aaca /dirmngr | |
parent | d9f0d99e31569835e295b990029c6dd19554299c (diff) | |
download | gpg2-5ce840383da7cf82ffa7dfaeda187f3fe3d591a7.tar.gz gpg2-5ce840383da7cf82ffa7dfaeda187f3fe3d591a7.tar.bz2 gpg2-5ce840383da7cf82ffa7dfaeda187f3fe3d591a7.zip |
Imported Upstream version 2.1.22upstream/2.1.22
Diffstat (limited to 'dirmngr')
-rw-r--r-- | dirmngr/Makefile.am | 8 | ||||
-rw-r--r-- | dirmngr/dirmngr-client.c | 2 | ||||
-rw-r--r-- | dirmngr/dirmngr.c | 88 | ||||
-rw-r--r-- | dirmngr/dirmngr.h | 9 | ||||
-rw-r--r-- | dirmngr/dirmngr_ldap.c | 2 | ||||
-rw-r--r-- | dirmngr/dns-stuff.c | 49 | ||||
-rw-r--r-- | dirmngr/dns.c | 288 | ||||
-rw-r--r-- | dirmngr/http.c | 328 | ||||
-rw-r--r-- | dirmngr/http.h | 5 | ||||
-rw-r--r-- | dirmngr/ks-action.c | 16 | ||||
-rw-r--r-- | dirmngr/ks-engine-finger.c | 2 | ||||
-rw-r--r-- | dirmngr/ks-engine-hkp.c | 272 | ||||
-rw-r--r-- | dirmngr/ks-engine-http.c | 26 | ||||
-rw-r--r-- | dirmngr/ldap.c | 1 | ||||
-rw-r--r-- | dirmngr/loadswdb.c | 1 | ||||
-rw-r--r-- | dirmngr/server.c | 42 | ||||
-rw-r--r-- | dirmngr/t-http.c | 31 |
17 files changed, 965 insertions, 205 deletions
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 34f2c5d..b404165 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -143,7 +143,7 @@ endif t_http_SOURCES = $(t_common_src) t-http.c http.c dns-stuff.c http-common.c t_http_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ $(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \ - $(GPG_ERROR_CFLAGS) $(KSBA_CFLAGS) + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(KSBA_CFLAGS) t_http_LDADD = $(t_common_ldadd) \ $(NTBTLS_LIBS) $(KSBA_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS) @@ -152,11 +152,13 @@ t_ldap_parse_uri_SOURCES = \ http.c http-common.c dns-stuff.c \ $(ldap_url) $(t_common_src) t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ - $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) + $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS) t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ - $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) + $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) t_dns_stuff_SOURCES = $(t_common_src) t-dns-stuff.c dns-stuff.c t_dns_stuff_LDADD = $(t_common_ldadd) $(DNSLIBS) diff --git a/dirmngr/dirmngr-client.c b/dirmngr/dirmngr-client.c index 4dc64bf..53b405e 100644 --- a/dirmngr/dirmngr-client.c +++ b/dirmngr/dirmngr-client.c @@ -80,7 +80,7 @@ static ARGPARSE_OPTS opts[] = { { oPEM, "pem", 0, N_("expect certificates in PEM format")}, { oForceDefaultResponder, "force-default-responder", 0, N_("force the use of the default OCSP responder")}, - { 0, NULL, 0, NULL } + ARGPARSE_end () }; diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 8393e0b..0d133c6 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -147,6 +147,8 @@ enum cmd_and_opt_values { oStandardResolver, oRecursiveResolver, oResolverTimeout, + oConnectTimeout, + oConnectQuickTimeout, aTest }; @@ -250,6 +252,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oStandardResolver, "standard-resolver", "@"), ARGPARSE_s_n (oRecursiveResolver, "recursive-resolver", "@"), ARGPARSE_s_i (oResolverTimeout, "resolver-timeout", "@"), + ARGPARSE_s_i (oConnectTimeout, "connect-timeout", "@"), + ARGPARSE_s_i (oConnectQuickTimeout, "connect-quick-timeout", "@"), ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing " "of all commands and options)\n")), @@ -277,6 +281,9 @@ static struct debug_flags_s debug_flags [] = #define DEFAULT_MAX_REPLIES 10 #define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */ +#define DEFAULT_CONNECT_TIMEOUT (15*1000) /* 15 seconds */ +#define DEFAULT_CONNECT_QUICK_TIMEOUT ( 2*1000) /* 2 seconds */ + /* For the cleanup handler we need to keep track of the socket's name. */ static const char *socket_name; /* If the socket has been redirected, this is the name of the @@ -327,8 +334,9 @@ static int network_activity_seen; static strlist_t hkp_cacert_filenames; -/* The timer tick used for housekeeping stuff. */ -#define TIMERTICK_INTERVAL (60) +/* The timer tick used for housekeeping stuff. The second constant is used when a shutdown is pending. */ +#define TIMERTICK_INTERVAL (60) +#define TIMERTICK_INTERVAL_SHUTDOWN (4) /* How oft to run the housekeeping. */ #define HOUSEKEEPING_INTERVAL (600) @@ -524,7 +532,17 @@ dirmngr_use_tor (void) { if (tor_mode == TOR_MODE_AUTO) { - /* FIXME: Figure out whether Tor is running. */ + /* Figure out whether Tor is running. */ + assuan_fd_t sock; + + sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR); + if (sock == ASSUAN_INVALID_FD) + tor_mode = TOR_MODE_NO; + else + { + tor_mode = TOR_MODE_YES; + assuan_sock_close (sock); + } } if (tor_mode == TOR_MODE_FORCE) @@ -602,6 +620,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) disable_check_own_socket = 0; enable_standard_resolver (0); set_dns_timeout (0); + opt.connect_timeout = 0; + opt.connect_quick_timeout = 0; return 1; } @@ -703,6 +723,14 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) set_dns_timeout (pargs->r.ret_int); break; + case oConnectTimeout: + opt.connect_timeout = pargs->r.ret_ulong * 1000; + break; + + case oConnectQuickTimeout: + opt.connect_quick_timeout = pargs->r.ret_ulong * 1000; + break; + default: return 0; /* Not handled. */ } @@ -716,6 +744,21 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) } +/* This fucntion is called after option parsing to adjust some values + * and call option setup functions. */ +static void +post_option_parsing (void) +{ + /* It would be too surpirsing if the quick timeout is larger than + * the standard value. */ + if (opt.connect_quick_timeout > opt.connect_timeout) + opt.connect_quick_timeout = opt.connect_timeout; + + set_debug (); + set_tor_mode (); +} + + #ifndef HAVE_W32_SYSTEM static int pid_suffix_callback (unsigned long *r_suffix) @@ -844,6 +887,10 @@ main (int argc, char **argv) /* Reset rereadable options to default values. */ parse_rereadable_options (NULL, 0); + /* Default TCP timeouts. */ + opt.connect_timeout = DEFAULT_CONNECT_TIMEOUT; + opt.connect_quick_timeout = DEFAULT_CONNECT_QUICK_TIMEOUT; + /* LDAP defaults. */ opt.add_new_ldapservers = 0; opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT; @@ -1031,8 +1078,7 @@ main (int argc, char **argv) log_printf ("\n"); } - set_debug (); - set_tor_mode (); + post_option_parsing (); /* Get LDAP server list from file. */ #if USE_LDAP @@ -1316,13 +1362,18 @@ main (int argc, char **argv) log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED); opt.running_detached = 1; - if (chdir("/")) + } +#endif + + if (!nodetach ) + { + if (gnupg_chdir (gnupg_daemon_rootdir ())) { - log_error ("chdir to / failed: %s\n", strerror (errno)); + log_error ("chdir to '%s' failed: %s\n", + gnupg_daemon_rootdir (), strerror (errno)); dirmngr_exit (1); } } -#endif thread_init (); cert_cache_init (hkp_cacert_filenames); @@ -1513,6 +1564,7 @@ dirmngr_init_default_ctrl (ctrl_t ctrl) if (opt.http_proxy) ctrl->http_proxy = xstrdup (opt.http_proxy); ctrl->http_no_crl = 1; + ctrl->timeout = opt.connect_timeout; } @@ -1774,8 +1826,7 @@ reread_configuration (void) } fclose (fp); - set_debug (); - set_tor_mode (); + post_option_parsing (); } @@ -1927,6 +1978,8 @@ time_for_housekeeping_p (time_t curtime) static void handle_tick (void) { + struct stat statbuf; + if (time_for_housekeeping_p (gnupg_get_time ())) { npth_t thread; @@ -1946,6 +1999,14 @@ handle_tick (void) npth_attr_destroy (&tattr); } } + + /* Check whether the homedir is still available. */ + if (!shutdown_pending + && stat (gnupg_homedir (), &statbuf) && errno == ENOENT) + { + shutdown_pending = 1; + log_info ("homedir has been removed - shutting down\n"); + } } @@ -2139,10 +2200,13 @@ handle_connections (assuan_fd_t listen_fd) npth_clock_gettime (&curtime); if (!(npth_timercmp (&curtime, &abstime, <))) { - /* Timeout. */ + /* Timeout. When a shutdown is pending we use a shorter + * interval to handle the shutdown more quickly. */ handle_tick (); npth_clock_gettime (&abstime); - abstime.tv_sec += TIMERTICK_INTERVAL; + abstime.tv_sec += (shutdown_pending + ? TIMERTICK_INTERVAL_SHUTDOWN + : TIMERTICK_INTERVAL); } npth_timersub (&abstime, &curtime, &timeout); diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index e10de09..1f660de 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -95,6 +95,10 @@ struct int force; /* Force loading outdated CRLs. */ + + unsigned int connect_timeout; /* Timeout for connect. */ + unsigned int connect_quick_timeout; /* Shorter timeout for connect. */ + int disable_http; /* Do not use HTTP at all. */ int disable_ldap; /* Do not use LDAP at all. */ int disable_ipv4; /* Do not use legacy IP addresses. */ @@ -194,6 +198,8 @@ struct server_control_s int audit_events; /* Send audit events to client. */ char *http_proxy; /* The used http_proxy or NULL. */ + unsigned int timeout; /* Timeout for connect calls in ms. */ + unsigned int http_no_crl:1; /* Do not check CRLs for https. */ }; @@ -223,6 +229,9 @@ int dirmngr_assuan_log_monitor (assuan_context_t ctx, unsigned int cat, void start_command_handler (gnupg_fd_t fd); gpg_error_t dirmngr_status (ctrl_t ctrl, const char *keyword, ...); gpg_error_t dirmngr_status_help (ctrl_t ctrl, const char *text); +gpg_error_t dirmngr_status_printf (ctrl_t ctrl, const char *keyword, + const char *format, + ...) GPGRT_ATTR_PRINTF(3,4); gpg_error_t dirmngr_tick (ctrl_t ctrl); /*-- http-ntbtls.c --*/ diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c index 836ced0..5a9ae97 100644 --- a/dirmngr/dirmngr_ldap.c +++ b/dirmngr/dirmngr_ldap.c @@ -150,7 +150,7 @@ static ARGPARSE_OPTS opts[] = { { oAttr, "attr", 2, N_("|STRING|return the attribute STRING")}, { oOnlySearchTimeout, "only-search-timeout", 0, "@"}, { oLogWithPID,"log-with-pid", 0, "@"}, - { 0, NULL, 0, NULL } + ARGPARSE_end () }; diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index a6c14cd..7324aae 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -45,6 +45,9 @@ # endif # include <netdb.h> #endif +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif #include <string.h> #include <unistd.h> @@ -111,6 +114,8 @@ #define DEFAULT_TIMEOUT 30 +#define RESOLV_CONF_NAME "/etc/resolv.conf" + /* Two flags to enable verbose and debug mode. */ static int opt_verbose; static int opt_debug; @@ -391,6 +396,39 @@ libdns_error_to_gpg_error (int serr) #endif /*USE_LIBDNS*/ +/* Return true if resolve.conf changed since it was last loaded. */ +#ifdef USE_LIBDNS +static int +resolv_conf_changed_p (void) +{ +#if defined(HAVE_W32_SYSTEM) || !defined(HAVE_STAT) + return 0; +#else + static time_t last_mtime; + const char *fname = RESOLV_CONF_NAME; + struct stat statbuf; + int changed = 0; + + if (stat (fname, &statbuf)) + { + log_error ("stat'ing '%s' failed: %s\n", + fname, gpg_strerror (gpg_error_from_syserror ())); + last_mtime = 1; /* Force a "changed" result the next time stat + * works. */ + } + else if (!last_mtime) + last_mtime = statbuf.st_mtime; + else if (last_mtime != statbuf.st_mtime) + { + changed = 1; + last_mtime = statbuf.st_mtime; + } + + return changed; +#endif +} +#endif /*USE_LIBDNS*/ + #ifdef USE_LIBDNS /* Initialize libdns. Returns 0 on success; prints a diagnostic and * returns an error code on failure. */ @@ -496,7 +534,8 @@ libdns_init (void) #else /* Unix */ const char *fname; - fname = "/etc/resolv.conf"; + fname = RESOLV_CONF_NAME; + resolv_conf_changed_p (); /* Reset timestamp. */ err = libdns_error_to_gpg_error (dns_resconf_loadpath (ld.resolv_conf, fname)); if (err) @@ -653,6 +692,14 @@ libdns_res_open (struct dns_resolver **r_res) *r_res = NULL; + /* Force a reload if resolv.conf has changed. */ + if (resolv_conf_changed_p ()) + { + if (opt_debug) + log_debug ("dns: resolv.conf changed - forcing reload\n"); + libdns_reinit_pending = 1; + } + if (libdns_reinit_pending) { libdns_reinit_pending = 0; diff --git a/dirmngr/dns.c b/dirmngr/dns.c index c660cf5..8e8b6db 100644 --- a/dirmngr/dns.c +++ b/dirmngr/dns.c @@ -205,8 +205,10 @@ typedef int socket_fd_t; #ifndef HAVE_STATIC_ASSERT #if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0) #define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */ +#elif defined(static_assert) +#define HAVE_STATIC_ASSERT 1 #else -#define HAVE_STATIC_ASSERT (defined static_assert) +#define HAVE_STATIC_ASSERT 0 #endif #endif @@ -380,7 +382,11 @@ const char *dns_strerror(int error) { * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE___ATOMIC_FETCH_ADD -#define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED) +#ifdef __ATOMIC_RELAXED +#define HAVE___ATOMIC_FETCH_ADD 1 +#else +#define HAVE___ATOMIC_FETCH_ADD 0 +#endif #endif #ifndef HAVE___ATOMIC_FETCH_SUB @@ -787,7 +793,11 @@ DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, s } /* dns_strnlcpy() */ -#define DNS_HAVE_SOCKADDR_UN (defined AF_UNIX && !defined _WIN32) +#if (defined AF_UNIX && !defined _WIN32) +#define DNS_HAVE_SOCKADDR_UN 1 +#else +#define DNS_HAVE_SOCKADDR_UN 0 +#endif static size_t dns_af_len(int af) { static const size_t table[AF_MAX] = { @@ -7051,11 +7061,19 @@ static void dns_socketclose(int *fd, const struct dns_options *opts) { #endif #ifndef HAVE_SOCK_CLOEXEC -#define HAVE_SOCK_CLOEXEC (defined SOCK_CLOEXEC) +#ifdef SOCK_CLOEXEC +#define HAVE_SOCK_CLOEXEC 1 +#else +#define HAVE_SOCK_CLOEXEC 0 +#endif #endif #ifndef HAVE_SOCK_NONBLOCK -#define HAVE_SOCK_NONBLOCK (defined SOCK_NONBLOCK) +#ifdef SOCK_NONBLOCK +#define HAVE_SOCK_NONBLOCK 1 +#else +#define HAVE_SOCK_NONBLOCK 0 +#endif #endif #define DNS_SO_MAXTRY 7 @@ -7583,14 +7601,30 @@ int dns_so_check(struct dns_socket *so) { retry: switch (so->state) { case DNS_SO_UDP_INIT: - so->state++; + if (so->remote.ss_family != so->local.ss_family) { + /* Family mismatch. Reinitialize. */ + if ((error = dns_so_closefd(so, &so->udp))) + goto error; + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + + /* If the user supplied an interface + statement, that is gone now. Sorry. */ + memset(&so->local, 0, sizeof so->local); + so->local.ss_family = so->remote.ss_family; + + if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, &error))) + goto error; + } + + so->state++; /* FALL THROUGH */ case DNS_SO_UDP_CONN: error = dns_connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)); dns_trace_sys_connect(so->trace, so->udp, SOCK_DGRAM, (struct sockaddr *)&so->remote, error); if (error) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_UDP_SEND: n = dns_send(so->udp, (void *)so->query->data, so->query->end, 0, &error); dns_trace_sys_send(so->trace, so->udp, SOCK_DGRAM, so->query->data, n, error); @@ -7600,7 +7634,7 @@ retry: so->stat.udp.sent.bytes += n; so->stat.udp.sent.count++; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_UDP_RECV: n = dns_recv(so->udp, (void *)so->answer->data, so->answer->size, 0, &error); dns_trace_sys_recv(so->trace, so->udp, SOCK_DGRAM, so->answer->data, n, error); @@ -7614,13 +7648,26 @@ retry: if ((error = dns_so_verify(so, so->answer))) goto trash; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_UDP_DONE: if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM) return 0; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_TCP_INIT: + if (so->remote.ss_family != so->local.ss_family) { + /* Family mismatch. Reinitialize. */ + if ((error = dns_so_closefd(so, &so->udp))) + goto error; + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + + /* If the user supplied an interface + statement, that is gone now. Sorry. */ + memset(&so->local, 0, sizeof so->local); + so->local.ss_family = so->remote.ss_family; + } + if (dns_so_tcp_keep(so)) { so->state = DNS_SO_TCP_SEND; @@ -7633,24 +7680,24 @@ retry: if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_TCP_CONN: error = dns_connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)); dns_trace_sys_connect(so->trace, so->tcp, SOCK_STREAM, (struct sockaddr *)&so->remote, error); if (error && error != DNS_EISCONN) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_TCP_SEND: if ((error = dns_so_tcp_send(so))) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_TCP_RECV: if ((error = dns_so_tcp_recv(so))) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_TCP_DONE: /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */ if (so->type != SOCK_STREAM) { @@ -7669,7 +7716,7 @@ retry: if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_CONN: { unsigned char method; @@ -7697,13 +7744,13 @@ retry: buffer[2] = method; so->state++; - } + } /* FALL THROUGH */ case DNS_SO_SOCKS_HELLO_SEND: if ((error = dns_so_tcp_send(so))) goto error; dns_so_tcp_recv_expect(so, 2); - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_HELLO_RECV: { unsigned char method; @@ -7751,7 +7798,7 @@ retry: } so->state++; - } + } /* FALL THROUGH */ case DNS_SO_SOCKS_AUTH_SEND: if ((error = dns_so_tcp_send(so))) goto error; @@ -7759,7 +7806,7 @@ retry: /* Skip the two length octets, and receive two octets. */ dns_so_tcp_recv_expect(so, 2); - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_AUTH_RECV: if ((error = dns_so_tcp_recv(so))) goto error; @@ -7776,7 +7823,7 @@ retry: goto error; } - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_REQUEST_PREPARE: /* Send request details (rfc-1928, 4). */ buffer = dns_so_tcp_send_buffer(so, so->remote.ss_family == AF_INET6 ? 22 : 10); @@ -7797,7 +7844,7 @@ retry: memcpy (buffer+8, &addr_in->sin_port, 2); /* DST.PORT */ } - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_REQUEST_SEND: if ((error = dns_so_tcp_send(so))) goto error; @@ -7805,7 +7852,7 @@ retry: /* Expect ten octets. This is the length of the * response assuming a IPv4 address is used. */ dns_so_tcp_recv_expect(so, 10); - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_REQUEST_RECV: if ((error = dns_so_tcp_recv(so))) goto error; @@ -7863,12 +7910,12 @@ retry: * the remaining bytes assuming an IPv6 address is * used. */ dns_so_tcp_recv_expect(so, 12); - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_REQUEST_RECV_V6: if ((error = dns_so_tcp_recv(so))) goto error; - so->state++; + so->state++; /* FALL THROUGH */ case DNS_SO_SOCKS_HANDSHAKE_DONE: /* We have not way to store the actual address used by * the server. Then again, we don't really care. */ @@ -8072,6 +8119,8 @@ enum dns_res_state { DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */ DNS_R_FOREACH_A, DNS_R_QUERY_A, + DNS_R_FOREACH_AAAA, + DNS_R_QUERY_AAAA, DNS_R_CNAME0_A, DNS_R_CNAME1_A, @@ -8494,7 +8543,7 @@ exec: switch (F->state) { case DNS_R_INIT: - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_GLUE: if (R->sp == 0) dgoto(R->sp, DNS_R_SWITCH); @@ -8608,17 +8657,17 @@ exec: } else if (error) goto error; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_SUBMIT: if ((error = R->cache->submit(F->query, R->cache))) goto error; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_CHECK: if ((error = R->cache->check(R->cache))) goto error; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_FETCH: error = 0; @@ -8643,7 +8692,7 @@ exec: R->search = 0; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_SEARCH: /* * XXX: We probably should only apply the domain search @@ -8655,12 +8704,12 @@ exec: if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) goto error; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_HINTS: if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error))) goto error; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_ITERATE: dns_rr_i_init(&F->hints_i, F->hints); @@ -8669,7 +8718,7 @@ exec: F->hints_i.sort = &dns_res_nameserv_cmp; F->hints_i.args[0] = F->hints->end; - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_FOREACH_NS: dns_rr_i_save(&F->hints_i); @@ -8731,8 +8780,22 @@ exec: F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { - if (!dns_rr_i_count(&F->hints_j)) + if (!dns_rr_i_count(&F->hints_j)) { + /* Check if we have in fact servers + with an IPv6 address. */ + dns_rr_i_init(&F->hints_j, F->hints); + F->hints_j.name = u.ns.host; + F->hints_j.type = DNS_T_AAAA; + F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; + if (dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { + /* We do. Reinitialize + iterator and handle it. */ + dns_rr_i_init(&F->hints_j, F->hints); + dgoto(R->sp, DNS_R_FOREACH_AAAA); + } + dgoto(R->sp, DNS_R_RESOLV0_NS); + } dgoto(R->sp, DNS_R_FOREACH_NS); } @@ -8761,7 +8824,7 @@ exec: goto error; F->state++; - } + } /* FALL THROUGH */ case DNS_R_QUERY_A: if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) dgoto(R->sp, DNS_R_FOREACH_A); @@ -8833,6 +8896,139 @@ exec: /* XXX: Should we copy F->answer to R->nodata? */ dgoto(R->sp, DNS_R_FOREACH_A); + case DNS_R_FOREACH_AAAA: { + struct dns_aaaa aaaa; + struct sockaddr_in6 sin6; + + /* + * NOTE: Iterator initialized in DNS_R_FOREACH_NS because + * this state is re-entrant, but we need to reset + * .name to a valid pointer each time. + */ + if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) + goto error; + + F->hints_j.name = u.ns.host; + F->hints_j.type = DNS_T_AAAA; + F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; + + if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { + if (!dns_rr_i_count(&F->hints_j)) { + /* Check if we have in fact servers + with an IPv4 address. */ + dns_rr_i_init(&F->hints_j, F->hints); + F->hints_j.name = u.ns.host; + F->hints_j.type = DNS_T_A; + F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; + if (dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { + /* We do. Reinitialize + iterator and handle it. */ + dns_rr_i_init(&F->hints_j, F->hints); + dgoto(R->sp, DNS_R_FOREACH_A); + } + + dgoto(R->sp, DNS_R_RESOLV0_NS); + } + + dgoto(R->sp, DNS_R_FOREACH_NS); + } + + if ((error = dns_aaaa_parse(&aaaa, &rr, F->hints))) + goto error; + + memset(&sin6, '\0', sizeof sin6); /* NB: silence valgrind */ + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = aaaa.addr; + if (R->sp == 0) + sin6.sin6_port = dns_hints_port(R->hints, AF_INET, &sin6.sin6_addr); + else + sin6.sin6_port = htons(53); + + if (DNS_DEBUG) { + char addr[INET6_ADDRSTRLEN + 1]; + dns_aaaa_print(addr, sizeof addr, &aaaa); + dns_header(F->query)->qid = dns_so_mkqid(&R->so); + DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp); + } + + dns_trace_setcname(R->trace, u.ns.host, (struct sockaddr *)&sin6); + + if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin6))) + goto error; + + F->state++; + } /* FALL THROUGH */ + case DNS_R_QUERY_AAAA: + if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) + dgoto(R->sp, DNS_R_FOREACH_AAAA); + + if ((error = dns_so_check(&R->so))) + goto error; + + if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) + goto error; + + if (DNS_DEBUG) { + DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp); + } + + if (dns_p_rcode(F->answer) == DNS_RC_FORMERR || + dns_p_rcode(F->answer) == DNS_RC_NOTIMP || + dns_p_rcode(F->answer) == DNS_RC_BADVERS) { + /* Temporarily disable EDNS0 and try again. */ + if (F->qflags & DNS_Q_EDNS0) { + F->qflags &= ~DNS_Q_EDNS0; + if ((error = dns_q_remake(&F->query, F->qflags))) + goto error; + + dgoto(R->sp, DNS_R_FOREACH_AAAA); + } + } + + if ((error = dns_rr_parse(&rr, 12, F->query))) + goto error; + + if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error))) + goto error; + else if (len >= sizeof u.name) + goto toolong; + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) { + dgoto(R->sp, DNS_R_FINISH); /* Found */ + } + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) { + F->ans_cname = rr; + + dgoto(R->sp, DNS_R_CNAME0_A); + } + + /* + * XXX: The condition here should probably check whether + * R->sp == 0, because DNS_R_SEARCH runs regardless of + * options.recurse. See DNS_R_BIND. + */ + if (!R->resconf->options.recurse) { + /* Make first answer our tentative answer */ + if (!R->nodata) + dns_p_movptr(&R->nodata, &F->answer); + + dgoto(R->sp, DNS_R_SEARCH); + } + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) { + dns_p_movptr(&F->hints, &F->answer); + + dgoto(R->sp, DNS_R_ITERATE); + } + + /* XXX: Should this go further up? */ + if (dns_header(F->answer)->aa) + dgoto(R->sp, DNS_R_FINISH); + + /* XXX: Should we copy F->answer to R->nodata? */ + + dgoto(R->sp, DNS_R_FOREACH_AAAA); case DNS_R_CNAME0_A: if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_FINISH); @@ -8864,7 +9060,7 @@ exec: dns_rr_i_init(&R->smart, F->answer); - F->state++; + F->state++; /* FALL THROUGH */ case DNS_R_SMART0_A: if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_DONE); @@ -9549,12 +9745,12 @@ exec: switch (ai->state) { case DNS_AI_S_INIT: - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_NEXTAF: if (!dns_ai_nextaf(ai)) dns_ai_goto(DNS_AI_S_DONE); - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_NUMERIC: if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) { if (ai->af.atype == AF_INET) { @@ -9577,19 +9773,19 @@ exec: if (ai->hints.ai_flags & AI_NUMERICHOST) dns_ai_goto(DNS_AI_S_NEXTAF); - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_SUBMIT: assert(ai->res); if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN))) return error; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_CHECK: if ((error = dns_res_check(ai->res))) return error; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_FETCH: if (!(ans = dns_res_fetch_and_study(ai->res, &error))) return error; @@ -9612,7 +9808,7 @@ exec: ai->i.type = dns_ai_qtype(ai); ai->i.sort = &dns_rr_i_order; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_FOREACH_I: if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error)) dns_ai_goto(DNS_AI_S_NEXTAF); @@ -9646,11 +9842,11 @@ exec: break; } /* switch() */ - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_INIT_G: ai->g_depth = 0; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_ITERATE_G: dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname); dns_rr_i_init(&ai->g, ai->glue); @@ -9658,7 +9854,7 @@ exec: ai->g.name = ai->g_cname; ai->g.type = ai->af.qtype; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_FOREACH_G: if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) { if (dns_rr_i_count(&ai->g) > 0) @@ -9682,12 +9878,12 @@ exec: if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN))) return error; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_CHECK_G: if ((error = dns_res_check(ai->res))) return error; - ai->state++; + ai->state++; /* FALL THROUGH */ case DNS_AI_S_FETCH_G: if (!(ans = dns_res_fetch_and_study(ai->res, &error))) return error; diff --git a/dirmngr/http.c b/dirmngr/http.c index f461e5d..0bedba0 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -70,6 +70,7 @@ # include <sys/socket.h> # include <sys/time.h> # include <time.h> +# include <fcntl.h> # include <netinet/in.h> # include <arpa/inet.h> # include <netdb.h> @@ -153,13 +154,14 @@ static int insert_escapes (char *buffer, const char *string, static uri_tuple_t parse_tuple (char *string); static gpg_error_t send_request (http_t hd, const char *httphost, const char *auth,const char *proxy, - const char *srvtag,strlist_t headers); + const char *srvtag, unsigned int timeout, + strlist_t headers); static char *build_rel_path (parsed_uri_t uri); static gpg_error_t parse_response (http_t hd); static gpg_error_t connect_server (const char *server, unsigned short port, unsigned int flags, const char *srvtag, - assuan_fd_t *r_sock); + unsigned int timeout, assuan_fd_t *r_sock); static gpgrt_ssize_t read_server (assuan_fd_t sock, void *buffer, size_t size); static gpg_error_t write_server (assuan_fd_t sock, const char *data, size_t length); @@ -259,6 +261,9 @@ struct http_session_s /* A per-session TLS verification callback. */ http_verify_cb_t verify_cb; void *verify_cb_value; + + /* The connect timeout */ + unsigned int connect_timeout; }; @@ -695,6 +700,7 @@ http_session_new (http_session_t *r_session, sess->flags = flags; sess->verify_cb = verify_cb; sess->verify_cb_value = verify_cb_value; + sess->connect_timeout = 0; #if HTTP_USE_NTBTLS { @@ -867,6 +873,15 @@ http_session_set_log_cb (http_session_t sess, } +/* Set the TIMEOUT in milliseconds for the connection's connect + * calls. Using 0 disables the timeout. */ +void +http_session_set_timeout (http_session_t sess, unsigned int timeout) +{ + sess->connect_timeout = timeout; +} + + /* Start a HTTP retrieval and on success store at R_HD a context @@ -898,7 +913,9 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url, err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS)); if (!err) - err = send_request (hd, httphost, auth, proxy, srvtag, headers); + err = send_request (hd, httphost, auth, proxy, srvtag, + hd->session? hd->session->connect_timeout : 0, + headers); if (err) { @@ -918,10 +935,10 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url, /* This function is useful to connect to a generic TCP service using this http abstraction layer. This has the advantage of providing - service tags and an estream interface. */ + service tags and an estream interface. TIMEOUT is in milliseconds. */ gpg_error_t http_raw_connect (http_t *r_hd, const char *server, unsigned short port, - unsigned int flags, const char *srvtag) + unsigned int flags, const char *srvtag, unsigned int timeout) { gpg_error_t err = 0; http_t hd; @@ -938,6 +955,10 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, log_error ("Tor support is not available\n"); return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); } + /* Non-blocking connects do not work with our Tor proxy because + * we can't continue the Socks protocol after the EINPROGRESS. + * Disable the timeout to use a blocking connect. */ + timeout = 0; } /* Create the handle. */ @@ -952,7 +973,7 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, { assuan_fd_t sock; - err = connect_server (server, port, hd->flags, srvtag, &sock); + err = connect_server (server, port, hd->flags, srvtag, timeout, &sock); if (err) { xfree (hd); @@ -1047,6 +1068,7 @@ http_wait_response (http_t hd) { gpg_error_t err; cookie_t cookie; + int use_tls; /* Make sure that we are in the data. */ http_start_data (hd); @@ -1057,6 +1079,7 @@ http_wait_response (http_t hd) if (!cookie) return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); + use_tls = cookie->use_tls; es_fclose (hd->fp_write); hd->fp_write = NULL; /* The close has released the cookie and thus we better set it to NULL. */ @@ -1075,7 +1098,7 @@ http_wait_response (http_t hd) return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); cookie->sock = my_socket_ref (hd->sock); cookie->session = http_session_ref (hd->session); - cookie->use_tls = hd->uri->use_tls; + cookie->use_tls = use_tls; hd->read_cookie = cookie; hd->fp_read = es_fopencookie (cookie, "r", cookie_functions); @@ -1202,14 +1225,16 @@ parse_uri (parsed_uri_t *ret_uri, const char *uri, { gpg_err_code_t ec; - *ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri)); + *ret_uri = xtrycalloc (1, sizeof **ret_uri + 2 * strlen (uri) + 1); if (!*ret_uri) return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); strcpy ((*ret_uri)->buffer, uri); + strcpy ((*ret_uri)->buffer + strlen (uri) + 1, uri); + (*ret_uri)->original = (*ret_uri)->buffer + strlen (uri) + 1; ec = do_parse_uri (*ret_uri, 0, no_scheme_check, force_tls); if (ec) { - xfree (*ret_uri); + http_release_parsed_uri (*ret_uri); *ret_uri = NULL; } return gpg_err_make (default_errsource, ec); @@ -1238,6 +1263,11 @@ http_release_parsed_uri (parsed_uri_t uri) { uri_tuple_t r, r2; + for (r = uri->params; r; r = r2) + { + r2 = r->next; + xfree (r); + } for (r = uri->query; r; r = r2) { r2 = r->next; @@ -1635,7 +1665,8 @@ is_hostname_port (const char *string) */ static gpg_error_t send_request (http_t hd, const char *httphost, const char *auth, - const char *proxy, const char *srvtag, strlist_t headers) + const char *proxy, const char *srvtag, unsigned int timeout, + strlist_t headers) { gpg_error_t err; const char *server; @@ -1645,6 +1676,9 @@ send_request (http_t hd, const char *httphost, const char *auth, char *proxy_authstr = NULL; char *authstr = NULL; assuan_fd_t sock; +#ifdef USE_TLS + int have_http_proxy = 0; +#endif if (hd->uri->use_tls && !hd->session) { @@ -1668,6 +1702,10 @@ send_request (http_t hd, const char *httphost, const char *auth, log_error ("Tor support is not available\n"); return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); } + /* Non-blocking connects do not work with our Tor proxy because + * we can't continue the Socks protocol after the EINPROGRESS. + * Disable the timeout to use a blocking connect. */ + timeout = 0; } server = *hd->uri->host ? hd->uri->host : "localhost"; @@ -1731,9 +1769,12 @@ send_request (http_t hd, const char *httphost, const char *auth, if (err) ; - else if (!strcmp (uri->scheme, "http") || !strcmp (uri->scheme, "socks4")) - ; - else if (!strcmp (uri->scheme, "socks5h")) +#ifdef USE_TLS + else if (!strcmp (uri->scheme, "http")) + have_http_proxy = 1; +#endif + else if (!strcmp (uri->scheme, "socks4") + || !strcmp (uri->scheme, "socks5h")) err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); else err = gpg_err_make (default_errsource, GPG_ERR_INV_URI); @@ -1762,12 +1803,12 @@ send_request (http_t hd, const char *httphost, const char *auth, err = connect_server (*uri->host ? uri->host : "localhost", uri->port ? uri->port : 80, - hd->flags, srvtag, &sock); + hd->flags, NULL, timeout, &sock); http_release_parsed_uri (uri); } else { - err = connect_server (server, port, hd->flags, srvtag, &sock); + err = connect_server (server, port, hd->flags, srvtag, timeout, &sock); } if (err) @@ -1782,6 +1823,94 @@ send_request (http_t hd, const char *httphost, const char *auth, return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); } +#if USE_TLS + if (have_http_proxy && hd->uri->use_tls) + { + int saved_flags; + cookie_t cookie; + + /* Try to use the CONNECT method to proxy our TLS stream. */ + request = es_bsprintf + ("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s", + httphost ? httphost : server, + port, + httphost ? httphost : server, + port, + proxy_authstr ? proxy_authstr : ""); + xfree (proxy_authstr); + proxy_authstr = NULL; + + if (! request) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) + log_debug_with_string (request, "http.c:request:"); + + cookie = xtrycalloc (1, sizeof *cookie); + if (! cookie) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + xfree (request); + return err; + } + cookie->sock = my_socket_ref (hd->sock); + hd->write_cookie = cookie; + + hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); + if (! hd->fp_write) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + my_socket_unref (cookie->sock, NULL, NULL); + xfree (cookie); + xfree (request); + hd->write_cookie = NULL; + return err; + } + else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write)) + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + xfree (request); + request = NULL; + + /* Make sure http_wait_response doesn't close the stream. */ + saved_flags = hd->flags; + hd->flags &= ~HTTP_FLAG_SHUTDOWN; + + /* Get the response. */ + err = http_wait_response (hd); + + /* Restore flags, destroy stream. */ + hd->flags = saved_flags; + es_fclose (hd->fp_read); + hd->fp_read = NULL; + hd->read_cookie = NULL; + + /* Reset state. */ + hd->in_data = 0; + + if (err) + return err; + + if (hd->status_code != 200) + { + request = es_bsprintf + ("CONNECT %s:%hu", + httphost ? httphost : server, + port); + + log_error (_("error accessing '%s': http status %u\n"), + request ? request : "out of core", + http_get_status_code (hd)); + + xfree (request); + return gpg_error (GPG_ERR_NO_DATA); + } + + /* We are done with the proxy, the code below will establish a + * TLS session and talk directly to the target server. */ + http_proxy = NULL; + } +#endif /* USE_TLS */ #if HTTP_USE_NTBTLS if (hd->uri->use_tls) @@ -2316,7 +2445,7 @@ parse_response (http_t hd) if (!len) return GPG_ERR_EOF; - if ((hd->flags & HTTP_FLAG_LOG_RESP)) + if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) log_debug_with_string (line, "http.c:response:\n"); } while (!*line); @@ -2361,7 +2490,7 @@ parse_response (http_t hd) /* Trim line endings of empty lines. */ if ((*line == '\r' && line[1] == '\n') || *line == '\n') *line = 0; - if ((hd->flags & HTTP_FLAG_LOG_RESP)) + if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP)) log_info ("http.c:RESP: '%.*s'\n", (int)strlen(line)-(*line&&line[1]?2:0),line); if (*line) @@ -2526,13 +2655,162 @@ my_sock_new_for_addr (struct sockaddr_storage *addr, int type, int proto) } +/* Call WSAGetLastError and map it to a libgpg-error. */ +#ifdef HAVE_W32_SYSTEM +static gpg_error_t +my_wsagetlasterror (void) +{ + int wsaerr; + gpg_err_code_t ec; + + wsaerr = WSAGetLastError (); + switch (wsaerr) + { + case WSAENOTSOCK: ec = GPG_ERR_EINVAL; break; + case WSAEWOULDBLOCK: ec = GPG_ERR_EAGAIN; break; + case ERROR_BROKEN_PIPE: ec = GPG_ERR_EPIPE; break; + case WSANOTINITIALISED: ec = GPG_ERR_ENOSYS; break; + case WSAENOBUFS: ec = GPG_ERR_ENOBUFS; break; + case WSAEMSGSIZE: ec = GPG_ERR_EMSGSIZE; break; + case WSAECONNREFUSED: ec = GPG_ERR_ECONNREFUSED; break; + case WSAEISCONN: ec = GPG_ERR_EISCONN; break; + case WSAEALREADY: ec = GPG_ERR_EALREADY; break; + case WSAETIMEDOUT: ec = GPG_ERR_ETIMEDOUT; break; + default: ec = GPG_ERR_EIO; break; + } + + return gpg_err_make (default_errsource, ec); +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Connect SOCK and return GPG_ERR_ETIMEOUT if a connection could not + * be established within TIMEOUT milliseconds. 0 indicates the + * system's default timeout. The other args are the usual connect + * args. On success 0 is returned, on timeout GPG_ERR_ETIMEDOUT, and + * another error code for other errors. On timeout the caller needs + * to close the socket as soon as possible to stop an ongoing + * handshake. + * + * This implementation is for well-behaving systems; see Stevens, + * Network Programming, 2nd edition, Vol 1, 15.4. */ +static gpg_error_t +connect_with_timeout (assuan_fd_t sock, + struct sockaddr *addr, int addrlen, + unsigned int timeout) +{ + gpg_error_t err; + int syserr; + socklen_t slen; + fd_set rset, wset; + struct timeval tval; + int n; + +#ifndef HAVE_W32_SYSTEM + int oflags; +# define RESTORE_BLOCKING() do { \ + fcntl (sock, F_SETFL, oflags); \ + } while (0) +#else /*HAVE_W32_SYSTEM*/ +# define RESTORE_BLOCKING() do { \ + unsigned long along = 0; \ + ioctlsocket (FD2INT (sock), FIONBIO, &along); \ + } while (0) +#endif /*HAVE_W32_SYSTEM*/ + + + if (!timeout) + { + /* Shortcut. */ + if (assuan_sock_connect (sock, addr, addrlen)) + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + else + err = 0; + return err; + } + + /* Switch the socket into non-blocking mode. */ +#ifdef HAVE_W32_SYSTEM + { + unsigned long along = 1; + if (ioctlsocket (FD2INT (sock), FIONBIO, &along)) + return my_wsagetlasterror (); + } +#else + oflags = fcntl (sock, F_GETFL, 0); + if (fcntl (sock, F_SETFL, oflags | O_NONBLOCK)) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +#endif + + /* Do the connect. */ + if (!assuan_sock_connect (sock, addr, addrlen)) + { + /* Immediate connect. Restore flags. */ + RESTORE_BLOCKING (); + return 0; /* Success. */ + } + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + if (gpg_err_code (err) != GPG_ERR_EINPROGRESS) + { + RESTORE_BLOCKING (); + return err; + } + + FD_ZERO (&rset); + FD_SET (FD2INT (sock), &rset); + wset = rset; + tval.tv_sec = timeout / 1000; + tval.tv_usec = (timeout % 1000) * 1000; + + n = my_select (FD2INT(sock)+1, &rset, &wset, NULL, &tval); + if (n < 0) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + RESTORE_BLOCKING (); + return err; + } + if (!n) + { + /* Timeout: We do not restore the socket flags on timeout + * because the caller is expected to close the socket. */ + return gpg_err_make (default_errsource, GPG_ERR_ETIMEDOUT); + } + if (!FD_ISSET (sock, &rset) && !FD_ISSET (sock, &wset)) + { + /* select misbehaved. */ + return gpg_err_make (default_errsource, GPG_ERR_SYSTEM_BUG); + } + + slen = sizeof (syserr); + if (getsockopt (FD2INT(sock), SOL_SOCKET, SO_ERROR, + (void*)&syserr, &slen) < 0) + { + /* Assume that this is Solaris which returns the error in ERRNO. */ + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + } + else if (syserr) + err = gpg_err_make (default_errsource, gpg_err_code_from_errno (syserr)); + else + err = 0; /* Connected. */ + + RESTORE_BLOCKING (); + + return err; + +#undef RESTORE_BLOCKING +} + + /* Actually connect to a server. On success 0 is returned and the * file descriptor for the socket is stored at R_SOCK; on error an - * error code is returned and ASSUAN_INVALID_FD is stored at - * R_SOCK. */ + * error code is returned and ASSUAN_INVALID_FD is stored at R_SOCK. + * TIMEOUT is the connect timeout in milliseconds. Note that the + * function tries to connect to all known addresses and the timeout is + * for each one. */ static gpg_error_t connect_server (const char *server, unsigned short port, - unsigned int flags, const char *srvtag, assuan_fd_t *r_sock) + unsigned int flags, const char *srvtag, unsigned int timeout, + assuan_fd_t *r_sock) { gpg_error_t err; assuan_fd_t sock = ASSUAN_INVALID_FD; @@ -2645,11 +2923,11 @@ connect_server (const char *server, unsigned short port, } anyhostaddr = 1; - if (assuan_sock_connect (sock, (struct sockaddr *)ai->addr, - ai->addrlen)) + err = connect_with_timeout (sock, (struct sockaddr *)ai->addr, + ai->addrlen, timeout); + if (err) { - last_err = gpg_err_make (default_errsource, - gpg_err_code_from_syserror ()); + last_err = err; } else { @@ -3016,7 +3294,7 @@ gpg_error_t http_verify_server_credentials (http_session_t sess) { #if HTTP_USE_GNUTLS - static const char const errprefix[] = "TLS verification of peer failed"; + static const char errprefix[] = "TLS verification of peer failed"; int rc; unsigned int status; const char *hostname; diff --git a/dirmngr/http.h b/dirmngr/http.h index 2609b9e..9fa462c 100644 --- a/dirmngr/http.h +++ b/dirmngr/http.h @@ -47,6 +47,7 @@ typedef struct uri_tuple_s *uri_tuple_t; struct parsed_uri_s { /* All these pointers point into BUFFER; most stuff is not escaped. */ + char *original; /* Unmodified copy of the parsed URI. */ char *scheme; /* Pointer to the scheme string (always lowercase). */ unsigned int is_http:1; /* This is a HTTP style URI. */ unsigned int use_tls:1; /* Whether TLS should be used. */ @@ -124,6 +125,7 @@ void http_session_set_log_cb (http_session_t sess, void (*cb)(http_session_t, gpg_error_t, const char *, const void **, size_t *)); +void http_session_set_timeout (http_session_t sess, unsigned int timeout); gpg_error_t http_parse_uri (parsed_uri_t *ret_uri, const char *uri, @@ -133,7 +135,8 @@ void http_release_parsed_uri (parsed_uri_t uri); gpg_error_t http_raw_connect (http_t *r_hd, const char *server, unsigned short port, - unsigned int flags, const char *srvtag); + unsigned int flags, const char *srvtag, + unsigned int timeout); gpg_error_t http_open (http_t *r_hd, http_req_t reqtype, const char *url, diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index 1087bb5..857aab1 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -232,7 +232,10 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, Need to think about a better strategy. */ for (uri = keyservers; !err && uri; uri = uri->next) { - int is_http = uri->parsed_uri->is_http; + int is_hkp_s = (strcmp (uri->parsed_uri->scheme, "hkp") == 0 + || strcmp (uri->parsed_uri->scheme, "hkps") == 0); + int is_http_s = (strcmp (uri->parsed_uri->scheme, "http") == 0 + || strcmp (uri->parsed_uri->scheme, "https") == 0); int is_ldap = 0; #if USE_LDAP @@ -241,7 +244,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); #endif - if (is_http || is_ldap) + if (is_hkp_s || is_http_s || is_ldap) { any_server = 1; for (sl = patterns; !err && sl; sl = sl->next) @@ -251,9 +254,12 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp); else #endif - { - err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp); - } + if (is_hkp_s) + err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp); + else if (is_http_s) + err = ks_http_fetch (ctrl, uri->parsed_uri->original, &infp); + else + BUG (); if (err) { diff --git a/dirmngr/ks-engine-finger.c b/dirmngr/ks-engine-finger.c index f56a9ff..e53a0ee 100644 --- a/dirmngr/ks-engine-finger.c +++ b/dirmngr/ks-engine-finger.c @@ -86,7 +86,7 @@ ks_finger_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp) ((dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR : 0) | (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0) | (opt.disable_ipv6? HTTP_FLAG_IGNORE_IPv6 : 0)), - NULL); + NULL, ctrl->timeout); if (err) { xfree (name); diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index ddb8549..4a0b08f 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -67,6 +67,8 @@ /* Number of retries done for a dead host etc. */ #define SEND_REQUEST_RETRIES 3 +enum ks_protocol { KS_PROTOCOL_HKP, KS_PROTOCOL_HKPS, KS_PROTOCOL_MAX }; + /* Objects used to maintain information about hosts. */ struct hostinfo_s; typedef struct hostinfo_s *hostinfo_t; @@ -74,9 +76,11 @@ struct hostinfo_s { time_t lastfail; /* Time we tried to connect and failed. */ time_t lastused; /* Time of last use. */ - int *pool; /* A -1 terminated array with indices into - HOSTTABLE or NULL if NAME is not a pool - name. */ + int *pool; /* An array with indices into HOSTTABLE or NULL + if NAME is not a pool name. */ + size_t pool_len; /* Length of POOL. */ + size_t pool_size; /* Allocated size of POOL. */ +#define MAX_POOL_SIZE 128 int poolidx; /* Index into POOL with the used host. -1 if not set. */ unsigned int v4:1; /* Host supports AF_INET. */ unsigned int v6:1; /* Host supports AF_INET6. */ @@ -84,12 +88,18 @@ struct hostinfo_s unsigned int dead:1; /* Host is currently unresponsive. */ unsigned int iporname_valid:1; /* The field IPORNAME below is valid */ /* (but may be NULL) */ + unsigned int did_a_lookup:1; /* Have we done an A lookup yet? */ + unsigned int did_srv_lookup:2; /* One bit per protocol indicating + whether we already did a SRV + lookup. */ time_t died_at; /* The time the host was marked dead. If this is 0 the host has been manually marked dead. */ char *cname; /* Canonical name of the host. Only set if this is a pool or NAME has a numerical IP address. */ char *iporname; /* Numeric IP address or name for printing. */ - unsigned short port; /* The port used by the host, 0 if unknown. */ + unsigned short port[KS_PROTOCOL_MAX]; + /* The port used by the host for all protocols, 0 + if unknown. */ char name[1]; /* The hostname. */ }; @@ -118,6 +128,8 @@ create_new_hostinfo (const char *name) return -1; strcpy (hi->name, name); hi->pool = NULL; + hi->pool_len = 0; + hi->pool_size = 0; hi->poolidx = -1; hi->lastused = (time_t)(-1); hi->lastfail = (time_t)(-1); @@ -125,11 +137,14 @@ create_new_hostinfo (const char *name) hi->v6 = 0; hi->onion = 0; hi->dead = 0; + hi->did_a_lookup = 0; + hi->did_srv_lookup = 0; hi->iporname_valid = 0; hi->died_at = 0; hi->cname = NULL; hi->iporname = NULL; - hi->port = 0; + hi->port[KS_PROTOCOL_HKP] = 0; + hi->port[KS_PROTOCOL_HKPS] = 0; /* Add it to the hosttable. */ for (idx=0; idx < hosttable_size; idx++) @@ -187,24 +202,24 @@ sort_hostpool (const void *xa, const void *xb) } -/* Return true if the host with the hosttable index TBLIDX is in POOL. */ +/* Return true if the host with the hosttable index TBLIDX is in HI->pool. */ static int -host_in_pool_p (int *pool, int tblidx) +host_in_pool_p (hostinfo_t hi, int tblidx) { int i, pidx; - for (i=0; (pidx = pool[i]) != -1; i++) + for (i = 0; i < hi->pool_len && (pidx = hi->pool[i]) != -1; i++) if (pidx == tblidx && hosttable[pidx]) return 1; return 0; } -/* Select a random host. Consult TABLE which indices into the global - hosttable. Returns index into TABLE or -1 if no host could be +/* Select a random host. Consult HI->pool which indices into the global + hosttable. Returns index into HI->pool or -1 if no host could be selected. */ static int -select_random_host (int *table) +select_random_host (hostinfo_t hi) { int *tbl; size_t tblsize; @@ -212,7 +227,9 @@ select_random_host (int *table) /* We create a new table so that we randomly select only from currently alive hosts. */ - for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++) + for (idx = 0, tblsize = 0; + idx < hi->pool_len && (pidx = hi->pool[idx]) != -1; + idx++) if (hosttable[pidx] && !hosttable[pidx]->dead) tblsize++; if (!tblsize) @@ -221,7 +238,9 @@ select_random_host (int *table) tbl = xtrymalloc (tblsize * sizeof *tbl); if (!tbl) return -1; - for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++) + for (idx = 0, tblsize = 0; + idx < hi->pool_len && (pidx = hi->pool[idx]) != -1; + idx++) if (hosttable[pidx] && !hosttable[pidx]->dead) tbl[tblsize++] = pidx; @@ -255,7 +274,7 @@ arecords_is_pool (dns_addrinfo_t aibuf) } -/* Print a warninng iff Tor is not running but Tor has been requested. +/* Print a warning iff Tor is not running but Tor has been requested. * Also return true if it is not running. */ static int tor_not_running_p (ctrl_t ctrl) @@ -281,20 +300,22 @@ tor_not_running_p (ctrl_t ctrl) /* Add the host AI under the NAME into the HOSTTABLE. If PORT is not - zero, it specifies which port to use to talk to the host. If NAME - specifies a pool (as indicated by IS_POOL), update the given - reference table accordingly. */ + zero, it specifies which port to use to talk to the host for + PROTOCOL. If NAME specifies a pool (as indicated by IS_POOL), + update the given reference table accordingly. */ static void add_host (const char *name, int is_pool, - const dns_addrinfo_t ai, unsigned short port, - int *reftbl, size_t reftblsize, int *refidx) + const dns_addrinfo_t ai, + enum ks_protocol protocol, unsigned short port) { gpg_error_t tmperr; char *tmphost; int idx, tmpidx; + hostinfo_t host; int i; idx = find_hostinfo (name); + host = hosttable[idx]; if (is_pool) { @@ -325,7 +346,7 @@ add_host (const char *name, int is_pool, log_info ("resolve_dns_addr failed while checking '%s': %s\n", name, gpg_strerror (tmperr)); } - else if ((*refidx) + 1 >= reftblsize) + else if (host->pool_len + 1 >= MAX_POOL_SIZE) { log_error ("resolve_dns_addr for '%s': '%s'" " [index table full - ignored]\n", name, tmphost); @@ -352,7 +373,7 @@ add_host (const char *name, int is_pool, else /* Set or update the entry. */ { if (port) - hosttable[tmpidx]->port = port; + hosttable[tmpidx]->port[protocol] = port; if (ai->family == AF_INET6) { @@ -365,17 +386,54 @@ add_host (const char *name, int is_pool, else BUG (); - for (i=0; i < *refidx; i++) - if (reftbl[i] == tmpidx) - break; - if (!(i < *refidx) && tmpidx != idx) - reftbl[(*refidx)++] = tmpidx; + /* If we updated the main entry, we're done. */ + if (idx == tmpidx) + goto leave; + + /* If we updated an existing entry, we're done. */ + for (i = 0; i < host->pool_len; i++) + if (host->pool[i] == tmpidx) + goto leave; + + /* Otherwise, we need to add it to the pool. Check if there + is space. */ + if (host->pool_len + 1 > host->pool_size) + { + int *new_pool; + size_t new_size; + + if (host->pool_size == 0) + new_size = 4; + else + new_size = host->pool_size * 2; + + new_pool = xtryrealloc (host->pool, + new_size * sizeof *new_pool); + + if (new_pool == NULL) + goto leave; + + host->pool = new_pool; + host->pool_size = new_size; + } + + /* Finally, add it. */ + log_assert (host->pool_len < host->pool_size); + host->pool[host->pool_len++] = tmpidx; } } + leave: xfree (tmphost); } +/* Sort the pool of the given hostinfo HI. */ +static void +hostinfo_sort_pool (hostinfo_t hi) +{ + qsort (hi->pool, hi->pool_len, sizeof *hi->pool, sort_hostpool); +} + /* Map the host name NAME to the actual to be used host name. This * allows us to manage round robin DNS names. We use our own strategy * to choose one of the hosts. For example we skip those hosts which @@ -393,12 +451,16 @@ add_host (const char *name, int is_pool, * pool. */ static gpg_error_t map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, - char **r_host, char *r_portstr, + enum ks_protocol protocol, char **r_host, char *r_portstr, unsigned int *r_httpflags, char **r_httphost) { gpg_error_t err = 0; hostinfo_t hi; int idx; + dns_addrinfo_t aibuf, ai; + int is_pool; + int new_hosts = 0; + char *cname; *r_host = NULL; if (r_httpflags) @@ -415,74 +477,62 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, /* See whether the host is in our table. */ idx = find_hostinfo (name); - if (idx == -1 && is_onion_address (name)) + if (idx == -1) { idx = create_new_hostinfo (name); if (idx == -1) return gpg_error_from_syserror (); hi = hosttable[idx]; - hi->onion = 1; + hi->onion = is_onion_address (name); } - else if (idx == -1) + else + hi = hosttable[idx]; + + is_pool = hi->pool != NULL; + + if (srvtag && !is_ip_address (name) + && ! hi->onion + && ! (hi->did_srv_lookup & 1 << protocol)) { - /* We never saw this host. Allocate a new entry. */ - dns_addrinfo_t aibuf, ai; - int *reftbl; - size_t reftblsize; - int refidx; - int is_pool = 0; - char *cname; struct srventry *srvs; unsigned int srvscount; - reftblsize = 100; - reftbl = xtrymalloc (reftblsize * sizeof *reftbl); - if (!reftbl) - return gpg_error_from_syserror (); - refidx = 0; - - idx = create_new_hostinfo (name); - if (idx == -1) + /* Check for SRV records. */ + err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount); + if (err) { - err = gpg_error_from_syserror (); - xfree (reftbl); + if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED) + tor_not_running_p (ctrl); return err; } - hi = hosttable[idx]; - if (srvtag && !is_ip_address (name)) + if (srvscount > 0) { - /* Check for SRV records. */ - err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount); - if (err) - { - xfree (reftbl); - if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED) - tor_not_running_p (ctrl); - return err; - } + int i; + if (! is_pool) + is_pool = srvscount > 1; - if (srvscount > 0) + for (i = 0; i < srvscount; i++) { - int i; - is_pool = srvscount > 1; - - for (i = 0; i < srvscount; i++) - { - err = resolve_dns_name (srvs[i].target, 0, - AF_UNSPEC, SOCK_STREAM, - &ai, &cname); - if (err) - continue; - dirmngr_tick (ctrl); - add_host (name, is_pool, ai, srvs[i].port, - reftbl, reftblsize, &refidx); - } - - xfree (srvs); + err = resolve_dns_name (srvs[i].target, 0, + AF_UNSPEC, SOCK_STREAM, + &ai, &cname); + if (err) + continue; + dirmngr_tick (ctrl); + add_host (name, is_pool, ai, protocol, srvs[i].port); + new_hosts = 1; } + + xfree (srvs); } + hi->did_srv_lookup |= 1 << protocol; + } + + if (! hi->did_a_lookup + && ! hi->onion) + { /* Find all A records for this entry and put them into the pool list - if any. */ err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname); @@ -516,32 +566,18 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, continue; dirmngr_tick (ctrl); - add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx); + add_host (name, is_pool, ai, 0, 0); + new_hosts = 1; } + + hi->did_a_lookup = 1; } - reftbl[refidx] = -1; xfree (cname); free_dns_addrinfo (aibuf); - - if (refidx && is_pool) - { - assert (!hi->pool); - hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl); - if (!hi->pool) - { - err = gpg_error_from_syserror (); - log_error ("shrinking index table in map_host failed: %s\n", - gpg_strerror (err)); - xfree (reftbl); - return err; - } - qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool); - } - else - xfree (reftbl); } + if (new_hosts) + hostinfo_sort_pool (hi); - hi = hosttable[idx]; if (hi->pool) { /* Deal with the pool name before selecting a host. */ @@ -563,7 +599,7 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, /* Select a host if needed. */ if (hi->poolidx == -1) { - hi->poolidx = select_random_host (hi->pool); + hi->poolidx = select_random_host (hi); if (hi->poolidx == -1) { log_error ("no alive host found in pool '%s'\n", name); @@ -586,7 +622,6 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, * find the canonical name so that it can be used in the HTTP * Host header. Fixme: We should store that name in the * hosttable. */ - dns_addrinfo_t aibuf, ai; char *host; err = resolve_dns_name (hi->name, 0, 0, SOCK_STREAM, &aibuf, NULL); @@ -650,9 +685,9 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, } return err; } - if (hi->port) + if (hi->port[protocol]) snprintf (r_portstr, 6 /* five digits and the sentinel */, - "%hu", hi->port); + "%hu", hi->port[protocol]); return 0; } @@ -740,7 +775,9 @@ ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive) /* If the host is a pool mark all member hosts. */ if (!err && hi->pool) { - for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++) + for (idx2 = 0; + !err && idx2 < hi->pool_len && (n = hi->pool[idx2]) != -1; + idx2++) { assert (n >= 0 && n < hosttable_size); @@ -753,7 +790,7 @@ ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive) if (hosttable[idx3] && hosttable[idx3]->pool && idx3 != idx - && host_in_pool_p (hosttable[idx3]->pool, n)) + && host_in_pool_p (hosttable[idx3], n)) break; } if (idx3 < hosttable_size) @@ -903,7 +940,7 @@ ks_hkp_print_hosttable (ctrl_t ctrl) { init_membuf (&mb, 256); put_membuf_printf (&mb, " . -->"); - for (idx2=0; hi->pool[idx2] != -1; idx2++) + for (idx2 = 0; idx2 < hi->pool_len && hi->pool[idx2] != -1; idx2++) { put_membuf_printf (&mb, " %d", hi->pool[idx2]); if (hi->poolidx == hi->pool[idx2]) @@ -971,6 +1008,7 @@ make_host_part (ctrl_t ctrl, const char *srvtag; char portstr[10]; char *hostname; + enum ks_protocol protocol; *r_hostport = NULL; @@ -978,15 +1016,17 @@ make_host_part (ctrl_t ctrl, { scheme = "https"; srvtag = no_srv? NULL : "pgpkey-https"; + protocol = KS_PROTOCOL_HKPS; } else /* HKP or HTTP. */ { scheme = "http"; srvtag = no_srv? NULL : "pgpkey-http"; + protocol = KS_PROTOCOL_HKP; } portstr[0] = 0; - err = map_host (ctrl, host, srvtag, force_reselect, + err = map_host (ctrl, host, srvtag, force_reselect, protocol, &hostname, portstr, r_httpflags, r_httphost); if (err) return err; @@ -1122,9 +1162,16 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, int redirects_left = MAX_REDIRECTS; estream_t fp = NULL; char *request_buffer = NULL; + parsed_uri_t uri = NULL; + int is_onion; *r_fp = NULL; + err = http_parse_uri (&uri, request, 0); + if (err) + goto leave; + is_onion = uri->onion; + err = http_session_new (&session, httphost, ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0) | HTTP_FLAG_TRUST_DEF), @@ -1132,6 +1179,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); + http_session_set_timeout (session, ctrl->timeout); once_more: err = http_open (&http, @@ -1209,6 +1257,23 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, request, s?s:"[none]", http_get_status_code (http)); if (s && *s && redirects_left-- ) { + if (is_onion) + { + /* Make sure that an onion address only redirects to + * another onion address. */ + http_release_parsed_uri (uri); + uri = NULL; + err = http_parse_uri (&uri, s, 0); + if (err) + goto leave; + + if (! uri->onion) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + } + xfree (request_buffer); request_buffer = xtrystrdup (s); if (request_buffer) @@ -1257,6 +1322,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, http_close (http, 0); http_session_release (session); xfree (request_buffer); + http_release_parsed_uri (uri); return err; } diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index 02269da..7fb7731 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -72,6 +72,13 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) int redirects_left = MAX_REDIRECTS; estream_t fp = NULL; char *request_buffer = NULL; + parsed_uri_t uri = NULL; + int is_onion; + + err = http_parse_uri (&uri, url, 0); + if (err) + goto leave; + is_onion = uri->onion; once_more: /* Note that we only use the system provided certificates with the @@ -83,6 +90,7 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); + http_session_set_timeout (session, ctrl->timeout); *r_fp = NULL; err = http_open (&http, @@ -144,6 +152,23 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) url, s?s:"[none]", http_get_status_code (http)); if (s && *s && redirects_left-- ) { + if (is_onion) + { + /* Make sure that an onion address only redirects to + * another onion address. */ + http_release_parsed_uri (uri); + uri = NULL; + err = http_parse_uri (&uri, s, 0); + if (err) + goto leave; + + if (! uri->onion) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + } + xfree (request_buffer); request_buffer = xtrystrdup (s); if (request_buffer) @@ -185,5 +210,6 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) http_close (http, 0); http_session_release (session); xfree (request_buffer); + http_release_parsed_uri (uri); return err; } diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index d661a68..adf8307 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -363,6 +363,7 @@ parse_one_pattern (const char *pattern) break; case '*': pattern++; + /* fall through */ default: /* Take as substring match. */ { const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))"; diff --git a/dirmngr/loadswdb.c b/dirmngr/loadswdb.c index 7791f68..bc00466 100644 --- a/dirmngr/loadswdb.c +++ b/dirmngr/loadswdb.c @@ -1,5 +1,6 @@ /* loadswdb.c - Load the swdb file from versions.gnupg.org * Copyright (C) 2016 g10 Code GmbH + * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. * diff --git a/dirmngr/server.c b/dirmngr/server.c index 237cb52..7ed6cde 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -2,6 +2,7 @@ * Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011, 2015 g10 Code GmbH * Copyright (C) 2014, 2015, 2016 Werner Koch + * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. * @@ -307,8 +308,8 @@ strcpy_escaped_plus (char *d, const unsigned char *s) } -/* This function returns true if a Tor server is running. The sattus - is cached for the current connection. */ +/* This function returns true if a Tor server is running. The status + * is cached for the current connection. */ static int is_tor_running (ctrl_t ctrl) { @@ -842,6 +843,8 @@ cmd_wkd_get (assuan_context_t ctx, char *line) opt_submission_addr = has_option (line, "--submission-address"); opt_policy_flags = has_option (line, "--policy-flags"); + if (has_option (line, "--quick")) + ctrl->timeout = opt.connect_quick_timeout; line = skip_options (line); mbox = mailbox_from_userid (line); @@ -893,7 +896,6 @@ cmd_wkd_get (assuan_context_t ctx, char *line) } } xfree (srvs); - log_debug ("srv: got '%s%s'\n", domain, portstr); } gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, mbox, strlen (mbox)); @@ -929,6 +931,13 @@ cmd_wkd_get (assuan_context_t ctx, char *line) encodedhash, NULL); no_log = 1; + if (uri) + { + err = dirmngr_status_printf (ctrl, "SOURCE", "https://%s%s", + domain, portstr); + if (err) + goto leave; + } } if (!uri) { @@ -2123,7 +2132,8 @@ cmd_ks_search (assuan_context_t ctx, char *line) char *p; estream_t outfp; - /* No options for now. */ + if (has_option (line, "--quick")) + ctrl->timeout = opt.connect_quick_timeout; line = skip_options (line); /* Break the line down into an strlist. Each pattern is @@ -2187,7 +2197,8 @@ cmd_ks_get (assuan_context_t ctx, char *line) char *p; estream_t outfp; - /* No options for now. */ + if (has_option (line, "--quick")) + ctrl->timeout = opt.connect_quick_timeout; line = skip_options (line); /* Break the line into a strlist. Each pattern is by @@ -2251,7 +2262,8 @@ cmd_ks_fetch (assuan_context_t ctx, char *line) gpg_error_t err; estream_t outfp; - /* No options for now. */ + if (has_option (line, "--quick")) + ctrl->timeout = opt.connect_quick_timeout; line = skip_options (line); err = ensure_keyserver (ctrl); /* FIXME: Why do we needs this here? */ @@ -2770,6 +2782,24 @@ dirmngr_status_help (ctrl_t ctrl, const char *text) return err; } + +/* This function is similar to print_assuan_status but takes a CTRL + * arg instead of an assuan context as first argument. */ +gpg_error_t +dirmngr_status_printf (ctrl_t ctrl, const char *keyword, + const char *format, ...) +{ + gpg_error_t err; + va_list arg_ptr; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + + va_start (arg_ptr, format); + err = vprint_assuan_status (ctx, keyword, format, arg_ptr); + va_end (arg_ptr); + return err; +} + + /* Send a tick progress indicator back. Fixme: This is only done for the currently active channel. */ gpg_error_t diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c index a3a74dd..440633d 100644 --- a/dirmngr/t-http.c +++ b/dirmngr/t-http.c @@ -40,6 +40,7 @@ #include "../common/util.h" #include "../common/logging.h" +#include "dns-stuff.h" #include "http.h" #include <ksba.h> @@ -203,6 +204,7 @@ main (int argc, char **argv) int no_crl = 0; const char *cafile = NULL; http_session_t session = NULL; + unsigned int timeout = 0; gpgrt_init (); log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID); @@ -224,6 +226,7 @@ main (int argc, char **argv) " --debug flyswatter\n" " --tls-debug N use TLS debug level N\n" " --cacert FNAME expect CA certificate in file FNAME\n" + " --timeout MS timeout for connect in MS\n" " --no-verify do not verify the certificate\n" " --force-tls use HTTP_FLAG_FORCE_TLS\n" " --force-tor use HTTP_FLAG_FORCE_TOR\n" @@ -261,6 +264,15 @@ main (int argc, char **argv) argc--; argv++; } } + else if (!strcmp (*argv, "--timeout")) + { + argc--; argv++; + if (argc) + { + timeout = strtoul (*argv, NULL, 10); + argc--; argv++; + } + } else if (!strcmp (*argv, "--no-verify")) { no_verify = 1; @@ -301,9 +313,25 @@ main (int argc, char **argv) if (!cafile) cafile = prepend_srcdir ("tls-ca.pem"); + if (verbose) + my_http_flags |= HTTP_FLAG_LOG_RESP; + + if (verbose || debug) + http_set_verbose (verbose, debug); + /* http.c makes use of the assuan socket wrapper. */ assuan_sock_init (); + if ((my_http_flags & HTTP_FLAG_FORCE_TOR)) + { + enable_dns_tormode (1); + if (assuan_sock_set_flag (ASSUAN_INVALID_FD, "tor-mode", 1)) + { + log_error ("error enabling Tor mode: %s\n", strerror (errno)); + log_info ("(is your Libassuan recent enough?)\n"); + } + } + #if HTTP_USE_NTBTLS log_info ("new session.\n"); err = http_session_new (&session, NULL, @@ -407,6 +435,9 @@ main (int argc, char **argv) http_release_parsed_uri (uri); uri = NULL; + if (session) + http_session_set_timeout (session, timeout); + rc = http_open_document (&hd, *argv, NULL, my_http_flags, NULL, session, NULL, NULL); if (rc) |