auth_basic.c revision 362181
1/* ====================================================================
2 *    Licensed to the Apache Software Foundation (ASF) under one
3 *    or more contributor license agreements.  See the NOTICE file
4 *    distributed with this work for additional information
5 *    regarding copyright ownership.  The ASF licenses this file
6 *    to you under the Apache License, Version 2.0 (the
7 *    "License"); you may not use this file except in compliance
8 *    with the License.  You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *    Unless required by applicable law or agreed to in writing,
13 *    software distributed under the License is distributed on an
14 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 *    KIND, either express or implied.  See the License for the
16 *    specific language governing permissions and limitations
17 *    under the License.
18 * ====================================================================
19 */
20
21/*** Basic authentication ***/
22
23#include <serf.h>
24#include <serf_private.h>
25#include <auth/auth.h>
26
27#include <apr.h>
28#include <apr_base64.h>
29#include <apr_strings.h>
30
31/* Stores the context information related to Basic authentication.
32   This information is stored in the per server cache in the serf context. */
33typedef struct basic_authn_info_t {
34    const char *header;
35    const char *value;
36} basic_authn_info_t;
37
38apr_status_t
39serf__handle_basic_auth(int code,
40                        serf_request_t *request,
41                        serf_bucket_t *response,
42                        const char *auth_hdr,
43                        const char *auth_attr,
44                        void *baton,
45                        apr_pool_t *pool)
46{
47    const char *tmp;
48    apr_size_t tmp_len;
49    serf_connection_t *conn = request->conn;
50    serf_context_t *ctx = conn->ctx;
51    serf__authn_info_t *authn_info;
52    basic_authn_info_t *basic_info;
53    apr_status_t status;
54    apr_pool_t *cred_pool;
55    char *username, *password, *realm_name;
56    const char *eq, *realm = NULL;
57
58    /* Can't do Basic authentication if there's no callback to get
59       username & password. */
60    if (!ctx->cred_cb) {
61        return SERF_ERROR_AUTHN_FAILED;
62    }
63
64    if (code == 401) {
65        authn_info = serf__get_authn_info_for_server(conn);
66    } else {
67        authn_info = &ctx->proxy_authn_info;
68    }
69    basic_info = authn_info->baton;
70
71    realm_name = NULL;
72    eq = strchr(auth_attr, '=');
73
74    if (eq && strncasecmp(auth_attr, "realm", 5) == 0) {
75        realm_name = apr_pstrdup(pool, eq + 1);
76        if (realm_name[0] == '\"') {
77            apr_size_t realm_len;
78
79            realm_len = strlen(realm_name);
80            if (realm_name[realm_len - 1] == '\"') {
81                realm_name[realm_len - 1] = '\0';
82                realm_name++;
83            }
84        }
85
86        if (!realm_name) {
87            return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
88        }
89
90        realm = serf__construct_realm(code == 401 ? HOST : PROXY,
91                                      conn, realm_name,
92                                      pool);
93    }
94
95    /* Ask the application for credentials */
96    apr_pool_create(&cred_pool, pool);
97    status = serf__provide_credentials(ctx,
98                                       &username, &password,
99                                       request, baton,
100                                       code, authn_info->scheme->name,
101                                       realm, cred_pool);
102    if (status) {
103        apr_pool_destroy(cred_pool);
104        return status;
105    }
106
107    tmp = apr_pstrcat(conn->pool, username, ":", password, NULL);
108    tmp_len = strlen(tmp);
109    apr_pool_destroy(cred_pool);
110
111    serf__encode_auth_header(&basic_info->value,
112                             authn_info->scheme->name,
113                             tmp, tmp_len, pool);
114    basic_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization";
115
116    return APR_SUCCESS;
117}
118
119apr_status_t
120serf__init_basic(int code,
121                 serf_context_t *ctx,
122                 apr_pool_t *pool)
123{
124    return APR_SUCCESS;
125}
126
127/* For Basic authentication we expect all authn info to be the same for all
128   connections in the context to the same server (same realm, username,
129   password). Therefore we can keep the header value in the per-server store
130   context instead of per connection.
131   TODO: we currently don't cache this info per realm, so each time a request
132   'switches realms', we have to ask the application for new credentials. */
133apr_status_t
134serf__init_basic_connection(const serf__authn_scheme_t *scheme,
135                            int code,
136                            serf_connection_t *conn,
137                            apr_pool_t *pool)
138{
139    serf_context_t *ctx = conn->ctx;
140    serf__authn_info_t *authn_info;
141
142    if (code == 401) {
143        authn_info = serf__get_authn_info_for_server(conn);
144    } else {
145        authn_info = &ctx->proxy_authn_info;
146    }
147
148    if (!authn_info->baton) {
149        authn_info->baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
150    }
151
152    return APR_SUCCESS;
153}
154
155apr_status_t
156serf__setup_request_basic_auth(peer_t peer,
157                               int code,
158                               serf_connection_t *conn,
159                               serf_request_t *request,
160                               const char *method,
161                               const char *uri,
162                               serf_bucket_t *hdrs_bkt)
163{
164    serf_context_t *ctx = conn->ctx;
165    serf__authn_info_t *authn_info;
166    basic_authn_info_t *basic_info;
167
168    if (peer == HOST) {
169        authn_info = serf__get_authn_info_for_server(conn);
170    } else {
171        authn_info = &ctx->proxy_authn_info;
172    }
173    basic_info = authn_info->baton;
174
175    if (basic_info && basic_info->header && basic_info->value) {
176        serf_bucket_headers_setn(hdrs_bkt, basic_info->header,
177                                 basic_info->value);
178        return APR_SUCCESS;
179    }
180
181    return SERF_ERROR_AUTHN_FAILED;
182}
183