~ubuntu-branches/ubuntu/trusty/serf/trusty-updates

« back to all changes in this revision

Viewing changes to auth/auth.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2013-12-31 13:17:16 UTC
  • mfrom: (1.1.8) (3.3.2 sid)
  • Revision ID: package-import@ubuntu.com-20131231131716-s862wc4uwdzxmrr1
Tags: 1.3.3-1
* Add myself to Uploaders.
* Change the watch file to better handle code.google.com.
* New upstream release.  (Closes: #716793)
  + Refresh patches/libtool
  + Update symbols
* Adapt packaging for the upstream switch to SCons.
  + control: + scons, - autotools-dev, autoconf
  + rules: Change configure/make calls to scons
* Rename libserf1 to libserf-1-1, following standard naming conventions.
* Enable hardening flags.
* Strip unnecessary RPATH from libserf.
* Honor DEB_BUILD_OPTIONS=parallel=X

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
#include <apr.h>
21
21
#include <apr_base64.h>
22
22
#include <apr_strings.h>
 
23
#include <apr_lib.h>
23
24
 
24
25
static apr_status_t
25
 
default_auth_response_handler(int code,
 
26
default_auth_response_handler(const serf__authn_scheme_t *scheme,
 
27
                              peer_t peer,
 
28
                              int code,
26
29
                              serf_connection_t *conn,
27
30
                              serf_request_t *request,
28
31
                              serf_bucket_t *response,
31
34
    return APR_SUCCESS;
32
35
}
33
36
 
 
37
/* These authentication schemes are in order of decreasing security, the topmost
 
38
   scheme will be used first when the server supports it.
 
39
 
 
40
   Each set of handlers should support both server (401) and proxy (407)
 
41
   authentication.
 
42
 
 
43
   Use lower case for the scheme names to enable case insensitive matching.
 
44
 */
34
45
static const serf__authn_scheme_t serf_authn_schemes[] = {
35
 
    {
36
 
        401,
 
46
#ifdef SERF_HAVE_SPNEGO
 
47
    {
 
48
        "Negotiate",
 
49
        "negotiate",
 
50
        SERF_AUTHN_NEGOTIATE,
 
51
        serf__init_spnego,
 
52
        serf__init_spnego_connection,
 
53
        serf__handle_spnego_auth,
 
54
        serf__setup_request_spnego_auth,
 
55
        serf__validate_response_spnego_auth,
 
56
    },
 
57
#ifdef WIN32
 
58
    {
 
59
        "NTLM",
 
60
        "ntlm",
 
61
        SERF_AUTHN_NTLM,
 
62
        serf__init_spnego,
 
63
        serf__init_spnego_connection,
 
64
        serf__handle_spnego_auth,
 
65
        serf__setup_request_spnego_auth,
 
66
        serf__validate_response_spnego_auth,
 
67
    },
 
68
#endif /* #ifdef WIN32 */
 
69
#endif /* SERF_HAVE_SPNEGO */
 
70
    {
 
71
        "Digest",
 
72
        "digest",
 
73
        SERF_AUTHN_DIGEST,
 
74
        serf__init_digest,
 
75
        serf__init_digest_connection,
 
76
        serf__handle_digest_auth,
 
77
        serf__setup_request_digest_auth,
 
78
        serf__validate_response_digest_auth,
 
79
    },
 
80
    {
37
81
        "Basic",
 
82
        "basic",
38
83
        SERF_AUTHN_BASIC,
39
84
        serf__init_basic,
40
85
        serf__init_basic_connection,
42
87
        serf__setup_request_basic_auth,
43
88
        default_auth_response_handler,
44
89
    },
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
90
    /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */
88
91
 
89
92
    /* sentinel */
91
94
};
92
95
 
93
96
 
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
97
/* Reads and discards all bytes in the response body. */
110
98
static apr_status_t discard_body(serf_bucket_t *response)
111
99
{
131
119
 *
132
120
 * Returns a non-0 value of a matching handler was found.
133
121
 */
134
 
static int handle_auth_header(void *baton,
135
 
                              const char *key,
136
 
                              const char *header)
 
122
static int handle_auth_headers(int code,
 
123
                               void *baton,
 
124
                               apr_hash_t *hdrs,
 
125
                               serf_request_t *request,
 
126
                               serf_bucket_t *response,
 
127
                               apr_pool_t *pool)
 
128
{
 
129
    const serf__authn_scheme_t *scheme;
 
130
    serf_connection_t *conn = request->conn;
 
131
    serf_context_t *ctx = conn->ctx;
 
132
    apr_status_t status;
 
133
 
 
134
    status = SERF_ERROR_AUTHN_NOT_SUPPORTED;
 
135
 
 
136
    /* Find the matching authentication handler.
 
137
       Note that we don't reuse the auth scheme stored in the context,
 
138
       as that may have changed. (ex. fallback from ntlm to basic.) */
 
139
    for (scheme = serf_authn_schemes; scheme->name != 0; ++scheme) {
 
140
        const char *auth_hdr;
 
141
        serf__auth_handler_func_t handler;
 
142
        serf__authn_info_t *authn_info;
 
143
 
 
144
        if (! (ctx->authn_types & scheme->type))
 
145
            continue;
 
146
 
 
147
        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
 
148
                      "Client supports: %s\n", scheme->name);
 
149
 
 
150
        auth_hdr = apr_hash_get(hdrs, scheme->key, APR_HASH_KEY_STRING);
 
151
 
 
152
        if (!auth_hdr)
 
153
            continue;
 
154
 
 
155
        if (code == 401) {
 
156
            authn_info = serf__get_authn_info_for_server(conn);
 
157
        } else {
 
158
            authn_info = &ctx->proxy_authn_info;
 
159
        }
 
160
 
 
161
        if (authn_info->failed_authn_types & scheme->type) {
 
162
            /* Skip this authn type since we already tried it before. */
 
163
            continue;
 
164
        }
 
165
 
 
166
        /* Found a matching scheme */
 
167
        status = APR_SUCCESS;
 
168
 
 
169
        handler = scheme->handle_func;
 
170
 
 
171
        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
 
172
                      "... matched: %s\n", scheme->name);
 
173
 
 
174
        /* If this is the first time we use this scheme on this context and/or
 
175
           this connection, make sure to initialize the authentication handler 
 
176
           first. */
 
177
        if (authn_info->scheme != scheme) {
 
178
            status = scheme->init_ctx_func(code, ctx, ctx->pool);
 
179
            if (!status) {
 
180
                status = scheme->init_conn_func(scheme, code, conn,
 
181
                                                conn->pool);
 
182
                if (!status)
 
183
                    authn_info->scheme = scheme;
 
184
                else
 
185
                    authn_info->scheme = NULL;
 
186
            }
 
187
        }
 
188
 
 
189
        if (!status) {
 
190
            const char *auth_attr = strchr(auth_hdr, ' ');
 
191
            if (auth_attr) {
 
192
                auth_attr++;
 
193
            }
 
194
 
 
195
            status = handler(code, request, response,
 
196
                             auth_hdr, auth_attr, baton, ctx->pool);
 
197
        }
 
198
 
 
199
        if (status == APR_SUCCESS)
 
200
            break;
 
201
 
 
202
        /* No success authenticating with this scheme, try the next.
 
203
           If no more authn schemes are found the status of this scheme will be
 
204
           returned.
 
205
        */
 
206
        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
 
207
                      "%s authentication failed.\n", scheme->name);
 
208
 
 
209
        /* Clear per-request auth_baton when switching to next auth scheme. */
 
210
        request->auth_baton = NULL;
 
211
 
 
212
        /* Remember failed auth types to skip in future. */
 
213
        authn_info->failed_authn_types |= scheme->type;
 
214
    }
 
215
 
 
216
    return status;
 
217
}
 
218
 
 
219
/**
 
220
 * Baton passed to the store_header_in_dict callback function
 
221
 */
 
222
typedef struct {
 
223
    const char *header;
 
224
    apr_pool_t *pool;
 
225
    apr_hash_t *hdrs;
 
226
} auth_baton_t;
 
227
 
 
228
static int store_header_in_dict(void *baton,
 
229
                                const char *key,
 
230
                                const char *header)
137
231
{
138
232
    auth_baton_t *ab = baton;
139
 
    int scheme_found = FALSE;
140
 
    const char *auth_name;
141
233
    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;
 
234
    char *auth_name, *c;
145
235
 
146
236
    /* We're only interested in xxxx-Authenticate headers. */
147
 
    if (strcmp(key, ab->header) != 0)
 
237
    if (strcasecmp(key, ab->header) != 0)
148
238
        return 0;
149
239
 
150
 
    /* Extract the authentication scheme name, and prepare for reading
151
 
       the attributes.  */
 
240
    /* Extract the authentication scheme name.  */
152
241
    auth_attr = strchr(header, ' ');
153
242
    if (auth_attr) {
154
243
        auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header);
155
 
        ++auth_attr;
156
244
    }
157
245
    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
 
            }
