/* 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; }
/* 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; }