Пример #1
0
/* A new connection is created to a server that's known to use
   Kerberos. */
apr_status_t
serf__init_kerb_connection(int code,
                           serf_connection_t *conn,
                           apr_pool_t *pool)
{
    gss_authn_info_t *gss_info;
    apr_status_t status;

    gss_info = apr_pcalloc(pool, sizeof(*gss_info));
    gss_info->pool = conn->pool;
    gss_info->state = gss_api_auth_not_started;
    status = serf__kerb_create_sec_context(&gss_info->gss_ctx, pool,
                                           gss_info->pool);

    if (status) {
        return status;
    }

    if (code == 401) {
        conn->authn_baton = gss_info;
    } else {
        conn->proxy_authn_baton = gss_info;
    }

    /* Make serf send the initial requests one by one */
    serf_connection_set_max_outstanding_requests(conn, 1);

    return APR_SUCCESS;
}
Пример #2
0
/* Read the header sent by the server (if any), invoke the gssapi authn
   code and use the resulting Server Ticket  on the next request to the
   server. */
static apr_status_t
do_auth(int code,
        gss_authn_info_t *gss_info,
        serf_connection_t *conn,
        const char *auth_hdr,
        apr_pool_t *pool)
{
    serf_context_t *ctx = conn->ctx;
    serf__authn_info_t *authn_info = (code == 401) ? &ctx->authn_info :
        &ctx->proxy_authn_info;
    const char *tmp = NULL;
    char *token = NULL;
    apr_size_t tmp_len = 0, token_len = 0;
    const char *space = NULL;
    apr_status_t status;

    /* The server will return a token as attribute to the Negotiate key.
       Negotiate YGwGCSqGSIb3EgECAgIAb10wW6ADAgEFoQMCAQ+iTzBNoAMCARCiRgREa6mouM
       BAMFqKVdTGtfpZNXKzyw4Yo1paphJdIA3VOgncaoIlXxZLnkHiIHS2v65pVvrp
       bRIyjF8xve9HxpnNIucCY9c=

       Read this base64 value, decode it and validate it so we're sure the server
       is who we expect it to be. */
    if (auth_hdr)
        space = strchr(auth_hdr, ' ');

    if (space) {
        token = apr_palloc(pool, apr_base64_decode_len(space + 1));
        token_len = apr_base64_decode(token, space + 1);
    }

    /* We can get a whole batch of 401 responses from the server, but we should
       only start the authentication phase once, so if we started authentication
       already ignore all responses with initial Negotiate authentication header.

       Note: as we set the max. transfer rate to one message at a time until the
       authentication cycle is finished, this check shouldn't be needed. */
    if (!token && gss_info->state != gss_api_auth_not_started)
        return APR_SUCCESS;

    status = gss_api_get_credentials(token, token_len, conn->host_info.hostname,
                                     &tmp, &tmp_len,
                                     gss_info);
    if (status)
        return status;

    serf__encode_auth_header(&gss_info->value, authn_info->scheme->name,
                             tmp,
                             tmp_len,
                             pool);
    gss_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization";

    /* If the handshake is finished tell serf it can send as much requests as it
       likes. */
    if (gss_info->state == gss_api_auth_completed)
        serf_connection_set_max_outstanding_requests(conn, 0);

    return APR_SUCCESS;
}
Пример #3
0
apr_status_t
serf__init_digest_connection(int code,
                             serf_connection_t *conn,
                             apr_pool_t *pool)
{
    /* Digest authentication is done per connection, so keep all progress
       information per connection. */
    if (code == 401) {
        conn->authn_baton = apr_pcalloc(pool, sizeof(digest_authn_info_t));
    } else {
        conn->proxy_authn_baton = apr_pcalloc(pool, sizeof(digest_authn_info_t));
    }

    /* Make serf send the initial requests one by one */
    serf_connection_set_max_outstanding_requests(conn, 1);

    return APR_SUCCESS;
}
Пример #4
0
/* A new connection is created to a server that's known to use
   Kerberos. */
