blob: b7267d65be9282e65c8da10b215456e66650f874 [file] [log] [blame]
/* Copyright 2009 Justin Erenkrantz and Greg Stein
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "auth_kerb.h"
#ifdef SERF_USE_GSSAPI
#include <apr_strings.h>
#include <gssapi/gssapi.h>
struct serf__kerb_context_t
{
/* GSSAPI context */
gss_ctx_id_t gss_ctx;
/* Mechanism used to authenticate, should be Kerberos. */
gss_OID gss_mech;
};
/* Cleans the GSS context object, when the pool used to create it gets
cleared or destroyed. */
static apr_status_t
cleanup_ctx(void *data)
{
OM_uint32 min_stat;
serf__kerb_context_t *ctx = data;
if (ctx->gss_ctx != GSS_C_NO_CONTEXT) {
if (gss_delete_sec_context(&min_stat, &ctx->gss_ctx,
GSS_C_NO_BUFFER) == GSS_S_FAILURE)
return APR_EGENERAL;
}
return APR_SUCCESS;
}
static apr_status_t
cleanup_sec_buffer(void *data)
{
OM_uint32 min_stat;
gss_buffer_desc *gss_buf = data;
gss_release_buffer(&min_stat, gss_buf);
return APR_SUCCESS;
}
apr_status_t
serf__kerb_create_sec_context(serf__kerb_context_t **ctx_p,
apr_pool_t *scratch_pool,
apr_pool_t *result_pool)
{
serf__kerb_context_t *ctx;
ctx = apr_pcalloc(result_pool, sizeof(*ctx));
ctx->gss_ctx = GSS_C_NO_CONTEXT;
ctx->gss_mech = GSS_C_NO_OID;
apr_pool_cleanup_register(result_pool, ctx,
cleanup_ctx,
apr_pool_cleanup_null);
*ctx_p = ctx;
return APR_SUCCESS;
}
apr_status_t
serf__kerb_init_sec_context(serf__kerb_context_t *ctx,
const char *service,
const char *hostname,
serf__kerb_buffer_t *input_buf,
serf__kerb_buffer_t *output_buf,
apr_pool_t *scratch_pool,
apr_pool_t *result_pool
)
{
gss_buffer_desc gss_input_buf = GSS_C_EMPTY_BUFFER;
gss_buffer_desc *gss_output_buf_p;
OM_uint32 gss_min_stat, gss_maj_stat;
gss_name_t host_gss_name;
gss_buffer_desc bufdesc;
/* Get the name for the HTTP service at the target host. */
bufdesc.value = apr_pstrcat(scratch_pool, service, "@", hostname, NULL);
bufdesc.length = strlen(bufdesc.value);
gss_maj_stat = gss_import_name (&gss_min_stat, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
&host_gss_name);
if(GSS_ERROR(gss_maj_stat)) {
return APR_EGENERAL;
}
/* If the server sent us a token, pass it to gss_init_sec_token for
validation. */
gss_input_buf.value = input_buf->value;
gss_input_buf.length = input_buf->length;
gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p));
/* Establish a security context to the server. */
gss_maj_stat = gss_init_sec_context
(&gss_min_stat, /* minor_status */
GSS_C_NO_CREDENTIAL, /* XXXXX claimant_cred_handle */
&ctx->gss_ctx, /* gssapi context handle */
host_gss_name, /* HTTP@server name */
ctx->gss_mech, /* mech_type (0 ininitially */
GSS_C_MUTUAL_FLAG, /* ensure the peer authenticates itself */
0, /* default validity period */
GSS_C_NO_CHANNEL_BINDINGS, /* do not use channel bindings */
&gss_input_buf, /* server token, initially empty */
&ctx->gss_mech, /* actual mech type */
gss_output_buf_p, /* output_token */
NULL, /* ret_flags */
NULL /* not interested in remaining validity */
);
apr_pool_cleanup_register(result_pool, gss_output_buf_p,
cleanup_sec_buffer,
apr_pool_cleanup_null);
output_buf->value = gss_output_buf_p->value;
output_buf->length = gss_output_buf_p->length;
switch(gss_maj_stat) {
case GSS_S_COMPLETE:
return APR_SUCCESS;
case GSS_S_CONTINUE_NEEDED:
return APR_EAGAIN;
default:
return APR_EGENERAL;
}
}
#endif /* SERF_USE_GSSAPI */