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. */
33 typedef struct basic_authn_info_t {
34 const char *header;
35 const char *value;
36 } basic_authn_info_t;
37
38 apr_status_t
serf__handle_basic_auth(int code,serf_request_t * request,serf_bucket_t * response,const char * auth_hdr,const char * auth_attr,void * baton,apr_pool_t * pool)39 serf__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
119 apr_status_t
serf__init_basic(int code,serf_context_t * ctx,apr_pool_t * pool)120 serf__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. */
133 apr_status_t
serf__init_basic_connection(const serf__authn_scheme_t * scheme,int code,serf_connection_t * conn,apr_pool_t * pool)134 serf__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
155 apr_status_t
serf__setup_request_basic_auth(peer_t peer,int code,serf_connection_t * conn,serf_request_t * request,const char * method,const char * uri,serf_bucket_t * hdrs_bkt)156 serf__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