apr_status_t
serf__init_spnego_connection(const serf__authn_scheme_t *scheme,
                             int code,
                             serf_connection_t *conn,
                             apr_pool_t *pool)
{
    serf_context_t *ctx = conn->ctx;
    serf__authn_info_t *authn_info;
    gss_authn_info_t *gss_info = NULL;

    /* For proxy authentication, reuse the gss context for all connections. 
       For server authentication, create a new gss context per connection. */
    if (code == 401) {
        authn_info = &conn->authn_info;
    } else {
        authn_info = &ctx->proxy_authn_info;
    }
    gss_info = authn_info->baton;

    if (!gss_info) {
        apr_status_t status;

        gss_info = apr_pcalloc(conn->pool, sizeof(*gss_info));
        gss_info->pool = conn->pool;
        gss_info->state = gss_api_auth_not_started;
        gss_info->pstate = pstate_init;
        status = serf__spnego_create_sec_context(&gss_info->gss_ctx, scheme,
                                                 gss_info->pool, pool);
        if (status) {
            return status;
        }
        authn_info->baton = gss_info;
    }

    /* Make serf send the initial requests one by one */
    serf_connection_set_max_outstanding_requests(conn, 1);

    serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
                  "Initialized Kerberos context for this connection.\n");

    return APR_SUCCESS;
}
Пример #5
0
apr_status_t
serf__handle_digest_auth(int code,
                         serf_request_t *request,
                         serf_bucket_t *response,
                         const char *auth_hdr,
                         const char *auth_attr,
                         void *baton,
                         apr_pool_t *pool)
{
    char *attrs;
    char *nextkv;
    const char *realm_name = NULL;
    const char *nonce = NULL;
    const char *algorithm = NULL;
    const char *qop = NULL;
    const char *opaque = NULL;
    const char *key;
    serf_connection_t *conn = request->conn;
    serf_context_t *ctx = conn->ctx;
    serf__authn_info_t *authn_info = (code == 401) ? &ctx->authn_info :
        &ctx->proxy_authn_info;
    digest_authn_info_t *digest_info = (code == 401) ? conn->authn_baton :
        conn->proxy_authn_baton;
    apr_status_t status;
    apr_pool_t *cred_pool;
    char *username, *password;

    /* Can't do Digest authentication if there's no callback to get
       username & password. */
    if (!ctx->cred_cb) {
        return SERF_ERROR_AUTHN_FAILED;
    }

    /* Need a copy cuz we're going to write NUL characters into the string.  */
    attrs = apr_pstrdup(pool, auth_attr);

    /* We're expecting a list of key=value pairs, separated by a comma.
       Ex. realm="SVN Digest",
       nonce="f+zTl/leBAA=e371bd3070adfb47b21f5fc64ad8cc21adc371a5",
       algorithm=MD5, qop="auth" */
    for ( ; (key = apr_strtok(attrs, ",", &nextkv)) != NULL; attrs = NULL) {
        char *val;

        val = strchr(key, '=');
        if (val == NULL)
            continue;
        *val++ = '\0';

        /* skip leading spaces */
        while (*key && *key == ' ')
            key++;

        /* If the value is quoted, then remove the quotes.  */
        if (*val == '"') {
            apr_size_t last = strlen(val) - 1;

            if (val[last] == '"') {
                val[last] = '\0';
                val++;
            }
        }

        if (strcmp(key, "realm") == 0)
            realm_name = val;
        else if (strcmp(key, "nonce") == 0)
            nonce = val;
        else if (strcmp(key, "algorithm") == 0)
            algorithm = val;
        else if (strcmp(key, "qop") == 0)
            qop = val;
        else if (strcmp(key, "opaque") == 0)
            opaque = val;

        /* Ignore all unsupported attributes. */
    }

    if (!realm_name) {
        return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
    }

    authn_info->realm = apr_psprintf(conn->pool, "<%s://%s:%d> %s",
                                     conn->host_info.scheme,
                                     conn->host_info.hostname,
                                     conn->host_info.port,
                                     realm_name);

    /* Ask the application for credentials */
    apr_pool_create(&cred_pool, pool);
    status = (*ctx->cred_cb)(&username, &password, request, baton,
                             code, authn_info->scheme->name,
                             authn_info->realm, cred_pool);
    if (status) {
        apr_pool_destroy(cred_pool);
        return status;
    }

    digest_info->header = (code == 401) ? "Authorization" :
                                          "Proxy-Authorization";

    /* Store the digest authentication parameters in the context relative
       to this connection, so we can use it to create the Authorization header
       when setting up requests. */
    digest_info->pool = conn->pool;
    digest_info->qop = apr_pstrdup(digest_info->pool, qop);
    digest_info->nonce = apr_pstrdup(digest_info->pool, nonce);
    digest_info->cnonce = NULL;
    digest_info->opaque = apr_pstrdup(digest_info->pool, opaque);
    digest_info->algorithm = apr_pstrdup(digest_info->pool, algorithm);
    digest_info->realm = apr_pstrdup(digest_info->pool, realm_name);
    digest_info->username = apr_pstrdup(digest_info->pool, username);
    digest_info->digest_nc++;

    digest_info->ha1 = build_digest_ha1(username, password, digest_info->realm,
                                        digest_info->pool);

    apr_pool_destroy(cred_pool);

    /* If the handshake is finished tell serf it can send as much requests as it
       likes. */
    serf_connection_set_max_outstanding_requests(conn, 0);

    return APR_SUCCESS;
}
Пример #6
0
int main(int argc, const char **argv)
{
    apr_status_t status;
    apr_pool_t *pool;
    serf_bucket_alloc_t *bkt_alloc;
    serf_context_t *context;
    serf_connection_t **connections;
    app_baton_t app_ctx;
    handler_baton_t handler_ctx;
    serf_bucket_t *req_hdrs = NULL;
    apr_uri_t url;
    const char *proxy = NULL;
    const char *raw_url, *method, *req_body_path = NULL;
    int count, inflight, conn_count;
    int i;
    int print_headers, debug;
    const char *username = NULL;
    const char *password = "";
    const char *pem_path = NULL, *pem_pwd = NULL;
    apr_getopt_t *opt;
    int opt_c;
    const char *opt_arg;

    apr_initialize();
    atexit(apr_terminate);

    apr_pool_create(&pool, NULL);
    /* serf_initialize(); */
    bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);

    /* Default to one round of fetching with no limit to max inflight reqs. */
    count = 1;
    inflight = 0;
    conn_count = 1;
    /* Default to GET. */
    method = "GET";
    /* Do not print headers by default. */
    print_headers = 0;
    /* Do not debug by default. */
    debug = 0;

    
    apr_getopt_init(&opt, pool, argc, argv);
    while ((status = apr_getopt_long(opt, options, &opt_c, &opt_arg)) ==
           APR_SUCCESS) {

        switch (opt_c) {
        case 'U':
            username = opt_arg;
            break;
        case 'P':
            password = opt_arg;
            break;
        case 'd':
            debug = 1;
            break;
        case 'f':
            req_body_path = opt_arg;
            break;
        case 'h':
            print_usage(pool);
            exit(0);
            break;
        case 'H':
            print_headers = 1;
            break;
        case 'm':
            method = opt_arg;
            break;
        case 'n':
            errno = 0;
            count = apr_strtoi64(opt_arg, NULL, 10);
            if (errno) {
                printf("Problem converting number of times to fetch URL (%d)\n",
                       errno);
                return errno;
            }
            break;
        case 'c':
            errno = 0;
            conn_count = apr_strtoi64(opt_arg, NULL, 10);
            if (errno) {
                printf("Problem converting number of concurrent connections to use (%d)\n",
                       errno);
                return errno;
            }

            if (conn_count <= 0) {
                printf("Invalid number of concurrent connections to use (%d)\n",
                       conn_count);
                return 1;
            }
            break;
        case 'x':
            errno = 0;
            inflight = apr_strtoi64(opt_arg, NULL, 10);
            if (errno) {
                printf("Problem converting number of requests to have outstanding (%d)\n",
                       errno);
                return errno;
            }
            break;
        case 'p':
            proxy = opt_arg;
            break;
        case 'r':
            {
                char *sep;
                char *hdr_val;

                if (req_hdrs == NULL) {
                    /* first request header, allocate bucket */
                    req_hdrs = serf_bucket_headers_create(bkt_alloc);
                }
                sep = strchr(opt_arg, ':');
                if ((sep == NULL) || (sep == opt_arg) || (strlen(sep) <= 1)) {
                    printf("Invalid request header string (%s)\n", opt_arg);
                    return EINVAL;
                }
                hdr_val = sep + 1;
                while (*hdr_val == ' ') {
                    hdr_val++;
                }
                serf_bucket_headers_setx(req_hdrs, opt_arg, (sep - opt_arg), 1,
                                         hdr_val, strlen(hdr_val), 1);
            }
            break;
        case CERTFILE:
            pem_path = opt_arg;
            break;
        case CERTPWD:
            pem_pwd = opt_arg;
            break;
        case 'v':
            puts("Serf version: " SERF_VERSION_STRING);
            exit(0);
        default:
            break;
        }
    }

    if (opt->ind != opt->argc - 1) {
        print_usage(pool);
        exit(-1);
    }

    raw_url = argv[opt->ind];

    apr_uri_parse(pool, raw_url, &url);
    if (!url.port) {
        url.port = apr_uri_port_of_scheme(url.scheme);
    }
    if (!url.path) {
        url.path = "/";
    }

    if (strcasecmp(url.scheme, "https") == 0) {
        app_ctx.using_ssl = 1;
    }
    else {
        app_ctx.using_ssl = 0;
    }

    if (strcasecmp(method, "HEAD") == 0) {
        app_ctx.head_request = 1;
    }
    else {
        app_ctx.head_request = 0;
    }

    app_ctx.hostinfo = url.hostinfo;
    app_ctx.pem_path = pem_path;
    app_ctx.pem_pwd = pem_pwd;

    context = serf_context_create(pool);
    app_ctx.serf_ctx = context;

    if (proxy)
    {
        apr_sockaddr_t *proxy_address = NULL;
        apr_port_t proxy_port;
        char *proxy_host;
        char *proxy_scope;

        status = apr_parse_addr_port(&proxy_host, &proxy_scope, &proxy_port, proxy, pool);
        if (status)
        {
            printf("Cannot parse proxy hostname/port: %d\n", status);
            apr_pool_destroy(pool);
            exit(1);
        }

        if (!proxy_host)
        {
            printf("Proxy hostname must be specified\n");
            apr_pool_destroy(pool);
            exit(1);
        }

        if (!proxy_port)
        {
            printf("Proxy port must be specified\n");
            apr_pool_destroy(pool);
            exit(1);
        }

        status = apr_sockaddr_info_get(&proxy_address, proxy_host, APR_UNSPEC,
                                       proxy_port, 0, pool);

        if (status)
        {
            printf("Cannot resolve proxy address '%s': %d\n", proxy_host, status);
            apr_pool_destroy(pool);
            exit(1);
        }

        serf_config_proxy(context, proxy_address);
    }

    if (username)
    {
        serf_config_authn_types(context, SERF_AUTHN_ALL);
    }
    else
    {
        serf_config_authn_types(context, SERF_AUTHN_NTLM | SERF_AUTHN_NEGOTIATE);
    }

    serf_config_credentials_callback(context, credentials_callback);

    /* Setup debug logging */
    if (debug)
    {
        serf_log_output_t *output;
        apr_status_t status;

        status = serf_logging_create_stream_output(&output,
                                                   context,
                                                   SERF_LOG_DEBUG,
                                                   SERF_LOGCOMP_ALL_MSG,
                                                   SERF_LOG_DEFAULT_LAYOUT,
                                                   stderr,
                                                   pool);

        if (!status)
            serf_logging_add_output(context, output);
    }

    /* ### Connection or Context should have an allocator? */
    app_ctx.bkt_alloc = bkt_alloc;

    connections = apr_pcalloc(pool, conn_count * sizeof(serf_connection_t*));
    for (i = 0; i < conn_count; i++)
    {
        conn_baton_t *conn_ctx = apr_pcalloc(pool, sizeof(*conn_ctx));
        conn_ctx->app = &app_ctx;
        conn_ctx->ssl_ctx = NULL;

        status = serf_connection_create2(&connections[i], context, url,
                                         conn_setup, conn_ctx,
                                         closed_connection, conn_ctx,
                                         pool);
        if (status) {
            printf("Error creating connection: %d\n", status);
            apr_pool_destroy(pool);
            exit(1);
        }

        serf_connection_set_max_outstanding_requests(connections[i], inflight);
    }

    handler_ctx.completed_requests = 0;
    handler_ctx.print_headers = print_headers;

