diff options
author | peter <peter@FreeBSD.org> | 2013-08-02 19:21:46 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2013-08-02 19:21:46 +0000 |
commit | bce4a8587da654a7a12e36d4a6822cd999c0aafc (patch) | |
tree | 27dab9bf5d6ff996ae09bc981c7d20022b26fa50 /contrib/serf/auth | |
parent | 5750092edd0958adc875d6012f6e656a430e71c6 (diff) | |
parent | 089059a1407b9504b9fdd10f3b602b94d36ed187 (diff) | |
download | FreeBSD-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.c | 367 | ||||
-rw-r--r-- | contrib/serf/auth/auth.h | 70 | ||||
-rw-r--r-- | contrib/serf/auth/auth_basic.c | 100 | ||||
-rw-r--r-- | contrib/serf/auth/auth_digest.c | 109 | ||||
-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, |