agent,dirmngr: Tiny restructuring.
[gnupg.git] / dirmngr / http.c
index aa33917..bc62c82 100644 (file)
@@ -27,7 +27,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 /* Simple HTTP client implementation.  We try to keep the code as
                         "01234567890@"                 \
                         "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
 
-/* A long counter type.  */
-#ifdef HAVE_STRTOULL
-typedef unsigned long long longcounter_t;
-# define counter_strtoul(a) strtoull ((a), NULL, 10)
-#else
-typedef unsigned long longcounter_t;
-# define counter_strtoul(a) strtoul ((a), NULL, 10)
-#endif
-
 #if HTTP_USE_NTBTLS
 typedef ntbtls_t         tls_session_t;
 # define USE_TLS 1
@@ -206,7 +197,7 @@ struct cookie_s
 
   /* The remaining content length and a flag telling whether to use
      the content length.  */
-  longcounter_t content_length;
+  uint64_t content_length;
   unsigned int content_length_valid:1;
 };
 typedef struct cookie_s *cookie_t;
@@ -270,6 +261,9 @@ static gpg_error_t (*tls_callback) (http_t, http_session_t, int);
 /* The list of files with trusted CA certificates.  */
 static strlist_t tls_ca_certlist;
 
+/* The global callback for net activity.  */
+static void (*netactivity_cb)(void);
+
 
 \f
 #if defined(HAVE_W32_SYSTEM) && !defined(HTTP_NO_WSASTARTUP)
@@ -501,6 +495,11 @@ http_register_tls_ca (const char *fname)
     }
   else
     {
+      /* Warn if we can't access right now, but register it anyway in
+         case it becomes accessible later */
+      if (access (fname, F_OK))
+        log_info (_("can't access '%s': %s\n"), fname,
+                  gpg_strerror (gpg_error_from_syserror()));
       sl = add_to_strlist (&tls_ca_certlist, fname);
       if (*sl->d && !strcmp (sl->d + strlen (sl->d) - 4, ".pem"))
         sl->flags = 1;
@@ -508,6 +507,25 @@ http_register_tls_ca (const char *fname)
 }
 
 
+/* Register a callback which is called every time the HTTP mode has
+ * made a successful connection to some server.  */
+void
+http_register_netactivity_cb (void (*cb)(void))
+{
+  netactivity_cb = cb;
+}
+
+
+/* Call the netactivity callback if any.  */
+static void
+notify_netactivity (void)
+{
+  if (netactivity_cb)
+    netactivity_cb ();
+}
+
+
+
 #ifdef USE_TLS
 /* Free the TLS session associated with SESS, if any.  */
 static void
