diff options
Diffstat (limited to 'content/fetchers')
-rw-r--r-- | content/fetchers/about/certificate.c | 306 | ||||
-rw-r--r-- | content/fetchers/about/chart.c | 2 | ||||
-rw-r--r-- | content/fetchers/curl.c | 675 | ||||
-rw-r--r-- | content/fetchers/file/dirlist.c | 3 |
4 files changed, 705 insertions, 281 deletions
diff --git a/content/fetchers/about/certificate.c b/content/fetchers/about/certificate.c index 0d0d6f5dc..6f634d22a 100644 --- a/content/fetchers/about/certificate.c +++ b/content/fetchers/about/certificate.c @@ -134,26 +134,29 @@ static nserror free_ns_cert_info(struct ns_cert_info *cinfo) #include <openssl/ssl.h> #include <openssl/x509v3.h> -/* OpenSSL 1.0.x, 1.0.2, 1.1.0 and 1.1.1 API all changed - * LibreSSL declares its OpenSSL version as 2.1 but only supports 1.0.x API - */ -#if (defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x1010000fL)) -/* 1.0.x */ +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +/* OpenSSL 1.1.1 or LibreSSL */ + +# if defined(LIBRESSL_VERSION_NUMBER) + /* LibreSSL */ +# if (LIBRESSL_VERSION_NUMBER < 0x3050000fL) + /* LibreSSL <3.5.0 */ -#if (defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x1000200fL)) -/* pre 1.0.2 */ +# if (LIBRESSL_VERSION_NUMBER < 0x2070000fL) + /* LibreSSL <2.7.0 */ static int ns_X509_get_signature_nid(X509 *cert) { return OBJ_obj2nid(cert->cert_info->key->algor->algorithm); } -#else -#define ns_X509_get_signature_nid X509_get_signature_nid -#endif static const unsigned char *ns_ASN1_STRING_get0_data(ASN1_STRING *asn1str) { return (const unsigned char *)ASN1_STRING_data(asn1str); } +# else +# define ns_X509_get_signature_nid X509_get_signature_nid +# define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data +# endif static const BIGNUM *ns_RSA_get0_n(const RSA *d) { @@ -164,58 +167,161 @@ static const BIGNUM *ns_RSA_get0_e(const RSA *d) { return d->e; } +# else + /* LibreSSL >= 3.5.0 */ +# define ns_X509_get_signature_nid X509_get_signature_nid +# define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data +# define ns_RSA_get0_n RSA_get0_n +# define ns_RSA_get0_e RSA_get0_e +# endif +# else + /* OpenSSL 1.1.1 */ +# define ns_X509_get_signature_nid X509_get_signature_nid +# define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data +# define ns_RSA_get0_n RSA_get0_n +# define ns_RSA_get0_e RSA_get0_e +# endif + +static int ns_EVP_PKEY_get_bn_param(const EVP_PKEY *pkey, + const char *key_name, BIGNUM **bn) { + RSA *rsa; + BIGNUM *result = NULL; + + /* Check parameters: only support allocation-form *bn */ + if (pkey == NULL || key_name == NULL || bn == NULL || *bn != NULL) + return 0; + + /* Only support RSA keys */ + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) + return 0; + + rsa = EVP_PKEY_get1_RSA((EVP_PKEY *) pkey); + if (rsa == NULL) + return 0; + + if (strcmp(key_name, "n") == 0) { + const BIGNUM *n = ns_RSA_get0_n(rsa); + if (n != NULL) + result = BN_dup(n); + } else if (strcmp(key_name, "e") == 0) { + const BIGNUM *e = ns_RSA_get0_e(rsa); + if (e != NULL) + result = BN_dup(e); + } -static int ns_RSA_bits(const RSA *rsa) -{ - return RSA_size(rsa) * 8; -} + RSA_free(rsa); -static int ns_DSA_bits(const DSA *dsa) -{ - return DSA_size(dsa) * 8; + *bn = result; + + return (result != NULL) ? 1 : 0; } -static int ns_DH_bits(const DH *dh) +static int ns_EVP_PKEY_get_utf8_string_param(const EVP_PKEY *pkey, + const char *key_name, char *str, size_t max_len, + size_t *out_len) { - return DH_size(dh) * 8; -} + const EC_GROUP *ecgroup; + const char *group; + EC_KEY *ec; + int ret = 0; -#elif (OPENSSL_VERSION_NUMBER < 0x1010100fL) -/* 1.1.0 */ -#define ns_X509_get_signature_nid X509_get_signature_nid -#define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data + if (pkey == NULL || key_name == NULL) + return 0; -static const BIGNUM *ns_RSA_get0_n(const RSA *r) -{ - const BIGNUM *n; - const BIGNUM *e; - const BIGNUM *d; - RSA_get0_key(r, &n, &e, &d); - return n; + /* Only support EC keys */ + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) + return 0; + + /* Only support fetching the group */ + if (strcmp(key_name, "group") != 0) + return 0; + + ec = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) pkey); + + ecgroup = EC_KEY_get0_group(ec); + if (ecgroup == NULL) { + group = ""; + } else { + group = OBJ_nid2ln(EC_GROUP_get_curve_name(ecgroup)); + } + + if (str != NULL && max_len > strlen(group)) { + strcpy(str, group); + str[strlen(group)] = '\0'; + ret = 1; + } + if (out_len != NULL) + *out_len = strlen(group); + + EC_KEY_free(ec); + + return ret; } -static const BIGNUM *ns_RSA_get0_e(const RSA *r) +static int ns_EVP_PKEY_get_octet_string_param(const EVP_PKEY *pkey, + const char *key_name, unsigned char *buf, size_t max_len, + size_t *out_len) { - const BIGNUM *n; - const BIGNUM *e; - const BIGNUM *d; - RSA_get0_key(r, &n, &e, &d); - return e; -} + const EC_GROUP *ecgroup; + const EC_POINT *ecpoint; + size_t len; + BN_CTX *bnctx; + EC_KEY *ec; + int ret = 0; + + if (pkey == NULL || key_name == NULL) + return 0; + + /* Only support EC keys */ + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) + return 0; -#define ns_RSA_bits RSA_bits -#define ns_DSA_bits DSA_bits -#define ns_DH_bits DH_bits + if (strcmp(key_name, "encoded-pub-key") != 0) + return 0; + ec = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) pkey); + if (ec == NULL) + return 0; + + ecgroup = EC_KEY_get0_group(ec); + if (ecgroup != NULL) { + ecpoint = EC_KEY_get0_public_key(ec); + if (ecpoint != NULL) { + bnctx = BN_CTX_new(); + len = EC_POINT_point2oct(ecgroup, + ecpoint, + POINT_CONVERSION_UNCOMPRESSED, + NULL, + 0, + bnctx); + if (len != 0 && len <= max_len) { + if (EC_POINT_point2oct(ecgroup, + ecpoint, + POINT_CONVERSION_UNCOMPRESSED, + buf, + len, + bnctx) == len) + ret = 1; + } + if (out_len != NULL) + *out_len = len; + BN_CTX_free(bnctx); + } + } + + EC_KEY_free(ec); + + return ret; +} #else -/* 1.1.1 and later */ +/* OpenSSL 3.x and later */ #define ns_X509_get_signature_nid X509_get_signature_nid #define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data #define ns_RSA_get0_n RSA_get0_n #define ns_RSA_get0_e RSA_get0_e -#define ns_RSA_bits RSA_bits -#define ns_DSA_bits DSA_bits -#define ns_DH_bits DH_bits +#define ns_EVP_PKEY_get_bn_param EVP_PKEY_get_bn_param +#define ns_EVP_PKEY_get_octet_string_param EVP_PKEY_get_octet_string_param +#define ns_EVP_PKEY_get_utf8_string_param EVP_PKEY_get_utf8_string_param #endif /** @@ -365,36 +471,43 @@ static char *bindup(unsigned char *bin, unsigned int binlen) /** * extract RSA key information to info structure * - * \param rsa The RSA key to examine. The reference is dropped on return + * \param pkey The RSA key to examine. * \param ikey The public key info structure to fill * \rerun NSERROR_OK on success else error code. */ static nserror -rsa_to_info(RSA *rsa, struct ns_cert_pkey *ikey) +rsa_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey) { + BIGNUM *n = NULL, *e = NULL; char *tmp; - if (rsa == NULL) { + if (ns_EVP_PKEY_get_bn_param(pkey, "n", &n) != 1) { + return NSERROR_BAD_PARAMETER; + } + + if (ns_EVP_PKEY_get_bn_param(pkey, "e", &e) != 1) { + BN_free(n); return NSERROR_BAD_PARAMETER; } ikey->algor = strdup("RSA"); - ikey->size = ns_RSA_bits(rsa); + ikey->size = EVP_PKEY_bits(pkey); - tmp = BN_bn2hex(ns_RSA_get0_n(rsa)); + tmp = BN_bn2hex(n); if (tmp != NULL) { ikey->modulus = hexdup(tmp); OPENSSL_free(tmp); } - tmp = BN_bn2dec(ns_RSA_get0_e(rsa)); + tmp = BN_bn2dec(e); if (tmp != NULL) { ikey->exponent = strdup(tmp); OPENSSL_free(tmp); } - RSA_free(rsa); + BN_free(e); + BN_free(n); return NSERROR_OK; } @@ -403,22 +516,16 @@ rsa_to_info(RSA *rsa, struct ns_cert_pkey *ikey) /** * extract DSA key information to info structure * - * \param dsa The RSA key to examine. The reference is dropped on return + * \param pkey The DSA key to examine. * \param ikey The public key info structure to fill * \rerun NSERROR_OK on success else error code. */ static nserror -dsa_to_info(DSA *dsa, struct ns_cert_pkey *ikey) +dsa_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey) { - if (dsa == NULL) { - return NSERROR_BAD_PARAMETER; - } - ikey->algor = strdup("DSA"); - ikey->size = ns_DSA_bits(dsa); - - DSA_free(dsa); + ikey->size = EVP_PKEY_bits(pkey); return NSERROR_OK; } @@ -427,22 +534,16 @@ dsa_to_info(DSA *dsa, struct ns_cert_pkey *ikey) /** * extract DH key information to info structure * - * \param dsa The RSA key to examine. The reference is dropped on return + * \param pkey The DH key to examine. * \param ikey The public key info structure to fill * \rerun NSERROR_OK on success else error code. */ static nserror -dh_to_info(DH *dh, struct ns_cert_pkey *ikey) +dh_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey) { - if (dh == NULL) { - return NSERROR_BAD_PARAMETER; - } - ikey->algor = strdup("Diffie Hellman"); - ikey->size = ns_DH_bits(dh); - - DH_free(dh); + ikey->size = EVP_PKEY_bits(pkey); return NSERROR_OK; } @@ -451,49 +552,50 @@ dh_to_info(DH *dh, struct ns_cert_pkey *ikey) /** * extract EC key information to info structure * - * \param ec The EC key to examine. The reference is dropped on return + * \param pkey The EC key to examine. * \param ikey The public key info structure to fill * \rerun NSERROR_OK on success else error code. */ static nserror -ec_to_info(EC_KEY *ec, struct ns_cert_pkey *ikey) +ec_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey) { - const EC_GROUP *ecgroup; - const EC_POINT *ecpoint; - BN_CTX *bnctx; - char *ecpoint_hex; - - if (ec == NULL) { - return NSERROR_BAD_PARAMETER; - } + size_t len; ikey->algor = strdup("Elliptic Curve"); - ecgroup = EC_KEY_get0_group(ec); - - if (ecgroup != NULL) { - ikey->size = EC_GROUP_get_degree(ecgroup); - - ikey->curve = strdup(OBJ_nid2ln(EC_GROUP_get_curve_name(ecgroup))); + ikey->size = EVP_PKEY_bits(pkey); + + len = 0; + ns_EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &len); + if (len != 0) { + ikey->curve = malloc(len + 1); + if (ikey->curve != NULL) { + if (ns_EVP_PKEY_get_utf8_string_param(pkey, "group", + ikey->curve, len + 1, NULL) == 0) { + free(ikey->curve); + ikey->curve = NULL; + } + } + } - ecpoint = EC_KEY_get0_public_key(ec); - if (ecpoint != NULL) { - bnctx = BN_CTX_new(); - ecpoint_hex = EC_POINT_point2hex(ecgroup, - ecpoint, - POINT_CONVERSION_UNCOMPRESSED, - bnctx); - ikey->public = hexdup(ecpoint_hex); - OPENSSL_free(ecpoint_hex); - BN_CTX_free(bnctx); + len = 0; + ns_EVP_PKEY_get_octet_string_param(pkey, "encoded-pub-key", + NULL, 0, &len); + if (len != 0) { + unsigned char *point = malloc(len); + if (point != NULL) { + if (ns_EVP_PKEY_get_octet_string_param(pkey, + "encoded-pub-key", point, len, + NULL) == 1) { + ikey->public = bindup(point, len); + } + free(point); } } - EC_KEY_free(ec); return NSERROR_OK; } - /** * extract public key information to info structure * @@ -512,19 +614,19 @@ pkey_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey) switch (EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: - res = rsa_to_info(EVP_PKEY_get1_RSA(pkey), ikey); + res = rsa_to_info(pkey, ikey); break; case EVP_PKEY_DSA: - res = dsa_to_info(EVP_PKEY_get1_DSA(pkey), ikey); + res = dsa_to_info(pkey, ikey); break; case EVP_PKEY_DH: - res = dh_to_info(EVP_PKEY_get1_DH(pkey), ikey); + res = dh_to_info(pkey, ikey); break; case EVP_PKEY_EC: - res = ec_to_info(EVP_PKEY_get1_EC_KEY(pkey), ikey); + res = ec_to_info(pkey, ikey); break; default: diff --git a/content/fetchers/about/chart.c b/content/fetchers/about/chart.c index 1565c54d8..c030c12b4 100644 --- a/content/fetchers/about/chart.c +++ b/content/fetchers/about/chart.c @@ -33,7 +33,9 @@ #include <math.h> #include <stdio.h> +#include "utils/config.h" #include "netsurf/inttypes.h" +#include "utils/config.h" #include "utils/utils.h" #include "utils/errors.h" #include "utils/nsurl.h" diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c index d36f44c09..680e60456 100644 --- a/content/fetchers/curl.c +++ b/content/fetchers/curl.c @@ -67,21 +67,38 @@ #define UPDATES_PER_SECOND 2 /** - * The ciphersuites the browser is prepared to use + * The ciphersuites the browser is prepared to use for TLS1.3 + */ +#define CIPHER_SUITES \ + "TLS_AES_256_GCM_SHA384:" \ + "TLS_CHACHA20_POLY1305_SHA256:" \ + "TLS_AES_128_GCM_SHA256" + +/** + * The ciphersuites the browser is prepared to use for TLS<1.3 */ #define CIPHER_LIST \ /* disable everything */ \ "-ALL:" \ - /* enable TLSv1.2 PFS suites */ \ - "EECDH+AES+TLSv1.2:EDH+AES+TLSv1.2:" \ - /* enable PFS AES GCM suites */ \ - "EECDH+AESGCM:EDH+AESGCM:" \ - /* Enable PFS AES CBC suites */ \ - "EECDH+AES:EDH+AES:" \ - /* Enable non-PFS fallback suite */ \ - "AES128-SHA:" \ - /* Remove any PFS suites using weak DSA key exchange */ \ - "-DSS" + /* enable TLSv1.2 ECDHE AES GCM suites */ \ + "EECDH+AESGCM+TLSv1.2:" \ + /* enable ECDHE CHACHA20/POLY1305 suites */ \ + "EECDH+CHACHA20:" \ + /* Sort above by strength */ \ + "@STRENGTH:" \ + /* enable ECDHE (auth=RSA, mac=SHA1) AES CBC suites */ \ + "EECDH+aRSA+AES+SHA1" + +/** + * The legacy cipher suites the browser is prepared to use for TLS<1.3 + */ +#define CIPHER_LIST_LEGACY \ + /* as above */ \ + CIPHER_LIST":" \ + /* enable (non-PFS) RSA AES GCM suites */ \ + "RSA+AESGCM:" \ + /* enable (non-PFS) RSA (mac=SHA1) AES CBC suites */ \ + "RSA+AES+SHA1" /* Open SSL compatability for certificate handling */ #ifdef WITH_OPENSSL @@ -89,33 +106,11 @@ #include <openssl/ssl.h> #include <openssl/x509v3.h> -/* OpenSSL 1.0.x to 1.1.0 certificate reference counting changed - * LibreSSL declares its OpenSSL version as 2.1 but only supports the old way - */ -#if (defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x1010000fL)) -static int ns_X509_up_ref(X509 *cert) -{ - cert->references++; - return 1; -} - -static void ns_X509_free(X509 *cert) -{ - cert->references--; - if (cert->references == 0) { - X509_free(cert); - } -} -#else -#define ns_X509_up_ref X509_up_ref -#define ns_X509_free X509_free -#endif - #else /* WITH_OPENSSL */ typedef char X509; -static void ns_X509_free(X509 *cert) +static void X509_free(X509 *cert) { free(cert); } @@ -216,6 +211,26 @@ struct cert_info { long err; /**< OpenSSL error code */ }; +#if LIBCURL_VERSION_NUM >= 0x072000 /* 7.32.0 depricated CURLOPT_PROGRESSFUNCTION*/ +#define NSCURLOPT_PROGRESS_FUNCTION CURLOPT_XFERINFOFUNCTION +#define NSCURLOPT_PROGRESS_DATA CURLOPT_XFERINFODATA +#define NSCURL_PROGRESS_T curl_off_t +#else +#define NSCURLOPT_PROGRESS_FUNCTION CURLOPT_PROGRESSFUNCTION +#define NSCURLOPT_PROGRESS_DATA CURLOPT_PROGRESSDATA +#define NSCURL_PROGRESS_T double +#endif + +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 depricated curl_formadd */ +#define NSCURL_POSTDATA_T curl_mime +#define NSCURL_POSTDATA_CURLOPT CURLOPT_MIMEPOST +#define NSCURL_POSTDATA_FREE(x) curl_mime_free(x) +#else +#define NSCURL_POSTDATA_T struct curl_httppost +#define NSCURL_POSTDATA_CURLOPT CURLOPT_HTTPPOST +#define NSCURL_POSTDATA_FREE(x) curl_formfree(x) +#endif + /** Information for a single fetch. */ struct curl_fetch_info { struct fetch *fetch_handle; /**< The fetch handle we're parented by. */ @@ -225,7 +240,7 @@ struct curl_fetch_info { bool abort; /**< Abort requested. */ bool stopped; /**< Download stopped on purpose. */ bool only_2xx; /**< Only HTTP 2xx responses acceptable. */ - bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */ + bool downgrade_tls; /**< Downgrade to TLS 1.2 */ nsurl *url; /**< URL of this fetch. */ lwc_string *host; /**< The hostname of this fetch. */ struct curl_slist *headers; /**< List of request headers. */ @@ -233,9 +248,11 @@ struct curl_fetch_info { unsigned long content_length; /**< Response Content-Length, or 0. */ char *cookie_string; /**< Cookie string for this fetch */ char *realm; /**< HTTP Auth Realm */ - char *post_urlenc; /**< Url encoded POST string, or 0. */ + struct fetch_postdata *postdata; /**< POST data */ + NSCURL_POSTDATA_T *curl_postdata; /**< POST data in curl representation */ + long http_code; /**< HTTP result code from cURL. */ - struct curl_httppost *post_multipart; /**< Multipart post data, or 0. */ + uint64_t last_progress_update; /**< Time of last progress update */ int cert_depth; /**< deepest certificate in use */ struct cert_info cert_data[MAX_CERT_DEPTH]; /**< HTTPS certificate data */ @@ -342,85 +359,95 @@ static bool fetch_curl_can_fetch(const nsurl *url) } + /** - * Convert a list of struct ::fetch_multipart_data to a list of - * struct curl_httppost for libcurl. + * allocate postdata */ -static struct curl_httppost * -fetch_curl_post_convert(const struct fetch_multipart_data *control) +static struct fetch_postdata * +fetch_curl_alloc_postdata(const char *post_urlenc, + const struct fetch_multipart_data *post_multipart) { - struct curl_httppost *post = 0, *last = 0; - CURLFORMcode code; - nserror ret; - - for (; control; control = control->next) { - if (control->file) { - char *leafname = NULL; - ret = guit->file->basename(control->value, &leafname, NULL); - if (ret != NSERROR_OK) { - continue; + struct fetch_postdata *postdata; + postdata = calloc(1, sizeof(struct fetch_postdata)); + if (postdata != NULL) { + + if (post_urlenc) { + postdata->type = FETCH_POSTDATA_URLENC; + postdata->data.urlenc = strdup(post_urlenc); + if (postdata->data.urlenc == NULL) { + free(postdata); + postdata = NULL; } - - /* We have to special case filenames of "", so curl - * a) actually attempts the fetch and - * b) doesn't attempt to open the file "" - */ - if (control->value[0] == '\0') { - /* dummy buffer - needs to be static so - * pointer's still valid when we go out - * of scope (not that libcurl should be - * attempting to access it, of course). - */ - static char buf; - - code = curl_formadd(&post, &last, - CURLFORM_COPYNAME, control->name, - CURLFORM_BUFFER, control->value, - /* needed, as basename("") == "." */ - CURLFORM_FILENAME, "", - CURLFORM_BUFFERPTR, &buf, - CURLFORM_BUFFERLENGTH, 0, - CURLFORM_CONTENTTYPE, - "application/octet-stream", - CURLFORM_END); - if (code != CURL_FORMADD_OK) - NSLOG(netsurf, INFO, - "curl_formadd: %d (%s)", code, - control->name); - } else { - char *mimetype = guit->fetch->mimetype(control->value); - code = curl_formadd(&post, &last, - CURLFORM_COPYNAME, control->name, - CURLFORM_FILE, control->rawfile, - CURLFORM_FILENAME, leafname, - CURLFORM_CONTENTTYPE, - (mimetype != 0 ? mimetype : "text/plain"), - CURLFORM_END); - if (code != CURL_FORMADD_OK) - NSLOG(netsurf, INFO, - "curl_formadd: %d (%s=%s)", - code, - control->name, - control->value); - free(mimetype); + } else if (post_multipart) { + postdata->type = FETCH_POSTDATA_MULTIPART; + postdata->data.multipart = fetch_multipart_data_clone(post_multipart); + if (postdata->data.multipart == NULL) { + free(postdata); + postdata = NULL; } - free(leafname); - } - else { - code = curl_formadd(&post, &last, - CURLFORM_COPYNAME, control->name, - CURLFORM_COPYCONTENTS, control->value, - CURLFORM_END); - if (code != CURL_FORMADD_OK) - NSLOG(netsurf, INFO, - "curl_formadd: %d (%s=%s)", code, - control->name, control->value); + } else { + postdata->type = FETCH_POSTDATA_NONE; } } + return postdata; +} - return post; +/** + * free postdata + */ +static void fetch_curl_free_postdata(struct fetch_postdata *postdata) +{ + if (postdata != NULL) { + switch (postdata->type) { + case FETCH_POSTDATA_NONE: + break; + case FETCH_POSTDATA_URLENC: + free(postdata->data.urlenc); + break; + case FETCH_POSTDATA_MULTIPART: + fetch_multipart_data_destroy(postdata->data.multipart); + break; + } + + free(postdata); + } } +/** + *construct a new fetch structure + */ +static struct curl_fetch_info *fetch_alloc(void) +{ + struct curl_fetch_info *fetch; + fetch = malloc(sizeof (*fetch)); + if (fetch == NULL) + return NULL; + + fetch->curl_handle = NULL; + fetch->sent_ssl_chain = false; + fetch->had_headers = false; + fetch->abort = false; + fetch->stopped = false; + fetch->only_2xx = false; + fetch->downgrade_tls = false; + fetch->headers = NULL; + fetch->url = NULL; + fetch->host = NULL; + fetch->location = NULL; + fetch->content_length = 0; + fetch->http_code = 0; + fetch->cookie_string = NULL; + fetch->realm = NULL; + fetch->last_progress_update = 0; + fetch->postdata = NULL; + fetch->curl_postdata = NULL; + + /* Clear certificate chain data */ + memset(fetch->cert_data, 0, sizeof(fetch->cert_data)); + fetch->cert_depth = -1; + + return fetch; +} /** * Start fetching data for the given URL. @@ -456,46 +483,22 @@ fetch_curl_setup(struct fetch *parent_fetch, struct curl_slist *slist; int i; - fetch = malloc(sizeof (*fetch)); + fetch = fetch_alloc(); if (fetch == NULL) - return 0; - - fetch->fetch_handle = parent_fetch; + return NULL; NSLOG(netsurf, INFO, "fetch %p, url '%s'", fetch, nsurl_access(url)); - /* construct a new fetch structure */ - fetch->curl_handle = NULL; - fetch->sent_ssl_chain = false; - fetch->had_headers = false; - fetch->abort = false; - fetch->stopped = false; fetch->only_2xx = only_2xx; fetch->downgrade_tls = downgrade_tls; - fetch->headers = NULL; + fetch->fetch_handle = parent_fetch; fetch->url = nsurl_ref(url); fetch->host = nsurl_get_component(url, NSURL_HOST); - fetch->location = NULL; - fetch->content_length = 0; - fetch->http_code = 0; - fetch->cookie_string = NULL; - fetch->realm = NULL; - fetch->post_urlenc = NULL; - fetch->post_multipart = NULL; - if (post_urlenc) { - fetch->post_urlenc = strdup(post_urlenc); - } else if (post_multipart) { - fetch->post_multipart = fetch_curl_post_convert(post_multipart); + if (fetch->host == NULL) { + goto failed; } - fetch->last_progress_update = 0; - - /* Clear certificate chain data */ - memset(fetch->cert_data, 0, sizeof(fetch->cert_data)); - fetch->cert_depth = -1; - - if ((fetch->host == NULL) || - (post_multipart != NULL && fetch->post_multipart == NULL) || - (post_urlenc != NULL && fetch->post_urlenc == NULL)) { + fetch->postdata = fetch_curl_alloc_postdata(post_urlenc, post_multipart); + if (fetch->postdata == NULL) { goto failed; } @@ -548,9 +551,7 @@ failed: lwc_string_unref(fetch->host); nsurl_unref(fetch->url); - free(fetch->post_urlenc); - if (fetch->post_multipart) - curl_formfree(fetch->post_multipart); + fetch_curl_free_postdata(fetch->postdata); curl_slist_free_all(fetch->headers); free(fetch); return NULL; @@ -730,7 +731,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx) */ if (!fetch->cert_data[depth].cert) { fetch->cert_data[depth].cert = X509_STORE_CTX_get_current_cert(x509_ctx); - ns_X509_up_ref(fetch->cert_data[depth].cert); + X509_up_ref(fetch->cert_data[depth].cert); fetch->cert_data[depth].err = X509_STORE_CTX_get_error(x509_ctx); } @@ -805,7 +806,8 @@ fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm) { struct curl_fetch_info *f = (struct curl_fetch_info *) parm; SSL_CTX *sslctx = _sslctx; - long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; /* set verify callback for each certificate in chain */ SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback); @@ -816,19 +818,14 @@ fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm) parm); if (f->downgrade_tls) { - /* Disable TLS 1.1/1.2 if the server can't cope with them */ -#ifdef SSL_OP_NO_TLSv1_1 - options |= SSL_OP_NO_TLSv1_1; -#endif -#ifdef SSL_OP_NO_TLSv1_2 - options |= SSL_OP_NO_TLSv1_2; + /* Disable TLS 1.3 if the server can't cope with it */ +#ifdef SSL_OP_NO_TLSv1_3 + options |= SSL_OP_NO_TLSv1_3; #endif #ifdef SSL_MODE_SEND_FALLBACK_SCSV /* Ensure server rejects the connection if downgraded too far */ SSL_CTX_set_mode(sslctx, SSL_MODE_SEND_FALLBACK_SCSV); #endif - /* Disable TLS1.2 ciphersuites */ - SSL_CTX_set_cipher_list(sslctx, CIPHER_LIST ":-TLSv1.2"); } SSL_CTX_set_options(sslctx, options); @@ -865,6 +862,301 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f) f->sent_ssl_chain = true; } +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 depricated curl_formadd */ + +/** + * curl mime data context + */ +struct curl_mime_ctx { + char *buffer; + curl_off_t size; + curl_off_t position; +}; + +static size_t mime_data_read_callback(char *buffer, size_t size, size_t nitems, void *arg) +{ + struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg; + curl_off_t sz = mctx->size - mctx->position; + + nitems *= size; + if(sz > (curl_off_t)nitems) { + sz = nitems; + } + if(sz) { + memcpy(buffer, mctx->buffer + mctx->position, sz); + } + mctx->position += sz; + return sz; +} + +static int mime_data_seek_callback(void *arg, curl_off_t offset, int origin) +{ + struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg; + + switch(origin) { + case SEEK_END: + offset += mctx->size; + break; + case SEEK_CUR: + offset += mctx->position; + break; + } + + if(offset < 0) { + return CURL_SEEKFUNC_FAIL; + } + mctx->position = offset; + return CURL_SEEKFUNC_OK; +} + +static void mime_data_free_callback(void *arg) +{ + struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg; + free(mctx); +} + +/** + * Convert a POST data list to a libcurl curl_mime. + * + * \param chandle curl fetch handle. + * \param multipart limked list of struct ::fetch_multipart forming post data. + */ +static curl_mime * +fetch_curl_postdata_convert(CURL *chandle, + const struct fetch_multipart_data *multipart) +{ + curl_mime *cmime; + curl_mimepart *part; + CURLcode code = CURLE_OK; + size_t value_len; + + cmime = curl_mime_init(chandle); + if (cmime == NULL) { + NSLOG(netsurf, WARNING, "postdata conversion failed to curl mime context"); + return NULL; + } + + /* iterate post data */ + for (; multipart != NULL; multipart = multipart->next) { + part = curl_mime_addpart(cmime); + if (part == NULL) { + goto convert_failed; + } + + code = curl_mime_name(part, multipart->name); + if (code != CURLE_OK) { + goto convert_failed; + } + + value_len = strlen(multipart->value); + + if (multipart->file && value_len==0) { + /* file entries with no filename require special handling */ + code=curl_mime_data(part, multipart->value, value_len); + if (code != CURLE_OK) { + goto convert_failed; + } + + code = curl_mime_filename(part, ""); + if (code != CURLE_OK) { + goto convert_failed; + } + + code = curl_mime_type(part, "application/octet-stream"); + if (code != CURLE_OK) { + goto convert_failed; + } + + } else if(multipart->file) { + /* file entry */ + nserror ret; + char *leafname = NULL; + char *mimetype = NULL; + + code = curl_mime_filedata(part, multipart->rawfile); + if (code != CURLE_OK) { + goto convert_failed; + } + + ret = guit->file->basename(multipart->value, &leafname, NULL); + if (ret != NSERROR_OK) { + goto convert_failed; + } + code = curl_mime_filename(part, leafname); + free(leafname); + if (code != CURLE_OK) { + goto convert_failed; + } + + mimetype = guit->fetch->mimetype(multipart->value); + if (mimetype == NULL) { + mimetype=strdup("text/plain"); + } + if (mimetype == NULL) { + goto convert_failed; + } + code = curl_mime_type(part, mimetype); + free(mimetype); + if (code != CURLE_OK) { + goto convert_failed; + } + + } else { + /* make the curl mime reference the existing multipart + * data which requires use of a callback and context. + */ + struct curl_mime_ctx *cb_ctx; + cb_ctx = malloc(sizeof(struct curl_mime_ctx)); + if (cb_ctx == NULL) { + goto convert_failed; + } + cb_ctx->buffer = multipart->value; + cb_ctx->size = value_len; + cb_ctx->position = 0; + code = curl_mime_data_cb(part, + value_len, + mime_data_read_callback, + mime_data_seek_callback, + mime_data_free_callback, + cb_ctx); + if (code != CURLE_OK) { + free(cb_ctx); + goto convert_failed; + } + } + } + + return cmime; + +convert_failed: + NSLOG(netsurf, WARNING, "postdata conversion failed with curl code: %d", code); + curl_mime_free(cmime); + return NULL; +} + +#else /* LIBCURL_VERSION_NUM >= 0x073800 */ + +/** + * Convert a list of struct ::fetch_multipart_data to a list of + * struct curl_httppost for libcurl. + */ +static struct curl_httppost * +fetch_curl_postdata_convert(CURL *chandle, + const struct fetch_multipart_data *control) +{ + struct curl_httppost *post = NULL, *last = NULL; + CURLFORMcode code; + nserror ret; + + for (; control; control = control->next) { + if (control->file) { + char *leafname = NULL; + ret = guit->file->basename(control->value, &leafname, NULL); + if (ret != NSERROR_OK) { + continue; + } + + /* We have to special case filenames of "", so curl + * a) actually attempts the fetch and + * b) doesn't attempt to open the file "" + */ + if (control->value[0] == '\0') { + /* dummy buffer - needs to be static so + * pointer's still valid when we go out + * of scope (not that libcurl should be + * attempting to access it, of course). + */ + static char buf; + + code = curl_formadd(&post, &last, + CURLFORM_COPYNAME, control->name, + CURLFORM_BUFFER, control->value, + /* needed, as basename("") == "." */ + CURLFORM_FILENAME, "", + CURLFORM_BUFFERPTR, &buf, + CURLFORM_BUFFERLENGTH, 0, + CURLFORM_CONTENTTYPE, + "application/octet-stream", + CURLFORM_END); + if (code != CURL_FORMADD_OK) + NSLOG(netsurf, INFO, + "curl_formadd: %d (%s)", code, + control->name); + } else { + char *mimetype = guit->fetch->mimetype(control->value); + code = curl_formadd(&post, &last, + CURLFORM_COPYNAME, control->name, + CURLFORM_FILE, control->rawfile, + CURLFORM_FILENAME, leafname, + CURLFORM_CONTENTTYPE, + (mimetype != 0 ? mimetype : "text/plain"), + CURLFORM_END); + if (code != CURL_FORMADD_OK) + NSLOG(netsurf, INFO, + "curl_formadd: %d (%s=%s)", + code, + control->name, + control->value); + free(mimetype); + } + free(leafname); + } else { + code = curl_formadd(&post, &last, + CURLFORM_COPYNAME, control->name, + CURLFORM_COPYCONTENTS, control->value, + CURLFORM_END); + if (code != CURL_FORMADD_OK) + NSLOG(netsurf, INFO, + "curl_formadd: %d (%s=%s)", code, + control->name, control->value); + } + } + + return post; +} + +#endif /* LIBCURL_VERSION_NUM >= 0x073800 */ + +/** + * Setup multipart post data + */ +static CURLcode fetch_curl_set_postdata(struct curl_fetch_info *f) +{ + CURLcode code = CURLE_OK; + +#undef SETOPT +#define SETOPT(option, value) { \ + code = curl_easy_setopt(f->curl_handle, option, value); \ + if (code != CURLE_OK) \ + return code; \ + } + + switch (f->postdata->type) { + case FETCH_POSTDATA_NONE: + SETOPT(CURLOPT_POSTFIELDS, NULL); + SETOPT(NSCURL_POSTDATA_CURLOPT, NULL); + SETOPT(CURLOPT_HTTPGET, 1L); + break; + + case FETCH_POSTDATA_URLENC: + SETOPT(NSCURL_POSTDATA_CURLOPT, NULL); + SETOPT(CURLOPT_HTTPGET, 0L); + SETOPT(CURLOPT_POSTFIELDS, f->postdata->data.urlenc); + break; + + case FETCH_POSTDATA_MULTIPART: + SETOPT(CURLOPT_POSTFIELDS, NULL); + SETOPT(CURLOPT_HTTPGET, 0L); + if (f->curl_postdata == NULL) { + f->curl_postdata = + fetch_curl_postdata_convert(f->curl_handle, + f->postdata->data.multipart); + } + SETOPT(NSCURL_POSTDATA_CURLOPT, f->curl_postdata); + break; + } + return code; +} /** * Set options specific for a fetch. @@ -888,20 +1180,11 @@ static CURLcode fetch_curl_set_options(struct curl_fetch_info *f) SETOPT(CURLOPT_PRIVATE, f); SETOPT(CURLOPT_WRITEDATA, f); SETOPT(CURLOPT_WRITEHEADER, f); - SETOPT(CURLOPT_PROGRESSDATA, f); + SETOPT(NSCURLOPT_PROGRESS_DATA, f); SETOPT(CURLOPT_HTTPHEADER, f->headers); - if (f->post_urlenc) { - SETOPT(CURLOPT_HTTPPOST, NULL); - SETOPT(CURLOPT_HTTPGET, 0L); - SETOPT(CURLOPT_POSTFIELDS, f->post_urlenc); - } else if (f->post_multipart) { - SETOPT(CURLOPT_POSTFIELDS, NULL); - SETOPT(CURLOPT_HTTPGET, 0L); - SETOPT(CURLOPT_HTTPPOST, f->post_multipart); - } else { - SETOPT(CURLOPT_POSTFIELDS, NULL); - SETOPT(CURLOPT_HTTPPOST, NULL); - SETOPT(CURLOPT_HTTPGET, 1L); + code = fetch_curl_set_postdata(f); + if (code != CURLE_OK) { + return code; } f->cookie_string = urldb_get_cookie(f->url, true); @@ -948,6 +1231,12 @@ static CURLcode fetch_curl_set_options(struct curl_fetch_info *f) SETOPT(CURLOPT_PROXY, NULL); } + + if (curl_with_openssl) { + SETOPT(CURLOPT_SSL_CIPHER_LIST, + f->downgrade_tls ? CIPHER_LIST_LEGACY : CIPHER_LIST); + } + /* Force-enable SSL session ID caching, as some distros are odd. */ SETOPT(CURLOPT_SSL_SESSIONID_CACHE, 1); @@ -1161,15 +1450,13 @@ static void fetch_curl_free(void *vf) if (f->headers) { curl_slist_free_all(f->headers); } - free(f->post_urlenc); - if (f->post_multipart) { - curl_formfree(f->post_multipart); - } + fetch_curl_free_postdata(f->postdata); + NSCURL_POSTDATA_FREE(f->curl_postdata); /* free certificate data */ for (i = 0; i < MAX_CERT_DEPTH; i++) { if (f->cert_data[i].cert != NULL) { - ns_X509_free(f->cert_data[i].cert); + X509_free(f->cert_data[i].cert); } } @@ -1199,7 +1486,7 @@ static bool fetch_curl_process_headers(struct curl_fetch_info *f) http_code = f->http_code; NSLOG(netsurf, INFO, "HTTP status code %li", http_code); - if (http_code == 304 && !f->post_urlenc && !f->post_multipart) { + if ((http_code == 304) && (f->postdata->type==FETCH_POSTDATA_NONE)) { /* Not Modified && GET request */ msg.type = FETCH_NOTMODIFIED; fetch_send_callback(&msg, f->fetch_handle); @@ -1435,10 +1722,10 @@ static void fetch_curl_poll(lwc_string *scheme_ignored) */ static int fetch_curl_progress(void *clientp, - double dltotal, - double dlnow, - double ultotal, - double ulnow) + NSCURL_PROGRESS_T dltotal, + NSCURL_PROGRESS_T dlnow, + NSCURL_PROGRESS_T ultotal, + NSCURL_PROGRESS_T ulnow) { static char fetch_progress_buffer[256]; /**< Progress buffer for cURL */ struct curl_fetch_info *f = (struct curl_fetch_info *) clientp; @@ -1505,6 +1792,24 @@ fetch_curl_debug(CURL *handle, } +static curl_socket_t fetch_curl_socket_open(void *clientp, + curlsocktype purpose, struct curl_sockaddr *address) +{ + (void) clientp; + (void) purpose; + + return (curl_socket_t) guit->fetch->socket_open( + address->family, address->socktype, + address->protocol); +} + +static int fetch_curl_socket_close(void *clientp, curl_socket_t item) +{ + (void) clientp; + + return guit->fetch->socket_close((int) item); +} + /** * Callback function for cURL. */ @@ -1711,8 +2016,10 @@ nserror fetch_curl_register(void) #undef SETOPT #define SETOPT(option, value) \ mcode = curl_multi_setopt(fetch_curl_multi, option, value); \ - if (mcode != CURLM_OK) \ - goto curl_multi_setopt_failed; + if (mcode != CURLM_OK) { \ + NSLOG(netsurf, ERROR, "attempting curl_multi_setopt(%s, ...)", #option); \ + goto curl_multi_setopt_failed; \ + } SETOPT(CURLMOPT_MAXCONNECTS, maxconnects); SETOPT(CURLMOPT_MAX_TOTAL_CONNECTIONS, maxconnects); @@ -1732,8 +2039,10 @@ nserror fetch_curl_register(void) #undef SETOPT #define SETOPT(option, value) \ code = curl_easy_setopt(fetch_blank_curl, option, value); \ - if (code != CURLE_OK) \ - goto curl_easy_setopt_failed; + if (code != CURLE_OK) { \ + NSLOG(netsurf, ERROR, "attempting curl_easy_setopt(%s, ...)", #option); \ + goto curl_easy_setopt_failed; \ + } SETOPT(CURLOPT_ERRORBUFFER, fetch_error_buffer); SETOPT(CURLOPT_DEBUGFUNCTION, fetch_curl_debug); @@ -1748,7 +2057,7 @@ nserror fetch_curl_register(void) SETOPT(CURLOPT_WRITEFUNCTION, fetch_curl_data); SETOPT(CURLOPT_HEADERFUNCTION, fetch_curl_header); - SETOPT(CURLOPT_PROGRESSFUNCTION, fetch_curl_progress); + SETOPT(NSCURLOPT_PROGRESS_FUNCTION, fetch_curl_progress); SETOPT(CURLOPT_NOPROGRESS, 0); SETOPT(CURLOPT_USERAGENT, user_agent_string()); SETOPT(CURLOPT_ENCODING, "gzip"); @@ -1756,6 +2065,8 @@ nserror fetch_curl_register(void) SETOPT(CURLOPT_LOW_SPEED_TIME, 180L); SETOPT(CURLOPT_NOSIGNAL, 1L); SETOPT(CURLOPT_CONNECTTIMEOUT, nsoption_uint(curl_fetch_timeout)); + SETOPT(CURLOPT_OPENSOCKETFUNCTION, fetch_curl_socket_open); + SETOPT(CURLOPT_CLOSESOCKETFUNCTION, fetch_curl_socket_close); if (nsoption_charp(ca_bundle) && strcmp(nsoption_charp(ca_bundle), "")) { @@ -1785,6 +2096,14 @@ nserror fetch_curl_register(void) /* only set the cipher list with openssl otherwise the * fetch fails with "Unknown cipher in list" */ +#if LIBCURL_VERSION_NUM >= 0x073d00 + /* Need libcurl 7.61.0 or later built against OpenSSL with + * TLS1.3 support */ + code = curl_easy_setopt(fetch_blank_curl, + CURLOPT_TLS13_CIPHERS, CIPHER_SUITES); + if (code != CURLE_OK && code != CURLE_NOT_BUILT_IN) + goto curl_easy_setopt_failed; +#endif SETOPT(CURLOPT_SSL_CIPHER_LIST, CIPHER_LIST); } diff --git a/content/fetchers/file/dirlist.c b/content/fetchers/file/dirlist.c index d49dc7fe7..345dbd8f3 100644 --- a/content/fetchers/file/dirlist.c +++ b/content/fetchers/file/dirlist.c @@ -29,6 +29,7 @@ #include "utils/messages.h" #include "utils/nscolour.h" +#include "netsurf/inttypes.h" #include "netsurf/types.h" #include "netsurf/plot_style.h" @@ -158,7 +159,7 @@ bool dirlist_generate_title(const char *title, char *buffer, int buffer_length) "<title>%s</title>\n" "<style>\n" "html {\n" - "\tbackground-color: #%06x;\n" + "\tbackground-color: #%06"PRIx32";\n" "}\n" "%s" "</style>\n" |