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