1251877Speter/* Copyright 2009 Justin Erenkrantz and Greg Stein 2251877Speter * 3251877Speter * Licensed under the Apache License, Version 2.0 (the "License"); 4251877Speter * you may not use this file except in compliance with the License. 5251877Speter * You may obtain a copy of the License at 6251877Speter * 7251877Speter * http://www.apache.org/licenses/LICENSE-2.0 8251877Speter * 9251877Speter * Unless required by applicable law or agreed to in writing, software 10251877Speter * distributed under the License is distributed on an "AS IS" BASIS, 11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12251877Speter * See the License for the specific language governing permissions and 13251877Speter * limitations under the License. 14251877Speter */ 15251877Speter 16251877Speter#include "serf.h" 17251877Speter#include "serf_private.h" 18251877Speter#include "auth.h" 19251877Speter 20251877Speter#include <apr.h> 21251877Speter#include <apr_base64.h> 22251877Speter#include <apr_strings.h> 23253895Speter#include <apr_lib.h> 24251877Speter 25251877Speterstatic apr_status_t 26262339Speterdefault_auth_response_handler(const serf__authn_scheme_t *scheme, 27262339Speter peer_t peer, 28251877Speter int code, 29251877Speter serf_connection_t *conn, 30251877Speter serf_request_t *request, 31251877Speter serf_bucket_t *response, 32251877Speter apr_pool_t *pool) 33251877Speter{ 34251877Speter return APR_SUCCESS; 35251877Speter} 36251877Speter 37253895Speter/* These authentication schemes are in order of decreasing security, the topmost 38253895Speter scheme will be used first when the server supports it. 39253895Speter 40253895Speter Each set of handlers should support both server (401) and proxy (407) 41253895Speter authentication. 42253895Speter 43253895Speter Use lower case for the scheme names to enable case insensitive matching. 44253895Speter */ 45251877Speterstatic const serf__authn_scheme_t serf_authn_schemes[] = { 46253895Speter#ifdef SERF_HAVE_SPNEGO 47251877Speter { 48253895Speter "Negotiate", 49253895Speter "negotiate", 50253895Speter SERF_AUTHN_NEGOTIATE, 51253895Speter serf__init_spnego, 52253895Speter serf__init_spnego_connection, 53253895Speter serf__handle_spnego_auth, 54253895Speter serf__setup_request_spnego_auth, 55253895Speter serf__validate_response_spnego_auth, 56251877Speter }, 57253895Speter#ifdef WIN32 58251877Speter { 59253895Speter "NTLM", 60253895Speter "ntlm", 61253895Speter SERF_AUTHN_NTLM, 62253895Speter serf__init_spnego, 63253895Speter serf__init_spnego_connection, 64253895Speter serf__handle_spnego_auth, 65253895Speter serf__setup_request_spnego_auth, 66253895Speter serf__validate_response_spnego_auth, 67251877Speter }, 68253895Speter#endif /* #ifdef WIN32 */ 69253895Speter#endif /* SERF_HAVE_SPNEGO */ 70251877Speter { 71251877Speter "Digest", 72253895Speter "digest", 73251877Speter SERF_AUTHN_DIGEST, 74251877Speter serf__init_digest, 75251877Speter serf__init_digest_connection, 76251877Speter serf__handle_digest_auth, 77251877Speter serf__setup_request_digest_auth, 78251877Speter serf__validate_response_digest_auth, 79251877Speter }, 80251877Speter { 81253895Speter "Basic", 82253895Speter "basic", 83253895Speter SERF_AUTHN_BASIC, 84253895Speter serf__init_basic, 85253895Speter serf__init_basic_connection, 86253895Speter serf__handle_basic_auth, 87253895Speter serf__setup_request_basic_auth, 88253895Speter default_auth_response_handler, 89251877Speter }, 90251877Speter /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */ 91251877Speter 92251877Speter /* sentinel */ 93251877Speter { 0 } 94251877Speter}; 95251877Speter 96251877Speter 97251877Speter/* Reads and discards all bytes in the response body. */ 98251877Speterstatic apr_status_t discard_body(serf_bucket_t *response) 99251877Speter{ 100251877Speter apr_status_t status; 101251877Speter const char *data; 102251877Speter apr_size_t len; 103251877Speter 104251877Speter while (1) { 105251877Speter status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len); 106251877Speter 107251877Speter if (status) { 108251877Speter return status; 109251877Speter } 110251877Speter 111251877Speter /* feed me */ 112251877Speter } 113251877Speter} 114251877Speter 115251877Speter/** 116251877Speter * handle_auth_header is called for each header in the response. It filters 117251877Speter * out the Authenticate headers (WWW or Proxy depending on what's needed) and 118251877Speter * tries to find a matching scheme handler. 119251877Speter * 120251877Speter * Returns a non-0 value of a matching handler was found. 121251877Speter */ 122253895Speterstatic int handle_auth_headers(int code, 123253895Speter void *baton, 124253895Speter apr_hash_t *hdrs, 125253895Speter serf_request_t *request, 126253895Speter serf_bucket_t *response, 127253895Speter apr_pool_t *pool) 128251877Speter{ 129253895Speter const serf__authn_scheme_t *scheme; 130253895Speter serf_connection_t *conn = request->conn; 131251877Speter serf_context_t *ctx = conn->ctx; 132253895Speter apr_status_t status; 133251877Speter 134253895Speter status = SERF_ERROR_AUTHN_NOT_SUPPORTED; 135251877Speter 136251877Speter /* Find the matching authentication handler. 137251877Speter Note that we don't reuse the auth scheme stored in the context, 138251877Speter as that may have changed. (ex. fallback from ntlm to basic.) */ 139253895Speter for (scheme = serf_authn_schemes; scheme->name != 0; ++scheme) { 140253895Speter const char *auth_hdr; 141253895Speter serf__auth_handler_func_t handler; 142253895Speter serf__authn_info_t *authn_info; 143253895Speter 144253895Speter if (! (ctx->authn_types & scheme->type)) 145251877Speter continue; 146251877Speter 147251877Speter serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 148251877Speter "Client supports: %s\n", scheme->name); 149251877Speter 150253895Speter auth_hdr = apr_hash_get(hdrs, scheme->key, APR_HASH_KEY_STRING); 151251877Speter 152253895Speter if (!auth_hdr) 153253895Speter continue; 154251877Speter 155262339Speter if (code == 401) { 156262339Speter authn_info = serf__get_authn_info_for_server(conn); 157262339Speter } else { 158262339Speter authn_info = &ctx->proxy_authn_info; 159262339Speter } 160262339Speter 161262339Speter if (authn_info->failed_authn_types & scheme->type) { 162262339Speter /* Skip this authn type since we already tried it before. */ 163262339Speter continue; 164262339Speter } 165262339Speter 166253895Speter /* Found a matching scheme */ 167253895Speter status = APR_SUCCESS; 168251877Speter 169253895Speter handler = scheme->handle_func; 170253895Speter 171253895Speter serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 172253895Speter "... matched: %s\n", scheme->name); 173253895Speter 174253895Speter /* If this is the first time we use this scheme on this context and/or 175253895Speter this connection, make sure to initialize the authentication handler 176253895Speter first. */ 177253895Speter if (authn_info->scheme != scheme) { 178253895Speter status = scheme->init_ctx_func(code, ctx, ctx->pool); 179251877Speter if (!status) { 180253895Speter status = scheme->init_conn_func(scheme, code, conn, 181253895Speter conn->pool); 182253895Speter if (!status) 183253895Speter authn_info->scheme = scheme; 184253895Speter else 185253895Speter authn_info->scheme = NULL; 186251877Speter } 187253895Speter } 188251877Speter 189253895Speter if (!status) { 190253895Speter const char *auth_attr = strchr(auth_hdr, ' '); 191253895Speter if (auth_attr) { 192253895Speter auth_attr++; 193251877Speter } 194251877Speter 195253895Speter status = handler(code, request, response, 196253895Speter auth_hdr, auth_attr, baton, ctx->pool); 197253895Speter } 198253895Speter 199253895Speter if (status == APR_SUCCESS) 200251877Speter break; 201253895Speter 202253895Speter /* No success authenticating with this scheme, try the next. 203253895Speter If no more authn schemes are found the status of this scheme will be 204253895Speter returned. 205253895Speter */ 206253895Speter serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 207253895Speter "%s authentication failed.\n", scheme->name); 208262339Speter 209262339Speter /* Clear per-request auth_baton when switching to next auth scheme. */ 210262339Speter request->auth_baton = NULL; 211262339Speter 212262339Speter /* Remember failed auth types to skip in future. */ 213262339Speter authn_info->failed_authn_types |= scheme->type; 214251877Speter } 215251877Speter 216253895Speter return status; 217251877Speter} 218251877Speter 219253895Speter/** 220253895Speter * Baton passed to the store_header_in_dict callback function 221253895Speter */ 222253895Spetertypedef struct { 223253895Speter const char *header; 224253895Speter apr_pool_t *pool; 225253895Speter apr_hash_t *hdrs; 226253895Speter} auth_baton_t; 227253895Speter 228253895Speterstatic int store_header_in_dict(void *baton, 229253895Speter const char *key, 230253895Speter const char *header) 231253895Speter{ 232253895Speter auth_baton_t *ab = baton; 233253895Speter const char *auth_attr; 234253895Speter char *auth_name, *c; 235253895Speter 236253895Speter /* We're only interested in xxxx-Authenticate headers. */ 237262339Speter if (strcasecmp(key, ab->header) != 0) 238253895Speter return 0; 239253895Speter 240253895Speter /* Extract the authentication scheme name. */ 241253895Speter auth_attr = strchr(header, ' '); 242253895Speter if (auth_attr) { 243253895Speter auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header); 244253895Speter } 245253895Speter else 246253895Speter auth_name = apr_pstrmemdup(ab->pool, header, strlen(header)); 247253895Speter 248253895Speter /* Convert scheme name to lower case to enable case insensitive matching. */ 249253895Speter for (c = auth_name; *c != '\0'; c++) 250253895Speter *c = (char)apr_tolower(*c); 251253895Speter 252253895Speter apr_hash_set(ab->hdrs, auth_name, APR_HASH_KEY_STRING, 253253895Speter apr_pstrdup(ab->pool, header)); 254253895Speter 255253895Speter return 0; 256253895Speter} 257253895Speter 258251877Speter/* Dispatch authentication handling. This function matches the possible 259251877Speter authentication mechanisms with those available. Server and proxy 260251877Speter authentication are evaluated separately. */ 261251877Speterstatic apr_status_t dispatch_auth(int code, 262251877Speter serf_request_t *request, 263251877Speter serf_bucket_t *response, 264251877Speter void *baton, 265251877Speter apr_pool_t *pool) 266251877Speter{ 267251877Speter serf_bucket_t *hdrs; 268251877Speter 269251877Speter if (code == 401 || code == 407) { 270251877Speter auth_baton_t ab = { 0 }; 271251877Speter const char *auth_hdr; 272251877Speter 273253895Speter ab.hdrs = apr_hash_make(pool); 274251877Speter ab.pool = pool; 275251877Speter 276251877Speter /* Before iterating over all authn headers, check if there are any. */ 277251877Speter if (code == 401) 278251877Speter ab.header = "WWW-Authenticate"; 279251877Speter else 280251877Speter ab.header = "Proxy-Authenticate"; 281251877Speter 282251877Speter hdrs = serf_bucket_response_get_headers(response); 283251877Speter auth_hdr = serf_bucket_headers_get(hdrs, ab.header); 284251877Speter 285251877Speter if (!auth_hdr) { 286251877Speter return SERF_ERROR_AUTHN_FAILED; 287251877Speter } 288251877Speter serf__log_skt(AUTH_VERBOSE, __FILE__, request->conn->skt, 289251877Speter "%s authz required. Response header(s): %s\n", 290251877Speter code == 401 ? "Server" : "Proxy", auth_hdr); 291251877Speter 292251877Speter 293253895Speter /* Store all WWW- or Proxy-Authenticate headers in a dictionary. 294253895Speter 295251877Speter Note: it is possible to have multiple Authentication: headers. We do 296251877Speter not want to combine them (per normal header combination rules) as that 297251877Speter would make it hard to parse. Instead, we want to individually parse 298251877Speter and handle each header in the response, looking for one that we can 299251877Speter work with. 300251877Speter */ 301251877Speter serf_bucket_headers_do(hdrs, 302253895Speter store_header_in_dict, 303251877Speter &ab); 304251877Speter 305253895Speter /* Iterate over all authentication schemes, in order of decreasing 306253895Speter security. Try to find a authentication schema the server support. */ 307253895Speter return handle_auth_headers(code, baton, ab.hdrs, 308253895Speter request, response, pool); 309251877Speter } 310251877Speter 311251877Speter return APR_SUCCESS; 312251877Speter} 313251877Speter 314251877Speter/* Read the headers of the response and try the available 315251877Speter handlers if authentication or validation is needed. */ 316251877Speterapr_status_t serf__handle_auth_response(int *consumed_response, 317251877Speter serf_request_t *request, 318251877Speter serf_bucket_t *response, 319251877Speter void *baton, 320251877Speter apr_pool_t *pool) 321251877Speter{ 322251877Speter apr_status_t status; 323251877Speter serf_status_line sl; 324251877Speter 325251877Speter *consumed_response = 0; 326251877Speter 327251877Speter /* TODO: the response bucket was created by the application, not at all 328251877Speter guaranteed that this is of type response_bucket!! */ 329251877Speter status = serf_bucket_response_status(response, &sl); 330251877Speter if (SERF_BUCKET_READ_ERROR(status)) { 331251877Speter return status; 332251877Speter } 333251877Speter if (!sl.version && (APR_STATUS_IS_EOF(status) || 334251877Speter APR_STATUS_IS_EAGAIN(status))) { 335251877Speter return status; 336251877Speter } 337251877Speter 338251877Speter status = serf_bucket_response_wait_for_headers(response); 339251877Speter if (status) { 340251877Speter if (!APR_STATUS_IS_EOF(status)) { 341251877Speter return status; 342251877Speter } 343251877Speter 344251877Speter /* If status is APR_EOF, there were no headers to read. 345251877Speter This can be ok in some situations, and it definitely 346251877Speter means there's no authentication requested now. */ 347251877Speter return APR_SUCCESS; 348251877Speter } 349251877Speter 350251877Speter if (sl.code == 401 || sl.code == 407) { 351251877Speter /* Authentication requested. */ 352251877Speter 353251877Speter /* Don't bother handling the authentication request if the response 354251877Speter wasn't received completely yet. Serf will call serf__handle_auth_response 355251877Speter again when more data is received. */ 356251877Speter status = discard_body(response); 357251877Speter *consumed_response = 1; 358251877Speter 359251877Speter /* Discard all response body before processing authentication. */ 360251877Speter if (!APR_STATUS_IS_EOF(status)) { 361251877Speter return status; 362251877Speter } 363251877Speter 364251877Speter status = dispatch_auth(sl.code, request, response, baton, pool); 365251877Speter if (status != APR_SUCCESS) { 366251877Speter return status; 367251877Speter } 368251877Speter 369251877Speter /* Requeue the request with the necessary auth headers. */ 370251877Speter /* ### Application doesn't know about this request! */ 371253895Speter if (request->ssltunnel) { 372253895Speter serf__ssltunnel_request_create(request->conn, 373253895Speter request->setup, 374253895Speter request->setup_baton); 375253895Speter } else { 376253895Speter serf_connection_priority_request_create(request->conn, 377253895Speter request->setup, 378253895Speter request->setup_baton); 379253895Speter } 380251877Speter 381251877Speter return APR_EOF; 382251877Speter } else { 383251877Speter serf__validate_response_func_t validate_resp; 384251877Speter serf_connection_t *conn = request->conn; 385251877Speter serf_context_t *ctx = conn->ctx; 386253895Speter serf__authn_info_t *authn_info; 387251877Speter apr_status_t resp_status = APR_SUCCESS; 388253895Speter 389253895Speter 390253895Speter /* Validate the response server authn headers. */ 391253895Speter authn_info = serf__get_authn_info_for_server(conn); 392253895Speter if (authn_info->scheme) { 393253895Speter validate_resp = authn_info->scheme->validate_response_func; 394262339Speter resp_status = validate_resp(authn_info->scheme, HOST, sl.code, 395262339Speter conn, request, response, pool); 396251877Speter } 397253895Speter 398253895Speter /* Validate the response proxy authn headers. */ 399253895Speter authn_info = &ctx->proxy_authn_info; 400253895Speter if (!resp_status && authn_info->scheme) { 401253895Speter validate_resp = authn_info->scheme->validate_response_func; 402262339Speter resp_status = validate_resp(authn_info->scheme, PROXY, sl.code, 403262339Speter conn, request, response, pool); 404251877Speter } 405253895Speter 406251877Speter if (resp_status) { 407251877Speter /* If there was an error in the final step of the authentication, 408251877Speter consider the reponse body as invalid and discard it. */ 409251877Speter status = discard_body(response); 410251877Speter *consumed_response = 1; 411251877Speter if (!APR_STATUS_IS_EOF(status)) { 412251877Speter return status; 413251877Speter } 414251877Speter /* The whole body was discarded, now return our error. */ 415251877Speter return resp_status; 416251877Speter } 417251877Speter } 418251877Speter 419251877Speter return APR_SUCCESS; 420251877Speter} 421251877Speter 422251877Speter/** 423251877Speter * base64 encode the authentication data and build an authentication 424251877Speter * header in this format: 425251877Speter * [SCHEME] [BASE64 of auth DATA] 426251877Speter */ 427251877Spetervoid serf__encode_auth_header(const char **header, 428251877Speter const char *scheme, 429251877Speter const char *data, apr_size_t data_len, 430251877Speter apr_pool_t *pool) 431251877Speter{ 432251877Speter apr_size_t encoded_len, scheme_len; 433251877Speter char *ptr; 434251877Speter 435251877Speter encoded_len = apr_base64_encode_len(data_len); 436251877Speter scheme_len = strlen(scheme); 437251877Speter 438251877Speter ptr = apr_palloc(pool, encoded_len + scheme_len + 1); 439251877Speter *header = ptr; 440251877Speter 441251877Speter apr_cpystrn(ptr, scheme, scheme_len + 1); 442251877Speter ptr += scheme_len; 443251877Speter *ptr++ = ' '; 444251877Speter 445251877Speter apr_base64_encode(ptr, data, data_len); 446251877Speter} 447253895Speter 448253895Speterconst char *serf__construct_realm(peer_t peer, 449253895Speter serf_connection_t *conn, 450253895Speter const char *realm_name, 451253895Speter apr_pool_t *pool) 452253895Speter{ 453253895Speter if (peer == HOST) { 454253895Speter return apr_psprintf(pool, "<%s://%s:%d> %s", 455253895Speter conn->host_info.scheme, 456253895Speter conn->host_info.hostname, 457253895Speter conn->host_info.port, 458253895Speter realm_name); 459253895Speter } else { 460253895Speter serf_context_t *ctx = conn->ctx; 461253895Speter 462253895Speter return apr_psprintf(pool, "<http://%s:%d> %s", 463253895Speter ctx->proxy_address->hostname, 464253895Speter ctx->proxy_address->port, 465253895Speter realm_name); 466253895Speter } 467253895Speter} 468253895Speter 469253895Speterserf__authn_info_t *serf__get_authn_info_for_server(serf_connection_t *conn) 470253895Speter{ 471253895Speter serf_context_t *ctx = conn->ctx; 472253895Speter serf__authn_info_t *authn_info; 473253895Speter 474253895Speter authn_info = apr_hash_get(ctx->server_authn_info, conn->host_url, 475253895Speter APR_HASH_KEY_STRING); 476253895Speter 477253895Speter if (!authn_info) { 478253895Speter authn_info = apr_pcalloc(ctx->pool, sizeof(serf__authn_info_t)); 479253895Speter apr_hash_set(ctx->server_authn_info, 480253895Speter apr_pstrdup(ctx->pool, conn->host_url), 481253895Speter APR_HASH_KEY_STRING, authn_info); 482253895Speter } 483253895Speter 484253895Speter return authn_info; 485253895Speter} 486