Blame auth/auth_basic.c

Packit 3adb1e
/* ====================================================================
Packit 3adb1e
 *    Licensed to the Apache Software Foundation (ASF) under one
Packit 3adb1e
 *    or more contributor license agreements.  See the NOTICE file
Packit 3adb1e
 *    distributed with this work for additional information
Packit 3adb1e
 *    regarding copyright ownership.  The ASF licenses this file
Packit 3adb1e
 *    to you under the Apache License, Version 2.0 (the
Packit 3adb1e
 *    "License"); you may not use this file except in compliance
Packit 3adb1e
 *    with the License.  You may obtain a copy of the License at
Packit 3adb1e
 *
Packit 3adb1e
 *      http://www.apache.org/licenses/LICENSE-2.0
Packit 3adb1e
 *
Packit 3adb1e
 *    Unless required by applicable law or agreed to in writing,
Packit 3adb1e
 *    software distributed under the License is distributed on an
Packit 3adb1e
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
Packit 3adb1e
 *    KIND, either express or implied.  See the License for the
Packit 3adb1e
 *    specific language governing permissions and limitations
Packit 3adb1e
 *    under the License.
Packit 3adb1e
 * ====================================================================
Packit 3adb1e
 */
Packit 3adb1e
Packit 3adb1e
/*** Basic authentication ***/
Packit 3adb1e
Packit 3adb1e
#include <serf.h>
Packit 3adb1e
#include <serf_private.h>
Packit 3adb1e
#include <auth/auth.h>
Packit 3adb1e
Packit 3adb1e
#include <apr.h>
Packit 3adb1e
#include <apr_base64.h>
Packit 3adb1e
#include <apr_strings.h>
Packit 3adb1e
Packit 3adb1e
/* Stores the context information related to Basic authentication.
Packit 3adb1e
   This information is stored in the per server cache in the serf context. */
