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