commit 0477b8aa8692d6f129e8a400a02d8048e1d37316
parent 5d74e3ee42c908cb190543c4c5b32930025bfc04
Author: Christian Grothoff <christian@grothoff.org>
Date: Tue, 11 Aug 2026 14:03:53 +0200
fix #11107
Diffstat:
4 files changed, 213 insertions(+), 9 deletions(-)
diff --git a/src/authorization/anastasis_authorization_plugin_email.c b/src/authorization/anastasis_authorization_plugin_email.c
@@ -341,6 +341,54 @@ email_start (void *cls,
/**
+ * Map the exit status of the Email helper onto an HTTP status and error code.
+ *
+ * The helpers shipped with Challenger use a banded exit-code scheme documented
+ * in challenger-send-email(1): anything below 10 means the challenge was
+ * transmitted -- for e-mail always 1 (accepted by the mail transfer agent),
+ * since delivery to a mailbox cannot be confirmed -- 10-19 blames the address
+ * the user gave us, 20-29 is a recipient that is temporarily unreachable,
+ * 30-39 is the transmission provider and 40-49 is our own configuration.
+ *
+ * Codes we do not recognise are reported as an upstream failure rather than
+ * blamed on the user, so that a helper predating this scheme -- which used
+ * small ad-hoc exit codes -- never yields a client error.
+ *
+ * @param exit_code exit status of the helper, which must have exited normally
+ * @param[out] http_status set to the HTTP status to return
+ * @return error code to return, #TALER_EC_NONE if the challenge was transmitted
+ */
+static enum TALER_ErrorCode
+classify_helper_status (unsigned long int exit_code,
+ unsigned int *http_status)
+{
+ if (exit_code < 10)
+ {
+ *http_status = MHD_HTTP_OK;
+ return TALER_EC_NONE;
+ }
+ if (exit_code < 20)
+ {
+ *http_status = MHD_HTTP_CONFLICT;
+ return TALER_EC_ANASTASIS_EMAIL_INVALID;
+ }
+ if (exit_code < 30)
+ {
+ *http_status = MHD_HTTP_SERVICE_UNAVAILABLE;
+ return TALER_EC_ANASTASIS_ADDRESS_UNREACHABLE;
+ }
+ if ( (exit_code >= 40) &&
+ (exit_code < 50) )
+ {
+ *http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return TALER_EC_ANASTASIS_HELPER_MISCONFIGURED;
+ }
+ *http_status = MHD_HTTP_BAD_GATEWAY;
+ return TALER_EC_ANASTASIS_EMAIL_HELPER_COMMAND_FAILED;
+}
+
+
+/**
* Function called when our Email helper has terminated.
*
* @param cls our `struct ANASTASIS_AUHTORIZATION_State`
@@ -500,18 +548,32 @@ email_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED;
}
if ( (GNUNET_OS_PROCESS_EXITED != as->pst) ||
- (0 != as->exit_code) )
+ (as->exit_code >= 10) )
{
char es[32];
+ unsigned int http_status;
+ enum TALER_ErrorCode ec;
+ if (GNUNET_OS_PROCESS_EXITED != as->pst)
+ {
+ /* Killed by a signal or otherwise abnormal: there is no exit code to
+ classify. */
+ http_status = MHD_HTTP_BAD_GATEWAY;
+ ec = TALER_EC_ANASTASIS_EMAIL_HELPER_COMMAND_FAILED;
+ }
+ else
+ {
+ ec = classify_helper_status (as->exit_code,
+ &http_status);
+ }
GNUNET_snprintf (es,
sizeof (es),
"%u/%d",
(unsigned int) as->exit_code,
as->pst);
mres = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_ANASTASIS_EMAIL_HELPER_COMMAND_FAILED,
+ http_status,
+ ec,
es);
if (MHD_YES != mres)
return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
diff --git a/src/authorization/anastasis_authorization_plugin_post.c b/src/authorization/anastasis_authorization_plugin_post.c
@@ -379,6 +379,55 @@ post_start (void *cls,
/**
+ * Map the exit status of the Post helper onto an HTTP status and error code.
+ *
+ * The helpers shipped with Challenger use a banded exit-code scheme documented
+ * in challenger-send-post(1): anything below 10 means the challenge was
+ * transmitted -- for physical mail always 1 (accepted for printing and
+ * dispatch), since delivery takes days and is never reported back -- 10-19
+ * blames the address the user gave us, 20-29 is a recipient that is
+ * temporarily unreachable, 30-39 is the transmission provider and 40-49 is our
+ * own configuration.
+ *
+ * Codes we do not recognise are reported as an upstream failure rather than
+ * blamed on the user, so that a helper predating this scheme -- which used
+ * small ad-hoc exit codes -- never yields a client error.
+ *
+ * @param exit_code exit status of the helper, which must have exited normally
+ * @param[out] http_status set to the HTTP status to return
+ * @return error code to return, #TALER_EC_NONE if the challenge was transmitted
+ */
+static enum TALER_ErrorCode
+classify_helper_status (unsigned long int exit_code,
+ unsigned int *http_status)
+{
+ if (exit_code < 10)
+ {
+ *http_status = MHD_HTTP_OK;
+ return TALER_EC_NONE;
+ }
+ if (exit_code < 20)
+ {
+ *http_status = MHD_HTTP_CONFLICT;
+ return TALER_EC_ANASTASIS_POST_INVALID;
+ }
+ if (exit_code < 30)
+ {
+ *http_status = MHD_HTTP_SERVICE_UNAVAILABLE;
+ return TALER_EC_ANASTASIS_ADDRESS_UNREACHABLE;
+ }
+ if ( (exit_code >= 40) &&
+ (exit_code < 50) )
+ {
+ *http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return TALER_EC_ANASTASIS_HELPER_MISCONFIGURED;
+ }
+ *http_status = MHD_HTTP_BAD_GATEWAY;
+ return TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED;
+}
+
+
+/**
* Function called when our Post helper has terminated.
*
* @param cls our `struct ANASTASIS_AUHTORIZATION_State`
@@ -578,18 +627,32 @@ post_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED;
}
if ( (GNUNET_OS_PROCESS_EXITED != as->pst) ||
- (0 != as->exit_code) )
+ (as->exit_code >= 10) )
{
char es[32];
+ unsigned int http_status;
+ enum TALER_ErrorCode ec;
+ if (GNUNET_OS_PROCESS_EXITED != as->pst)
+ {
+ /* Killed by a signal or otherwise abnormal: there is no exit code to
+ classify. */
+ http_status = MHD_HTTP_BAD_GATEWAY;
+ ec = TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED;
+ }
+ else
+ {
+ ec = classify_helper_status (as->exit_code,
+ &http_status);
+ }
GNUNET_snprintf (es,
sizeof (es),
"%u/%d",
(unsigned int) as->exit_code,
as->pst);
mres = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED,
+ http_status,
+ ec,
es);
if (MHD_YES != mres)
return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
diff --git a/src/authorization/anastasis_authorization_plugin_sms.c b/src/authorization/anastasis_authorization_plugin_sms.c
@@ -308,6 +308,55 @@ sms_start (void *cls,
/**
+ * Map the exit status of the SMS helper onto an HTTP status and error code.
+ *
+ * The helpers shipped with Challenger use a banded exit-code scheme documented
+ * in challenger-send-sms(1): anything below 10 means the challenge was
+ * transmitted (0 confirmed on the handset, 1 accepted by the provider, 2
+ * suppressed as a duplicate of a message already in flight), 10-19 blames the
+ * address the user gave us, 20-29 is a recipient that is temporarily
+ * unreachable, 30-39 is the transmission provider and 40-49 is our own
+ * configuration.
+ *
+ * Codes we do not recognise are reported as an upstream failure rather than
+ * blamed on the user, so that a helper predating this scheme -- which used
+ * small ad-hoc exit codes -- never yields a client error.
+ *
+ * @param exit_code exit status of the helper, which must have exited normally
+ * @param[out] http_status set to the HTTP status to return
+ * @return error code to return, #TALER_EC_NONE if the challenge was transmitted
+ */
+static enum TALER_ErrorCode
+classify_helper_status (unsigned long int exit_code,
+ unsigned int *http_status)
+{
+ if (exit_code < 10)
+ {
+ *http_status = MHD_HTTP_OK;
+ return TALER_EC_NONE;
+ }
+ if (exit_code < 20)
+ {
+ *http_status = MHD_HTTP_CONFLICT;
+ return TALER_EC_ANASTASIS_SMS_PHONE_INVALID;
+ }
+ if (exit_code < 30)
+ {
+ *http_status = MHD_HTTP_SERVICE_UNAVAILABLE;
+ return TALER_EC_ANASTASIS_ADDRESS_UNREACHABLE;
+ }
+ if ( (exit_code >= 40) &&
+ (exit_code < 50) )
+ {
+ *http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return TALER_EC_ANASTASIS_HELPER_MISCONFIGURED;
+ }
+ *http_status = MHD_HTTP_BAD_GATEWAY;
+ return TALER_EC_ANASTASIS_SMS_HELPER_COMMAND_FAILED;
+}
+
+
+/**
* Function called when our SMS helper has terminated.
*
* @param cls our `struct ANASTASIS_AUHTORIZATION_State`
@@ -451,18 +500,32 @@ sms_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED;
}
if ( (GNUNET_OS_PROCESS_EXITED != as->pst) ||
- (0 != as->exit_code) )
+ (as->exit_code >= 10) )
{
char es[32];
+ unsigned int http_status;
+ enum TALER_ErrorCode ec;
+ if (GNUNET_OS_PROCESS_EXITED != as->pst)
+ {
+ /* Killed by a signal or otherwise abnormal: there is no exit code to
+ classify. */
+ http_status = MHD_HTTP_BAD_GATEWAY;
+ ec = TALER_EC_ANASTASIS_SMS_HELPER_COMMAND_FAILED;
+ }
+ else
+ {
+ ec = classify_helper_status (as->exit_code,
+ &http_status);
+ }
GNUNET_snprintf (es,
sizeof (es),
"%u/%d",
(unsigned int) as->exit_code,
as->pst);
mres = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_ANASTASIS_SMS_HELPER_COMMAND_FAILED,
+ http_status,
+ ec,
es);
if (MHD_YES != mres)
return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
diff --git a/src/include/anastasis_error_codes.h b/src/include/anastasis_error_codes.h
@@ -5025,6 +5025,22 @@ enum TALER_ErrorCode
/**
+ * The address is valid but the recipient could not be reached: the handset is switched off or out of coverage, or the message expired before delivery. Retrying later may succeed. Used by all authorization methods that transmit a challenge via an external helper.
+ * Returned with an HTTP status code of #MHD_HTTP_SERVICE_UNAVAILABLE (503).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ TALER_EC_ANASTASIS_ADDRESS_UNREACHABLE = 8250,
+
+
+ /**
+ * The transmission helper is misconfigured: a required credential is absent, the configured credentials were refused by the provider, or the provider account has insufficient balance. The provider operator should check the logs and the service configuration. Used by all authorization methods that transmit a challenge via an external helper.
+ * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR (500).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ TALER_EC_ANASTASIS_HELPER_MISCONFIGURED = 8251,
+
+
+ /**
* The given if-none-match header is malformed.
* Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
* (A value of 0 indicates that the error is generated client-side).