210
 
            /* Let the caller now if the authentication setup was succesful
211
 
               or not. */
212
 
            ab->status = status;
213
 
 
214
 
            break;
215
 
        }
216
 
    }
217
 
 
218
 
    /* If a matching scheme handler was found, we can stop iterating
219
 
       over the response headers - so return a non-0 value. */
220
 
    return scheme_found;
 
246
        auth_name = apr_pstrmemdup(ab->pool, header, strlen(header));
 
247
 
 
248
    /* Convert scheme name to lower case to enable case insensitive matching. */
 
249
    for (c = auth_name; *c != '\0'; c++)
 
250
        *c = (char)apr_tolower(*c);
 
251
 
 
252
    apr_hash_set(ab->hdrs, auth_name, APR_HASH_KEY_STRING,
 
253
                 apr_pstrdup(ab->pool, header));
 
254
 
 
255
    return 0;
221
256
}
222
257
 
223
258
/* Dispatch authentication handling. This function matches the possible
235
270
        auth_baton_t ab = { 0 };
236
271
        const char *auth_hdr;
237
272
 
238
 
        ab.code = code;
239
 
        ab.status = APR_SUCCESS;
240
 
        ab.request = request;
241
 
        ab.response = response;
242
 
        ab.baton = baton;
 
273
        ab.hdrs = apr_hash_make(pool);
243
274
        ab.pool = pool;
244
275
 
245
276
        /* Before iterating over all authn headers, check if there are any. */
