#include <string.h>
#include "dh_gssapi.h"
static dh_channel_binding_t
GSS2DH_channel_binding(dh_channel_binding_t dh_binding,
gss_channel_bindings_t gss_binding)
{
if (gss_binding == GSS_C_NO_CHANNEL_BINDINGS)
return (NULL);
dh_binding->initiator_addrtype = gss_binding->initiator_addrtype;
dh_binding->initiator_address.dh_buffer_desc_len =
(uint32_t)gss_binding->initiator_address.length;
if (gss_binding->initiator_address.length !=
dh_binding->initiator_address.dh_buffer_desc_len)
return (NULL);
dh_binding->initiator_address.dh_buffer_desc_val =
gss_binding->initiator_address.value;
dh_binding->acceptor_addrtype = gss_binding->acceptor_addrtype;
dh_binding->acceptor_address.dh_buffer_desc_len =
(uint32_t)gss_binding->acceptor_address.length;
dh_binding->acceptor_address.dh_buffer_desc_val =
gss_binding->acceptor_address.value;
dh_binding->application_data.dh_buffer_desc_len =
(uint32_t)gss_binding->application_data.length;
dh_binding->application_data.dh_buffer_desc_val =
gss_binding->application_data.value;
return (dh_binding);
}
static gss_channel_bindings_t
DH2GSS_channel_binding(gss_channel_bindings_t gss_binding,
dh_channel_binding_t dh_binding)
{
if (dh_binding == NULL)
return (GSS_C_NO_CHANNEL_BINDINGS);
gss_binding->initiator_addrtype = dh_binding->initiator_addrtype;
gss_binding->initiator_address.length =
dh_binding->initiator_address.dh_buffer_desc_len;
gss_binding->initiator_address.value =
dh_binding->initiator_address.dh_buffer_desc_val;
gss_binding->acceptor_addrtype = dh_binding->acceptor_addrtype;
gss_binding->acceptor_address.length =
dh_binding->acceptor_address.dh_buffer_desc_len;
gss_binding->acceptor_address.value =
dh_binding->acceptor_address.dh_buffer_desc_val;
gss_binding->application_data.length =
dh_binding->application_data.dh_buffer_desc_len;
gss_binding->application_data.value =
dh_binding->application_data.dh_buffer_desc_val;
return (gss_binding);
}
static bool_t
gss_buffer_cmp(gss_buffer_t b1, gss_buffer_t b2)
{
if (b1->length != b2->length)
return (FALSE);
if (b1->length == 0)
return (TRUE);
if (b1->value == b2->value)
return (TRUE);
if (b1->value == 0 || b2->value == 0)
return (FALSE);
return (memcmp(b1->value, b2->value, b1->length) == 0);
}
static bool_t
gss_chanbind_cmp(gss_channel_bindings_t local, gss_channel_bindings_t remote)
{
if (local == NULL)
return (TRUE);
if (remote == NULL)
return (FALSE);
if (local->initiator_addrtype != remote->initiator_addrtype)
return (FALSE);
if (local->initiator_addrtype != GSS_C_AF_NULLADDR)
if (gss_buffer_cmp(&local->initiator_address,
&remote->initiator_address) == FALSE)
return (FALSE);
if (local->acceptor_addrtype != remote->acceptor_addrtype)
return (FALSE);
if (local->acceptor_addrtype != GSS_C_AF_NULLADDR)
if (gss_buffer_cmp(&local->acceptor_address,
&remote->acceptor_address) == FALSE)
return (FALSE);
return (gss_buffer_cmp(&local->application_data,
&remote->application_data));
}
static
OM_uint32
gen_accept_token(dh_gss_context_t ctx,
gss_channel_bindings_t channel,
gss_buffer_t output )
{
dh_token_desc token;
dh_cntx_t accept = &token.ver.dh_version_u.
body.dh_token_body_desc_u.accept_context.cntx;
dh_key_set keys;
dh_channel_binding_desc dh_binding;
token.ver.verno = ctx->proto_version;
token.ver.dh_version_u.body.type = DH_ACCEPT_CNTX;
accept->remote = ctx->local;
accept->local = ctx->remote;
accept->flags = ctx->flags;
accept->expire = ctx->expire;
accept->channel = GSS2DH_channel_binding(&dh_binding, channel);
keys.dh_key_set_len = ctx->no_keys;
keys.dh_key_set_val = ctx->keys;
return (__make_token(output, NULL, &token, &keys));
}
static OM_uint32
validate_cred(dh_context_t cntx,
OM_uint32 *minor,
dh_cred_id_t cred,
gss_cred_usage_t usage,
dh_principal *netname )
{
*minor = DH_SUCCESS;
*netname = NULL;
if (!cntx->keyopts->key_secretkey_is_set()) {
*minor = DH_NO_SECRET;
return (GSS_S_NO_CRED);
}
if ((*netname = cntx->keyopts->get_principal()) == NULL) {
*minor = DH_NO_PRINCIPAL;
return (GSS_S_NO_CRED);
}
if ((gss_cred_id_t)cred != GSS_C_NO_CREDENTIAL) {
if ((cred->usage != usage &&
cred->usage != GSS_C_BOTH) ||
strcmp(*netname, cred->principal) != 0) {
free(*netname);
return (GSS_S_NO_CRED);
}
if (cred->expire != GSS_C_INDEFINITE &&
time(0) > cred->expire) {
free(*netname);
return (GSS_S_CREDENTIALS_EXPIRED);
}
}
return (GSS_S_COMPLETE);
}
static OM_uint32
establish_session_keys(dh_context_t dhctx, const char *remote,
dh_key_set_t keys, dh_signature_t sig, dh_token_t token)
{
OM_uint32 stat;
int i, j;
des_block *saved_keys;
char *saved_sig;
int key_was_from_cache = 1;
if ((saved_keys = New(des_block, keys->dh_key_set_len)) == NULL)
return (DH_NOMEM_FAILURE);
for (i = 0; i < keys->dh_key_set_len; i++)
saved_keys[i] = keys->dh_key_set_val[i];
if ((saved_sig = New(char, sig->dh_signature_len)) == NULL) {
Free(saved_keys);
return (DH_NOMEM_FAILURE);
}
memcpy(saved_sig, sig->dh_signature_val, sig->dh_signature_len);
for (i = 0; key_was_from_cache && i < 2; i++) {
if (i == 1)
key_was_from_cache = 0;
if (dhctx->keyopts->key_decryptsessions(remote,
keys->dh_key_set_val,
keys->dh_key_set_len,
&key_was_from_cache)) {
Free(saved_keys);
Free(saved_sig);
return (DH_SESSION_CIPHER_FAILURE);
}
#ifdef DH_DEBUG
fprintf(stderr, "Received session keys %s the cache:\n",
key_was_form_cache ? "using" : "not using");
for (i = 0; i < keys->dh_key_set_len; i++)
fprintf(stderr, "%08.8x%08.8x ",
keys->dh_key_set_val[i].key.high,
keys->dh_key_set_val[i].key.low);
fprintf(stderr, "\n");
#endif
if ((stat = __verify_sig(token, DH_MECH_QOP, keys, sig)) ==
DH_SUCCESS) {
Free(saved_keys);
Free(saved_sig);
return (DH_SUCCESS);
}
for (j = 0; j < keys->dh_key_set_len; j++)
keys->dh_key_set_val[j] = saved_keys[j];
memcpy(sig->dh_signature_val, saved_sig, sig->dh_signature_len);
}
Free(saved_keys);
Free(saved_sig);
return (stat);
}
OM_uint32
__dh_gss_accept_sec_context(void *ctx,
OM_uint32 *minor,
gss_ctx_id_t *gss_ctx,
gss_cred_id_t cred,
gss_buffer_t input,
gss_channel_bindings_t channel,
gss_name_t *principal,
gss_OID* mech,
gss_buffer_t output,
OM_uint32 *flags,
OM_uint32 *expire,
gss_cred_id_t *del_cred )
{
dh_token_desc token;
dh_context_t dhctx = (dh_context_t)ctx;
dh_gss_context_t g_cntx = NULL;
dh_principal netname = NULL;
dh_init_context_t clnt;
OM_uint32 stat;
int i;
dh_signature sig;
struct gss_channel_bindings_struct dh_binding_desc;
gss_channel_bindings_t dh_binding;
if (input == NULL)
return (GSS_S_CALL_INACCESSIBLE_READ);
if (minor == NULL || output == NULL || gss_ctx == NULL)
return (GSS_S_CALL_INACCESSIBLE_WRITE);
*minor = 0;
if (principal)
*principal = NULL;
if (mech)
*mech = GSS_C_NO_OID;
if (flags)
*flags = 0;
if (expire)
*expire = 0;
if (del_cred)
*del_cred = GSS_C_NO_CREDENTIAL;
output->length = 0;
output->value = 0;
if (*gss_ctx != GSS_C_NO_CONTEXT)
return (GSS_S_NO_CONTEXT);
stat = validate_cred(dhctx, minor,
(dh_cred_id_t)cred, GSS_C_ACCEPT, &netname);
if (stat != GSS_S_COMPLETE)
return (stat);
memset(&sig, 0, sizeof (sig));
if (*minor = __get_ap_token(input, dhctx->mech, &token, &sig)) {
free(netname);
__free_signature(&sig);
return (GSS_S_DEFECTIVE_TOKEN);
}
clnt = &token.ver.dh_version_u.body.dh_token_body_desc_u.init_context;
if (strcmp(clnt->cntx.local, netname) != 0) {
free(netname);
*minor = DH_NOT_LOCAL;
stat = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
}
free(netname);
if (token.ver.verno != DH_PROTO_VERSION) {
*minor = DH_PROTO_MISMATCH;
stat = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
}
if ((*minor = establish_session_keys(dhctx, clnt->cntx.remote,
&clnt->keys,
&sig, &token)) != DH_SUCCESS) {
stat = GSS_S_BAD_SIG;
goto cleanup;
}
dh_binding = DH2GSS_channel_binding(&dh_binding_desc,
clnt->cntx.channel);
if (!gss_chanbind_cmp(channel, dh_binding)) {
stat = GSS_S_BAD_BINDINGS;
goto cleanup;
}
if ((g_cntx = New(dh_gss_context_desc, 1)) == NULL) {
*minor = DH_NOMEM_FAILURE;
stat = GSS_S_FAILURE;
goto cleanup;
}
g_cntx->state = ESTABLISHED;
g_cntx->initiate = 0;
g_cntx->proto_version = token.ver.verno;
__dh_init_seq_hist(g_cntx);
g_cntx->debug = 0;
if ((g_cntx->remote = strdup(clnt->cntx.remote)) == NULL) {
*minor = DH_NOMEM_FAILURE;
stat = GSS_S_FAILURE;
goto cleanup;
}
if ((g_cntx->local = strdup(clnt->cntx.local)) == NULL) {
*minor = DH_NOMEM_FAILURE;
stat = GSS_S_FAILURE;
goto cleanup;
}
g_cntx->no_keys = clnt->keys.dh_key_set_len;
if ((g_cntx->keys = New(des_block, g_cntx->no_keys)) == NULL) {
*minor = DH_NOMEM_FAILURE;
stat = GSS_S_FAILURE;
goto cleanup;
}
for (i = 0; i < g_cntx->no_keys; i++)
g_cntx->keys[i] = clnt->keys.dh_key_set_val[i];
g_cntx->flags = clnt->cntx.flags;
g_cntx->expire = clnt->cntx.expire;
if (g_cntx->flags & GSS_C_MUTUAL_FLAG) {
if (*minor = gen_accept_token(g_cntx, channel, output)) {
stat = GSS_S_FAILURE;
goto cleanup;
}
}
if ((*minor = __dh_install_context(g_cntx)) != DH_SUCCESS) {
stat = GSS_S_FAILURE;
goto cleanup;
}
*gss_ctx = (gss_ctx_id_t)g_cntx;
if (principal)
*principal = (gss_name_t)strdup(g_cntx->remote);
if (flags)
*flags = g_cntx->flags;
if (expire)
*expire = g_cntx->expire;
if (mech)
*mech = dhctx->mech;
__free_signature(&sig);
xdr_free(xdr_dh_token_desc, (char *)&token);
return (GSS_S_COMPLETE);
cleanup:
if (g_cntx) {
__dh_destroy_seq_hist(g_cntx);
(void) __dh_remove_context(g_cntx);
free(g_cntx->remote);
free(g_cntx->local);
Free(g_cntx->keys);
Free(g_cntx);
}
__free_signature(&sig);
xdr_free(xdr_dh_token_desc, (char *)&token);
return (stat);
}
static
OM_uint32
gen_init_token(dh_gss_context_t cntx,
dh_context_t dhctx,
gss_channel_bindings_t channel,
gss_buffer_t result )
{
dh_token_desc token;
dh_init_context_t remote;
dh_key_set keys, ukeys;
int i, stat;
dh_channel_binding_desc dh_binding;
if ((keys.dh_key_set_val = New(des_block, cntx->no_keys)) == NULL)
return (DH_NOMEM_FAILURE);
keys.dh_key_set_len = cntx->no_keys;
for (i = 0; i < cntx->no_keys; i++)
keys.dh_key_set_val[i] = cntx->keys[i];
memset(&token, 0, sizeof (token));
token.ver.verno = cntx->proto_version;
token.ver.dh_version_u.body.type = DH_INIT_CNTX;
remote = &token.ver.dh_version_u.body.dh_token_body_desc_u.init_context;
remote->cntx.remote = cntx->local;
remote->cntx.local = cntx->remote;
remote->cntx.flags = cntx->flags;
remote->cntx.expire = cntx->expire;
remote->cntx.channel = GSS2DH_channel_binding(&dh_binding, channel);
remote->keys = keys;
if (dhctx->keyopts->key_encryptsessions(cntx->remote,
keys.dh_key_set_val,
cntx->no_keys)) {
Free(keys.dh_key_set_val);
return (DH_SESSION_CIPHER_FAILURE);
}
ukeys.dh_key_set_len = cntx->no_keys;
ukeys.dh_key_set_val = cntx->keys;
stat = __make_ap_token(result, dhctx->mech, &token, &ukeys);
Free(keys.dh_key_set_val);
return (stat);
}
static
OM_uint32
create_context(OM_uint32 *minor,
dh_context_t cntx,
dh_gss_context_t *gss_ctx,
dh_principal netname,
dh_principal target,
gss_channel_bindings_t channel,
OM_uint32 flags_req,
OM_uint32 time_req,
OM_uint32 *flags_rec,
OM_uint32 *time_rec,
gss_buffer_t results )
{
dh_gss_context_t dh_gss_ctx;
time_t now = time(0);
OM_uint32 expire;
if ((*gss_ctx = dh_gss_ctx = New(dh_gss_context_desc, 1)) == NULL) {
*minor = DH_NOMEM_FAILURE;
return (GSS_S_FAILURE);
}
dh_gss_ctx->state = INCOMPLETE;
dh_gss_ctx->initiate = 1;
dh_gss_ctx->proto_version = DH_PROTO_VERSION;
__dh_init_seq_hist(dh_gss_ctx);
dh_gss_ctx->debug = 0;
dh_gss_ctx->local = NULL;
if ((dh_gss_ctx->remote = strdup(target)) == NULL) {
*minor = DH_NOMEM_FAILURE;
goto cleanup;
}
if ((dh_gss_ctx->local = strdup(netname)) == NULL) {
*minor = DH_NOMEM_FAILURE;
goto cleanup;
}
dh_gss_ctx->no_keys = 3;
dh_gss_ctx->keys = New(des_block, 3);
if (dh_gss_ctx->keys == NULL) {
*minor = DH_NOMEM_FAILURE;
goto cleanup;
}
if (cntx->keyopts->key_gendeskeys(dh_gss_ctx->keys, 3)) {
*minor = DH_NOMEM_FAILURE;
goto cleanup;
}
#ifdef DH_DEBUG
{
int i;
fprintf(stderr, "Generated session keys:\n");
for (i = 0; i < dh_gss_ctx->no_keys; i++)
fprintf(stderr, "%08.8x%08.8x ",
dh_gss_ctx->keys[i].key.high,
dh_gss_ctx->keys[i].key.low);
fprintf(stderr, "\n");
}
#endif
dh_gss_ctx->flags = (flags_req &
(GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG |
GSS_C_SEQUENCE_FLAG | GSS_C_REPLAY_FLAG));
dh_gss_ctx->flags |= GSS_C_INTEG_FLAG;
if (flags_rec)
*flags_rec = dh_gss_ctx->flags;
expire = time_req ? time_req : GSS_C_INDEFINITE;
dh_gss_ctx->expire = expire == GSS_C_INDEFINITE ?
expire : expire + now;
if (time_rec)
*time_rec = expire;
*minor = gen_init_token(dh_gss_ctx, cntx,
channel, results);
if (*minor != DH_SUCCESS)
goto cleanup;
if ((*minor = __dh_install_context(dh_gss_ctx)) != DH_SUCCESS)
goto cleanup;
dh_gss_ctx->state = dh_gss_ctx->flags & GSS_C_MUTUAL_FLAG ?
INCOMPLETE : ESTABLISHED;
return (dh_gss_ctx->state == ESTABLISHED ?
GSS_S_COMPLETE : GSS_S_CONTINUE_NEEDED);
cleanup:
__dh_destroy_seq_hist(dh_gss_ctx);
free(dh_gss_ctx->remote);
free(dh_gss_ctx->local);
Free(dh_gss_ctx->keys);
Free(dh_gss_ctx);
*gss_ctx = (dh_gss_context_t)GSS_C_NO_CONTEXT;
return (GSS_S_FAILURE);
}
static
OM_uint32
continue_context(OM_uint32 *minor, gss_buffer_t token,
dh_gss_context_t dh_gss_ctx, gss_channel_bindings_t channel)
{
dh_key_set keys;
dh_token_desc tok;
dh_cntx_t remote_ctx;
struct gss_channel_bindings_struct remote_chan_desc;
gss_channel_bindings_t remote_chan;
*minor = DH_SUCCESS;
if (token == NULL || token->length == 0)
return (GSS_S_DEFECTIVE_TOKEN);
keys.dh_key_set_len = dh_gss_ctx->no_keys;
keys.dh_key_set_val = dh_gss_ctx->keys;
if (*minor = __get_token(token, NULL, &tok, &keys))
return (*minor == DH_VERIFIER_MISMATCH ?
GSS_S_BAD_SIG : GSS_S_DEFECTIVE_TOKEN);
if (tok.ver.verno != dh_gss_ctx->proto_version) {
*minor = DH_PROTO_MISMATCH;
xdr_free(xdr_dh_token_desc, (char *)&tok);
return (GSS_S_DEFECTIVE_TOKEN);
}
if (tok.ver.dh_version_u.body.type != DH_ACCEPT_CNTX) {
xdr_free(xdr_dh_token_desc, (char *)&tok);
return (GSS_S_DEFECTIVE_TOKEN);
}
remote_ctx = &tok.ver.dh_version_u.
body.dh_token_body_desc_u.accept_context.cntx;
if (strcmp(remote_ctx->remote, dh_gss_ctx->remote) ||
strcmp(remote_ctx->local, dh_gss_ctx->local)) {
xdr_free(xdr_dh_token_desc, (char *)&tok);
return (GSS_S_DEFECTIVE_TOKEN);
}
remote_chan = DH2GSS_channel_binding(&remote_chan_desc,
remote_ctx->channel);
if (!gss_chanbind_cmp(channel, remote_chan)) {
xdr_free(xdr_dh_token_desc, (char *)&tok);
return (GSS_S_BAD_BINDINGS);
}
dh_gss_ctx->flags = remote_ctx->flags;
dh_gss_ctx->state = ESTABLISHED;
xdr_free(xdr_dh_token_desc, (char *)&tok);
return (GSS_S_COMPLETE);
}
OM_uint32
__dh_gss_init_sec_context(void *ctx,
OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t *context,
gss_name_t target,
gss_OID mech,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_channel_bindings_t channel,
gss_buffer_t input_token,
gss_OID *mech_rec,
gss_buffer_t output_token,
OM_uint32 *flags_rec,
OM_uint32 *time_rec )
{
dh_context_t cntx = (dh_context_t)ctx;
dh_gss_context_t dh_gss_ctx = (dh_gss_context_t)*context;
dh_principal netname;
dh_cred_id_t dh_cred = (dh_cred_id_t)cred;
OM_uint32 stat;
if (minor == 0 || output_token == 0)
return (GSS_S_CALL_INACCESSIBLE_WRITE);
*minor = DH_SUCCESS;
output_token->length = 0;
output_token->value = NULL;
if (mech_rec)
*mech_rec = cntx->mech;
if ((mech != GSS_C_NULL_OID) &&
(!__OID_equal(mech, cntx->mech))) {
return (GSS_S_BAD_MECH);
}
stat = validate_cred(cntx, minor, dh_cred, GSS_C_INITIATE, &netname);
if (stat != GSS_S_COMPLETE)
return (stat);
if (dh_gss_ctx == (dh_gss_context_t)GSS_C_NO_CONTEXT) {
if (input_token != GSS_C_NO_BUFFER &&
input_token->length != 0)
return (GSS_S_DEFECTIVE_TOKEN);
stat = create_context(minor, cntx, &dh_gss_ctx, netname,
(dh_principal)target, channel, req_flags,
time_req, flags_rec, time_rec,
output_token);
*context = (gss_ctx_id_t)dh_gss_ctx;
} else {
if ((*minor = __dh_validate_context(dh_gss_ctx)) != DH_SUCCESS)
return (GSS_S_NO_CONTEXT);
stat = continue_context(minor,
input_token, dh_gss_ctx, channel);
}
free(netname);
return (stat);
}