summaryrefslogtreecommitdiffstats
path: root/contrib/serf/auth
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2013-08-02 19:21:46 +0000
committerpeter <peter@FreeBSD.org>2013-08-02 19:21:46 +0000
commitbce4a8587da654a7a12e36d4a6822cd999c0aafc (patch)
tree27dab9bf5d6ff996ae09bc981c7d20022b26fa50 /contrib/serf/auth
parent5750092edd0958adc875d6012f6e656a430e71c6 (diff)
parent089059a1407b9504b9fdd10f3b602b94d36ed187 (diff)
downloadFreeBSD-src-bce4a8587da654a7a12e36d4a6822cd999c0aafc.zip
FreeBSD-src-bce4a8587da654a7a12e36d4a6822cd999c0aafc.tar.gz
Update serf 1.2.1 -> 1.3.0 for svn
Diffstat (limited to 'contrib/serf/auth')
-rw-r--r--contrib/serf/auth/auth.c367
-rw-r--r--contrib/serf/auth/auth.h70
-rw-r--r--contrib/serf/auth/auth_basic.c100
-rw-r--r--contrib/serf/auth/auth_digest.c109
-rw-r--r--contrib/serf/auth/auth_spnego.c (renamed from contrib/serf/auth/auth_kerb.c)88
-rw-r--r--contrib/serf/auth/auth_spnego.h (renamed from contrib/serf/auth/auth_kerb.h)51
-rw-r--r--contrib/serf/auth/auth_spnego_gss.c (renamed from contrib/serf/auth/auth_kerb_gss.c)56
-rw-r--r--contrib/serf/auth/auth_spnego_sspi.c (renamed from contrib/serf/auth/auth_kerb_sspi.c)75
8 files changed, 545 insertions, 371 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;
+}
diff --git a/contrib/serf/auth/auth.h b/contrib/serf/auth/auth.h
index 4f075db..ea14115 100644
--- a/contrib/serf/auth/auth.h
+++ b/contrib/serf/auth/auth.h
@@ -16,7 +16,7 @@
#ifndef AUTH_H
#define AUTH_H
-#include "auth_kerb.h"
+#include "auth_spnego.h"
#ifdef __cplusplus
extern "C" {
@@ -26,11 +26,19 @@ void serf__encode_auth_header(const char **header, const char *protocol,
const char *data, apr_size_t data_len,
apr_pool_t *pool);
+/* Prefixes the realm_name with a string containing scheme, hostname and port
+ of the connection, for providing it to the application. */
+const char *serf__construct_realm(peer_t peer,
+ serf_connection_t *conn,
+ const char *realm_name,
+ apr_pool_t *pool);
+
/** Basic authentication **/
apr_status_t serf__init_basic(int code,
serf_context_t *ctx,
apr_pool_t *pool);
-apr_status_t serf__init_basic_connection(int code,
+apr_status_t serf__init_basic_connection(const serf__authn_scheme_t *scheme,
+ int code,
serf_connection_t *conn,
apr_pool_t *pool);
apr_status_t serf__handle_basic_auth(int code,
@@ -52,7 +60,8 @@ apr_status_t serf__setup_request_basic_auth(peer_t peer,
apr_status_t serf__init_digest(int code,
serf_context_t *ctx,
apr_pool_t *pool);
-apr_status_t serf__init_digest_connection(int code,
+apr_status_t serf__init_digest_connection(const serf__authn_scheme_t *scheme,
+ int code,
serf_connection_t *conn,
apr_pool_t *pool);
apr_status_t serf__handle_digest_auth(int code,
@@ -76,34 +85,35 @@ apr_status_t serf__validate_response_digest_auth(peer_t peer,
serf_bucket_t *response,
apr_pool_t *pool);
-#ifdef SERF_HAVE_KERB
+#ifdef SERF_HAVE_SPNEGO
/** Kerberos authentication **/
-apr_status_t serf__init_kerb(int code,
- serf_context_t *ctx,
- apr_pool_t *pool);
-apr_status_t serf__init_kerb_connection(int code,
- serf_connection_t *conn,
- apr_pool_t *pool);
-apr_status_t serf__handle_kerb_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);
-apr_status_t serf__setup_request_kerb_auth(peer_t peer,
- int code,
- serf_connection_t *conn,
- serf_request_t *request,
- const char *method,
- const char *uri,
- serf_bucket_t *hdrs_bkt);
-apr_status_t serf__validate_response_kerb_auth(peer_t peer,
- int code,
- serf_connection_t *conn,
- serf_request_t *request,
- serf_bucket_t *response,
- apr_pool_t *pool);
+apr_status_t serf__init_spnego(int code,
+ serf_context_t *ctx,
+ apr_pool_t *pool);
+apr_status_t serf__init_spnego_connection(const serf__authn_scheme_t *scheme,
+ int code,
+ serf_connection_t *conn,
+ apr_pool_t *pool);
+apr_status_t serf__handle_spnego_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);
+apr_status_t serf__setup_request_spnego_auth(peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ const char *method,
+ const char *uri,
+ serf_bucket_t *hdrs_bkt);
+apr_status_t serf__validate_response_spnego_auth(peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ apr_pool_t *pool);
#endif /* SERF_HAVE_SPNEGO */
#ifdef __cplusplus
diff --git a/contrib/serf/auth/auth_basic.c b/contrib/serf/auth/auth_basic.c
index 7a0589c..53b5d95 100644
--- a/contrib/serf/auth/auth_basic.c
+++ b/contrib/serf/auth/auth_basic.c
@@ -23,6 +23,8 @@
#include <apr_base64.h>
#include <apr_strings.h>
+/* Stores the context information related to Basic authentication.
+ This information is stored in the per server cache in the serf context. */
typedef struct basic_authn_info_t {
const char *header;
const char *value;
@@ -41,12 +43,12 @@ serf__handle_basic_auth(int code,
apr_size_t tmp_len;
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;
- basic_authn_info_t *basic_info = authn_info->baton;
+ serf__authn_info_t *authn_info;
+ basic_authn_info_t *basic_info;
apr_status_t status;
apr_pool_t *cred_pool;
- char *username, *password;
+ char *username, *password, *realm_name;
+ const char *eq, *realm;
/* Can't do Basic authentication if there's no callback to get
username & password. */
@@ -54,20 +56,25 @@ serf__handle_basic_auth(int code,
return SERF_ERROR_AUTHN_FAILED;
}
- if (!authn_info->realm) {
- char *realm_name = NULL;
- const char *eq = strchr(auth_attr, '=');
+ if (code == 401) {
+ authn_info = serf__get_authn_info_for_server(conn);
+ } else {
+ authn_info = &ctx->proxy_authn_info;
+ }
+ basic_info = authn_info->baton;
- if (eq && strncasecmp(auth_attr, "realm", 5) == 0) {
- realm_name = apr_pstrdup(pool, eq + 1);
- if (realm_name[0] == '\"') {
- apr_size_t realm_len;
+ realm_name = NULL;
+ eq = strchr(auth_attr, '=');
- realm_len = strlen(realm_name);
- if (realm_name[realm_len - 1] == '\"') {
- realm_name[realm_len - 1] = '\0';
- realm_name++;
- }
+ if (eq && strncasecmp(auth_attr, "realm", 5) == 0) {
+ realm_name = apr_pstrdup(pool, eq + 1);
+ if (realm_name[0] == '\"') {
+ apr_size_t realm_len;
+
+ realm_len = strlen(realm_name);
+ if (realm_name[realm_len - 1] == '\"') {
+ realm_name[realm_len - 1] = '\0';
+ realm_name++;
}
}
@@ -75,18 +82,18 @@ serf__handle_basic_auth(int code,
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);
+ realm = serf__construct_realm(code == 401 ? HOST : PROXY,
+ conn, realm_name,
+ pool);
}
/* 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);
+ status = serf__provide_credentials(ctx,
+ &username, &password,
+ request, baton,
+ code, authn_info->scheme->name,
+ realm, cred_pool);
if (status) {
apr_pool_destroy(cred_pool);
return status;
@@ -104,28 +111,39 @@ serf__handle_basic_auth(int code,
return APR_SUCCESS;
}
-/* For Basic authentication we expect all authn info to be the same for all
- connections in the context (same realm, username, password). Therefore we
- can keep the header value in the context instead of per connection. */
apr_status_t
serf__init_basic(int code,
serf_context_t *ctx,
apr_pool_t *pool)
{
- if (code == 401) {
- ctx->authn_info.baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
- } else {
- ctx->proxy_authn_info.baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
- }
-
return APR_SUCCESS;
}
+/* For Basic authentication we expect all authn info to be the same for all
+ connections in the context to the same server (same realm, username,
+ password). Therefore we can keep the header value in the per-server store
+ context instead of per connection.
+ TODO: we currently don't cache this info per realm, so each time a request
+ 'switches realms', we have to ask the application for new credentials. */
apr_status_t
-serf__init_basic_connection(int code,
+serf__init_basic_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;
+
+ if (code == 401) {
+ authn_info = serf__get_authn_info_for_server(conn);
+ } else {
+ authn_info = &ctx->proxy_authn_info;
+ }
+
+ if (!authn_info->baton) {
+ authn_info->baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
+ }
+
return APR_SUCCESS;
}
@@ -139,17 +157,19 @@ serf__setup_request_basic_auth(peer_t peer,
serf_bucket_t *hdrs_bkt)
{
serf_context_t *ctx = conn->ctx;
- basic_authn_info_t *authn_info;
+ serf__authn_info_t *authn_info;
+ basic_authn_info_t *basic_info;
if (peer == HOST) {
- authn_info = ctx->authn_info.baton;
+ authn_info = serf__get_authn_info_for_server(conn);
} else {
- authn_info = ctx->proxy_authn_info.baton;
+ authn_info = &ctx->proxy_authn_info;
}
+ basic_info = authn_info->baton;
- if (authn_info && authn_info->header && authn_info->value) {
- serf_bucket_headers_setn(hdrs_bkt, authn_info->header,
- authn_info->value);
+ if (basic_info && basic_info->header && basic_info->value) {
+ serf_bucket_headers_setn(hdrs_bkt, basic_info->header,
+ basic_info->value);
return APR_SUCCESS;
}
diff --git a/contrib/serf/auth/auth_digest.c b/contrib/serf/auth/auth_digest.c
index 1c4adf0..7403386 100644
--- a/contrib/serf/auth/auth_digest.c
+++ b/contrib/serf/auth/auth_digest.c
@@ -27,8 +27,12 @@
/** Digest authentication, implements RFC 2617. **/
+/* TODO: add support for the domain attribute. This defines the protection
+ space, so that serf can decide per URI if it should reuse the cached
+ credentials for the server, or not. */
+
/* Stores the context information related to Digest authentication.
- The context is per connection. */
+ This information is stored in the per server cache in the serf context. */
typedef struct digest_authn_info_t {
/* nonce-count for digest authentication */
unsigned int digest_nc;
@@ -217,7 +221,7 @@ serf__handle_digest_auth(int code,
{
char *attrs;
char *nextkv;
- const char *realm_name = NULL;
+ const char *realm, *realm_name = NULL;
const char *nonce = NULL;
const char *algorithm = NULL;
const char *qop = NULL;
@@ -225,10 +229,8 @@ serf__handle_digest_auth(int code,
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;
+ serf__authn_info_t *authn_info;
+ digest_authn_info_t *digest_info;
apr_status_t status;
apr_pool_t *cred_pool;
char *username, *password;
@@ -239,6 +241,13 @@ serf__handle_digest_auth(int code,
return SERF_ERROR_AUTHN_FAILED;
}
+ if (code == 401) {
+ authn_info = serf__get_authn_info_for_server(conn);
+ } else {
+ authn_info = &ctx->proxy_authn_info;
+ }
+ digest_info = authn_info->baton;
+
/* Need a copy cuz we're going to write NUL characters into the string. */
attrs = apr_pstrdup(pool, auth_attr);
@@ -286,17 +295,17 @@ serf__handle_digest_auth(int code,
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);
+ realm = serf__construct_realm(code == 401 ? HOST : PROXY,
+ conn, realm_name,
+ pool);
/* 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);
+ status = serf__provide_credentials(ctx,
+ &username, &password,
+ request, baton,
+ code, authn_info->scheme->name,
+ realm, cred_pool);
if (status) {
apr_pool_destroy(cred_pool);
return status;
@@ -305,9 +314,12 @@ serf__handle_digest_auth(int code,
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. */
+ /* Store the digest authentication parameters in the context cached for
+ this server in the serf context, so we can use it to create the
+ Authorization header when setting up requests on the same or different
+ connections (e.g. in case of KeepAlive off on the server).
+ TODO: we currently don't cache this info per realm, so each time a request
+ 'switches realms', we have to ask the application for new credentials. */
digest_info->pool = conn->pool;
digest_info->qop = apr_pstrdup(digest_info->pool, qop);
digest_info->nonce = apr_pstrdup(digest_info->pool, nonce);
@@ -339,16 +351,22 @@ serf__init_digest(int code,
}
apr_status_t
-serf__init_digest_connection(int code,
+serf__init_digest_connection(const serf__authn_scheme_t *scheme,
+ int code,
serf_connection_t *conn,
apr_pool_t *pool)
{
- /* Digest authentication is done per connection, so keep all progress
- information per connection. */
+ serf_context_t *ctx = conn->ctx;
+ serf__authn_info_t *authn_info;
+
if (code == 401) {
- conn->authn_baton = apr_pcalloc(pool, sizeof(digest_authn_info_t));
+ authn_info = serf__get_authn_info_for_server(conn);
} else {
- conn->proxy_authn_baton = apr_pcalloc(pool, sizeof(digest_authn_info_t));
+ authn_info = &ctx->proxy_authn_info;
+ }
+
+ if (!authn_info->baton) {
+ authn_info->baton = apr_pcalloc(pool, sizeof(digest_authn_info_t));
}
/* Make serf send the initial requests one by one */
@@ -366,23 +384,44 @@ serf__setup_request_digest_auth(peer_t peer,
const char *uri,
serf_bucket_t *hdrs_bkt)
{
- digest_authn_info_t *digest_info = (peer == HOST) ? conn->authn_baton :
- conn->proxy_authn_baton;
+ serf_context_t *ctx = conn->ctx;
+ serf__authn_info_t *authn_info;
+ digest_authn_info_t *digest_info;
apr_status_t status = APR_SUCCESS;
+ if (peer == HOST) {
+ authn_info = serf__get_authn_info_for_server(conn);
+ } else {
+ authn_info = &ctx->proxy_authn_info;
+ }
+ digest_info = authn_info->baton;
+
if (digest_info && digest_info->realm) {
const char *value;
- apr_uri_t parsed_uri;
+ const char *path;
/* TODO: per request pool? */
- /* Extract path from uri. */
- status = apr_uri_parse(conn->pool, uri, &parsed_uri);
+ /* for request 'CONNECT serf.googlecode.com:443', the uri also should be
+ serf.googlecode.com:443. apr_uri_parse can't handle this, so special
+ case. */
+ if (strcmp(method, "CONNECT") == 0)
+ path = uri;
+ else {
+ apr_uri_t parsed_uri;
+
+ /* Extract path from uri. */
+ status = apr_uri_parse(conn->pool, uri, &parsed_uri);
+ if (status)
+ return status;
+
+ path = parsed_uri.path;
+ }
/* Build a new Authorization header. */
digest_info->header = (peer == HOST) ? "Authorization" :
"Proxy-Authorization";
- value = build_auth_header(digest_info, parsed_uri.path, method,
+ value = build_auth_header(digest_info, path, method,
conn->pool);
serf_bucket_headers_setn(hdrs_bkt, digest_info->header,
@@ -392,7 +431,7 @@ serf__setup_request_digest_auth(peer_t peer,
/* Store the uri of this request on the serf_request_t object, to make
it available when validating the Authentication-Info header of the
matching response. */
- request->auth_baton = parsed_uri.path;
+ request->auth_baton = path;
}
return status;
@@ -413,8 +452,7 @@ serf__validate_response_digest_auth(peer_t peer,
const char *qop = NULL;
const char *nc_str = NULL;
serf_bucket_t *hdrs;
- digest_authn_info_t *digest_info = (peer == HOST) ? conn->authn_baton :
- conn->proxy_authn_baton;
+ serf_context_t *ctx = conn->ctx;
hdrs = serf_bucket_response_get_headers(response);
@@ -468,6 +506,15 @@ serf__validate_response_digest_auth(peer_t peer,
const char *ha2, *tmp, *resp_hdr_hex;
unsigned char resp_hdr[APR_MD5_DIGESTSIZE];
const char *req_uri = request->auth_baton;
+ serf__authn_info_t *authn_info;
+ digest_authn_info_t *digest_info;
+
+ if (peer == HOST) {
+ authn_info = serf__get_authn_info_for_server(conn);
+ } else {
+ authn_info = &ctx->proxy_authn_info;
+ }
+ digest_info = authn_info->baton;
ha2 = build_digest_ha2(req_uri, "", qop, pool);
tmp = apr_psprintf(pool, "%s:%s:%s:%s:%s:%s",
diff --git a/contrib/serf/auth/auth_kerb.c b/contrib/serf/auth/auth_spnego.c
index 27fb968..4d22ec1 100644
--- a/contrib/serf/auth/auth_kerb.c
+++ b/contrib/serf/auth/auth_spnego.c
@@ -13,9 +13,9 @@
* limitations under the License.
*/
-#include "auth_kerb.h"
+#include "auth_spnego.h"
-#ifdef SERF_HAVE_KERB
+#ifdef SERF_HAVE_SPNEGO
/** These functions implement SPNEGO-based Kerberos and NTLM authentication,
* using either GSS-API (RFC 2743) or SSPI on Windows.
@@ -31,8 +31,6 @@
#include <apr_strings.h>
/** TODO:
- ** - This implements the SPNEGO mechanism, not Kerberos directly. Adapt
- ** filename, functions & comments.
** - send session key directly on new connections where we already know
** the server requires Kerberos authn.
** - Add a way for serf to give detailed error information back to the
@@ -166,7 +164,7 @@ typedef struct
apr_pool_t *pool;
/* GSSAPI context */
- serf__kerb_context_t *gss_ctx;
+ serf__spnego_context_t *gss_ctx;
/* Current state of the authentication cycle. */
gss_api_auth_state state;
@@ -188,8 +186,8 @@ gss_api_get_credentials(char *token, apr_size_t token_len,
const char **buf, apr_size_t *buf_len,
gss_authn_info_t *gss_info)
{
- serf__kerb_buffer_t input_buf;
- serf__kerb_buffer_t output_buf;
+ serf__spnego_buffer_t input_buf;
+ serf__spnego_buffer_t output_buf;
apr_status_t status = APR_SUCCESS;
/* If the server sent us a token, pass it to gss_init_sec_token for
@@ -203,8 +201,8 @@ gss_api_get_credentials(char *token, apr_size_t token_len,
}
/* Establish a security context to the server. */
- status = serf__kerb_init_sec_context
- (gss_info->gss_ctx,
+ status = serf__spnego_init_sec_context(
+ gss_info->gss_ctx,
KRB_HTTP_SERVICE, hostname,
&input_buf,
&output_buf,
@@ -248,13 +246,18 @@ do_auth(peer_t peer,
apr_pool_t *pool)
{
serf_context_t *ctx = conn->ctx;
- serf__authn_info_t *authn_info = (peer == HOST) ? &ctx->authn_info :
- &ctx->proxy_authn_info;
+ 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;
@@ -306,7 +309,7 @@ do_auth(peer_t peer,
/* If the server didn't provide us with a token, start with a new initial
step in the SPNEGO authentication. */
if (!token) {
- serf__kerb_reset_sec_context(gss_info->gss_ctx);
+ serf__spnego_reset_sec_context(gss_info->gss_ctx);
gss_info->state = gss_api_auth_not_started;
}
@@ -339,9 +342,9 @@ do_auth(peer_t peer,
}
apr_status_t
-serf__init_kerb(int code,
- serf_context_t *ctx,
- apr_pool_t *pool)
+serf__init_spnego(int code,
+ serf_context_t *ctx,
+ apr_pool_t *pool)
{
return APR_SUCCESS;
}
@@ -349,19 +352,20 @@ serf__init_kerb(int code,
/* 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)
+serf__init_spnego_connection(const serf__authn_scheme_t *scheme,
+ 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 = 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__kerb_create_sec_context(&gss_info->gss_ctx, pool,
- gss_info->pool);
+ status = serf__spnego_create_sec_context(&gss_info->gss_ctx, scheme,
+ gss_info->pool, pool);
if (status) {
return status;
@@ -384,13 +388,13 @@ serf__init_kerb_connection(int code,
/* A 40x response was received, handle the authentication. */
apr_status_t
-serf__handle_kerb_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)
+serf__handle_spnego_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)
{
serf_connection_t *conn = request->conn;
gss_authn_info_t *gss_info = (code == 401) ? conn->authn_baton :
@@ -406,13 +410,13 @@ serf__handle_kerb_auth(int code,
/* Setup the authn headers on this request message. */
apr_status_t
-serf__setup_request_kerb_auth(peer_t peer,
- int code,
- serf_connection_t *conn,
- serf_request_t *request,
- const char *method,
- const char *uri,
- serf_bucket_t *hdrs_bkt)
+serf__setup_request_spnego_auth(peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ const char *method,
+ const char *uri,
+ serf_bucket_t *hdrs_bkt)
{
gss_authn_info_t *gss_info = (peer == HOST) ? conn->authn_baton :
conn->proxy_authn_baton;
@@ -488,12 +492,12 @@ serf__setup_request_kerb_auth(peer_t peer,
* data which should be validated by the client (mutual authentication).
*/
apr_status_t
-serf__validate_response_kerb_auth(peer_t peer,
- int code,
- serf_connection_t *conn,
- serf_request_t *request,
- serf_bucket_t *response,
- apr_pool_t *pool)
+serf__validate_response_spnego_auth(peer_t peer,
+ int code,
+ serf_connection_t *conn,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ apr_pool_t *pool)
{
gss_authn_info_t *gss_info;
const char *auth_hdr_name;
@@ -549,4 +553,4 @@ serf__validate_response_kerb_auth(peer_t peer,
return APR_SUCCESS;
}
-#endif /* SERF_HAVE_GSSAPI */
+#endif /* SERF_HAVE_SPNEGO */
diff --git a/contrib/serf/auth/auth_kerb.h b/contrib/serf/auth/auth_spnego.h
index 0972c56..5af3b09 100644
--- a/contrib/serf/auth/auth_kerb.h
+++ b/contrib/serf/auth/auth_spnego.h
@@ -13,32 +13,34 @@
* limitations under the License.
*/
-#ifndef AUTH_KERB_H
-#define AUTH_KERB_H
+#ifndef AUTH_SPNEGO_H
+#define AUTH_SPNEGO_H
#include <apr.h>
#include <apr_pools.h>
+#include "serf.h"
+#include "serf_private.h"
#if defined(SERF_HAVE_SSPI)
-#define SERF_HAVE_KERB
+#define SERF_HAVE_SPNEGO
#define SERF_USE_SSPI
#elif defined(SERF_HAVE_GSSAPI)
-#define SERF_HAVE_KERB
+#define SERF_HAVE_SPNEGO
#define SERF_USE_GSSAPI
#endif
-#ifdef SERF_HAVE_KERB
+#ifdef SERF_HAVE_SPNEGO
#ifdef __cplusplus
extern "C" {
#endif
-typedef struct serf__kerb_context_t serf__kerb_context_t;
+typedef struct serf__spnego_context_t serf__spnego_context_t;
-typedef struct serf__kerb_buffer_t {
+typedef struct serf__spnego_buffer_t {
apr_size_t length;
void *value;
-} serf__kerb_buffer_t;
+} serf__spnego_buffer_t;
/* Create outbound security context.
*
@@ -48,9 +50,10 @@ typedef struct serf__kerb_buffer_t {
*
*/
apr_status_t
-serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
- apr_pool_t *scratch_pool,
- apr_pool_t *result_pool);
+serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
+ const serf__authn_scheme_t *scheme,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Initialize outbound security context.
*
@@ -58,7 +61,7 @@ serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
* application and a remote peer.
*
* CTX is pointer to existing context created using
- * serf__kerb_create_sec_context() function.
+ * serf__spnego_create_sec_context() function.
*
* SERVICE is name of Kerberos service name. Usually 'HTTP'. HOSTNAME is
* canonical name of destination server. Caller should resolve server's alias
@@ -78,21 +81,21 @@ serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
* for a return token.
*
* - APR_SUCCESS The security context was successfully initialized. There is no
- * need for another serf__kerb_init_sec_context call. If the function returns
+ * need for another serf__spnego_init_sec_context call. If the function returns
* an output token, that is, if the OUTPUT_BUF is of nonzero length, that
* token must be sent to the server.
*
* Other returns values indicates error.
*/
apr_status_t
-serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
- const char *service,
- const char *hostname,
- serf__kerb_buffer_t *input_buf,
- serf__kerb_buffer_t *output_buf,
- apr_pool_t *scratch_pool,
- apr_pool_t *result_pool
- );
+serf__spnego_init_sec_context(serf__spnego_context_t *ctx,
+ const char *service,
+ const char *hostname,
+ serf__spnego_buffer_t *input_buf,
+ serf__spnego_buffer_t *output_buf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool
+ );
/*
* Reset a previously created security context so we can start with a new one.
@@ -101,12 +104,12 @@ serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
* where each request requires a new security context.
*/
apr_status_t
-serf__kerb_reset_sec_context(serf__kerb_context_t *ctx);
+serf__spnego_reset_sec_context(serf__spnego_context_t *ctx);
#ifdef __cplusplus
}
#endif
-#endif /* SERF_HAVE_KERB */
+#endif /* SERF_HAVE_SPNEGO */
-#endif /* !AUTH_KERB_H */
+#endif /* !AUTH_SPNEGO_H */
diff --git a/contrib/serf/auth/auth_kerb_gss.c b/contrib/serf/auth/auth_spnego_gss.c
index 69d93ab..aa3ebc6 100644
--- a/contrib/serf/auth/auth_kerb_gss.c
+++ b/contrib/serf/auth/auth_spnego_gss.c
@@ -15,7 +15,7 @@
#include "serf.h"
#include "serf_private.h"
-#include "auth_kerb.h"
+#include "auth_spnego.h"
#ifdef SERF_USE_GSSAPI
#include <apr_strings.h>
@@ -33,7 +33,7 @@ static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" };
#define GSS_SPNEGO_MECHANISM &spnego_mech_oid
#endif
-struct serf__kerb_context_t
+struct serf__spnego_context_t
{
/* GSSAPI context */
gss_ctx_id_t gss_ctx;
@@ -44,7 +44,7 @@ struct serf__kerb_context_t
static void
log_error(int verbose_flag, const char *filename,
- serf__kerb_context_t *ctx,
+ serf__spnego_context_t *ctx,
OM_uint32 err_maj_stat,
OM_uint32 err_min_stat,
const char *msg)
@@ -81,13 +81,19 @@ log_error(int verbose_flag, const char *filename,
static apr_status_t
cleanup_ctx(void *data)
{
- OM_uint32 min_stat;
- serf__kerb_context_t *ctx = data;
+ serf__spnego_context_t *ctx = data;
if (ctx->gss_ctx != GSS_C_NO_CONTEXT) {
- if (gss_delete_sec_context(&min_stat, &ctx->gss_ctx,
- GSS_C_NO_BUFFER) == GSS_S_FAILURE)
- return APR_EGENERAL;
+ OM_uint32 gss_min_stat, gss_maj_stat;
+
+ gss_maj_stat = gss_delete_sec_context(&gss_min_stat, &ctx->gss_ctx,
+ GSS_C_NO_BUFFER);
+ if(GSS_ERROR(gss_maj_stat)) {
+ log_error(AUTH_VERBOSE, __FILE__, ctx,
+ gss_maj_stat, gss_min_stat,
+ "Error cleaning up GSS security context");
+ return SERF_ERROR_AUTHN_FAILED;
+ }
}
return APR_SUCCESS;
@@ -105,11 +111,12 @@ cleanup_sec_buffer(void *data)
}
apr_status_t
-serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
- apr_pool_t *scratch_pool,
- apr_pool_t *result_pool)
+serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
+ const serf__authn_scheme_t *scheme,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- serf__kerb_context_t *ctx;
+ serf__spnego_context_t *ctx;
ctx = apr_pcalloc(result_pool, sizeof(*ctx));
@@ -126,7 +133,7 @@ serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
}
apr_status_t
-serf__kerb_reset_sec_context(serf__kerb_context_t *ctx)
+serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
{
OM_uint32 dummy_stat;
@@ -139,14 +146,14 @@ serf__kerb_reset_sec_context(serf__kerb_context_t *ctx)
}
apr_status_t
-serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
- const char *service,
- const char *hostname,
- serf__kerb_buffer_t *input_buf,
- serf__kerb_buffer_t *output_buf,
- apr_pool_t *scratch_pool,
- apr_pool_t *result_pool
- )
+serf__spnego_init_sec_context(serf__spnego_context_t *ctx,
+ const char *service,
+ const char *hostname,
+ serf__spnego_buffer_t *input_buf,
+ serf__spnego_buffer_t *output_buf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool
+ )
{
gss_buffer_desc gss_input_buf = GSS_C_EMPTY_BUFFER;
gss_buffer_desc *gss_output_buf_p;
@@ -164,7 +171,10 @@ serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
GSS_C_NT_HOSTBASED_SERVICE,
&host_gss_name);
if(GSS_ERROR(gss_maj_stat)) {
- return APR_EGENERAL;
+ log_error(AUTH_VERBOSE, __FILE__, ctx,
+ gss_maj_stat, gss_min_stat,
+ "Error converting principal name to GSS internal format ");
+ return SERF_ERROR_AUTHN_FAILED;
}
/* If the server sent us a token, pass it to gss_init_sec_token for
@@ -207,7 +217,7 @@ serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
log_error(AUTH_VERBOSE, __FILE__, ctx,
gss_maj_stat, gss_min_stat,
"Error during Kerberos handshake");
- return APR_EGENERAL;
+ return SERF_ERROR_AUTHN_FAILED;
}
}
diff --git a/contrib/serf/auth/auth_kerb_sspi.c b/contrib/serf/auth/auth_spnego_sspi.c
index ba023cb..ef13428 100644
--- a/contrib/serf/auth/auth_kerb_sspi.c
+++ b/contrib/serf/auth/auth_spnego_sspi.c
@@ -13,8 +13,9 @@
* limitations under the License.
*/
-#include "auth_kerb.h"
+#include "auth_spnego.h"
#include "serf.h"
+#include "serf_private.h"
#ifdef SERF_USE_SSPI
#include <apr.h>
@@ -28,11 +29,18 @@
#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L)
#endif
-struct serf__kerb_context_t
+struct serf__spnego_context_t
{
CredHandle sspi_credentials;
CtxtHandle sspi_context;
BOOL initalized;
+ apr_pool_t *pool;
+
+ /* Service Principal Name (SPN) used for authentication. */
+ const char *target_name;
+
+ /* One of SERF_AUTHN_* authentication types.*/
+ int authn_type;
};
/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped
@@ -79,7 +87,7 @@ map_sspi_status(SECURITY_STATUS sspi_status)
static apr_status_t
cleanup_ctx(void *data)
{
- serf__kerb_context_t *ctx = data;
+ serf__spnego_context_t *ctx = data;
if (SecIsValidHandle(&ctx->sspi_context)) {
DeleteSecurityContext(&ctx->sspi_context);
@@ -103,25 +111,35 @@ cleanup_sec_buffer(void *data)
}
apr_status_t
-serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
- apr_pool_t *scratch_pool,
- apr_pool_t *result_pool)
+serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
+ const serf__authn_scheme_t *scheme,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
SECURITY_STATUS sspi_status;
- serf__kerb_context_t *ctx;
+ serf__spnego_context_t *ctx;
+ const char *sspi_package;
ctx = apr_pcalloc(result_pool, sizeof(*ctx));
SecInvalidateHandle(&ctx->sspi_context);
SecInvalidateHandle(&ctx->sspi_credentials);
ctx->initalized = FALSE;
+ ctx->pool = result_pool;
+ ctx->target_name = NULL;
+ ctx->authn_type = scheme->type;
apr_pool_cleanup_register(result_pool, ctx,
cleanup_ctx,
apr_pool_cleanup_null);
+ if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
+ sspi_package = "Negotiate";
+ else
+ sspi_package = "NTLM";
+
sspi_status = AcquireCredentialsHandle(
- NULL, "Negotiate", SECPKG_CRED_OUTBOUND,
+ NULL, sspi_package, SECPKG_CRED_OUTBOUND,
NULL, NULL, NULL, NULL,
&ctx->sspi_credentials, NULL);
@@ -161,7 +179,7 @@ get_canonical_hostname(const char **canonname,
}
apr_status_t
-serf__kerb_reset_sec_context(serf__kerb_context_t *ctx)
+serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
{
if (SecIsValidHandle(&ctx->sspi_context)) {
DeleteSecurityContext(&ctx->sspi_context);
@@ -174,14 +192,14 @@ serf__kerb_reset_sec_context(serf__kerb_context_t *ctx)
}
apr_status_t
-serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
- const char *service,
- const char *hostname,
- serf__kerb_buffer_t *input_buf,
- serf__kerb_buffer_t *output_buf,
- apr_pool_t *scratch_pool,
- apr_pool_t *result_pool
- )
+serf__spnego_init_sec_context(serf__spnego_context_t *ctx,
+ const char *service,
+ const char *hostname,
+ serf__spnego_buffer_t *input_buf,
+ serf__spnego_buffer_t *output_buf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool
+ )
{
SECURITY_STATUS status;
ULONG actual_attr;
@@ -189,15 +207,26 @@ serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
SecBufferDesc sspi_in_buffer_desc;
SecBuffer sspi_out_buffer;
SecBufferDesc sspi_out_buffer_desc;
- char *target_name;
apr_status_t apr_status;
const char *canonname;
- apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
- if (apr_status) {
- return apr_status;
+ if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
+ apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
+ if (apr_status) {
+ return apr_status;
+ }
+
+ ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
+ NULL);
+
+ serf__log(AUTH_VERBOSE, __FILE__,
+ "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
+ }
+ else if (ctx->authn_type == SERF_AUTHN_NTLM)
+ {
+ /* Target name is not used for NTLM authentication. */
+ ctx->target_name = NULL;
}
- target_name = apr_pstrcat(scratch_pool, service, "/", canonname, NULL);
/* Prepare input buffer description. */
sspi_in_buffer.BufferType = SECBUFFER_TOKEN;
@@ -220,7 +249,7 @@ serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
status = InitializeSecurityContext(
&ctx->sspi_credentials,
ctx->initalized ? &ctx->sspi_context : NULL,
- target_name,
+ ctx->target_name,
ISC_REQ_ALLOCATE_MEMORY
| ISC_REQ_MUTUAL_AUTH
| ISC_REQ_CONFIDENTIALITY,
OpenPOWER on IntegriCloud