Blame auth/auth_spnego_sspi.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
#include "auth_spnego.h"
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_private.h"
Packit 3adb1e
Packit 3adb1e
#ifdef SERF_USE_SSPI
Packit 3adb1e
#include <apr.h>
Packit 3adb1e
#include <apr_strings.h>
Packit 3adb1e
Packit 3adb1e
#define SECURITY_WIN32
Packit 3adb1e
#include <sspi.h>
Packit 3adb1e
Packit 3adb1e
/* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */
Packit 3adb1e
#ifndef SEC_E_MUTUAL_AUTH_FAILED
Packit 3adb1e
#define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L)
Packit 3adb1e
#endif
Packit 3adb1e
Packit 3adb1e
struct serf__spnego_context_t
Packit 3adb1e
{
Packit 3adb1e
    CredHandle sspi_credentials;
Packit 3adb1e
    CtxtHandle sspi_context;
Packit 3adb1e
    BOOL initalized;
Packit 3adb1e
    apr_pool_t *pool;
Packit 3adb1e
Packit 3adb1e
    /* Service Principal Name (SPN) used for authentication. */
Packit 3adb1e
    const char *target_name;
Packit 3adb1e
Packit 3adb1e
    /* One of SERF_AUTHN_* authentication types.*/
Packit 3adb1e
    int authn_type;
Packit 3adb1e
};
Packit 3adb1e
Packit 3adb1e
/* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped
Packit 3adb1e
 * to our own codes and some to Win32 error codes:
Packit 3adb1e
 * http://support.microsoft.com/kb/113996
Packit 3adb1e
 */
Packit 3adb1e
static apr_status_t
Packit 3adb1e
map_sspi_status(SECURITY_STATUS sspi_status)
Packit 3adb1e
{
Packit 3adb1e
    switch(sspi_status)
Packit 3adb1e
    {
Packit 3adb1e
    case SEC_E_INSUFFICIENT_MEMORY:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES);
Packit 3adb1e
    case SEC_E_INVALID_HANDLE:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE);
Packit 3adb1e
    case SEC_E_UNSUPPORTED_FUNCTION:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION);
Packit 3adb1e
    case SEC_E_TARGET_UNKNOWN:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH);
Packit 3adb1e
    case SEC_E_INTERNAL_ERROR:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR);
Packit 3adb1e
    case SEC_E_SECPKG_NOT_FOUND:
Packit 3adb1e
    case SEC_E_BAD_PKGID:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE);
Packit 3adb1e
    case SEC_E_NO_IMPERSONATION:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE);
Packit 3adb1e
    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS);
Packit 3adb1e
    case SEC_E_UNTRUSTED_ROOT:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE);
Packit 3adb1e
    case SEC_E_WRONG_PRINCIPAL:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME);
Packit 3adb1e
    case SEC_E_MUTUAL_AUTH_FAILED:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED);
Packit 3adb1e
    case SEC_E_TIME_SKEW:
Packit 3adb1e
        return APR_FROM_OS_ERROR(ERROR_TIME_SKEW);
Packit 3adb1e
    default:
Packit 3adb1e
        return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Cleans the SSPI context object, when the pool used to create it gets
Packit 3adb1e
   cleared or destroyed. */