#if APR_VERSION_AT_LEAST(1, 3, 0)
    apr_file_open_flags_stdout(&handler_ctx.output_file, APR_BUFFERED, pool);
#else
    apr_file_open_stdout(&handler_ctx.output_file, pool);
#endif

    handler_ctx.host = url.hostinfo;
    handler_ctx.method = method;
    handler_ctx.path = apr_pstrcat(pool,
                                   url.path,
                                   url.query ? "?" : "",
                                   url.query ? url.query : "",
                                   NULL);
    handler_ctx.username = username;
    handler_ctx.password = password;
    handler_ctx.auth_attempts = 0;

    handler_ctx.req_body_path = req_body_path;

    handler_ctx.acceptor = accept_response;
    handler_ctx.acceptor_baton = &app_ctx;
    handler_ctx.handler = handle_response;
    handler_ctx.req_hdrs = req_hdrs;

    for (i = 0; i < count; i++) {
        /* We don't need the returned request here. */
        serf_connection_request_create(connections[i % conn_count],
                                       setup_request, &handler_ctx);
    }

    while (1) {
        status = serf_context_run(context, SERF_DURATION_FOREVER, pool);
        if (APR_STATUS_IS_TIMEUP(status))
            continue;
        if (status) {
            char buf[200];
            const char *err_string;
            err_string = serf_error_string(status);
            if (!err_string) {
                err_string = apr_strerror(status, buf, sizeof(buf));
            }

            printf("Error running context: (%d) %s\n", status, err_string);
            apr_pool_destroy(pool);
            exit(1);
        }
        if (apr_atomic_read32(&handler_ctx.completed_requests) >= count) {
            break;
        }
        /* Debugging purposes only! */
        serf_debug__closed_conn(app_ctx.bkt_alloc);
    }

    apr_file_close(handler_ctx.output_file);

    for (i = 0; i < conn_count; i++)
    {
        serf_connection_close(connections[i]);
    }

    apr_pool_destroy(pool);
    return 0;
}
Пример #7
0
/* Function is called when 2xx responses are received. Normally we don't
 * have to do anything, except for the first response after the
 * authentication handshake. This specific response includes authentication
 * data which should be validated by the client (mutual authentication).
 */
