1251877Speter/* Copyright 2009 Justin Erenkrantz and Greg Stein
2251877Speter *
3251877Speter * Licensed under the Apache License, Version 2.0 (the "License");
4251877Speter * you may not use this file except in compliance with the License.
5251877Speter * You may obtain a copy of the License at
6251877Speter *
7251877Speter *     http://www.apache.org/licenses/LICENSE-2.0
8251877Speter *
9251877Speter * Unless required by applicable law or agreed to in writing, software
10251877Speter * distributed under the License is distributed on an "AS IS" BASIS,
11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12251877Speter * See the License for the specific language governing permissions and
13251877Speter * limitations under the License.
14251877Speter */
15251877Speter
16251877Speter/*** Basic authentication ***/
17251877Speter
18251877Speter#include <serf.h>
19251877Speter#include <serf_private.h>
20251877Speter#include <auth/auth.h>
21251877Speter
22251877Speter#include <apr.h>
23251877Speter#include <apr_base64.h>
24251877Speter#include <apr_strings.h>
25251877Speter
26253895Speter/* Stores the context information related to Basic authentication.
27253895Speter   This information is stored in the per server cache in the serf context. */
28251877Spetertypedef struct basic_authn_info_t {
29251877Speter    const char *header;
30251877Speter    const char *value;
31251877Speter} basic_authn_info_t;
32251877Speter
33251877Speterapr_status_t
34251877Speterserf__handle_basic_auth(int code,
35251877Speter                        serf_request_t *request,
36251877Speter                        serf_bucket_t *response,
37251877Speter                        const char *auth_hdr,
38251877Speter                        const char *auth_attr,
39251877Speter                        void *baton,
40251877Speter                        apr_pool_t *pool)
41251877Speter{
42251877Speter    const char *tmp;
43251877Speter    apr_size_t tmp_len;
44251877Speter    serf_connection_t *conn = request->conn;
45251877Speter    serf_context_t *ctx = conn->ctx;
46253895Speter    serf__authn_info_t *authn_info;
47253895Speter    basic_authn_info_t *basic_info;
48251877Speter    apr_status_t status;
49251877Speter    apr_pool_t *cred_pool;
50253895Speter    char *username, *password, *realm_name;
51262339Speter    const char *eq, *realm = NULL;
52251877Speter
53251877Speter    /* Can't do Basic authentication if there's no callback to get
54251877Speter       username & password. */
55251877Speter    if (!ctx->cred_cb) {
56251877Speter        return SERF_ERROR_AUTHN_FAILED;
57251877Speter    }
58251877Speter
59253895Speter    if (code == 401) {
60253895Speter        authn_info = serf__get_authn_info_for_server(conn);
61253895Speter    } else {
62253895Speter        authn_info = &ctx->proxy_authn_info;
63253895Speter    }
64253895Speter    basic_info = authn_info->baton;
65251877Speter
66253895Speter    realm_name = NULL;
67253895Speter    eq = strchr(auth_attr, '=');
68251877Speter
69253895Speter    if (eq && strncasecmp(auth_attr, "realm", 5) == 0) {
70253895Speter        realm_name = apr_pstrdup(pool, eq + 1);
71253895Speter        if (realm_name[0] == '\"') {
72253895Speter            apr_size_t realm_len;
73253895Speter
74253895Speter            realm_len = strlen(realm_name);
75253895Speter            if (realm_name[realm_len - 1] == '\"') {
76253895Speter                realm_name[realm_len - 1] = '\0';
77253895Speter                realm_name++;
78251877Speter            }
79251877Speter        }
80251877Speter
81251877Speter        if (!realm_name) {
82251877Speter            return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
83251877Speter        }
84251877Speter
85253895Speter        realm = serf__construct_realm(code == 401 ? HOST : PROXY,
86253895Speter                                      conn, realm_name,
87253895Speter                                      pool);
88251877Speter    }
89251877Speter
90251877Speter    /* Ask the application for credentials */
91251877Speter    apr_pool_create(&cred_pool, pool);
92253895Speter    status = serf__provide_credentials(ctx,
93253895Speter                                       &username, &password,
94253895Speter                                       request, baton,
95253895Speter                                       code, authn_info->scheme->name,
96253895Speter                                       realm, cred_pool);
97251877Speter    if (status) {
98251877Speter        apr_pool_destroy(cred_pool);
99251877Speter        return status;
100251877Speter    }
101251877Speter
102251877Speter    tmp = apr_pstrcat(conn->pool, username, ":", password, NULL);
103251877Speter    tmp_len = strlen(tmp);
104251877Speter    apr_pool_destroy(cred_pool);
105251877Speter
106251877Speter    serf__encode_auth_header(&basic_info->value,
107251877Speter                             authn_info->scheme->name,
108251877Speter                             tmp, tmp_len, pool);
109251877Speter    basic_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization";
110251877Speter
111251877Speter    return APR_SUCCESS;
112251877Speter}
113251877Speter
114251877Speterapr_status_t
115251877Speterserf__init_basic(int code,
116251877Speter                 serf_context_t *ctx,
117251877Speter                 apr_pool_t *pool)
118251877Speter{
119251877Speter    return APR_SUCCESS;
120251877Speter}
121251877Speter
122253895Speter/* For Basic authentication we expect all authn info to be the same for all
123253895Speter   connections in the context to the same server (same realm, username,
124253895Speter   password). Therefore we can keep the header value in the per-server store
125253895Speter   context instead of per connection.
126253895Speter   TODO: we currently don't cache this info per realm, so each time a request
127253895Speter   'switches realms', we have to ask the application for new credentials. */
128251877Speterapr_status_t
129253895Speterserf__init_basic_connection(const serf__authn_scheme_t *scheme,
130253895Speter                            int code,
131251877Speter                            serf_connection_t *conn,
132251877Speter                            apr_pool_t *pool)
133251877Speter{
134253895Speter    serf_context_t *ctx = conn->ctx;
135253895Speter    serf__authn_info_t *authn_info;
136253895Speter
137253895Speter    if (code == 401) {
138253895Speter        authn_info = serf__get_authn_info_for_server(conn);
139253895Speter    } else {
140253895Speter        authn_info = &ctx->proxy_authn_info;
141253895Speter    }
142253895Speter
143253895Speter    if (!authn_info->baton) {
144253895Speter        authn_info->baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
145253895Speter    }
146253895Speter
147251877Speter    return APR_SUCCESS;
148251877Speter}
149251877Speter
150251877Speterapr_status_t
151251877Speterserf__setup_request_basic_auth(peer_t peer,
152251877Speter                               int code,
153251877Speter                               serf_connection_t *conn,
154251877Speter                               serf_request_t *request,
155251877Speter                               const char *method,
156251877Speter                               const char *uri,
157251877Speter                               serf_bucket_t *hdrs_bkt)
158251877Speter{
159251877Speter    serf_context_t *ctx = conn->ctx;
160253895Speter    serf__authn_info_t *authn_info;
161253895Speter    basic_authn_info_t *basic_info;
162251877Speter
163251877Speter    if (peer == HOST) {
164253895Speter        authn_info = serf__get_authn_info_for_server(conn);
165251877Speter    } else {
166253895Speter        authn_info = &ctx->proxy_authn_info;
167251877Speter    }
168253895Speter    basic_info = authn_info->baton;
169251877Speter
170253895Speter    if (basic_info && basic_info->header && basic_info->value) {
171253895Speter        serf_bucket_headers_setn(hdrs_bkt, basic_info->header,
172253895Speter                                 basic_info->value);
173251877Speter        return APR_SUCCESS;
174251877Speter    }
175251877Speter
176251877Speter    return SERF_ERROR_AUTHN_FAILED;
177251877Speter}
178