Blame auth/auth_digest.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
/*** Digest 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
#include <apr_uuid.h>
Packit 3adb1e
#include <apr_md5.h>
Packit 3adb1e
Packit 3adb1e
/** Digest authentication, implements RFC 2617. **/
Packit 3adb1e
Packit 3adb1e
/* TODO: add support for the domain attribute. This defines the protection
Packit 3adb1e
   space, so that serf can decide per URI if it should reuse the cached
Packit 3adb1e
   credentials for the server, or not. */
Packit 3adb1e
Packit 3adb1e
/* Stores the context information related to Digest authentication.
Packit 3adb1e
   This information is stored in the per server cache in the serf context. */
Packit 3adb1e
typedef struct digest_authn_info_t {
Packit 3adb1e
    /* nonce-count for digest authentication */
Packit 3adb1e
    unsigned int digest_nc;
Packit 3adb1e
Packit 3adb1e
    const char *header;
Packit 3adb1e
Packit 3adb1e
    const char *ha1;
Packit 3adb1e
Packit 3adb1e
    const char *realm;
Packit 3adb1e
    const char *cnonce;
Packit 3adb1e
    const char *nonce;
Packit 3adb1e
    const char *opaque;
Packit 3adb1e
    const char *algorithm;
Packit 3adb1e
    const char *qop;
Packit 3adb1e
    const char *username;
Packit 3adb1e
Packit 3adb1e
    apr_pool_t *pool;
Packit 3adb1e
} digest_authn_info_t;
Packit 3adb1e
Packit 3adb1e
static char
Packit 3adb1e
int_to_hex(int v)
Packit 3adb1e
{
Packit 3adb1e
    return (v < 10) ? '0' + v : 'a' + (v - 10);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/**
Packit 3adb1e
 * Convert a string if ASCII characters HASHVAL to its hexadecimal
Packit 3adb1e
 * representation.
Packit 3adb1e
 *
Packit 3adb1e
 * The returned string will be allocated in the POOL and be null-terminated.
Packit 3adb1e
 */
Packit 3adb1e
static const char *
Packit 3adb1e
hex_encode(const unsigned char *hashval,
Packit 3adb1e
           apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    int i;
Packit 3adb1e
    char *hexval = apr_palloc(pool, (APR_MD5_DIGESTSIZE * 2) + 1);
Packit 3adb1e
    for (i = 0; i < APR_MD5_DIGESTSIZE; i++) {
Packit 3adb1e
        hexval[2 * i] = int_to_hex((hashval[i] >> 4) & 0xf);
Packit 3adb1e
        hexval[2 * i + 1] = int_to_hex(hashval[i] & 0xf);
Packit 3adb1e
    }
Packit 3adb1e
    hexval[APR_MD5_DIGESTSIZE * 2] = '\0';
Packit 3adb1e
    return hexval;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/**
Packit 3adb1e
 * Returns a 36-byte long string of random characters.
Packit 3adb1e
 * UUIDs are formatted as: 00112233-4455-6677-8899-AABBCCDDEEFF.
Packit 3adb1e
 *
Packit 3adb1e
 * The returned string will be allocated in the POOL and be null-terminated.
Packit 3adb1e
 */
Packit 3adb1e
static const char *
Packit 3adb1e
random_cnonce(apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    apr_uuid_t uuid;
Packit 3adb1e
    char *buf = apr_palloc(pool, APR_UUID_FORMATTED_LENGTH + 1);
Packit 3adb1e
Packit 3adb1e
    apr_uuid_get(&uuid);
Packit 3adb1e
    apr_uuid_format(buf, &uuid);
Packit 3adb1e
Packit 3adb1e
    return hex_encode((unsigned char*)buf, pool);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t
Packit 3adb1e
build_digest_ha1(const char **out_ha1,
Packit 3adb1e
                 const char *username,
Packit 3adb1e
                 const char *password,
Packit 3adb1e
                 const char *realm_name,
Packit 3adb1e
                 apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    const char *tmp;
Packit 3adb1e
    unsigned char ha1[APR_MD5_DIGESTSIZE];
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    /* calculate ha1:
Packit 3adb1e
       MD5 hash of the combined user name, authentication realm and password */
Packit 3adb1e
    tmp = apr_psprintf(pool, "%s:%s:%s",
Packit 3adb1e
                       username,
Packit 3adb1e
                       realm_name,
Packit 3adb1e
                       password);
Packit 3adb1e
    status = apr_md5(ha1, tmp, strlen(tmp));
Packit 3adb1e
    if (status)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    *out_ha1 = hex_encode(ha1, pool);
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t
Packit 3adb1e
build_digest_ha2(const char **out_ha2,
Packit 3adb1e
                 const char *uri,
Packit 3adb1e
                 const char *method,
Packit 3adb1e
                 const char *qop,
Packit 3adb1e
                 apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    if (!qop || strcmp(qop, "auth") == 0) {
Packit 3adb1e
        const char *tmp;
Packit 3adb1e
        unsigned char ha2[APR_MD5_DIGESTSIZE];
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
Packit 3adb1e
        /* calculate ha2:
Packit 3adb1e
           MD5 hash of the combined method and URI */
Packit 3adb1e
        tmp = apr_psprintf(pool, "%s:%s",
Packit 3adb1e
                           method,
Packit 3adb1e
                           uri);
Packit 3adb1e
        status = apr_md5(ha2, tmp, strlen(tmp));
Packit 3adb1e
        if (status)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        *out_ha2 = hex_encode(ha2, pool);
Packit 3adb1e
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    } else {
Packit 3adb1e
        /* TODO: auth-int isn't supported! */
Packit 3adb1e
        return APR_ENOTIMPL;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t
Packit 3adb1e
build_auth_header(const char **out_header,
Packit 3adb1e
                  digest_authn_info_t *digest_info,
Packit 3adb1e
                  const char *path,
Packit 3adb1e
                  const char *method,
Packit 3adb1e
                  apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    char *hdr;
Packit 3adb1e
    const char *ha2;
Packit 3adb1e
    const char *response;
Packit 3adb1e
    unsigned char response_hdr[APR_MD5_DIGESTSIZE];
Packit 3adb1e
    const char *response_hdr_hex;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    status = build_digest_ha2(&ha2, path, method, digest_info->qop, pool);
Packit 3adb1e
    if (status)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    hdr = apr_psprintf(pool,
Packit 3adb1e
                       "Digest realm=\"%s\","
Packit 3adb1e
                       " username=\"%s\","
Packit 3adb1e
                       " nonce=\"%s\","
Packit 3adb1e
                       " uri=\"%s\"",
Packit 3adb1e
                       digest_info->realm, digest_info->username,
Packit 3adb1e
                       digest_info->nonce,
Packit 3adb1e
                       path);
Packit 3adb1e
Packit 3adb1e
    if (digest_info->qop) {
Packit 3adb1e
        if (! digest_info->cnonce)
Packit 3adb1e
            digest_info->cnonce = random_cnonce(digest_info->pool);
Packit 3adb1e
Packit 3adb1e
        hdr = apr_psprintf(pool, "%s, nc=%08x, cnonce=\"%s\", qop=\"%s\"",
Packit 3adb1e
                           hdr,
Packit 3adb1e
                           digest_info->digest_nc,
Packit 3adb1e
                           digest_info->cnonce,
Packit 3adb1e
                           digest_info->qop);
Packit 3adb1e
Packit 3adb1e
        /* Build the response header:
Packit 3adb1e
           MD5 hash of the combined HA1 result, server nonce (nonce),
Packit 3adb1e
           request counter (nc), client nonce (cnonce),
Packit 3adb1e
           quality of protection code (qop) and HA2 result. */
Packit 3adb1e
        response = apr_psprintf(pool, "%s:%s:%08x:%s:%s:%s",
Packit 3adb1e
                                digest_info->ha1, digest_info->nonce,
Packit 3adb1e
                                digest_info->digest_nc,
Packit 3adb1e
                                digest_info->cnonce, digest_info->qop, ha2);
Packit 3adb1e
    } else {
Packit 3adb1e
        /* Build the response header:
Packit 3adb1e
           MD5 hash of the combined HA1 result, server nonce (nonce)
Packit 3adb1e
           and HA2 result. */
Packit 3adb1e
        response = apr_psprintf(pool, "%s:%s:%s",
Packit 3adb1e
                                digest_info->ha1, digest_info->nonce, ha2);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    status = apr_md5(response_hdr, response, strlen(response));
Packit 3adb1e
    if (status)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    response_hdr_hex = hex_encode(response_hdr, pool);
Packit 3adb1e
Packit 3adb1e
    hdr = apr_psprintf(pool, "%s, response=\"%s\"", hdr, response_hdr_hex);
Packit 3adb1e
Packit 3adb1e
    if (digest_info->opaque) {
Packit 3adb1e
        hdr = apr_psprintf(pool, "%s, opaque=\"%s\"", hdr,
Packit 3adb1e
                           digest_info->opaque);
Packit 3adb1e
    }
Packit 3adb1e
    if (digest_info->algorithm) {
Packit 3adb1e
        hdr = apr_psprintf(pool, "%s, algorithm=\"%s\"", hdr,
Packit 3adb1e
                           digest_info->algorithm);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    *out_header = hdr;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__handle_digest_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
    char *attrs;
Packit 3adb1e
    char *nextkv;
Packit 3adb1e
    const char *realm, *realm_name = NULL;
Packit 3adb1e
    const char *nonce = NULL;
Packit 3adb1e
    const char *algorithm = NULL;
Packit 3adb1e
    const char *qop = NULL;
Packit 3adb1e
    const char *opaque = NULL;
Packit 3adb1e
    const char *key;
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
    digest_authn_info_t *digest_info;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    apr_pool_t *cred_pool;
Packit 3adb1e
    char *username, *password;
Packit 3adb1e
Packit 3adb1e
    /* Can't do Digest 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
    digest_info = authn_info->baton;
Packit 3adb1e
Packit 3adb1e
    /* Need a copy cuz we're going to write NUL characters into the string.  */
Packit 3adb1e
    attrs = apr_pstrdup(pool, auth_attr);
Packit 3adb1e
Packit 3adb1e
    /* We're expecting a list of key=value pairs, separated by a comma.
Packit 3adb1e
       Ex. realm="SVN Digest",
Packit 3adb1e
       nonce="f+zTl/leBAA=e371bd3070adfb47b21f5fc64ad8cc21adc371a5",
Packit 3adb1e
       algorithm=MD5, qop="auth" */
Packit 3adb1e
    for ( ; (key = apr_strtok(attrs, ",", &nextkv)) != NULL; attrs = NULL) {
Packit 3adb1e
        char *val;
Packit 3adb1e
Packit 3adb1e
        val = strchr(key, '=');
Packit 3adb1e
        if (val == NULL)
Packit 3adb1e
            continue;
Packit 3adb1e
        *val++ = '\0';
Packit 3adb1e
Packit 3adb1e
        /* skip leading spaces */
Packit 3adb1e
        while (*key && *key == ' ')
Packit 3adb1e
            key++;
Packit 3adb1e
Packit 3adb1e
        /* If the value is quoted, then remove the quotes.  */
Packit 3adb1e
        if (*val == '"') {
Packit 3adb1e
            apr_size_t last = strlen(val) - 1;
Packit 3adb1e
Packit 3adb1e
            if (val[last] == '"') {
Packit 3adb1e
                val[last] = '\0';
Packit 3adb1e
                val++;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (strcmp(key, "realm") == 0)
Packit 3adb1e
            realm_name = val;
Packit 3adb1e
        else if (strcmp(key, "nonce") == 0)
Packit 3adb1e
            nonce = val;
Packit 3adb1e
        else if (strcmp(key, "algorithm") == 0)
Packit 3adb1e
            algorithm = val;
Packit 3adb1e
        else if (strcmp(key, "qop") == 0)
Packit 3adb1e
            qop = val;
Packit 3adb1e
        else if (strcmp(key, "opaque") == 0)
Packit 3adb1e
            opaque = val;
Packit 3adb1e
Packit 3adb1e
        /* Ignore all unsupported attributes. */
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
    /* 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
    digest_info->header = (code == 401) ? "Authorization" :
Packit 3adb1e
                                          "Proxy-Authorization";
Packit 3adb1e
Packit 3adb1e
    /* Store the digest authentication parameters in the context cached for
Packit 3adb1e
       this server in the serf context, so we can use it to create the
Packit 3adb1e
       Authorization header when setting up requests on the same or different
Packit 3adb1e
       connections (e.g. in case of KeepAlive off on the server).
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
    digest_info->pool = conn->pool;
Packit 3adb1e
    digest_info->qop = apr_pstrdup(digest_info->pool, qop);
Packit 3adb1e
    digest_info->nonce = apr_pstrdup(digest_info->pool, nonce);
Packit 3adb1e
    digest_info->cnonce = NULL;
Packit 3adb1e
    digest_info->opaque = apr_pstrdup(digest_info->pool, opaque);
Packit 3adb1e
    digest_info->algorithm = apr_pstrdup(digest_info->pool, algorithm);
Packit 3adb1e
    digest_info->realm = apr_pstrdup(digest_info->pool, realm_name);
Packit 3adb1e
    digest_info->username = apr_pstrdup(digest_info->pool, username);
Packit 3adb1e
    digest_info->digest_nc++;
Packit 3adb1e
Packit 3adb1e
    status = build_digest_ha1(&digest_info->ha1, username, password,
Packit 3adb1e
                              digest_info->realm, digest_info->pool);
Packit 3adb1e
Packit 3adb1e
    apr_pool_destroy(cred_pool);
Packit 3adb1e
Packit 3adb1e
    /* If the handshake is finished tell serf it can send as much requests as it
Packit 3adb1e
       likes. */
Packit 3adb1e
    serf_connection_set_max_outstanding_requests(conn, 0);
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__init_digest(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
apr_status_t
Packit 3adb1e
serf__init_digest_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(digest_authn_info_t));
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Make serf send the initial requests one by one */
Packit 3adb1e
    serf_connection_set_max_outstanding_requests(conn, 1);
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__setup_request_digest_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
    digest_authn_info_t *digest_info;
Packit 3adb1e
    apr_status_t status;
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
    digest_info = authn_info->baton;
Packit 3adb1e
Packit 3adb1e
    if (digest_info && digest_info->realm) {
Packit 3adb1e
        const char *value;
Packit 3adb1e
        const char *path;
Packit 3adb1e
Packit 3adb1e
        /* TODO: per request pool? */
Packit 3adb1e
Packit 3adb1e
        /* for request 'CONNECT serf.googlecode.com:443', the uri also should be
Packit 3adb1e
           serf.googlecode.com:443. apr_uri_parse can't handle this, so special
Packit 3adb1e
           case. */
Packit 3adb1e
        if (strcmp(method, "CONNECT") == 0)
Packit 3adb1e
            path = uri;
Packit 3adb1e
        else {
Packit 3adb1e
            apr_uri_t parsed_uri;
Packit 3adb1e
Packit 3adb1e
            /* Extract path from uri. */
Packit 3adb1e
            status = apr_uri_parse(conn->pool, uri, &parsed_uri);
Packit 3adb1e
            if (status)
Packit 3adb1e
                return status;
Packit 3adb1e
Packit 3adb1e
            path = parsed_uri.path;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* Build a new Authorization header. */
Packit 3adb1e
        digest_info->header = (peer == HOST) ? "Authorization" :
Packit 3adb1e
            "Proxy-Authorization";
Packit 3adb1e
        status = build_auth_header(&value, digest_info, path, method,
Packit 3adb1e
                                   conn->pool);
Packit 3adb1e
        if (status)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        serf_bucket_headers_setn(hdrs_bkt, digest_info->header,
Packit 3adb1e
                                 value);
Packit 3adb1e
        digest_info->digest_nc++;
Packit 3adb1e
Packit 3adb1e
        /* Store the uri of this request on the serf_request_t object, to make
Packit 3adb1e
           it available when validating the Authentication-Info header of the
Packit 3adb1e
           matching response. */
Packit 3adb1e
        request->auth_baton = (void *)path;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__validate_response_digest_auth(const serf__authn_scheme_t *scheme,
Packit 3adb1e
                                    peer_t peer,
Packit 3adb1e
                                    int code,
Packit 3adb1e
                                    serf_connection_t *conn,
Packit 3adb1e
                                    serf_request_t *request,
Packit 3adb1e
                                    serf_bucket_t *response,
Packit 3adb1e
                                    apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    const char *key;
Packit 3adb1e
    char *auth_attr;
Packit 3adb1e
    char *nextkv;
Packit 3adb1e
    const char *rspauth = NULL;
Packit 3adb1e
    const char *qop = NULL;
Packit 3adb1e
    const char *nc_str = NULL;
Packit 3adb1e
    serf_bucket_t *hdrs;
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    hdrs = serf_bucket_response_get_headers(response);
Packit 3adb1e
Packit 3adb1e
    /* Need a copy cuz we're going to write NUL characters into the string.  */
Packit 3adb1e
    if (peer == HOST)
Packit 3adb1e
        auth_attr = apr_pstrdup(pool,
Packit 3adb1e
            serf_bucket_headers_get(hdrs, "Authentication-Info"));
Packit 3adb1e
    else
Packit 3adb1e
        auth_attr = apr_pstrdup(pool,
Packit 3adb1e
            serf_bucket_headers_get(hdrs, "Proxy-Authentication-Info"));
Packit 3adb1e
Packit 3adb1e
    /* If there's no Authentication-Info header there's nothing to validate. */
Packit 3adb1e
    if (! auth_attr)
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
Packit 3adb1e
    /* We're expecting a list of key=value pairs, separated by a comma.
Packit 3adb1e
       Ex. rspauth="8a4b8451084b082be6b105e2b7975087",
Packit 3adb1e
       cnonce="346531653132652d303033392d3435", nc=00000007,
Packit 3adb1e
       qop=auth */
Packit 3adb1e
    for ( ; (key = apr_strtok(auth_attr, ",", &nextkv)) != NULL; auth_attr = NULL) {
Packit 3adb1e
        char *val;
Packit 3adb1e
Packit 3adb1e
        val = strchr(key, '=');
Packit 3adb1e
        if (val == NULL)
Packit 3adb1e
            continue;
Packit 3adb1e
        *val++ = '\0';
Packit 3adb1e
Packit 3adb1e
        /* skip leading spaces */
Packit 3adb1e
        while (*key && *key == ' ')
Packit 3adb1e
            key++;
Packit 3adb1e
Packit 3adb1e
        /* If the value is quoted, then remove the quotes.  */
Packit 3adb1e
        if (*val == '"') {
Packit 3adb1e
            apr_size_t last = strlen(val) - 1;
Packit 3adb1e
Packit 3adb1e
            if (val[last] == '"') {
Packit 3adb1e
                val[last] = '\0';
Packit 3adb1e
                val++;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (strcmp(key, "rspauth") == 0)
Packit 3adb1e
            rspauth = val;
Packit 3adb1e
        else if (strcmp(key, "qop") == 0)
Packit 3adb1e
            qop = val;
Packit 3adb1e
        else if (strcmp(key, "nc") == 0)
Packit 3adb1e
            nc_str = val;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (rspauth) {
Packit 3adb1e
        const char *ha2, *tmp, *resp_hdr_hex;
Packit 3adb1e
        unsigned char resp_hdr[APR_MD5_DIGESTSIZE];
Packit 3adb1e
        const char *req_uri = request->auth_baton;
Packit 3adb1e
        serf__authn_info_t *authn_info;
Packit 3adb1e
        digest_authn_info_t *digest_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
        digest_info = authn_info->baton;
Packit 3adb1e
Packit 3adb1e
        status = build_digest_ha2(&ha2, req_uri, "", qop, pool);
Packit 3adb1e
        if (status)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        tmp = apr_psprintf(pool, "%s:%s:%s:%s:%s:%s",
Packit 3adb1e
                           digest_info->ha1, digest_info->nonce, nc_str,
Packit 3adb1e
                           digest_info->cnonce, digest_info->qop, ha2);
Packit 3adb1e
        apr_md5(resp_hdr, tmp, strlen(tmp));
Packit 3adb1e
        resp_hdr_hex =  hex_encode(resp_hdr, pool);
Packit 3adb1e
Packit 3adb1e
        /* Incorrect response-digest in Authentication-Info header. */
Packit 3adb1e
        if (strcmp(rspauth, resp_hdr_hex) != 0) {
Packit 3adb1e
            return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}