|
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 |
}
|