Blame auth/auth_spnego_gss.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 "serf.h"
Packit 3adb1e
#include "serf_private.h"
Packit 3adb1e
#include "auth_spnego.h"
Packit 3adb1e
Packit 3adb1e
#ifdef SERF_USE_GSSAPI
Packit 3adb1e
#include <apr_strings.h>
Packit 3adb1e
#include <gssapi/gssapi.h>
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
/* This module can support all authentication mechanisms as provided by
Packit 3adb1e
   the GSS-API implementation, but for now it only supports SPNEGO for
Packit 3adb1e
   Negotiate.
Packit 3adb1e
   SPNEGO can delegate authentication to Kerberos if supported by the
Packit 3adb1e
   host. */
Packit 3adb1e
Packit 3adb1e
#ifndef GSS_SPNEGO_MECHANISM
Packit 3adb1e
static gss_OID_desc spnego_mech_oid = { 6, "\x2b\x06\x01\x05\x05\x02" };
Packit 3adb1e
#define GSS_SPNEGO_MECHANISM &spnego_mech_oid
Packit 3adb1e
#endif
Packit 3adb1e
Packit 3adb1e
struct serf__spnego_context_t
Packit 3adb1e
{
Packit 3adb1e
    /* GSSAPI context */
Packit 3adb1e
    gss_ctx_id_t gss_ctx;
Packit 3adb1e
Packit 3adb1e
    /* Mechanism used to authenticate. */
Packit 3adb1e
    gss_OID gss_mech;
Packit 3adb1e
};
Packit 3adb1e
Packit 3adb1e
static void
Packit 3adb1e
log_error(int verbose_flag, apr_socket_t *skt,
Packit 3adb1e
          serf__spnego_context_t *ctx,
Packit 3adb1e
          OM_uint32 err_maj_stat,
Packit 3adb1e
          OM_uint32 err_min_stat,
Packit 3adb1e
          const char *msg)
