auth_basic.c revision 262339
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/*** Basic authentication ***/
17
18#include <serf.h>
19#include <serf_private.h>
20#include <auth/auth.h>
21
22#include <apr.h>
23#include <apr_base64.h>
24#include <apr_strings.h>
25
26/* Stores the context information related to Basic authentication.
27   This information is stored in the per server cache in the serf context. */
28typedef struct basic_authn_info_t {
29    const char *header;
30    const char *value;
31} basic_authn_info_t;
32
33apr_status_t
34serf__handle_basic_auth(int code,
35                        serf_request_t *request,
36                        serf_bucket_t *response,
37                        const char *auth_hdr,
38                        const char *auth_attr,
39                        void *baton,
40                        apr_pool_t *pool)
41{
42    const char *tmp;
43    apr_size_t tmp_len;
44    serf_connection_t *conn = request->conn;
45    serf_context_t *ctx = conn->ctx;
46    serf__authn_info_t *authn_info;
47    basic_authn_info_t *basic_info;
48    apr_status_t status;
49    apr_pool_t *cred_pool;
50    char *username, *password, *realm_name;
51    const char *eq, *realm = NULL;
52
53    /* Can't do Basic authentication if there's no callback to get
54       username & password. */
55    if (!ctx->cred_cb) {
56        return SERF_ERROR_AUTHN_FAILED;
57    }
58
59    if (code == 401) {
60        authn_info = serf__get_authn_info_for_server(conn);
61    } else {
62        authn_info = &ctx->proxy_authn_info;
63    }
64    basic_info = authn_info->baton;
65
66    realm_name = NULL;
67    eq = strchr(auth_attr, '=');
68
69    if (eq && strncasecmp(auth_attr, "realm", 5) == 0) {
70        realm_name = apr_pstrdup(pool, eq + 1);
71        if (realm_name[0] == '\"') {
72            apr_size_t realm_len;
73
74            realm_len = strlen(realm_name);
75            if (realm_name[realm_len - 1] == '\"') {
76                realm_name[realm_len - 1] = '\0';
77                realm_name++;
78            }
79        }
80
81        if (!realm_name) {
82            return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
83        }
84
85        realm = serf__construct_realm(code == 401 ? HOST : PROXY,
86                                      conn, realm_name,
87                                      pool);
88    }
89
90    /* Ask the application for credentials */
91    apr_pool_create(&cred_pool, pool);
92    status = serf__provide_credentials(ctx,
93                                       &username, &password,
94                                       request, baton,
95                                       code, authn_info->scheme->name,
96                                       realm, cred_pool);
97    if (status) {
98        apr_pool_destroy(cred_pool);
99        return status;
100    }
101
102    tmp = apr_pstrcat(conn->pool, username, ":", password, NULL);
103    tmp_len = strlen(tmp);
104    apr_pool_destroy(cred_pool);
105
106    serf__encode_auth_header(&basic_info->value,
107                             authn_info->scheme->name,
108                             tmp, tmp_len, pool);
109    basic_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization";
110
111    return APR_SUCCESS;
112}
113
114apr_status_t
115serf__init_basic(int code,
116                 serf_context_t *ctx,
117                 apr_pool_t *pool)
118{
119    return APR_SUCCESS;
120}
121
122/* For Basic authentication we expect all authn info to be the same for all
123   connections in the context to the same server (same realm, username,
124   password). Therefore we can keep the header value in the per-server store
125   context instead of per connection.
126   TODO: we currently don't cache this info per realm, so each time a request
127   'switches realms', we have to ask the application for new credentials. */
128apr_status_t
129serf__init_basic_connection(const serf__authn_scheme_t *scheme,
130                            int code,
131                            serf_connection_t *conn,
132                            apr_pool_t *pool)
133{
134    serf_context_t *ctx = conn->ctx;
135    serf__authn_info_t *authn_info;
136
137    if (code == 401) {
138        authn_info = serf__get_authn_info_for_server(conn);
139    } else {
140        authn_info = &ctx->proxy_authn_info;
141    }
142
143    if (!authn_info->baton) {
144        authn_info->baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
145    }
146
147    return APR_SUCCESS;
148}
149
150apr_status_t
151serf__setup_request_basic_auth(peer_t peer,
152                               int code,
153                               serf_connection_t *conn,
154                               serf_request_t *request,
155                               const char *method,
156                               const char *uri,
157                               serf_bucket_t *hdrs_bkt)
158{
159    serf_context_t *ctx = conn->ctx;
160    serf__authn_info_t *authn_info;
161    basic_authn_info_t *basic_info;
162
163    if (peer == HOST) {
164        authn_info = serf__get_authn_info_for_server(conn);
165    } else {
166        authn_info = &ctx->proxy_authn_info;
167    }
168    basic_info = authn_info->baton;
169
170    if (basic_info && basic_info->header && basic_info->value) {
171        serf_bucket_headers_setn(hdrs_bkt, basic_info->header,
172                                 basic_info->value);
173        return APR_SUCCESS;
174    }
175
176    return SERF_ERROR_AUTHN_FAILED;
177}
178