Fix redirect URL validation bypass

It turns out that browsers silently convert backslash characters into
forward slashes, while apr_uri_parse() does not.

This mismatch allows an attacker to bypass the redirect URL validation
by using an URL like:

  https://sp.example.org/mellon/logout?ReturnTo=https:%5c%5cmalicious.example.org/

mod_auth_mellon will assume that it is a relative URL and allow the
request to pass through, while the browsers will use it as an absolute
url and redirect to https://malicious.example.org/ .

This patch fixes this issue by rejecting all redirect URLs with
backslashes.
char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options)
{
    apr_array_header_t *names = apr_array_make(pool, 4, sizeof(const char *));

    if (options & ECP_SERVICE_OPTION_CHANNEL_BINDING) {
        APR_ARRAY_PUSH(names, const char *) = "channel-binding";
        options &= ~ECP_SERVICE_OPTION_CHANNEL_BINDING;
    }

    if (options & ECP_SERVICE_OPTION_HOLDER_OF_KEY) {
        APR_ARRAY_PUSH(names, const char *) = "holder-of-key";
        options &= ~ECP_SERVICE_OPTION_HOLDER_OF_KEY;
    }

    if (options & ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED) {
        APR_ARRAY_PUSH(names, const char *) = "want-authn-signed";
        options &= ~ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED;
    }

    if (options & ECP_SERVICE_OPTION_DELEGATION) {
        APR_ARRAY_PUSH(names, const char *) = "delegation";
        options &= ~ECP_SERVICE_OPTION_DELEGATION;
    }

    if (options) {
        APR_ARRAY_PUSH(names, const char *) =
            apr_psprintf(pool, "(unknown bits = %#x)", options);
    }

    return apr_array_pstrcat(pool, names, ',');
}

char *am_ecp_service_options_str(apr_pool_t *pool, ECPServiceOptions options)
{
    apr_array_header_t *names = apr_array_make(pool, 4, sizeof(const char *));

    if (options & ECP_SERVICE_OPTION_CHANNEL_BINDING) {
        APR_ARRAY_PUSH(names, const char *) = "channel-binding";
        options &= ~ECP_SERVICE_OPTION_CHANNEL_BINDING;
    }

    if (options & ECP_SERVICE_OPTION_HOLDER_OF_KEY) {
        APR_ARRAY_PUSH(names, const char *) = "holder-of-key";
        options &= ~ECP_SERVICE_OPTION_HOLDER_OF_KEY;
    }

    if (options & ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED) {
        APR_ARRAY_PUSH(names, const char *) = "want-authn-signed";
        options &= ~ECP_SERVICE_OPTION_WANT_AUTHN_SIGNED;
    }

    if (options & ECP_SERVICE_OPTION_DELEGATION) {
        APR_ARRAY_PUSH(names, const char *) = "delegation";
        options &= ~ECP_SERVICE_OPTION_DELEGATION;
    }

    if (options) {
        APR_ARRAY_PUSH(names, const char *) =
            apr_psprintf(pool, "(unknown bits = %#x)", options);
    }

    return apr_array_pstrcat(pool, names, ',');
}
