Unverified Commit 65f5c86d authored by Björn Stenberg's avatar Björn Stenberg Committed by Daniel Stenberg
Browse files

HSTS: support for HTTP Strict Transport Security using libhsts

This patch adds two new configure parameters:
 --with-libhsts=PATH to point to libhsts
 --with-hsts-file=FILE to specify location of the dafsa file that
contains the domain database
parent f0b7b106
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -175,6 +175,7 @@ curl_verbose_msg="enabled (--disable-verbose)"
   curl_rtmp_msg="no      (--with-librtmp)"
  curl_mtlnk_msg="no      (--with-libmetalink)"
    curl_psl_msg="no      (--with-libpsl)"
   curl_hsts_msg="no      (--with-libhsts)"

    ssl_backends=

@@ -3341,6 +3342,102 @@ if test X"$want_h2" != Xno; then

fi

dnl **********************************************************************
dnl Check for libhsts
dnl **********************************************************************
dnl libhsts project home page: https://gitlab.com/rockdaboot/libhsts
OPT_HSTS=off
AC_ARG_WITH(libhsts,dnl
AC_HELP_STRING([--with-libhsts=PATH],[Where to look for libhsts, PATH points to the libhsts installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
AC_HELP_STRING([--without-libhsts], [disable HSTS]),
  OPT_HSTS=$withval)

if test X"$OPT_HSTS" != Xno; then
  dnl backup the pre-hsts variables
  CLEANLDFLAGS="$LDFLAGS"
  CLEANCPPFLAGS="$CPPFLAGS"
  CLEANLIBS="$LIBS"

  case "$OPT_HSTS" in
  yes)
    dnl --with-hsts (without path) used
    CURL_CHECK_PKGCONFIG(libhsts)

    if test "$PKGCONFIG" != "no" ; then
      LIB_HSTS=`$PKGCONFIG --libs-only-l libhsts`
      LD_HSTS=`$PKGCONFIG --libs-only-L libhsts`
      CPP_HSTS=`$PKGCONFIG --cflags-only-I libhsts`
      version=`$PKGCONFIG --modversion libhsts`
      DIR_HSTS=`echo $LD_HSTS | $SED -e 's/-L//'`
    fi

    ;;
  off)
    dnl no --with-hsts option given, just check default places
    ;;
  *)
    dnl use the given --with-hsts spot
    PREFIX_HSTS=$OPT_HSTS
    ;;
  esac

  dnl if given with a prefix, we set -L and -I based on that
  if test -n "$PREFIX_HSTS"; then
    LIB_HSTS="-lhsts"
    LD_HSTS=-L${PREFIX_HSTS}/lib$libsuff
    CPP_HSTS=-I${PREFIX_HSTS}/include
    DIR_HSTS=${PREFIX_HSTS}/lib$libsuff
  fi

  LDFLAGS="$LDFLAGS $LD_HSTS"
  CPPFLAGS="$CPPFLAGS $CPP_HSTS"
  LIBS="$LIB_HSTS $LIBS"

  AC_CHECK_LIB(hsts, hsts_search)

  AC_CHECK_HEADERS(libhsts.h,
    curl_hsts_msg="enabled (libhsts)"
    HAVE_HSTS=1
    AC_DEFINE(USE_HSTS, 1, [if HSTS is in use])
    AC_SUBST(USE_HSTS, [1])
  )

  if test X"$OPT_HSTS" != Xoff &&
     test "$HAVE_HSTS" != "1"; then
    AC_MSG_ERROR([HSTS libs and/or directories were not found where specified!])
  fi

  if test "$HAVE_HSTS" = "1"; then
    if test -n "$DIR_HSTS"; then
       dnl when the hsts shared libs were found in a path that the run-time
       dnl linker doesn't search through, we need to add it to LD_LIBRARY_PATH
       dnl to prevent further configure tests to fail due to this

       if test "x$cross_compiling" != "xyes"; then
         LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$DIR_HSTS"
         export LD_LIBRARY_PATH
         AC_MSG_NOTICE([Added $DIR_HSTS to LD_LIBRARY_PATH])
       fi
    fi

    AC_ARG_WITH(hsts-file,
    AC_HELP_STRING([--with-hsts-file=FILE],
                   [Path to hsts.dafsa file]),
                   [ HSTS_FILE="$withval" ] )
    if test -n "$HSTS_FILE" ; then
      AC_DEFINE_UNQUOTED(HSTS_FILE, "$HSTS_FILE",
                         [Path to hsts.dafsa file] )
    else
      AC_MSG_ERROR([When enabling HSTS, you also need to specify --with-hsts-file pointing to the hsts.dafsa file])
    fi
  else
    dnl no hsts, revert back to clean variables
    LDFLAGS=$CLEANLDFLAGS
    CPPFLAGS=$CLEANCPPFLAGS
    LIBS=$CLEANLIBS
  fi
fi