Packit 3adb1e
{
Packit 3adb1e
    OM_uint32 maj_stat, min_stat;
Packit 3adb1e
    gss_buffer_desc stat_buff;
Packit 3adb1e
    OM_uint32 msg_ctx = 0;
Packit 3adb1e
Packit 3adb1e
    if (verbose_flag) {
Packit 3adb1e
        maj_stat = gss_display_status(&min_stat,
Packit 3adb1e
                                      err_maj_stat,
Packit 3adb1e
                                      GSS_C_GSS_CODE,
Packit 3adb1e
                                      ctx->gss_mech,
Packit 3adb1e
                                      &msg_ctx,
Packit 3adb1e
                                      &stat_buff);
Packit 3adb1e
        if (maj_stat == GSS_S_COMPLETE ||
Packit 3adb1e
            maj_stat == GSS_S_FAILURE) {
Packit 3adb1e
            maj_stat = gss_display_status(&min_stat,
Packit 3adb1e
                                          err_min_stat,
Packit 3adb1e
                                          GSS_C_MECH_CODE,
Packit 3adb1e
                                          ctx->gss_mech,
Packit 3adb1e
                                          &msg_ctx,
Packit 3adb1e
                                          &stat_buff);
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        serf__log_skt(verbose_flag, __FILE__, skt,
Packit 3adb1e
                  "%s (%x,%d): %s\n", msg,
Packit 3adb1e
                  err_maj_stat, err_min_stat, stat_buff.value);
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Cleans the GSS 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 (ctx->gss_ctx != GSS_C_NO_CONTEXT) {
Packit 3adb1e
        OM_uint32 gss_min_stat, gss_maj_stat;
Packit 3adb1e
Packit 3adb1e
        gss_maj_stat = gss_delete_sec_context(&gss_min_stat, &ctx->gss_ctx,
Packit 3adb1e
                                              GSS_C_NO_BUFFER);
Packit 3adb1e
        if(GSS_ERROR(gss_maj_stat)) {
Packit 3adb1e
            log_error(AUTH_VERBOSE, NULL, ctx,
Packit 3adb1e
                      gss_maj_stat, gss_min_stat,
Packit 3adb1e
                      "Error cleaning up GSS security context");
Packit 3adb1e
            return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
        }
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
    OM_uint32 min_stat;
Packit 3adb1e
    gss_buffer_desc *gss_buf = data;
Packit 3adb1e
Packit 3adb1e
    gss_release_buffer(&min_stat, gss_buf);
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
    serf__spnego_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = apr_pcalloc(result_pool, sizeof(*ctx));
Packit 3adb1e
Packit 3adb1e
    ctx->gss_ctx = GSS_C_NO_CONTEXT;
Packit 3adb1e
    ctx->gss_mech = GSS_SPNEGO_MECHANISM;
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
    *ctx_p = ctx;
Packit 3adb1e
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
    OM_uint32 dummy_stat;
Packit 3adb1e
Packit 3adb1e
    if (ctx->gss_ctx)
Packit 3adb1e
        (void)gss_delete_sec_context(&dummy_stat, &ctx->gss_ctx,
Packit 3adb1e
                                     GSS_C_NO_BUFFER);
Packit 3adb1e
    ctx->gss_ctx = GSS_C_NO_CONTEXT;
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
    gss_buffer_desc gss_input_buf = GSS_C_EMPTY_BUFFER;
Packit 3adb1e
    gss_buffer_desc *gss_output_buf_p;
Packit 3adb1e
    OM_uint32 gss_min_stat, gss_maj_stat;
Packit 3adb1e
    gss_name_t host_gss_name;
Packit 3adb1e
    gss_buffer_desc bufdesc;
Packit 3adb1e
    gss_OID dummy; /* unused */
Packit 3adb1e
Packit 3adb1e
    /* Get the name for the HTTP service at the target host. */
Packit 3adb1e
    /* TODO: should be shared between multiple requests. */
Packit 3adb1e
    bufdesc.value = apr_pstrcat(scratch_pool, service, "@", hostname, NULL);
Packit 3adb1e
    bufdesc.length = strlen(bufdesc.value);
Packit 3adb1e
    serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                  "Get principal for %s\n", bufdesc.value);
Packit 3adb1e
    gss_maj_stat = gss_import_name (&gss_min_stat, &bufdesc,
Packit 3adb1e
                                    GSS_C_NT_HOSTBASED_SERVICE,
Packit 3adb1e
                                    &host_gss_name);
Packit 3adb1e
    if(GSS_ERROR(gss_maj_stat)) {
Packit 3adb1e
        log_error(AUTH_VERBOSE, conn->skt, ctx,
Packit 3adb1e
                  gss_maj_stat, gss_min_stat,
Packit 3adb1e
                  "Error converting principal name to GSS internal format ");
Packit 3adb1e
        return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* If the server sent us a token, pass it to gss_init_sec_token for
Packit 3adb1e
       validation. */
Packit 3adb1e
    gss_input_buf.value = input_buf->value;
Packit 3adb1e
    gss_input_buf.length = input_buf->length;
Packit 3adb1e
Packit 3adb1e
    gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p));
Packit 3adb1e
Packit 3adb1e
    /* Establish a security context to the server. */
Packit 3adb1e
    gss_maj_stat = gss_init_sec_context
Packit 3adb1e
        (&gss_min_stat,             /* minor_status */
Packit 3adb1e
         GSS_C_NO_CREDENTIAL,       /* XXXXX claimant_cred_handle */
Packit 3adb1e
         &ctx->gss_ctx,              /* gssapi context handle */
Packit 3adb1e
         host_gss_name,             /* HTTP@server name */
Packit 3adb1e
         ctx->gss_mech,             /* mech_type (SPNEGO) */
Packit 3adb1e
         GSS_C_MUTUAL_FLAG,         /* ensure the peer authenticates itself */
Packit 3adb1e
         0,                         /* default validity period */
Packit 3adb1e
         GSS_C_NO_CHANNEL_BINDINGS, /* do not use channel bindings */
Packit 3adb1e
         &gss_input_buf,            /* server token, initially empty */
Packit 3adb1e
         &dummy,                    /* actual mech type */
Packit 3adb1e
         gss_output_buf_p,           /* output_token */
Packit 3adb1e
         NULL,                      /* ret_flags */
Packit 3adb1e
         NULL                       /* not interested in remaining validity */
Packit 3adb1e
         );
Packit 3adb1e
Packit 3adb1e
    apr_pool_cleanup_register(result_pool, gss_output_buf_p,
Packit 3adb1e
                              cleanup_sec_buffer,
Packit 3adb1e
                              apr_pool_cleanup_null);
Packit 3adb1e
Packit 3adb1e
    output_buf->value = gss_output_buf_p->value;
Packit 3adb1e
    output_buf->length = gss_output_buf_p->length;
Packit 3adb1e
Packit 3adb1e
    switch(gss_maj_stat) {
Packit 3adb1e
    case GSS_S_COMPLETE:
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    case GSS_S_CONTINUE_NEEDED:
Packit 3adb1e
        return APR_EAGAIN;
Packit 3adb1e
    default:
Packit 3adb1e
        log_error(AUTH_VERBOSE, conn->skt, ctx,
Packit 3adb1e
                  gss_maj_stat, gss_min_stat,
Packit 3adb1e
                  "Error during Kerberos handshake");
Packit 3adb1e
        return SERF_ERROR_AUTHN_FAILED;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
#endif /* SERF_USE_GSSAPI */