Packit 3adb1e
typedef struct basic_authn_info_t {
Packit 3adb1e
    const char *header;
Packit 3adb1e
    const char *value;
Packit 3adb1e
} basic_authn_info_t;
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__handle_basic_auth(int code,
Packit 3adb1e
                        serf_request_t *request,
Packit 3adb1e
                        serf_bucket_t *response,
Packit 3adb1e
                        const char *auth_hdr,
Packit 3adb1e
                        const char *auth_attr,
Packit 3adb1e
                        void *baton,
Packit 3adb1e
                        apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    const char *tmp;
Packit 3adb1e
    apr_size_t tmp_len;
Packit 3adb1e
    serf_connection_t *conn = request->conn;
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    serf__authn_info_t *authn_info;
Packit 3adb1e
    basic_authn_info_t *basic_info;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    apr_pool_t *cred_pool;
Packit 3adb1e
    char *username, *password, *realm_name;
Packit 3adb1e
    const char *eq, *realm = NULL;
Packit 3adb1e
Packit 3adb1e
    /* Can't do Basic authentication if there's no callback to get
Packit 3adb1e
       username & password. */
Packit 3adb1e
    if (!ctx->cred_cb) {
Packit 3adb1e
        return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (code == 401) {
Packit 3adb1e
        authn_info = serf__get_authn_info_for_server(conn);
Packit 3adb1e
    } else {
Packit 3adb1e
        authn_info = &ctx->proxy_authn_info;
Packit 3adb1e
    }
Packit 3adb1e
    basic_info = authn_info->baton;
Packit 3adb1e
Packit 3adb1e
    realm_name = NULL;
Packit 3adb1e
    eq = strchr(auth_attr, '=');
Packit 3adb1e
Packit 3adb1e
    if (eq && strncasecmp(auth_attr, "realm", 5) == 0) {
Packit 3adb1e
        realm_name = apr_pstrdup(pool, eq + 1);
Packit 3adb1e
        if (realm_name[0] == '\"') {
Packit 3adb1e
            apr_size_t realm_len;
Packit 3adb1e
Packit 3adb1e
            realm_len = strlen(realm_name);
Packit 3adb1e
            if (realm_name[realm_len - 1] == '\"') {
Packit 3adb1e
                realm_name[realm_len - 1] = '\0';
Packit 3adb1e
                realm_name++;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (!realm_name) {
Packit 3adb1e
            return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        realm = serf__construct_realm(code == 401 ? HOST : PROXY,
Packit 3adb1e
                                      conn, realm_name,
Packit 3adb1e
                                      pool);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Ask the application for credentials */
Packit 3adb1e
    apr_pool_create(&cred_pool, pool);
Packit 3adb1e
    status = serf__provide_credentials(ctx,
Packit 3adb1e
                                       &username, &password,
Packit 3adb1e
                                       request, baton,
Packit 3adb1e
                                       code, authn_info->scheme->name,
Packit 3adb1e
                                       realm, cred_pool);
Packit 3adb1e
    if (status) {
Packit 3adb1e
        apr_pool_destroy(cred_pool);
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    tmp = apr_pstrcat(conn->pool, username, ":", password, NULL);
Packit 3adb1e
    tmp_len = strlen(tmp);
Packit 3adb1e
    apr_pool_destroy(cred_pool);
Packit 3adb1e
Packit 3adb1e
    serf__encode_auth_header(&basic_info->value,
Packit 3adb1e
                             authn_info->scheme->name,
Packit 3adb1e
                             tmp, tmp_len, pool);
Packit 3adb1e
    basic_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization";
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__init_basic(int code,
Packit 3adb1e
                 serf_context_t *ctx,
Packit 3adb1e
                 apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* For Basic authentication we expect all authn info to be the same for all
Packit 3adb1e
   connections in the context to the same server (same realm, username,
Packit 3adb1e
   password). Therefore we can keep the header value in the per-server store
Packit 3adb1e
   context instead of per connection.
Packit 3adb1e
   TODO: we currently don't cache this info per realm, so each time a request
Packit 3adb1e
   'switches realms', we have to ask the application for new credentials. */
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__init_basic_connection(const serf__authn_scheme_t *scheme,
Packit 3adb1e
                            int code,
Packit 3adb1e
                            serf_connection_t *conn,
Packit 3adb1e
                            apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    serf__authn_info_t *authn_info;
Packit 3adb1e
Packit 3adb1e
    if (code == 401) {
Packit 3adb1e
        authn_info = serf__get_authn_info_for_server(conn);
Packit 3adb1e
    } else {
Packit 3adb1e
        authn_info = &ctx->proxy_authn_info;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (!authn_info->baton) {
Packit 3adb1e
        authn_info->baton = apr_pcalloc(pool, sizeof(basic_authn_info_t));
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__setup_request_basic_auth(peer_t peer,
Packit 3adb1e
                               int code,
Packit 3adb1e
                               serf_connection_t *conn,
Packit 3adb1e
                               serf_request_t *request,
Packit 3adb1e
                               const char *method,
Packit 3adb1e
                               const char *uri,
Packit 3adb1e
                               serf_bucket_t *hdrs_bkt)
Packit 3adb1e
{
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    serf__authn_info_t *authn_info;
Packit 3adb1e
    basic_authn_info_t *basic_info;
Packit 3adb1e
Packit 3adb1e
    if (peer == HOST) {
Packit 3adb1e
        authn_info = serf__get_authn_info_for_server(conn);
Packit 3adb1e
    } else {
Packit 3adb1e
        authn_info = &ctx->proxy_authn_info;
Packit 3adb1e
    }
Packit 3adb1e
    basic_info = authn_info->baton;
Packit 3adb1e
Packit 3adb1e
    if (basic_info && basic_info->header && basic_info->value) {
Packit 3adb1e
        serf_bucket_headers_setn(hdrs_bkt, basic_info->header,
Packit 3adb1e
                                 basic_info->value);
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
}