~ubuntu-branches/ubuntu/trusty/serf/trusty-proposed

« back to all changes in this revision

Viewing changes to auth/auth.c

  • Committer: Bazaar Package Importer
  • Author(s): Peter Samuelson
  • Date: 2011-06-03 03:18:07 UTC
  • mto: (9.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: james.westby@ubuntu.com-20110603031807-6y8ky58ac0q82etq
Tags: upstream-0.7.2
ImportĀ upstreamĀ versionĀ 0.7.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2009 Justin Erenkrantz and Greg Stein
 
2
 *
 
3
 * Licensed under the Apache License, Version 2.0 (the "License");
 
4
 * you may not use this file except in compliance with the License.
 
5
 * You may obtain a copy of the License at
 
6
 *
 
7
 *     http://www.apache.org/licenses/LICENSE-2.0
 
8
 *
 
9
 * Unless required by applicable law or agreed to in writing, software
 
10
 * distributed under the License is distributed on an "AS IS" BASIS,
 
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
12
 * See the License for the specific language governing permissions and
 
13
 * limitations under the License.
 
14
 */
 
15
 
 
16
#include "serf.h"
 
17
#include "serf_private.h"
 
18
#include "auth.h"
 
19
 
 
20
#include <apr.h>
 
21
#include <apr_base64.h>
 
22
#include <apr_strings.h>
 
23
 
 
24
static apr_status_t
 
25
default_auth_response_handler(int code,
 
26
                              serf_connection_t *conn,
 
27
                              serf_request_t *request,
 
28
                              serf_bucket_t *response,
 
29
                              apr_pool_t *pool)
 
30
{
 
31
    return APR_SUCCESS;
 
32
}
 
33
 
 
34
static const serf__authn_scheme_t serf_authn_schemes[] = {
 
35
    {
 
36
        401,
 
37
        "Basic",
 
38
        SERF_AUTHN_BASIC,
 
39
        serf__init_basic,
 
40
        serf__init_basic_connection,
 
41
        serf__handle_basic_auth,
 
42
        serf__setup_request_basic_auth,
 
43
        default_auth_response_handler,
 
44
    },
 
45
    {
 
46
          407,
 
47
          "Basic",
 
48
          SERF_AUTHN_BASIC,
 
49
          serf__init_basic,
 
50
          serf__init_basic_connection,
 
51
          serf__handle_basic_auth,
 
52
          serf__setup_request_basic_auth,
 
53
          default_auth_response_handler,
 
54
    },
 
55
    {
 
56
        401,
 
57
        "Digest",
 
58
        SERF_AUTHN_DIGEST,
 
59
        serf__init_digest,
 
60
        serf__init_digest_connection,
 
61
        serf__handle_digest_auth,
 
62
        serf__setup_request_digest_auth,
 
63
        serf__validate_response_digest_auth,
 
64
    },
 
65
    {
 
66
        407,
 
67
        "Digest",
 
68
        SERF_AUTHN_DIGEST,
 
69
        serf__init_digest,
 
70
        serf__init_digest_connection,
 
71
        serf__handle_digest_auth,
 
72
        serf__setup_request_digest_auth,
 
73
        serf__validate_response_digest_auth,
 
74
    },
 
75
#ifdef SERF_HAVE_KERB
 
76
    {
 
77
        401,
 
78
        "Negotiate",
 
79
        SERF_AUTHN_NEGOTIATE,
 
80
        serf__init_kerb,
 
81
        serf__init_kerb_connection,
 
82
        serf__handle_kerb_auth,
 
83
        serf__setup_request_kerb_auth,
 
84
        serf__validate_response_kerb_auth,
 
85
    },
 
86
#endif
 
87
    /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */
 
88
 
 
89
    /* sentinel */
 
90
    { 0 }
 
91
};
 
92
 
 
93
 
 
94
/**
 
95
 * Baton passed to the response header callback function
 
96
 */
 
97
typedef struct {
 
98
    int code;
 
99
    apr_status_t status;
 
100
    const char *header;
 
101
    serf_request_t *request;
 
102
    serf_bucket_t *response;
 
103
    void *baton;
 
104
    apr_pool_t *pool;
 
105
    const serf__authn_scheme_t *scheme;
 
106
    const char *last_scheme_name;
 
107
} auth_baton_t;
 
108
 
 
109
/* Reads and discards all bytes in the response body. */
 
110
static apr_status_t discard_body(serf_bucket_t *response)
 
111
{
 
112
    apr_status_t status;
 
113
    const char *data;
 
114
    apr_size_t len;
 
115
 
 
116
    while (1) {
 
117
        status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len);
 
118
 
 
119
        if (status) {
 
120
            return status;
 
121
        }
 
122
 
 
123
        /* feed me */
 
124
    }
 
125
}
 
126
 
 
127
/**
 
128
 * handle_auth_header is called for each header in the response. It filters
 
129
 * out the Authenticate headers (WWW or Proxy depending on what's needed) and
 
130
 * tries to find a matching scheme handler.
 
131
 *
 
132
 * Returns a non-0 value of a matching handler was found.
 
133
 */
 