Packit 3adb1e
static apr_status_t
Packit 3adb1e
cleanup_ctx(void *data)
Packit 3adb1e
{
Packit 3adb1e
    serf__spnego_context_t *ctx = data;
Packit 3adb1e
Packit 3adb1e
    if (SecIsValidHandle(&ctx->sspi_context)) {
Packit 3adb1e
        DeleteSecurityContext(&ctx->sspi_context);
Packit 3adb1e
        SecInvalidateHandle(&ctx->sspi_context);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (SecIsValidHandle(&ctx->sspi_credentials)) {
Packit 3adb1e
        FreeCredentialsHandle(&ctx->sspi_credentials);
Packit 3adb1e
        SecInvalidateHandle(&ctx->sspi_credentials);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t
Packit 3adb1e
cleanup_sec_buffer(void *data)
Packit 3adb1e
{
Packit 3adb1e
    FreeContextBuffer(data);
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
Packit 3adb1e
                                const serf__authn_scheme_t *scheme,
Packit 3adb1e
                                apr_pool_t *result_pool,
Packit 3adb1e
                                apr_pool_t *scratch_pool)
Packit 3adb1e
{
Packit 3adb1e
    SECURITY_STATUS sspi_status;
Packit 3adb1e
    serf__spnego_context_t *ctx;
Packit 3adb1e
    const char *sspi_package;
Packit 3adb1e
Packit 3adb1e
    ctx = apr_pcalloc(result_pool, sizeof(*ctx));
Packit 3adb1e
Packit 3adb1e
    SecInvalidateHandle(&ctx->sspi_context);
Packit 3adb1e
    SecInvalidateHandle(&ctx->sspi_credentials);
Packit 3adb1e
    ctx->initalized = FALSE;
Packit 3adb1e
    ctx->pool = result_pool;
Packit 3adb1e
    ctx->target_name = NULL;
Packit 3adb1e
    ctx->authn_type = scheme->type;
Packit 3adb1e
Packit 3adb1e
    apr_pool_cleanup_register(result_pool, ctx,
Packit 3adb1e
                              cleanup_ctx,
Packit 3adb1e
                              apr_pool_cleanup_null);
Packit 3adb1e
Packit 3adb1e
    if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
Packit 3adb1e
        sspi_package = "Negotiate";
Packit 3adb1e
    else
Packit 3adb1e
        sspi_package = "NTLM";
Packit 3adb1e
Packit 3adb1e
    sspi_status = AcquireCredentialsHandleA(
Packit 3adb1e
        NULL, sspi_package, SECPKG_CRED_OUTBOUND,
Packit 3adb1e
        NULL, NULL, NULL, NULL,
Packit 3adb1e
        &ctx->sspi_credentials, NULL);
Packit 3adb1e
Packit 3adb1e
    if (FAILED(sspi_status)) {
Packit 3adb1e
        return map_sspi_status(sspi_status);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    *ctx_p = ctx;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t
Packit 3adb1e
get_canonical_hostname(const char **canonname,
Packit 3adb1e
                       const char *hostname,
Packit 3adb1e
                       apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    struct addrinfo hints;
Packit 3adb1e
    struct addrinfo *addrinfo;
Packit 3adb1e
Packit 3adb1e
    ZeroMemory(&hints, sizeof(hints));
Packit 3adb1e
    hints.ai_flags = AI_CANONNAME;
Packit 3adb1e
Packit 3adb1e
    if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) {
Packit 3adb1e
        return apr_get_netos_error();
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (addrinfo) {
Packit 3adb1e
        *canonname = apr_pstrdup(pool, addrinfo->ai_canonname);
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        *canonname = apr_pstrdup(pool, hostname);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    freeaddrinfo(addrinfo);
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    if (SecIsValidHandle(&ctx->sspi_context)) {
Packit 3adb1e
        DeleteSecurityContext(&ctx->sspi_context);
Packit 3adb1e
        SecInvalidateHandle(&ctx->sspi_context);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    ctx->initalized = FALSE;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__spnego_init_sec_context(serf_connection_t *conn,
Packit 3adb1e
                              serf__spnego_context_t *ctx,
Packit 3adb1e
                              const char *service,
Packit 3adb1e
                              const char *hostname,
Packit 3adb1e
                              serf__spnego_buffer_t *input_buf,
Packit 3adb1e
                              serf__spnego_buffer_t *output_buf,
Packit 3adb1e
                              apr_pool_t *result_pool,
Packit 3adb1e
                              apr_pool_t *scratch_pool
Packit 3adb1e
                              )
Packit 3adb1e
{
Packit 3adb1e
    SECURITY_STATUS status;
Packit 3adb1e
    ULONG actual_attr;
Packit 3adb1e
    SecBuffer sspi_in_buffer;
Packit 3adb1e
    SecBufferDesc sspi_in_buffer_desc;
Packit 3adb1e
    SecBuffer sspi_out_buffer;
Packit 3adb1e
    SecBufferDesc sspi_out_buffer_desc;
Packit 3adb1e
    apr_status_t apr_status;
Packit 3adb1e
    const char *canonname;
Packit 3adb1e
Packit 3adb1e
    if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
Packit 3adb1e
        apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
Packit 3adb1e
        if (apr_status) {
Packit 3adb1e
            return apr_status;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
Packit 3adb1e
                                       NULL);
Packit 3adb1e
Packit 3adb1e
        serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                      "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
Packit 3adb1e
    }
Packit 3adb1e
    else if (ctx->authn_type == SERF_AUTHN_NTLM)
Packit 3adb1e
    {
Packit 3adb1e
        /* Target name is not used for NTLM authentication. */
Packit 3adb1e
        ctx->target_name = NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Prepare input buffer description. */
Packit 3adb1e
    sspi_in_buffer.BufferType = SECBUFFER_TOKEN;
Packit 3adb1e
    sspi_in_buffer.pvBuffer = input_buf->value;
Packit 3adb1e
    sspi_in_buffer.cbBuffer = input_buf->length; 
Packit 3adb1e
Packit 3adb1e
    sspi_in_buffer_desc.cBuffers = 1;
Packit 3adb1e
    sspi_in_buffer_desc.pBuffers = &sspi_in_buffer;
Packit 3adb1e
    sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION;
Packit 3adb1e
Packit 3adb1e
    /* Output buffers. Output buffer will be allocated by system. */
Packit 3adb1e
    sspi_out_buffer.BufferType = SECBUFFER_TOKEN;
Packit 3adb1e
    sspi_out_buffer.pvBuffer = NULL; 
Packit 3adb1e
    sspi_out_buffer.cbBuffer = 0;
Packit 3adb1e
Packit 3adb1e
    sspi_out_buffer_desc.cBuffers = 1;
Packit 3adb1e
    sspi_out_buffer_desc.pBuffers = &sspi_out_buffer;
Packit 3adb1e
    sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION;
Packit 3adb1e
Packit 3adb1e
    status = InitializeSecurityContextA(
Packit 3adb1e
        &ctx->sspi_credentials,
Packit 3adb1e
        ctx->initalized ? &ctx->sspi_context : NULL,
Packit 3adb1e
        ctx->target_name,
Packit 3adb1e
        ISC_REQ_ALLOCATE_MEMORY
Packit 3adb1e
        | ISC_REQ_MUTUAL_AUTH
Packit 3adb1e
        | ISC_REQ_CONFIDENTIALITY,
Packit 3adb1e
        0,                          /* Reserved1 */
Packit 3adb1e
        SECURITY_NETWORK_DREP,
Packit 3adb1e
        &sspi_in_buffer_desc,
Packit 3adb1e
        0,                          /* Reserved2 */
Packit 3adb1e
        &ctx->sspi_context,
Packit 3adb1e
        &sspi_out_buffer_desc,
Packit 3adb1e
        &actual_attr,
Packit 3adb1e
        NULL);
Packit 3adb1e
Packit 3adb1e
    if (sspi_out_buffer.cbBuffer > 0) {
Packit 3adb1e
        apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer,
Packit 3adb1e
                                  cleanup_sec_buffer,
Packit 3adb1e
                                  apr_pool_cleanup_null);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    ctx->initalized = TRUE;
Packit 3adb1e
Packit 3adb1e
    /* Finish authentication if SSPI requires so. */
Packit 3adb1e
    if (status == SEC_I_COMPLETE_NEEDED
Packit 3adb1e
        || status == SEC_I_COMPLETE_AND_CONTINUE)
Packit 3adb1e
    {
Packit 3adb1e
        CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    output_buf->value = sspi_out_buffer.pvBuffer;
Packit 3adb1e
    output_buf->length = sspi_out_buffer.cbBuffer;
Packit 3adb1e
Packit 3adb1e
    switch(status) {
Packit 3adb1e
    case SEC_I_COMPLETE_AND_CONTINUE:
Packit 3adb1e
    case SEC_I_CONTINUE_NEEDED:
Packit 3adb1e
        return APR_EAGAIN;
Packit 3adb1e
Packit 3adb1e
    case SEC_I_COMPLETE_NEEDED:
Packit 3adb1e
    case SEC_E_OK:
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
Packit 3adb1e
    default:
Packit 3adb1e
        return map_sspi_status(status);
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
#endif /* SERF_USE_SSPI */