commit 35292b8520f926a4c8d67bf5f6248e0eb615973a
parent 0e0ecbbdf9488914e3ee27c659cd2f438fea3e70
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 11 Oct 2026 14:50:22 +0200
refuse http:// for oauth2 endpoints unless in debug mode
Diffstat:
3 files changed, 81 insertions(+), 38 deletions(-)
diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c
@@ -351,6 +351,65 @@ oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
/**
+ * Check that @a url, given in option @a option of section @a section, is
+ * an http(s) URL acceptable for an OAuth 2.0 endpoint. RFC 6749 (Sections
+ * 3.1, 3.2 and 10.9) requires TLS for these endpoints, as otherwise the
+ * client secret, authorization codes and access tokens are transmitted in
+ * the clear. Hence a plaintext "http://" URL is refused unless
+ * @a debug_mode is set, in which case it is accepted with a warning.
+ *
+ * @param section configuration section that configured @a url
+ * @param option configuration option that configured @a url
+ * @param url URL that was configured
+ * @param debug_mode true if KYC_OAUTH2_DEBUG_MODE is set
+ * @return true if @a url may be used
+ */
+static bool
+check_endpoint_url (const char *section,
+ const char *option,
+ const char *url,
+ bool debug_mode)
+{
+ if (! TALER_url_valid_charset (url))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ option,
+ "not a valid URL");
+ return false;
+ }
+ if (0 == strncasecmp (url,
+ "https://",
+ strlen ("https://")))
+ return true;
+ if (0 != strncasecmp (url,
+ "http://",
+ strlen ("http://")))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ option,
+ "not a valid URL");
+ return false;
+ }
+ if (! debug_mode)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ option,
+ "OAuth 2.0 endpoints must use https:// (RFC 6749); plaintext http:// is only accepted with KYC_OAUTH2_DEBUG_MODE = YES");
+ return false;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "KYC_OAUTH2_DEBUG_MODE is set: accepting plaintext http:// endpoint `%s' in option `%s' of section `%s'; the client secret, authorization codes and access tokens will be transmitted in the clear. Never use this in production!\n",
+ url,
+ option,
+ section);
+ return true;
+}
+
+
+/**
* Load the configuration of the KYC provider.
*
* @param cls closure
@@ -368,6 +427,12 @@ oauth2_load_configuration (void *cls,
pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
pd->ps = ps;
pd->section = GNUNET_strdup (provider_section_name);
+ /* parse debug mode first, it decides whether http:// URLs are allowed */
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_yesno (ps->cfg,
+ provider_section_name,
+ "KYC_OAUTH2_DEBUG_MODE"))
+ pd->debug_mode = true;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (ps->cfg,
provider_section_name,
@@ -416,18 +481,11 @@ oauth2_load_configuration (void *cls,
oauth2_unload_configuration (pd);
return NULL;
}
- if ( (! TALER_url_valid_charset (s)) ||
- ( (0 != strncasecmp (s,
- "http://",
- strlen ("http://"))) &&
- (0 != strncasecmp (s,
- "https://",
- strlen ("https://"))) ) )
+ if (! check_endpoint_url (provider_section_name,
+ "KYC_OAUTH2_TOKEN_URL",
+ s,
+ pd->debug_mode))
{
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- provider_section_name,
- "KYC_OAUTH2_TOKEN_URL",
- "not a valid URL");
GNUNET_free (s);
oauth2_unload_configuration (pd);
return NULL;
@@ -446,18 +504,11 @@ oauth2_load_configuration (void *cls,
oauth2_unload_configuration (pd);
return NULL;
}
- if ( (! TALER_url_valid_charset (s)) ||
- ( (0 != strncasecmp (s,
- "http://",
- strlen ("http://"))) &&
- (0 != strncasecmp (s,
- "https://",
- strlen ("https://"))) ) )
+ if (! check_endpoint_url (provider_section_name,
+ "KYC_OAUTH2_AUTHORIZE_URL",
+ s,
+ pd->debug_mode))
{
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- provider_section_name,
- "KYC_OAUTH2_AUTHORIZE_URL",
- "not a valid URL");
oauth2_unload_configuration (pd);
GNUNET_free (s);
return NULL;
@@ -505,18 +556,11 @@ oauth2_load_configuration (void *cls,
oauth2_unload_configuration (pd);
return NULL;
}
- if ( (! TALER_url_valid_charset (s)) ||
- ( (0 != strncasecmp (s,
- "http://",
- strlen ("http://"))) &&
- (0 != strncasecmp (s,
- "https://",
- strlen ("https://"))) ) )
+ if (! check_endpoint_url (provider_section_name,
+ "KYC_OAUTH2_INFO_URL",
+ s,
+ pd->debug_mode))
{
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- provider_section_name,
- "KYC_INFO_URL",
- "not a valid URL");
GNUNET_free (s);
oauth2_unload_configuration (pd);
return NULL;
@@ -563,11 +607,6 @@ oauth2_load_configuration (void *cls,
oauth2_unload_configuration (pd);
return NULL;
}
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_yesno (ps->cfg,
- provider_section_name,
- "KYC_OAUTH2_DEBUG_MODE"))
- pd->debug_mode = true;
return pd;
}
diff --git a/src/testing/test_exchange_api.conf b/src/testing/test_exchange_api.conf
@@ -241,6 +241,8 @@ KYC_OAUTH2_CLIENT_ID = taler-exchange
KYC_OAUTH2_CLIENT_SECRET = exchange-secret
KYC_OAUTH2_POST_URL = http://example.com/
KYC_OAUTH2_CONVERTER_HELPER = taler-exchange-kyc-oauth2-test-converter.sh
+# The test OAuth 2.0 service listens on plaintext http://localhost.
+KYC_OAUTH2_DEBUG_MODE = YES
[kyc-check-oauth-test-id]
VOLUNTARY = NO
diff --git a/src/testing/test_exchange_api_age_restriction.conf b/src/testing/test_exchange_api_age_restriction.conf
@@ -87,6 +87,8 @@ KYC_OAUTH2_CLIENT_ID = taler-exchange
KYC_OAUTH2_CLIENT_SECRET = exchange-secret
KYC_OAUTH2_POST_URL = http://example.com/
KYC_OAUTH2_CONVERTER_HELPER = taler-exchange-kyc-oauth2-test-converter.sh
+# The test OAuth 2.0 service listens on plaintext http://localhost.
+KYC_OAUTH2_DEBUG_MODE = YES
[kyc-check-oauth-test-id]
VOLUNTARY = NO