134
static int handle_auth_header(void *baton,
 
135
                              const char *key,
 
136
                              const char *header)
 
137
{
 
138
    auth_baton_t *ab = baton;
 
139
    int scheme_found = FALSE;
 
140
    const char *auth_name;
 
141
    const char *auth_attr;
 
142
    const serf__authn_scheme_t *scheme = NULL;
 
143
    serf_connection_t *conn = ab->request->conn;
 
144
    serf_context_t *ctx = conn->ctx;
 
145
 
 
146
    /* We're only interested in xxxx-Authenticate headers. */
 
147
    if (strcmp(key, ab->header) != 0)
 
148
        return 0;
 
149
 
 
150
    /* Extract the authentication scheme name, and prepare for reading
 
151
       the attributes.  */
 
152
    auth_attr = strchr(header, ' ');
 
153
    if (auth_attr) {
 
154
        auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header);
 
155
        ++auth_attr;
 
156
    }
 
157
    else
 
158
        auth_name = header;
 
159
 
 
160
    ab->last_scheme_name = auth_name;
 
161
 
 
162
    /* Find the matching authentication handler.
 
163
       Note that we don't reuse the auth scheme stored in the context,
 
164
       as that may have changed. (ex. fallback from ntlm to basic.) */
 
165
    for (scheme = serf_authn_schemes; scheme->code != 0; ++scheme) {
 
166
        if (ab->code == scheme->code &&
 
167
            strcmp(auth_name, scheme->name) == 0 &&
 
168
            ctx->authn_types & scheme->type) {
 
169
            serf__auth_handler_func_t handler = scheme->handle_func;
 
170
            apr_status_t status = 0;
 
171
 
 
172
            /* If this is the first time we use this scheme on this connection,
 
173
               make sure to initialize the authentication handler first. */
 
174
            if (ab->code == 401 && ctx->authn_info.scheme != scheme) {
 
175
                status = scheme->init_ctx_func(ab->code, ctx, ctx->pool);
 
176
                if (!status) {
 
177
                    status = scheme->init_conn_func(ab->code, conn, conn->pool);
 
178
 
 
179
                    if (!status)
 
180
                        ctx->authn_info.scheme = scheme;
 
181
                    else
 
182
                        ctx->authn_info.scheme = NULL;
 
183
                }
 
184
            }
 
185
            else if (ab->code == 407 && ctx->proxy_authn_info.scheme != scheme) {
 
186
                status = scheme->init_ctx_func(ab->code, ctx, ctx->pool);
 
187
                if (!status) {
 
188
                    status = scheme->init_conn_func(ab->code, conn, conn->pool);
 
189
 
 
190
                    if (!status)
 
191
                        ctx->proxy_authn_info.scheme = scheme;
 
192
                    else
 
193
                        ctx->proxy_authn_info.scheme = NULL;
 
194
                }
 
195
            }
 
196
 
 
197
            if (!status) {
 
198
                scheme_found = TRUE;
 
199
                ab->scheme = scheme;
 
200
                status = handler(ab->code, ab->request, ab->response,
 
201
                                 header, auth_attr, ab->baton, ctx->pool);
 
202
            }
 
203
 
 
204
            /* If the authentication fails, cache the error for now. Try the
 
205
               next available scheme. If there's none raise the error. */
 
206
            if (status) {
 
207
                scheme_found = FALSE;
 
208
                scheme = NULL;
 
209
                ab->status = status;
 
210
            }
 
211
 
 
212
            break;
 
213
        }
 
214
    }
 
215
 
 
216
    /* If a matching scheme handler was found, we can stop iterating
 
217
       over the response headers - so return a non-0 value. */
 
218
    return scheme_found;
 
219
}
 
220
 
 
221
/* Dispatch authentication handling. This function matches the possible
 
222
   authentication mechanisms with those available. Server and proxy
 
223
   authentication are evaluated separately. */
 
224
static apr_status_t dispatch_auth(int code,
 
225
                                  serf_request_t *request,
 
226
                                  serf_bucket_t *response,
 
227
                                  void *baton,
 
228
                                  apr_pool_t *pool)
 