apr_status_t
serf__validate_response_spnego_auth(const serf__authn_scheme_t *scheme,
                                    peer_t peer,
                                    int code,
                                    serf_connection_t *conn,
                                    serf_request_t *request,
                                    serf_bucket_t *response,
                                    apr_pool_t *pool)
{
    serf_context_t *ctx = conn->ctx;
    gss_authn_info_t *gss_info;
    const char *auth_hdr_name;

    /* TODO: currently this function is only called when a response includes
       an Authenticate header. This header is optional. If the server does
       not provide this header on the first 2xx response, we will not promote
       the connection from undecided to stateful. This won't break anything,
       but means we stay in non-pipelining mode. */
    serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
                  "Validate Negotiate response header.\n");

    if (peer == HOST) {
        gss_info = conn->authn_info.baton;
        auth_hdr_name = "WWW-Authenticate";
    } else {
        gss_info = ctx->proxy_authn_info.baton;
        auth_hdr_name = "Proxy-Authenticate";
    }

    if (gss_info->state != gss_api_auth_completed) {
        serf_bucket_t *hdrs;
        const char *auth_hdr_val;
        apr_status_t status;

        hdrs = serf_bucket_response_get_headers(response);
        auth_hdr_val = get_auth_header(hdrs, auth_hdr_name, scheme->name,
                                       pool);

        if (auth_hdr_val) {
            status = do_auth(peer, code, gss_info, conn, request, auth_hdr_val,
                             pool);
            if (status) {
                return status;
            }
        } else {
            /* No Authenticate headers, nothing to validate: authentication
               completed.*/
            gss_info->state = gss_api_auth_completed;

            serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
                          "SPNEGO handshake completed.\n");
        }
    }

    if (gss_info->state == gss_api_auth_completed) {
        switch(gss_info->pstate) {
            case pstate_init:
                /* Authentication of the first request is done. */
                gss_info->pstate = pstate_undecided;
                break;
            case pstate_undecided:
                /* The server didn't request for authentication even though
                   we didn't add an Authorization header to previous
                   request. That means it supports persistent authentication. */
                gss_info->pstate = pstate_stateful;
                serf_connection_set_max_outstanding_requests(conn, 0);
                break;
            default:
                /* Nothing to do here. */
                break;
        }
    }

    return APR_SUCCESS;
}
Пример #8
0
/* do_auth is invoked in two situations:
   - when a response from a server is received that contains an authn header
     (either from a 40x or 2xx response)
   - when a request is prepared on a connection with stateless authentication.

   Read the header sent by the server (if any), invoke the gssapi authn
   code and use the resulting Server Ticket on the next request to the
   server. */
