diff options
Diffstat (limited to 'auth/auth.c')
-rw-r--r-- | auth/auth.c | 367 |
1 files changed, 209 insertions, 158 deletions
diff --git a/auth/auth.c b/auth/auth.c index c78b5197d23b..6b7d3953128e 100644 --- a/auth/auth.c +++ b/auth/auth.c @@ -20,6 +20,7 @@ #include <apr.h> #include <apr_base64.h> #include <apr_strings.h> +#include <apr_lib.h> static apr_status_t default_auth_response_handler(peer_t peer, @@ -32,30 +33,42 @@ default_auth_response_handler(peer_t peer, return APR_SUCCESS; } +/* These authentication schemes are in order of decreasing security, the topmost + scheme will be used first when the server supports it. + + Each set of handlers should support both server (401) and proxy (407) + authentication. + + Use lower case for the scheme names to enable case insensitive matching. + */ static const serf__authn_scheme_t serf_authn_schemes[] = { +#ifdef SERF_HAVE_SPNEGO { - 401, - "Basic", - SERF_AUTHN_BASIC, - serf__init_basic, - serf__init_basic_connection, - serf__handle_basic_auth, - serf__setup_request_basic_auth, - default_auth_response_handler, + "Negotiate", + "negotiate", + SERF_AUTHN_NEGOTIATE, + serf__init_spnego, + serf__init_spnego_connection, + serf__handle_spnego_auth, + serf__setup_request_spnego_auth, + serf__validate_response_spnego_auth, }, +#ifdef WIN32 { - 407, - "Basic", - SERF_AUTHN_BASIC, - serf__init_basic, - serf__init_basic_connection, - serf__handle_basic_auth, - serf__setup_request_basic_auth, - default_auth_response_handler, + "NTLM", + "ntlm", + SERF_AUTHN_NTLM, + serf__init_spnego, + serf__init_spnego_connection, + serf__handle_spnego_auth, + serf__setup_request_spnego_auth, + serf__validate_response_spnego_auth, }, +#endif /* #ifdef WIN32 */ +#endif /* SERF_HAVE_SPNEGO */ { - 401, "Digest", + "digest", SERF_AUTHN_DIGEST, serf__init_digest, serf__init_digest_connection, @@ -64,37 +77,15 @@ static const serf__authn_scheme_t serf_authn_schemes[] = { serf__validate_response_digest_auth, }, { - 407, - "Digest", - SERF_AUTHN_DIGEST, - serf__init_digest, - serf__init_digest_connection, - serf__handle_digest_auth, - serf__setup_request_digest_auth, - serf__validate_response_digest_auth, - }, -#ifdef SERF_HAVE_KERB - { - 401, - "Negotiate", - SERF_AUTHN_NEGOTIATE, - serf__init_kerb, - serf__init_kerb_connection, - serf__handle_kerb_auth, - serf__setup_request_kerb_auth, - serf__validate_response_kerb_auth, - }, - { - 407, - "Negotiate", - SERF_AUTHN_NEGOTIATE, - serf__init_kerb, - serf__init_kerb_connection, - serf__handle_kerb_auth, - serf__setup_request_kerb_auth, - serf__validate_response_kerb_auth, + "Basic", + "basic", + SERF_AUTHN_BASIC, + serf__init_basic, + serf__init_basic_connection, + serf__handle_basic_auth, + serf__setup_request_basic_auth, + default_auth_response_handler, }, -#endif /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */ /* sentinel */ @@ -102,21 +93,6 @@ static const serf__authn_scheme_t serf_authn_schemes[] = { }; -/** - * Baton passed to the response header callback function - */ -typedef struct { - int code; - apr_status_t status; - const char *header; - serf_request_t *request; - serf_bucket_t *response; - void *baton; - apr_pool_t *pool; - const serf__authn_scheme_t *scheme; - const char *last_scheme_name; -} auth_baton_t; - /* Reads and discards all bytes in the response body. */ static apr_status_t discard_body(serf_bucket_t *response) { @@ -142,99 +118,128 @@ static apr_status_t discard_body(serf_bucket_t *response) * * Returns a non-0 value of a matching handler was found. */ -static int handle_auth_header(void *baton, - const char *key, - const char *header) +static int handle_auth_headers(int code, + void *baton, + apr_hash_t *hdrs, + serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool) { - auth_baton_t *ab = baton; - int scheme_found = FALSE; - const char *auth_name; - const char *auth_attr; - const serf__authn_scheme_t *scheme = NULL; - serf_connection_t *conn = ab->request->conn; + const serf__authn_scheme_t *scheme; + serf_connection_t *conn = request->conn; serf_context_t *ctx = conn->ctx; + apr_status_t status; - /* We're only interested in xxxx-Authenticate headers. */ - if (strcmp(key, ab->header) != 0) - return 0; - - /* Extract the authentication scheme name, and prepare for reading - the attributes. */ - auth_attr = strchr(header, ' '); - if (auth_attr) { - auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header); - ++auth_attr; - } - else - auth_name = header; - - ab->last_scheme_name = auth_name; + status = SERF_ERROR_AUTHN_NOT_SUPPORTED; /* Find the matching authentication handler. Note that we don't reuse the auth scheme stored in the context, as that may have changed. (ex. fallback from ntlm to basic.) */ - for (scheme = serf_authn_schemes; scheme->code != 0; ++scheme) { - if (! (ab->code == scheme->code && - ctx->authn_types & scheme->type)) + for (scheme = serf_authn_schemes; scheme->name != 0; ++scheme) { + const char *auth_hdr; + serf__auth_handler_func_t handler; + serf__authn_info_t *authn_info; + + if (! (ctx->authn_types & scheme->type)) continue; serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, "Client supports: %s\n", scheme->name); - if (strcmp(auth_name, scheme->name) == 0) { - serf__auth_handler_func_t handler = scheme->handle_func; - apr_status_t status = 0; - - serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, - "... matched: %s\n", scheme->name); - /* If this is the first time we use this scheme on this connection, - make sure to initialize the authentication handler first. */ - if (ab->code == 401 && ctx->authn_info.scheme != scheme) { - status = scheme->init_ctx_func(ab->code, ctx, ctx->pool); - if (!status) { - status = scheme->init_conn_func(ab->code, conn, conn->pool); - - if (!status) - ctx->authn_info.scheme = scheme; - else - ctx->authn_info.scheme = NULL; - } - } - else if (ab->code == 407 && ctx->proxy_authn_info.scheme != scheme) { - status = scheme->init_ctx_func(ab->code, ctx, ctx->pool); - if (!status) { - status = scheme->init_conn_func(ab->code, conn, conn->pool); - - if (!status) - ctx->proxy_authn_info.scheme = scheme; - else - ctx->proxy_authn_info.scheme = NULL; - } - } + auth_hdr = apr_hash_get(hdrs, scheme->key, APR_HASH_KEY_STRING); + + if (!auth_hdr) + continue; + + /* Found a matching scheme */ + status = APR_SUCCESS; + + handler = scheme->handle_func; + + serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, + "... matched: %s\n", scheme->name); + + if (code == 401) { + authn_info = serf__get_authn_info_for_server(conn); + } else { + authn_info = &ctx->proxy_authn_info; + } + /* If this is the first time we use this scheme on this context and/or + this connection, make sure to initialize the authentication handler + first. */ + if (authn_info->scheme != scheme) { + status = scheme->init_ctx_func(code, ctx, ctx->pool); if (!status) { - scheme_found = TRUE; - ab->scheme = scheme; - status = handler(ab->code, ab->request, ab->response, - header, auth_attr, ab->baton, ctx->pool); + status = scheme->init_conn_func(scheme, code, conn, + conn->pool); + if (!status) + authn_info->scheme = scheme; + else + authn_info->scheme = NULL; } + } - /* If the authentication fails, cache the error for now. Try the - next available scheme. If there's none raise the error. */ - if (status) { - scheme_found = FALSE; - scheme = NULL; + if (!status) { + const char *auth_attr = strchr(auth_hdr, ' '); + if (auth_attr) { + auth_attr++; } - /* Let the caller now if the authentication setup was succesful - or not. */ - ab->status = status; - break; + status = handler(code, request, response, + auth_hdr, auth_attr, baton, ctx->pool); } + + if (status == APR_SUCCESS) + break; + + /* No success authenticating with this scheme, try the next. + If no more authn schemes are found the status of this scheme will be + returned. + */ + serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, + "%s authentication failed.\n", scheme->name); } - /* If a matching scheme handler was found, we can stop iterating - over the response headers - so return a non-0 value. */ - return scheme_found; + return status; +} + +/** + * Baton passed to the store_header_in_dict callback function + */ +typedef struct { + const char *header; + apr_pool_t *pool; + apr_hash_t *hdrs; +} auth_baton_t; + +static int store_header_in_dict(void *baton, + const char *key, + const char *header) +{ + auth_baton_t *ab = baton; + const char *auth_attr; + char *auth_name, *c; + + /* We're only interested in xxxx-Authenticate headers. */ + if (strcmp(key, ab->header) != 0) + return 0; + + /* Extract the authentication scheme name. */ + auth_attr = strchr(header, ' '); + if (auth_attr) { + auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header); + } + else + auth_name = apr_pstrmemdup(ab->pool, header, strlen(header)); + + /* Convert scheme name to lower case to enable case insensitive matching. */ + for (c = auth_name; *c != '\0'; c++) + *c = (char)apr_tolower(*c); + + apr_hash_set(ab->hdrs, auth_name, APR_HASH_KEY_STRING, + apr_pstrdup(ab->pool, header)); + + return 0; } /* Dispatch authentication handling. This function matches the possible @@ -252,11 +257,7 @@ static apr_status_t dispatch_auth(int code, auth_baton_t ab = { 0 }; const char *auth_hdr; - ab.code = code; - ab.status = APR_SUCCESS; - ab.request = request; - ab.response = response; - ab.baton = baton; + ab.hdrs = apr_hash_make(pool); ab.pool = pool; /* Before iterating over all authn headers, check if there are any. */ @@ -275,8 +276,8 @@ static apr_status_t dispatch_auth(int code, "%s authz required. Response header(s): %s\n", code == 401 ? "Server" : "Proxy", auth_hdr); - /* Iterate over all headers. Try to find a matching authentication scheme - handler. + + /* Store all WWW- or Proxy-Authenticate headers in a dictionary. Note: it is possible to have multiple Authentication: headers. We do not want to combine them (per normal header combination rules) as that @@ -285,15 +286,13 @@ static apr_status_t dispatch_auth(int code, work with. */ serf_bucket_headers_do(hdrs, - handle_auth_header, + store_header_in_dict, &ab); - if (ab.status != APR_SUCCESS) - return ab.status; - if (!ab.scheme || ab.scheme->name == NULL) { - /* No matching authentication found. */ - return SERF_ERROR_AUTHN_NOT_SUPPORTED; - } + /* Iterate over all authentication schemes, in order of decreasing + security. Try to find a authentication schema the server support. */ + return handle_auth_headers(code, baton, ab.hdrs, + request, response, pool); } return APR_SUCCESS; @@ -356,28 +355,41 @@ apr_status_t serf__handle_auth_response(int *consumed_response, /* Requeue the request with the necessary auth headers. */ /* ### Application doesn't know about this request! */ - serf_connection_priority_request_create(request->conn, - request->setup, - request->setup_baton); + if (request->ssltunnel) { + serf__ssltunnel_request_create(request->conn, + request->setup, + request->setup_baton); + } else { + serf_connection_priority_request_create(request->conn, + request->setup, + request->setup_baton); + } return APR_EOF; } else { - /* Validate the response authn headers if needed. */ serf__validate_response_func_t validate_resp; serf_connection_t *conn = request->conn; serf_context_t *ctx = conn->ctx; + serf__authn_info_t *authn_info; apr_status_t resp_status = APR_SUCCESS; - - if (ctx->authn_info.scheme) { - validate_resp = ctx->authn_info.scheme->validate_response_func; + + + /* Validate the response server authn headers. */ + authn_info = serf__get_authn_info_for_server(conn); + if (authn_info->scheme) { + validate_resp = authn_info->scheme->validate_response_func; resp_status = validate_resp(HOST, sl.code, conn, request, response, pool); } - if (!resp_status && ctx->proxy_authn_info.scheme) { - validate_resp = ctx->proxy_authn_info.scheme->validate_response_func; + + /* Validate the response proxy authn headers. */ + authn_info = &ctx->proxy_authn_info; + if (!resp_status && authn_info->scheme) { + validate_resp = authn_info->scheme->validate_response_func; resp_status = validate_resp(PROXY, sl.code, conn, request, response, pool); } + if (resp_status) { /* If there was an error in the final step of the authentication, consider the reponse body as invalid and discard it. */ @@ -419,3 +431,42 @@ void serf__encode_auth_header(const char **header, apr_base64_encode(ptr, data, data_len); } + +const char *serf__construct_realm(peer_t peer, + serf_connection_t *conn, + const char *realm_name, + apr_pool_t *pool) +{ + if (peer == HOST) { + return apr_psprintf(pool, "<%s://%s:%d> %s", + conn->host_info.scheme, + conn->host_info.hostname, + conn->host_info.port, + realm_name); + } else { + serf_context_t *ctx = conn->ctx; + + return apr_psprintf(pool, "<http://%s:%d> %s", + ctx->proxy_address->hostname, + ctx->proxy_address->port, + realm_name); + } +} + +serf__authn_info_t *serf__get_authn_info_for_server(serf_connection_t *conn) +{ + serf_context_t *ctx = conn->ctx; + serf__authn_info_t *authn_info; + + authn_info = apr_hash_get(ctx->server_authn_info, conn->host_url, + APR_HASH_KEY_STRING); + + if (!authn_info) { + authn_info = apr_pcalloc(ctx->pool, sizeof(serf__authn_info_t)); + apr_hash_set(ctx->server_authn_info, + apr_pstrdup(ctx->pool, conn->host_url), + APR_HASH_KEY_STRING, authn_info); + } + + return authn_info; +} |