From 631bc65253e25fa6a3c6b4a11cb06245cee58293 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 11 Dec 2019 14:30:55 +0100 Subject: add logic for privacy policy --- src/exchange-tools/taler-exchange-keyup.c | 2 +- src/exchange/exchange.conf | 15 + src/exchange/taler-exchange-httpd.c | 4 + src/exchange/taler-exchange-httpd_refresh_reveal.c | 6 +- src/exchange/taler-exchange-httpd_terms.c | 507 ++------------------- src/exchange/taler-exchange-httpd_terms.h | 18 + src/lib/exchange_api_handle.c | 2 +- 7 files changed, 81 insertions(+), 473 deletions(-) diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index d974ec46a..e6e3db0d1 100644 --- a/src/exchange-tools/taler-exchange-keyup.c +++ b/src/exchange-tools/taler-exchange-keyup.c @@ -765,7 +765,7 @@ create_denomkey_issue (const struct CoinTypeParams *params, GNUNET_assert (NULL != dki->denom_priv.rsa_private_key); dki->denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_private_key_get_public ( - dki->denom_priv.rsa_private_key); + dki->denom_priv.rsa_private_key); GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, &dki->issue.properties.denom_hash); dki->issue.properties.master = master_public_key; diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 79b1877d9..c2426bc7f 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -67,3 +67,18 @@ LOOKAHEAD_SIGN = 32 weeks 1 day # how long do we provide to clients denomination and signing keys # ahead of time? LOOKAHEAD_PROVIDE = 4 weeks 1 day + + +# Directory with our terms of service. +# TERMS_DIR = + +# Etag / filename for the terms of service. +# TERMS_ETAG = + + +# Directory with our privacy policy. +# PRIVACY_DIR = + +# Etag / filename for the privacy policy. +# PRIVACY_ETAG = + diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 001d49df8..a8f32b030 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -256,6 +256,10 @@ handle_mhd_request (void *cls, { "/terms", MHD_HTTP_METHOD_GET, NULL, NULL, 0, &TEH_handler_terms, MHD_HTTP_OK }, + /* Privacy policy */ + { "/privacy", MHD_HTTP_METHOD_GET, NULL, + NULL, 0, + &TEH_handler_privacy, MHD_HTTP_OK }, /* Return key material and fundamental properties for this exchange */ { "/keys", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c index fb2602c61..97e687ea9 100644 --- a/src/exchange/taler-exchange-httpd_refresh_reveal.c +++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c @@ -771,9 +771,9 @@ handle_refresh_reveal_json (struct MHD_Connection *connection, { rctx->ev_sigs[i].rsa_signature = GNUNET_CRYPTO_rsa_sign_blinded ( - rctx->dkis[i]->denom_priv.rsa_private_key, - rctx->rcds[i].coin_ev, - rctx->rcds[i].coin_ev_size); + rctx->dkis[i]->denom_priv.rsa_private_key, + rctx->rcds[i].coin_ev, + rctx->rcds[i].coin_ev_size); if (NULL == rctx->ev_sigs[i].rsa_signature) { GNUNET_break (0); diff --git a/src/exchange/taler-exchange-httpd_terms.c b/src/exchange/taler-exchange-httpd_terms.c index b3d7c3442..47905f601 100644 --- a/src/exchange/taler-exchange-httpd_terms.c +++ b/src/exchange/taler-exchange-httpd_terms.c @@ -26,118 +26,16 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_responses.h" - -/** - * Entry in the terms-of-service array. - */ -struct Terms -{ - /** - * Mime type of the terms. - */ - const char *mime_type; - - /** - * The terms (NOT 0-terminated!). - */ - const void *terms; - - /** - * The desired language. - */ - char *language; - - /** - * Number of bytes in @e terms. - */ - size_t terms_size; -}; - - -/** - * Array of terms of service, terminated by NULL/0 value. - */ -static struct Terms *terms; - -/** - * Length of the #terms array. - */ -static unsigned int terms_len; - -/** - * Etag to use for the terms of service (= version). - */ -static char *terms_etag; - - /** - * Check if @a mime matches the @a accept_pattern. - * - * @param accept_pattern a mime pattern like text/plain or image/ - * @param mime the mime type to match - * @return true if @a mime matches the @a accept_pattern + * Our terms of service. */ -static bool -mime_matches (const char *accept_pattern, - const char *mime) -{ - const char *da = strchr (accept_pattern, '/'); - const char *dm = strchr (mime, '/'); - - if ( (NULL == da) || - (NULL == dm) ) - return (0 == strcmp ("*", accept_pattern)); - return - ( ( (1 == da - accept_pattern) && - ('*' == *accept_pattern) ) || - ( (da - accept_pattern == dm - mime) && - (0 == strncasecmp (accept_pattern, - mime, - da - accept_pattern)) ) ) && - ( (0 == strcmp (da, "/*")) || - (0 == strcasecmp (da, - dm)) ); -} +static struct TALER_MHD_Legal *tos; /** - * Check if @a lang matches the @a language_pattern, and if so with - * which preference. - * - * @param language_pattern a language preferences string - * like "fr-CH, fr;q=0.9, en;q=0.8, *;q=0.1" - * @param lang the 2-digit language to match - * @return q-weight given for @a lang in @a language_pattern, 1.0 if no weights are given; - * 0 if @a lang is not in @a language_pattern + * Our privacy policy. */ -static double -language_matches (const char *language_pattern, - const char *lang) -{ - char *p = GNUNET_strdup (language_pattern); - char *sptr; - double r = 0.0; - - for (char *tok = strtok_r (p, ", ", &sptr); - NULL != tok; - tok = strtok_r (NULL, ", ", &sptr)) - { - char *sptr2; - char *lp = strtok_r (tok, ";", &sptr2); - char *qp = strtok_r (NULL, ";", &sptr2); - double q = 1.0; - - GNUNET_break_op ( (NULL == qp) || - (1 == sscanf (qp, - "q=%lf", - &q)) ); - if (0 == strcasecmp (lang, - lp)) - r = GNUNET_MAX (r, q); - } - GNUNET_free (p); - return r; -} +static struct TALER_MHD_Legal *pp; /** @@ -157,340 +55,38 @@ TEH_handler_terms (struct TEH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - struct MHD_Response *resp; - struct Terms *t; - (void) rh; (void) upload_data; (void) upload_data_size; (void) connection_cls; - { - const char *etag; - - etag = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_IF_NONE_MATCH); - if ( (NULL != etag) && - (NULL != terms_etag) && - (0 == strcasecmp (etag, - terms_etag)) ) - { - int ret; - - resp = MHD_create_response_from_buffer (0, - NULL, - MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, - MHD_HTTP_NOT_MODIFIED, - resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (resp); - return ret; - } - } - - t = NULL; - { - const char *mime; - const char *lang; - - mime = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT); - if (NULL == mime) - mime = "text/html"; - lang = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT_LANGUAGE); - if (NULL == lang) - lang = "en"; - /* Find best match: must match mime type (if possible), and if - mime type matches, ideally also language */ - for (unsigned int i = 0; i < terms_len; i++) - { - struct Terms *p = &terms[i]; - - if ( (NULL == t) || - (mime_matches (mime, - p->mime_type)) ) - { - if ( (NULL == t) || - (! mime_matches (mime, - t->mime_type)) || - (language_matches (lang, - p->language) > - language_matches (lang, - t->language) ) ) - t = p; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Best match for %s/%s: %s / %s\n", - lang, - mime, - (NULL != t) ? t->mime_type : "", - (NULL != t) ? t->language : ""); - } - - if (NULL == t) - { - /* Default terms of service if none are configured */ - static struct Terms none = { - .mime_type = "text/plain", - .terms = "Terms of service not configured", - .language = "en", - .terms_size = strlen ("Terms of service not configured") - }; - t = &none; - } - - /* try to compress the response */ - resp = NULL; - if (MHD_YES == - TALER_MHD_can_compress (connection)) - { - void *buf = GNUNET_memdup (t->terms, - t->terms_size); - size_t buf_size = t->terms_size; - - if (TALER_MHD_body_compress (&buf, - &buf_size)) - { - resp = MHD_create_response_from_buffer (buf_size, - buf, - MHD_RESPMEM_MUST_FREE); - if (MHD_NO == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) - { - GNUNET_break (0); - MHD_destroy_response (resp); - resp = NULL; - } - } - else - { - GNUNET_free (buf); - } - } - if (NULL == resp) - { - /* could not generate compressed response, return uncompressed */ - resp = MHD_create_response_from_buffer (t->terms_size, - (void *) t->terms, - MHD_RESPMEM_PERSISTENT); - } - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_ETAG, - terms_etag)); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_CONTENT_TYPE, - t->mime_type)); - { - int ret; - - ret = MHD_queue_response (connection, - MHD_HTTP_OK, - resp); - MHD_destroy_response (resp); - return ret; - } -} - - -/** - * Load all the terms of service from @a path under language @a lang - * from file @a name - * - * @param path where the terms are found - * @param lang which language directory to crawl - * @param name specific file to access - */ -static void -load_terms (const char *path, - const char *lang, - const char *name) -{ - static struct MimeMap - { - const char *ext; - const char *mime; - } mm[] = { - { .ext = ".html", .mime = "text/html" }, - { .ext = ".htm", .mime = "text/html" }, - { .ext = ".txt", .mime = "text/plain" }, - { .ext = ".pdf", .mime = "application/pdf" }, - { .ext = ".jpg", .mime = "image/jpeg" }, - { .ext = ".jpeg", .mime = "image/jpeg" }, - { .ext = ".png", .mime = "image/png" }, - { .ext = ".gif", .mime = "image/gif" }, - { .ext = NULL, .mime = NULL } - }; - const char *ext = strrchr (name, '.'); - const char *mime; - - if (NULL == ext) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unsupported file `%s' in directory `%s/%s': lacks extension\n", - name, - path, - lang); - return; - } - if ( (NULL == terms_etag) || - (0 != strncmp (terms_etag, - name, - ext - name - 1)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n", - name, - terms_etag, - path, - lang); - return; - } - mime = NULL; - for (unsigned int i = 0; NULL != mm[i].ext; i++) - if (0 == strcasecmp (mm[i].ext, - ext)) - { - mime = mm[i].mime; - break; - } - if (NULL == mime) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unsupported file extension `%s' of file `%s' in directory `%s/%s'\n", - ext, - name, - path, - lang); - return; - } - /* try to read the file with the terms of service */ - { - struct stat st; - char *fn; - int fd; - - GNUNET_asprintf (&fn, - "%s/%s/%s", - path, - lang, - name); - fd = open (fn, O_RDONLY); - if (-1 == fd) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "open", - fn); - GNUNET_free (fn); - return; - } - GNUNET_free (fn); - if (0 != fstat (fd, &st)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fstat", - fn); - (void) close (fd); - GNUNET_free (fn); - return; - } - if (SIZE_MAX < st.st_size) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fstat-size", - fn); - (void) close (fd); - GNUNET_free (fn); - return; - } - { - char *buf; - size_t bsize; - ssize_t ret; - - bsize = (size_t) st.st_size; - buf = GNUNET_malloc_large (bsize); - if (NULL == buf) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "malloc"); - (void) close (fd); - GNUNET_free (fn); - return; - } - ret = read (fd, - buf, - bsize); - if ( (ret < 0) || - (bsize != ((size_t) ret)) ) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "read", - fn); - (void) close (fd); - GNUNET_free (buf); - GNUNET_free (fn); - return; - } - (void) close (fd); - GNUNET_free (fn); - - /* append to global list of terms of service */ - { - struct Terms t = { - .mime_type = mime, - .terms = buf, - .language = GNUNET_strdup (lang), - .terms_size = bsize - }; - - GNUNET_array_append (terms, - terms_len, - t); - } - } - } + return TALER_MHD_reply_legal (connection, + tos); } /** - * Load all the terms of service from @a path under language @a lang. + * Handle a "/privacy" request. * - * @param path where the terms are found - * @param lang which language directory to crawl + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code */ -static void -load_language (const char *path, - const char *lang) +int +TEH_handler_privacy (struct TEH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) { - char *dname; - DIR *d; - - GNUNET_asprintf (&dname, - "%s/%s", - path, - lang); - d = opendir (dname); - for (struct dirent *de = readdir (d); - NULL != de; - de = readdir (d)) - { - const char *fn = de->d_name; - - if (fn[0] == '.') - continue; - load_terms (path, lang, fn); - } - closedir (d); - free (dname); + (void) rh; + (void) upload_data; + (void) upload_data_size; + (void) connection_cls; + return TALER_MHD_reply_legal (connection, + pp); } @@ -502,45 +98,20 @@ load_language (const char *path, void TEH_load_terms (const struct GNUNET_CONFIGURATION_Handle *cfg) { - char *path; - DIR *d; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "TERMS_ETAG", - &terms_etag)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "exchange", - "TERMS_ETAG"); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "TERMS_DIR", - &path)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "exchange", - "TERMS_DIR"); - - return; - } - d = opendir (path); - for (struct dirent *de = readdir (d); - NULL != de; - de = readdir (d)) - { - const char *lang = de->d_name; - - if (lang[0] == '.') - continue; - load_language (path, lang); - } - closedir (d); - free (path); + tos = TALER_MHD_legal_load (cfg, + "exchange", + "TERMS_DIR", + "TERMS_ETAG"); + if (NULL == tos) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Terms of service not configured\n"); + pp = TALER_MHD_legal_load (cfg, + "exchange", + "PRIVACY_DIR", + "PRIVACY_ETAG"); + if (NULL == pp) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Privacy policy not configured\n"); } diff --git a/src/exchange/taler-exchange-httpd_terms.h b/src/exchange/taler-exchange-httpd_terms.h index 18a42809d..75909df9a 100644 --- a/src/exchange/taler-exchange-httpd_terms.h +++ b/src/exchange/taler-exchange-httpd_terms.h @@ -46,6 +46,24 @@ TEH_handler_terms (struct TEH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size); +/** + * Handle a "/privacy" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TEH_handler_privacy (struct TEH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + /** * Load our terms of service as per configuration. * diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index efac70777..04de37677 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -1262,7 +1262,7 @@ keys_completed_cb (void *cls, for (unsigned int i = 0; i