254
285
        if (!auth_hdr) {
255
286
            return SERF_ERROR_AUTHN_FAILED;
256
287
        }
257
 
 
258
 
        /* Iterate over all headers. Try to find a matching authentication scheme
259
 
           handler.
 
288
        serf__log_skt(AUTH_VERBOSE, __FILE__, request->conn->skt,
 
289
                      "%s authz required. Response header(s): %s\n",
 
290
                      code == 401 ? "Server" : "Proxy", auth_hdr);
 
291
 
 
292
 
 
293
        /* Store all WWW- or Proxy-Authenticate headers in a dictionary.
260
294
 
261
295
           Note: it is possible to have multiple Authentication: headers. We do
262
296
           not want to combine them (per normal header combination rules) as that
265
299
           work with.
266
300
        */
267
301
        serf_bucket_headers_do(hdrs,
268
 
                               handle_auth_header,
 
302
                               store_header_in_dict,
269
303
                               &ab);
270
 
        if (ab.status != APR_SUCCESS)
271
 
            return ab.status;
272
 
 
273
 
        if (!ab.scheme || ab.scheme->name == NULL) {
274
 
            /* No matching authentication found. */
275
 
            return SERF_ERROR_AUTHN_NOT_SUPPORTED;
276
 
        }
277
 
    } else {
278
 
        /* Validate the response authn headers if needed. */
279
 
 
 
304
 
 
305
        /* Iterate over all authentication schemes, in order of decreasing
 
306
           security. Try to find a authentication schema the server support. */
 
307
        return handle_auth_headers(code, baton, ab.hdrs,
 
308
                                   request, response, pool);
280
309
    }
281
310
 
282
311
    return APR_SUCCESS;
295
324
 
296
325
    *consumed_response = 0;
297
326
 
 
327
    /* TODO: the response bucket was created by the application, not at all
 
328
       guaranteed that this is of type response_bucket!! */
298
329
    status = serf_bucket_response_status(response, &sl);