dnl **********************************************************************
dnl Check for zsh completion path
dnl **********************************************************************
@@ -4350,6 +4447,8 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
  PSL:              ${curl_psl_msg}
  Alt-svc:          ${curl_altsvc_msg}
  HTTP2:            ${curl_h2_msg}
  HSTS:             ${curl_hsts_msg} (using ${HSTS_FILE})
  HSTS        :     ${curl_hsts_msg} (using ${HSTS_FILE})
  Protocols:        ${SUPPORT_PROTOCOLS}
  Features:         ${SUPPORT_FEATURES}
])
+3 −0
Original line number Diff line number Diff line
@@ -149,6 +149,9 @@ libcurl was built with support for TLS-SRP. (Added in 7.21.4)
.IP CURL_VERSION_NTLM_WB
libcurl was built with support for NTLM delegation to a winbind helper.
(Added in 7.22.0)
.IP CURL_VERSION_HSTS
libcurl was built with support for HSTS
(Added in 7.62.0)
.IP CURL_VERSION_HTTP2
libcurl was built with support for HTTP2.
(Added in 7.33.0)
+1 −0
Original line number Diff line number Diff line
@@ -922,6 +922,7 @@ CURL_VERSION_CURLDEBUG 7.19.6
CURL_VERSION_DEBUG              7.10.6
CURL_VERSION_GSSAPI             7.38.0
CURL_VERSION_GSSNEGOTIATE       7.10.6        7.38.0
CURL_VERSION_HSTS               7.62.0
CURL_VERSION_HTTP2              7.33.0
CURL_VERSION_HTTPS_PROXY        7.52.0
CURL_VERSION_IDN                7.12.0
+1 −0
Original line number Diff line number Diff line
@@ -2787,6 +2787,7 @@ typedef struct {
#define CURL_VERSION_MULTI_SSL    (1<<22) /* Multiple SSL backends available */
#define CURL_VERSION_BROTLI       (1<<23) /* Brotli features are present. */
#define CURL_VERSION_ALTSVC       (1<<24) /* Alt-Svc handling built-in */
#define CURL_VERSION_HSTS         (1<<25) /* HSTS features are present */

 /*
 * NAME curl_version_info()
+46 −3
Original line number Diff line number Diff line
@@ -122,6 +122,9 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "strdup.h"
#include "setopt.h"
#include "altsvc.h"
#ifdef USE_HSTS
#include <libhsts.h>
#endif

/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -291,6 +294,10 @@ void Curl_freeset(struct Curl_easy *data)
  data->change.url = NULL;

  Curl_mime_cleanpart(&data->set.mimepost);

#ifdef USE_HSTS
  hsts_free(data->set.hsts);
#endif
}

/* free the URL pieces */
@@ -617,6 +624,27 @@ CURLcode Curl_open(struct Curl_easy **curl)

      Curl_http2_init_state(&data->state);
    }

#ifdef USE_HSTS
    {
      hsts_status_t hsts_status = HSTS_ERR_INPUT_FAILURE;
#if DEBUGBUILD
      char *debug_hsts_file = curl_getenv("CURL_HSTSFILE");
      if(debug_hsts_file) {
        DEBUGF(fprintf(stderr, "DEBUG: HSTS file override: %s\n",
                       debug_hsts_file));
        hsts_status = hsts_load_file(debug_hsts_file, &data->set.hsts);
        free(debug_hsts_file);
      }
      else
#endif
      hsts_status = hsts_load_file(HSTS_FILE, &data->set.hsts);
      if(hsts_status < HSTS_SUCCESS) {
        fprintf(stderr, "Failed loading HSTS database, error code %d!\n",
                hsts_status);
      }
    }
#endif
  }

  if(result) {
@@ -1821,22 +1849,37 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme)
{
  const struct Curl_handler * const *pp;
  const struct Curl_handler *p;
  /* Scan protocol handler table and match against 'scheme'. The handler may
     be changed later when the protocol specific setup function is called. */

  /* Scan protocol handler table and match against 'protostr' to set a few
     variables based on the URL. Now that the handler may be changed later
     when the protocol specific setup function is called. */
  for(pp = protocols; (p = *pp) != NULL; pp++)
    if(strcasecompare(p->scheme, scheme))
      /* Protocol found in table. Check if allowed */
      return p;

  return NULL; /* not found */
}


static CURLcode findprotocol(struct Curl_easy *data,
                             struct connectdata *conn,
                             const char *protostr)
{
  const struct Curl_handler *p = Curl_builtin_scheme(protostr);

#ifdef USE_HSTS
  /* HSTS means we override any http access with https if the domain
     is listed in the HSTS database */
  if(data->set.hsts && strcasecompare(protostr, "http")) {
    hsts_status_t hsts_status = hsts_search(data->set.hsts,
                                            conn->host.name, 0, NULL);
    if(hsts_status == HSTS_SUCCESS) {
      infof(data, "Domain found in HSTS database, upgrading to https\n");
      p = Curl_builtin_scheme("https");
    }
  }
#endif

  if(p && /* Protocol found in table. Check if allowed */
     (data->set.allowed_protocols & p->protocol)) {

Loading