1362181Sdim/* ====================================================================
2362181Sdim *    Licensed to the Apache Software Foundation (ASF) under one
3362181Sdim *    or more contributor license agreements.  See the NOTICE file
4362181Sdim *    distributed with this work for additional information
5362181Sdim *    regarding copyright ownership.  The ASF licenses this file
6362181Sdim *    to you under the Apache License, Version 2.0 (the
7362181Sdim *    "License"); you may not use this file except in compliance
8362181Sdim *    with the License.  You may obtain a copy of the License at
9251877Speter *
10362181Sdim *      http://www.apache.org/licenses/LICENSE-2.0
11251877Speter *
12362181Sdim *    Unless required by applicable law or agreed to in writing,
13362181Sdim *    software distributed under the License is distributed on an
14362181Sdim *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15362181Sdim *    KIND, either express or implied.  See the License for the
16362181Sdim *    specific language governing permissions and limitations
17362181Sdim *    under the License.
18362181Sdim * ====================================================================
19251877Speter */
20251877Speter
21251877Speter#include "serf.h"
22251877Speter#include "serf_private.h"
23251877Speter#include "auth.h"
24251877Speter
25251877Speter#include <apr.h>
26251877Speter#include <apr_base64.h>
27251877Speter#include <apr_strings.h>
28253895Speter#include <apr_lib.h>
29251877Speter
30251877Speterstatic apr_status_t
31262324Speterdefault_auth_response_handler(const serf__authn_scheme_t *scheme,
32262324Speter                              peer_t peer,
33251877Speter                              int code,
34251877Speter                              serf_connection_t *conn,
35251877Speter                              serf_request_t *request,
36251877Speter                              serf_bucket_t *response,
37251877Speter                              apr_pool_t *pool)
38251877Speter{
39251877Speter    return APR_SUCCESS;
40251877Speter}
41251877Speter
42253895Speter/* These authentication schemes are in order of decreasing security, the topmost
43253895Speter   scheme will be used first when the server supports it.
44253895Speter
45253895Speter   Each set of handlers should support both server (401) and proxy (407)
46253895Speter   authentication.
47253895Speter
48253895Speter   Use lower case for the scheme names to enable case insensitive matching.
49253895Speter */
50251877Speterstatic const serf__authn_scheme_t serf_authn_schemes[] = {
51253895Speter#ifdef SERF_HAVE_SPNEGO
52251877Speter    {
53253895Speter        "Negotiate",
54253895Speter        "negotiate",
55253895Speter        SERF_AUTHN_NEGOTIATE,
56253895Speter        serf__init_spnego,
57253895Speter        serf__init_spnego_connection,
58253895Speter        serf__handle_spnego_auth,
59253895Speter        serf__setup_request_spnego_auth,
60253895Speter        serf__validate_response_spnego_auth,
61251877Speter    },
62253895Speter#ifdef WIN32
63251877Speter    {
64253895Speter        "NTLM",
65253895Speter        "ntlm",
66253895Speter        SERF_AUTHN_NTLM,
67253895Speter        serf__init_spnego,
68253895Speter        serf__init_spnego_connection,
69253895Speter        serf__handle_spnego_auth,
70253895Speter        serf__setup_request_spnego_auth,
71253895Speter        serf__validate_response_spnego_auth,
72251877Speter    },
73253895Speter#endif /* #ifdef WIN32 */
74253895Speter#endif /* SERF_HAVE_SPNEGO */
75251877Speter    {
76251877Speter        "Digest",
77253895Speter        "digest",
78251877Speter        SERF_AUTHN_DIGEST,
79251877Speter        serf__init_digest,
80251877Speter        serf__init_digest_connection,
81251877Speter        serf__handle_digest_auth,
82251877Speter        serf__setup_request_digest_auth,
83251877Speter        serf__validate_response_digest_auth,
84251877Speter    },
85251877Speter    {
86253895Speter        "Basic",
87253895Speter        "basic",
88253895Speter        SERF_AUTHN_BASIC,
89253895Speter        serf__init_basic,
90253895Speter        serf__init_basic_connection,
91253895Speter        serf__handle_basic_auth,
92253895Speter        serf__setup_request_basic_auth,
93253895Speter        default_auth_response_handler,
94251877Speter    },
95251877Speter    /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */
96251877Speter
97251877Speter    /* sentinel */
98251877Speter    { 0 }
99251877Speter};
100251877Speter
101251877Speter
102251877Speter/* Reads and discards all bytes in the response body. */
103251877Speterstatic apr_status_t discard_body(serf_bucket_t *response)
104251877Speter{
105251877Speter    apr_status_t status;
106251877Speter    const char *data;
107251877Speter    apr_size_t len;
108251877Speter
109251877Speter    while (1) {
110251877Speter        status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len);
111251877Speter
112251877Speter        if (status) {
113251877Speter            return status;
114251877Speter        }
115251877Speter
116251877Speter        /* feed me */
117251877Speter    }
118251877Speter}
119251877Speter
120251877Speter/**
121251877Speter * handle_auth_header is called for each header in the response. It filters
122251877Speter * out the Authenticate headers (WWW or Proxy depending on what's needed) and
123251877Speter * tries to find a matching scheme handler.
124251877Speter *
125251877Speter * Returns a non-0 value of a matching handler was found.
126251877Speter */
127253895Speterstatic int handle_auth_headers(int code,
128253895Speter                               void *baton,
129253895Speter                               apr_hash_t *hdrs,
130253895Speter                               serf_request_t *request,
131253895Speter                               serf_bucket_t *response,
132253895Speter                               apr_pool_t *pool)
133251877Speter{
134253895Speter    const serf__authn_scheme_t *scheme;
135253895Speter    serf_connection_t *conn = request->conn;
136251877Speter    serf_context_t *ctx = conn->ctx;
137253895Speter    apr_status_t status;
138251877Speter
139253895Speter    status = SERF_ERROR_AUTHN_NOT_SUPPORTED;
140251877Speter
141251877Speter    /* Find the matching authentication handler.
142251877Speter       Note that we don't reuse the auth scheme stored in the context,
143251877Speter       as that may have changed. (ex. fallback from ntlm to basic.) */
144253895Speter    for (scheme = serf_authn_schemes; scheme->name != 0; ++scheme) {
145253895Speter        const char *auth_hdr;
146253895Speter        serf__auth_handler_func_t handler;
147253895Speter        serf__authn_info_t *authn_info;
148253895Speter
149253895Speter        if (! (ctx->authn_types & scheme->type))
150251877Speter            continue;
151251877Speter
152251877Speter        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
153251877Speter                      "Client supports: %s\n", scheme->name);
154251877Speter
155253895Speter        auth_hdr = apr_hash_get(hdrs, scheme->key, APR_HASH_KEY_STRING);
156251877Speter
157253895Speter        if (!auth_hdr)
158253895Speter            continue;
159251877Speter
160262324Speter        if (code == 401) {
161262324Speter            authn_info = serf__get_authn_info_for_server(conn);
162262324Speter        } else {
163262324Speter            authn_info = &ctx->proxy_authn_info;
164262324Speter        }
165262324Speter
166262324Speter        if (authn_info->failed_authn_types & scheme->type) {
167262324Speter            /* Skip this authn type since we already tried it before. */
168262324Speter            continue;
169262324Speter        }
170262324Speter
171253895Speter        /* Found a matching scheme */
172253895Speter        status = APR_SUCCESS;
173251877Speter
174253895Speter        handler = scheme->handle_func;
175253895Speter
176253895Speter        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
177253895Speter                      "... matched: %s\n", scheme->name);
178253895Speter
179253895Speter        /* If this is the first time we use this scheme on this context and/or
180253895Speter           this connection, make sure to initialize the authentication handler
181253895Speter           first. */
182253895Speter        if (authn_info->scheme != scheme) {
183253895Speter            status = scheme->init_ctx_func(code, ctx, ctx->pool);
184251877Speter            if (!status) {
185253895Speter                status = scheme->init_conn_func(scheme, code, conn,
186253895Speter                                                conn->pool);
187253895Speter                if (!status)
188253895Speter                    authn_info->scheme = scheme;
189253895Speter                else
190253895Speter                    authn_info->scheme = NULL;
191251877Speter            }
192253895Speter        }
193251877Speter
194253895Speter        if (!status) {
195253895Speter            const char *auth_attr = strchr(auth_hdr, ' ');
196253895Speter            if (auth_attr) {
197253895Speter                auth_attr++;
198251877Speter            }
199251877Speter
200253895Speter            status = handler(code, request, response,
201253895Speter                             auth_hdr, auth_attr, baton, ctx->pool);
202253895Speter        }
203253895Speter
204253895Speter        if (status == APR_SUCCESS)
205251877Speter            break;
206253895Speter
207253895Speter        /* No success authenticating with this scheme, try the next.
208253895Speter           If no more authn schemes are found the status of this scheme will be
209253895Speter           returned.
210253895Speter        */
211253895Speter        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
212253895Speter                      "%s authentication failed.\n", scheme->name);
213262324Speter
214262324Speter        /* Clear per-request auth_baton when switching to next auth scheme. */
215262324Speter        request->auth_baton = NULL;
216262324Speter
217262324Speter        /* Remember failed auth types to skip in future. */
218262324Speter        authn_info->failed_authn_types |= scheme->type;
219251877Speter    }
220251877Speter
221253895Speter    return status;
222251877Speter}
223251877Speter
224253895Speter/**
225253895Speter * Baton passed to the store_header_in_dict callback function
226253895Speter */
227253895Spetertypedef struct {
228253895Speter    const char *header;
229253895Speter    apr_pool_t *pool;
230253895Speter    apr_hash_t *hdrs;
231253895Speter} auth_baton_t;
232253895Speter
233253895Speterstatic int store_header_in_dict(void *baton,
234253895Speter                                const char *key,
235253895Speter                                const char *header)
236253895Speter{
237253895Speter    auth_baton_t *ab = baton;
238253895Speter    const char *auth_attr;
239253895Speter    char *auth_name, *c;
240253895Speter
241253895Speter    /* We're only interested in xxxx-Authenticate headers. */
242262324Speter    if (strcasecmp(key, ab->header) != 0)
243253895Speter        return 0;
244253895Speter
245253895Speter    /* Extract the authentication scheme name.  */
246253895Speter    auth_attr = strchr(header, ' ');
247253895Speter    if (auth_attr) {
248253895Speter        auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header);
249253895Speter    }
250253895Speter    else
251253895Speter        auth_name = apr_pstrmemdup(ab->pool, header, strlen(header));
252253895Speter
253253895Speter    /* Convert scheme name to lower case to enable case insensitive matching. */
254253895Speter    for (c = auth_name; *c != '\0'; c++)
255253895Speter        *c = (char)apr_tolower(*c);
256253895Speter
257253895Speter    apr_hash_set(ab->hdrs, auth_name, APR_HASH_KEY_STRING,
258253895Speter                 apr_pstrdup(ab->pool, header));
259253895Speter
260253895Speter    return 0;
261253895Speter}
262253895Speter
263251877Speter/* Dispatch authentication handling. This function matches the possible
264251877Speter   authentication mechanisms with those available. Server and proxy
265251877Speter   authentication are evaluated separately. */
266251877Speterstatic apr_status_t dispatch_auth(int code,
267251877Speter                                  serf_request_t *request,
268251877Speter                                  serf_bucket_t *response,
269251877Speter                                  void *baton,
270251877Speter                                  apr_pool_t *pool)
271251877Speter{
272251877Speter    serf_bucket_t *hdrs;
273251877Speter
274251877Speter    if (code == 401 || code == 407) {
275251877Speter        auth_baton_t ab = { 0 };
276251877Speter        const char *auth_hdr;
277251877Speter
278253895Speter        ab.hdrs = apr_hash_make(pool);
279251877Speter        ab.pool = pool;
280251877Speter
281251877Speter        /* Before iterating over all authn headers, check if there are any. */
282251877Speter        if (code == 401)
283251877Speter            ab.header = "WWW-Authenticate";
284251877Speter        else
285251877Speter            ab.header = "Proxy-Authenticate";
286251877Speter
287251877Speter        hdrs = serf_bucket_response_get_headers(response);
288251877Speter        auth_hdr = serf_bucket_headers_get(hdrs, ab.header);
289251877Speter
290251877Speter        if (!auth_hdr) {
291251877Speter            return SERF_ERROR_AUTHN_FAILED;
292251877Speter        }
293251877Speter        serf__log_skt(AUTH_VERBOSE, __FILE__, request->conn->skt,
294251877Speter                      "%s authz required. Response header(s): %s\n",
295251877Speter                      code == 401 ? "Server" : "Proxy", auth_hdr);
296251877Speter
297251877Speter
298253895Speter        /* Store all WWW- or Proxy-Authenticate headers in a dictionary.
299253895Speter
300251877Speter           Note: it is possible to have multiple Authentication: headers. We do
301251877Speter           not want to combine them (per normal header combination rules) as that
302251877Speter           would make it hard to parse. Instead, we want to individually parse
303251877Speter           and handle each header in the response, looking for one that we can
304251877Speter           work with.
305251877Speter        */
306251877Speter        serf_bucket_headers_do(hdrs,
307253895Speter                               store_header_in_dict,
308251877Speter                               &ab);
309251877Speter
310253895Speter        /* Iterate over all authentication schemes, in order of decreasing
311253895Speter           security. Try to find a authentication schema the server support. */
312253895Speter        return handle_auth_headers(code, baton, ab.hdrs,
313253895Speter                                   request, response, pool);
314251877Speter    }
315251877Speter
316251877Speter    return APR_SUCCESS;
317251877Speter}
318251877Speter
319251877Speter/* Read the headers of the response and try the available
320251877Speter   handlers if authentication or validation is needed. */
321251877Speterapr_status_t serf__handle_auth_response(int *consumed_response,
322251877Speter                                        serf_request_t *request,
323251877Speter                                        serf_bucket_t *response,
324251877Speter                                        void *baton,
325251877Speter                                        apr_pool_t *pool)
326251877Speter{
327251877Speter    apr_status_t status;
328251877Speter    serf_status_line sl;
329251877Speter
330251877Speter    *consumed_response = 0;
331251877Speter
332251877Speter    /* TODO: the response bucket was created by the application, not at all
333251877Speter       guaranteed that this is of type response_bucket!! */
334251877Speter    status = serf_bucket_response_status(response, &sl);
335251877Speter    if (SERF_BUCKET_READ_ERROR(status)) {
336251877Speter        return status;
337251877Speter    }
338251877Speter    if (!sl.version && (APR_STATUS_IS_EOF(status) ||
339251877Speter                        APR_STATUS_IS_EAGAIN(status))) {
340251877Speter        return status;
341251877Speter    }
342251877Speter
343251877Speter    status = serf_bucket_response_wait_for_headers(response);
344251877Speter    if (status) {
345251877Speter        if (!APR_STATUS_IS_EOF(status)) {
346251877Speter            return status;
347251877Speter        }
348251877Speter
349251877Speter        /* If status is APR_EOF, there were no headers to read.
350251877Speter           This can be ok in some situations, and it definitely
351251877Speter           means there's no authentication requested now. */
352251877Speter        return APR_SUCCESS;
353251877Speter    }
354251877Speter
355251877Speter    if (sl.code == 401 || sl.code == 407) {
356251877Speter        /* Authentication requested. */
357251877Speter
358251877Speter        /* Don't bother handling the authentication request if the response
359251877Speter           wasn't received completely yet. Serf will call serf__handle_auth_response
360251877Speter           again when more data is received. */
361251877Speter        status = discard_body(response);
362251877Speter        *consumed_response = 1;
363251877Speter
364251877Speter        /* Discard all response body before processing authentication. */
365251877Speter        if (!APR_STATUS_IS_EOF(status)) {
366251877Speter            return status;
367251877Speter        }
368251877Speter
369251877Speter        status = dispatch_auth(sl.code, request, response, baton, pool);
370251877Speter        if (status != APR_SUCCESS) {
371251877Speter            return status;
372251877Speter        }
373251877Speter
374251877Speter        /* Requeue the request with the necessary auth headers. */
375251877Speter        /* ### Application doesn't know about this request! */
376253895Speter        if (request->ssltunnel) {
377253895Speter            serf__ssltunnel_request_create(request->conn,
378253895Speter                                           request->setup,
379253895Speter                                           request->setup_baton);
380253895Speter        } else {
381253895Speter            serf_connection_priority_request_create(request->conn,
382253895Speter                                                    request->setup,
383253895Speter                                                    request->setup_baton);
384253895Speter        }
385251877Speter
386251877Speter        return APR_EOF;
387251877Speter    } else {
388251877Speter        serf__validate_response_func_t validate_resp;
389251877Speter        serf_connection_t *conn = request->conn;
390251877Speter        serf_context_t *ctx = conn->ctx;
391253895Speter        serf__authn_info_t *authn_info;
392251877Speter        apr_status_t resp_status = APR_SUCCESS;
393253895Speter
394253895Speter
395253895Speter        /* Validate the response server authn headers. */
396253895Speter        authn_info = serf__get_authn_info_for_server(conn);
397253895Speter        if (authn_info->scheme) {
398253895Speter            validate_resp = authn_info->scheme->validate_response_func;
399262324Speter            resp_status = validate_resp(authn_info->scheme, HOST, sl.code,
400262324Speter                                        conn, request, response, pool);
401251877Speter        }
402253895Speter
403253895Speter        /* Validate the response proxy authn headers. */
404253895Speter        authn_info = &ctx->proxy_authn_info;
405253895Speter        if (!resp_status && authn_info->scheme) {
406253895Speter            validate_resp = authn_info->scheme->validate_response_func;
407262324Speter            resp_status = validate_resp(authn_info->scheme, PROXY, sl.code,
408262324Speter                                        conn, request, response, pool);
409251877Speter        }
410253895Speter
411251877Speter        if (resp_status) {
412251877Speter            /* If there was an error in the final step of the authentication,
413251877Speter               consider the reponse body as invalid and discard it. */
414251877Speter            status = discard_body(response);
415251877Speter            *consumed_response = 1;
416251877Speter            if (!APR_STATUS_IS_EOF(status)) {
417251877Speter                return status;
418251877Speter            }
419251877Speter            /* The whole body was discarded, now return our error. */
420251877Speter            return resp_status;
421251877Speter        }
422251877Speter    }
423251877Speter
424251877Speter    return APR_SUCCESS;
425251877Speter}
426251877Speter
427251877Speter/**
428251877Speter * base64 encode the authentication data and build an authentication
429251877Speter * header in this format:
430251877Speter * [SCHEME] [BASE64 of auth DATA]
431251877Speter */
432251877Spetervoid serf__encode_auth_header(const char **header,
433251877Speter                              const char *scheme,
434251877Speter                              const char *data, apr_size_t data_len,
435251877Speter                              apr_pool_t *pool)
436251877Speter{
437251877Speter    apr_size_t encoded_len, scheme_len;
438251877Speter    char *ptr;
439251877Speter
440251877Speter    encoded_len = apr_base64_encode_len(data_len);
441251877Speter    scheme_len = strlen(scheme);
442251877Speter
443251877Speter    ptr = apr_palloc(pool, encoded_len + scheme_len + 1);
444251877Speter    *header = ptr;
445251877Speter
446251877Speter    apr_cpystrn(ptr, scheme, scheme_len + 1);
447251877Speter    ptr += scheme_len;
448251877Speter    *ptr++ = ' ';
449251877Speter
450251877Speter    apr_base64_encode(ptr, data, data_len);
451251877Speter}
452253895Speter
453253895Speterconst char *serf__construct_realm(peer_t peer,
454253895Speter                                  serf_connection_t *conn,
455253895Speter                                  const char *realm_name,
456253895Speter                                  apr_pool_t *pool)
457253895Speter{
458253895Speter    if (peer == HOST) {
459253895Speter        return apr_psprintf(pool, "<%s://%s:%d> %s",
460253895Speter                            conn->host_info.scheme,
461253895Speter                            conn->host_info.hostname,
462253895Speter                            conn->host_info.port,
463253895Speter                            realm_name);
464253895Speter    } else {
465253895Speter        serf_context_t *ctx = conn->ctx;
466253895Speter
467253895Speter        return apr_psprintf(pool, "<http://%s:%d> %s",
468253895Speter                            ctx->proxy_address->hostname,
469253895Speter                            ctx->proxy_address->port,
470253895Speter                            realm_name);
471253895Speter    }
472253895Speter}
473253895Speter
474253895Speterserf__authn_info_t *serf__get_authn_info_for_server(serf_connection_t *conn)
475253895Speter{
476253895Speter    serf_context_t *ctx = conn->ctx;
477253895Speter    serf__authn_info_t *authn_info;
478253895Speter
479253895Speter    authn_info = apr_hash_get(ctx->server_authn_info, conn->host_url,
480253895Speter                              APR_HASH_KEY_STRING);
481253895Speter
482253895Speter    if (!authn_info) {
483253895Speter        authn_info = apr_pcalloc(ctx->pool, sizeof(serf__authn_info_t));
484253895Speter        apr_hash_set(ctx->server_authn_info,
485253895Speter                     apr_pstrdup(ctx->pool, conn->host_url),
486253895Speter                     APR_HASH_KEY_STRING, authn_info);
487253895Speter    }
488253895Speter
489253895Speter    return authn_info;
490253895Speter}
491