299
330
    if (SERF_BUCKET_READ_ERROR(status)) {
300
331
        return status;
337
368
 
338
369
        /* Requeue the request with the necessary auth headers. */
339
370
        /* ### Application doesn't know about this request! */
340
 
        serf_connection_priority_request_create(request->conn,
341
 
                                                request->setup,
342
 
                                                request->setup_baton);
 
371
        if (request->ssltunnel) {
 
372
            serf__ssltunnel_request_create(request->conn,
 
373
                                           request->setup,
 
374
                                           request->setup_baton);
 
375
        } else {
 
376
            serf_connection_priority_request_create(request->conn,
 
377
                                                    request->setup,
 
378
                                                    request->setup_baton);
 
379
        }
343
380
 
344
381
        return APR_EOF;
 
382
    } else {
 
383
        serf__validate_response_func_t validate_resp;
 
384
        serf_connection_t *conn = request->conn;
 
385
        serf_context_t *ctx = conn->ctx;
 
386
        serf__authn_info_t *authn_info;
 
387
        apr_status_t resp_status = APR_SUCCESS;
 
388
 
 
389
 
 
390
        /* Validate the response server authn headers. */
 
391
        authn_info = serf__get_authn_info_for_server(conn);
 
392
        if (authn_info->scheme) {
 
393
            validate_resp = authn_info->scheme->validate_response_func;
 
394
            resp_status = validate_resp(authn_info->scheme, HOST, sl.code,
 
395
                                        conn, request, response, pool);
 
396
        }
 
397
 
 
398
        /* Validate the response proxy authn headers. */
 
399
        authn_info = &ctx->proxy_authn_info;
 
400
        if (!resp_status && authn_info->scheme) {
 
401
            validate_resp = authn_info->scheme->validate_response_func;
 
402
            resp_status = validate_resp(authn_info->scheme, PROXY, sl.code,
 
403
                                        conn, request, response, pool);
 
404
        }
 
405
 
 
406
        if (resp_status) {
 
407
            /* If there was an error in the final step of the authentication,
 
408
               consider the reponse body as invalid and discard it. */
 
409
            status = discard_body(response);
 
410
            *consumed_response = 1;
 
411
            if (!APR_STATUS_IS_EOF(status)) {
 
412
                return status;
 
413
            }
 
414
            /* The whole body was discarded, now return our error. */
 
415
            return resp_status;
 
416
        }
345
417
    }
346
418
 
347
419
    return APR_SUCCESS;
372
444
 
373
445
    apr_base64_encode(ptr, data, data_len);
374
446
}
 
447
 
 
448
const char *serf__construct_realm(peer_t peer,
 
449
                                  serf_connection_t *conn,
 
450
                                  const char *realm_name,
 
451
                                  apr_pool_t *pool)
 
452
{
 
453
    if (peer == HOST) {
 
454
        return apr_psprintf(pool, "<%s://%s:%d> %s",
 
455
                            conn->host_info.scheme,
 
456
                            conn->host_info.hostname,
 
457
                            conn->host_info.port,
 
458
                            realm_name);
 
459
    } else {
 
460
        serf_context_t *ctx = conn->ctx;
 
461
 
 
462
        return apr_psprintf(pool, "<http://%s:%d> %s",
 
463
                            ctx->proxy_address->hostname,
 
464
                            ctx->proxy_address->port,
 
465
                            realm_name);
 
466
    }
 
467
}
 
468
 
 
469
serf__authn_info_t *serf__get_authn_info_for_server(serf_connection_t *conn)
 
470
{
 
471
    serf_context_t *ctx = conn->ctx;
 
472
    serf__authn_info_t *authn_info;
 
473
 
 
474
    authn_info = apr_hash_get(ctx->server_authn_info, conn->host_url,
 
475
                              APR_HASH_KEY_STRING);
 
476
 
 
477
    if (!authn_info) {
 
478
        authn_info = apr_pcalloc(ctx->pool, sizeof(serf__authn_info_t));
 
479
        apr_hash_set(ctx->server_authn_info,
 
480
                     apr_pstrdup(ctx->pool, conn->host_url),
 
481
                     APR_HASH_KEY_STRING, authn_info);
 
482
    }
 
483
 
 
484
    return authn_info;
 
485
}