@@ -560,10 +578,14 @@ http_session_release (http_session_t sess)
 
 
 /* Create a new session object which is currently used to enable TLS
-   support.  It may eventually allow reusing existing connections.  */
+ * support.  It may eventually allow reusing existing connections.
+ * Valid values for FLAGS are:
+ *   HTTP_FLAG_TRUST_DEF - Use the CAs set with http_register_tls_ca
+ *   HTTP_FLAG_TRUST_SYS - Also use the CAs defined by the system
+ */
 gpg_error_t
 http_session_new (http_session_t *r_session, const char *tls_priority,
-                  const char *intended_hostname)
+                  const char *intended_hostname, unsigned int flags)
 {
   gpg_error_t err;
   http_session_t sess;
@@ -591,6 +613,8 @@ http_session_new (http_session_t *r_session, const char *tls_priority,
     const char *errpos;
     int rc;
     strlist_t sl;
+    int add_system_cas = !!(flags & HTTP_FLAG_TRUST_SYS);
+    int is_hkps_pool;
 
     rc = gnutls_certificate_allocate_credentials (&sess->certcred);
     if (rc < 0)
@@ -601,13 +625,14 @@ http_session_new (http_session_t *r_session, const char *tls_priority,
         goto leave;
       }
 
+    is_hkps_pool = (intended_hostname
+                    && !ascii_strcasecmp (intended_hostname,
+                                          "hkps.pool.sks-keyservers.net"));
+
     /* If the user has not specified a CA list, and they are looking
      * for the hkps pool from sks-keyservers.net, then default to
      * Kristian's certificate authority:  */
-    if (!tls_ca_certlist
-        && intended_hostname
-        && !ascii_strcasecmp (intended_hostname,
-                              "hkps.pool.sks-keyservers.net"))
+    if (!tls_ca_certlist && is_hkps_pool)
       {
         char *pemname = make_filename_try (gnupg_datadir (),
                                            "sks-keyservers.netCA.pem", NULL);
@@ -629,14 +654,36 @@ http_session_new (http_session_t *r_session, const char *tls_priority,
       }
 
     /* Add configured certificates to the session.  */
-    for (sl = tls_ca_certlist; sl; sl = sl->next)
+    if ((flags & HTTP_FLAG_TRUST_DEF))
       {
-        rc = gnutls_certificate_set_x509_trust_file
-          (sess->certcred, sl->d,
-           (sl->flags & 1)? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER);
+        for (sl = tls_ca_certlist; sl; sl = sl->next)
+          {
+            rc = gnutls_certificate_set_x509_trust_file
+              (sess->certcred, sl->d,
+               (sl->flags & 1)? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER);
+            if (rc < 0)
+              log_info ("setting CA from file '%s' failed: %s\n",
+                        sl->d, gnutls_strerror (rc));
+          }
+        if (!tls_ca_certlist && !is_hkps_pool)
+          add_system_cas = 1;
+      }
+
+    /* Add system certificates to the session.  */
+    if (add_system_cas)
+      {
+#if GNUTLS_VERSION_NUMBER >= 0x030014
+        static int shown;
+
+        rc = gnutls_certificate_set_x509_system_trust (sess->certcred);
         if (rc < 0)
-          log_info ("setting CA from file '%s' failed: %s\n",
-                    sl->d, gnutls_strerror (rc));
+          log_info ("setting system CAs failed: %s\n", gnutls_strerror (rc));
+        else if (!shown)
+          {
+            shown = 1;
+            log_info ("number of system provided CAs: %d\n", rc);
+          }
+#endif /* gnutls >= 3.0.20 */
       }
 
     rc = gnutls_init (&sess->tls_session, GNUTLS_CLIENT);
@@ -720,7 +767,7 @@ http_session_set_log_cb (http_session_t sess,
 \f
 /* Start a HTTP retrieval and on success store at R_HD a context
    pointer for completing the request and to wait for the response.
-   If HTTPHOST is not NULL it is used hor the Host header instead of a
+   If HTTPHOST is not NULL it is used for the Host header instead of a
    Host header derived from the URL. */
 gpg_error_t
 http_open (http_t *r_hd, http_req_t reqtype, const char *url,
@@ -913,7 +960,7 @@ http_wait_response (http_t hd)
 
   /* Shutdown one end of the socket is desired.  As per HTTP/1.0 this
      is not required but some very old servers (e.g. the original pksd
-     key server didn't worked without it.  */
+     keyserver didn't worked without it.  */
   if ((hd->flags & HTTP_FLAG_SHUTDOWN))
     shutdown (hd->sock->fd, 1);
   hd->in_data = 0;
@@ -2146,7 +2193,7 @@ parse_response (http_t hd)
       if (s)
         {
           cookie->content_length_valid = 1;
-          cookie->content_length = counter_strtoul (s);
+          cookie->content_length = string_to_u64 (s);
         }
     }
 
@@ -2264,6 +2311,8 @@ connect_server (const char *server, unsigned short port,
             *r_host_not_found = 1;
           log_error ("can't connect to '%s': %s\n", server, strerror (errno));
         }
+      else
+        notify_netactivity ();
       return sock;
 
 #else /*!ASSUAN_SOCK_TOR*/
@@ -2356,7 +2405,10 @@ connect_server (const char *server, unsigned short port,
           if (ret)
             last_errno = errno;
           else
-            connected = 1;
+            {
+              connected = 1;
+              notify_netactivity ();
+            }
         }
       free_dns_addrinfo (aibuf);
     }