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; +} | 