static apr_status_t
do_auth(peer_t peer,
        int code,
        gss_authn_info_t *gss_info,
        serf_connection_t *conn,
        serf_request_t *request,
        const char *auth_hdr,
        apr_pool_t *pool)
{
    serf_context_t *ctx = conn->ctx;
    serf__authn_info_t *authn_info;
    const char *tmp = NULL;
    char *token = NULL;
    apr_size_t tmp_len = 0, token_len = 0;
    apr_status_t status;

    if (peer == HOST) {
        authn_info = serf__get_authn_info_for_server(conn);
    } else {
        authn_info = &ctx->proxy_authn_info;
    }

    /* Is this a response from a host/proxy? auth_hdr should always be set. */
    if (code && auth_hdr) {
        const char *space = NULL;
        /* The server will return a token as attribute to the Negotiate key.
           Negotiate YGwGCSqGSIb3EgECAgIAb10wW6ADAgEFoQMCAQ+iTzBNoAMCARCiRgREa6
           mouMBAMFqKVdTGtfpZNXKzyw4Yo1paphJdIA3VOgncaoIlXxZLnkHiIHS2v65pVvrp
           bRIyjF8xve9HxpnNIucCY9c=

           Read this base64 value, decode it and validate it so we're sure the
           server is who we expect it to be. */
        space = strchr(auth_hdr, ' ');

        if (space) {
            token = apr_palloc(pool, apr_base64_decode_len(space + 1));
            token_len = apr_base64_decode(token, space + 1);
        }
    } else {
        /* This is a new request, not a retry in response to a 40x of the
           host/proxy. 
           Only add the Authorization header if we know the server requires
           per-request authentication (stateless). */
        if (gss_info->pstate != pstate_stateless)
            return APR_SUCCESS;
    }

    switch(gss_info->pstate) {
        case pstate_init:
            /* Nothing to do here */
            break;
        case pstate_undecided: /* Fall through */
        case pstate_stateful:
            {
                /* Switch to stateless mode, from now on handle authentication
                   of each request with a new gss context. This is easiest to
                   manage when sending requests one by one. */
                serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
                              "Server requires per-request SPNEGO authn, "
                              "switching to stateless mode.\n");

                gss_info->pstate = pstate_stateless;
                serf_connection_set_max_outstanding_requests(conn, 1);
                break;
            }
        case pstate_stateless:
            /* Nothing to do here */
            break;
    }

    if (request->auth_baton && !token) {
        /* We provided token with this request, but server responded with empty
           authentication header. This means server rejected our credentials.
           XXX: Probably we need separate error code for this case like
           SERF_ERROR_AUTHN_CREDS_REJECTED? */
        return SERF_ERROR_AUTHN_FAILED;
    }

    /* If the server didn't provide us with a token, start with a new initial
       step in the SPNEGO authentication. */
    if (!token) {
        serf__spnego_reset_sec_context(gss_info->gss_ctx);
        gss_info->state = gss_api_auth_not_started;
    }

    if (peer == HOST) {
        status = gss_api_get_credentials(conn,
                                         token, token_len,
                                         conn->host_info.hostname,
                                         &tmp, &tmp_len,
                                         gss_info);
    } else {
        char *proxy_host = conn->ctx->proxy_address->hostname;
        status = gss_api_get_credentials(conn,
                                         token, token_len, proxy_host,
                                         &tmp, &tmp_len,
                                         gss_info);
    }
    if (status)
        return status;

    /* On the next request, add an Authorization header. */
    if (tmp_len) {
        serf__encode_auth_header(&gss_info->value, authn_info->scheme->name,
                                 tmp,
                                 tmp_len,
                                 pool);
        gss_info->header = (peer == HOST) ?
            "Authorization" : "Proxy-Authorization";
    }

    return APR_SUCCESS;
}