summaryrefslogtreecommitdiffstats
path: root/contrib/serf/auth/auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/serf/auth/auth.c')
-rw-r--r--contrib/serf/auth/auth.c367
1 files changed, 209 insertions, 158 deletions
diff --git a/contrib/serf/auth/auth.c b/contrib/serf/auth/auth.c
index c78b519..6b7d395 100644
--- a/contrib/serf/auth/auth.c
+++ b/contrib/serf/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;
+}
OpenPOWER on IntegriCloud