229
{
 
230
    serf_bucket_t *hdrs;
 
231
 
 
232
    if (code == 401 || code == 407) {
 
233
        auth_baton_t ab = { 0 };
 
234
        const char *auth_hdr;
 
235
 
 
236
        ab.code = code;
 
237
        ab.status = APR_SUCCESS;
 
238
        ab.request = request;
 
239
        ab.response = response;
 
240
        ab.baton = baton;
 
241
        ab.pool = pool;
 
242
 
 
243
        /* Before iterating over all authn headers, check if there are any. */
 
244
        if (code == 401)
 
245
            ab.header = "WWW-Authenticate";
 
246
        else
 
247
            ab.header = "Proxy-Authenticate";
 
248
 
 
249
        hdrs = serf_bucket_response_get_headers(response);
 
250
        auth_hdr = serf_bucket_headers_get(hdrs, ab.header);
 
251
 
 
252
        if (!auth_hdr) {
 
253
            return SERF_ERROR_AUTHN_FAILED;
 
254
        }
 
255
 
 
256
        /* Iterate over all headers. Try to find a matching authentication scheme
 
257
           handler.
 
258
 
 
259
           Note: it is possible to have multiple Authentication: headers. We do
 
260
           not want to combine them (per normal header combination rules) as that
 
261
           would make it hard to parse. Instead, we want to individually parse
 
262
           and handle each header in the response, looking for one that we can
 
263
           work with.
 
264
        */
 
265
        serf_bucket_headers_do(hdrs,
 
266
                               handle_auth_header,
 
267
                               &ab);
 
268
        if (ab.status != APR_SUCCESS)
 
269
            return ab.status;
 
270
 
 
271
        if (!ab.scheme || ab.scheme->name == NULL) {
 
272
            /* No matching authentication found. */
 
273
            return SERF_ERROR_AUTHN_NOT_SUPPORTED;
 
274
        }
 
275
    } else {
 
276
        /* Validate the response authn headers if needed. */
 
277
 
 
278
    }
 
279
 
 
280
    return APR_SUCCESS;
 
281
}
 
282
 
 
283
/* Read the headers of the response and try the available
 
284
   handlers if authentication or validation is needed. */
 
285
apr_status_t serf__handle_auth_response(int *consumed_response,
 
286
                                        serf_request_t *request,
 
287
                                        serf_bucket_t *response,
 
288
                                        void *baton,
 
289
                                        apr_pool_t *pool)
 
290
{
 
291
    apr_status_t status;
 
292
    serf_status_line sl;
 
293
 
 
294
    *consumed_response = 0;
 
295
 
 
296
    status = serf_bucket_response_status(response, &sl);
 
297
    if (SERF_BUCKET_READ_ERROR(status)) {
 
298
        return status;
 
299
    }
 
300
    if (!sl.version && (APR_STATUS_IS_EOF(status) ||
 
301
                        APR_STATUS_IS_EAGAIN(status))) {
 
302
        return status;
 
303
    }
 
304
 
 
305
    status = serf_bucket_response_wait_for_headers(response);
 
306
    if (status) {
 
307
        if (!APR_STATUS_IS_EOF(status)) {
 
308
            return status;
 
309
        }
 
310
 
 
311
        /* If status is APR_EOF, there were no headers to read.
 
312
           This can be ok in some situations, and it definitely
 
313
           means there's no authentication requested now. */
 
314
        return APR_SUCCESS;
 
315
    }
 
316
 
 
317
    if (sl.code == 401 || sl.code == 407) {
 
318
        /* Authentication requested. */
 
319
 
 
320
        /* Don't bother handling the authentication request if the response
 
321
           wasn't received completely yet. Serf will call serf__handle_auth_response
 
322
           again when more data is received. */
 
323
        status = discard_body(response);
 
324
        *consumed_response = 1;
 
325
        
 
326
        /* Discard all response body before processing authentication. */
 
327
        if (!APR_STATUS_IS_EOF(status)) {
 
328
            return status;
 
329
        }
 
330
 
 
331
        status = dispatch_auth(sl.code, request, response, baton, pool);
 
332
        if (status != APR_SUCCESS) {
 
333
            return status;
 
334
        }
 
335
 
 
336
        /* Requeue the request with the necessary auth headers. */
 
337
        /* ### Application doesn't know about this request! */
 
338
        serf_connection_priority_request_create(request->conn,
 
339
                                                request->setup,
 
340
                                                request->setup_baton);
 
341
 
 
342
        return APR_EOF;
 
343
    }
 
344
 
 
345
    return APR_SUCCESS;
 
346
}
 
347
 
 
348
/**
 
349
 * base64 encode the authentication data and build an authentication
 
350
 * header in this format:
 
351
 * [SCHEME] [BASE64 of auth DATA]
 
352
 */
 
353
void serf__encode_auth_header(const char **header,
 
354
                              const char *scheme,
 
355
                              const char *data, apr_size_t data_len,
 
356
                              apr_pool_t *pool)
 
357
{
 
358
    apr_size_t encoded_len, scheme_len;
 
359
    char *ptr;
 
360
 
 
361
    encoded_len = apr_base64_encode_len(data_len);
 
362
    scheme_len = strlen(scheme);
 
363
 
 
364
    ptr = apr_palloc(pool, encoded_len + scheme_len + 1);
 
365
    *header = ptr;
 
366
 
 
367
    apr_cpystrn(ptr, scheme, scheme_len + 1);
 
368
    ptr += scheme_len;
 
369
    *ptr++ = ' ';
 
370
 
 
371
    apr_base64_encode(ptr, data, data_len);
 
372
}