2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/note.h>
2N/A#include "dh_gssapi.h"
2N/A
2N/A/*
2N/A * This module contains the implementation of the gssapi context support
2N/A * routines for the Diffie-Hellman mechanism.
2N/A *
2N/A * The GSS routines that are supported by this module are:
2N/A * gss_context_time
2N/A * gss_delete_sec_context
2N/A * gss_inquire_context
2N/A * gss_wrap_size_limit
2N/A *
2N/A * The following routines are not supported for the Diffie-Hellman
2N/A * Mechanism at this time.
2N/A * gss_export_sec_context
2N/A * gss_import_sec_context
2N/A *
2N/A * The following routine is not supported since it is obsolete in version 2
2N/A * of the GSS-API.
2N/A * gss_process_context_token.
2N/A *
2N/A * Note that support for gss_init_sec_context and gss_accept_sec_context is
2N/A * found in context_establish.c
2N/A */
2N/A
2N/AOM_uint32
2N/A__dh_gss_context_time(
2N/A OM_uint32 * minor, /* GSS minor status */
2N/A gss_ctx_id_t context, /* GSS context handle */
2N/A OM_uint32* time_remaining /* Time remaining */)
2N/A
2N/A{
2N/A /* Context is a dh context */
2N/A dh_gss_context_t cntx = (dh_gss_context_t)context;
2N/A time_t now = time(0);
2N/A
2N/A if (minor == 0)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A if (time_remaining == 0)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A /* Validate context */
2N/A if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
2N/A return (GSS_S_NO_CONTEXT);
2N/A
2N/A /* See if it is always valid */
2N/A if (cntx->expire == (time_t)GSS_C_INDEFINITE) {
2N/A *time_remaining = GSS_C_INDEFINITE;
2N/A return (GSS_S_COMPLETE);
2N/A }
2N/A
2N/A /* Calculate the remainning time */
2N/A *time_remaining = (now < cntx->expire) ? cntx->expire - now : 0;
2N/A
2N/A /* Return expired if there is no time left */
2N/A return (*time_remaining ? GSS_S_COMPLETE : GSS_S_CONTEXT_EXPIRED);
2N/A}
2N/A
2N/A/*
2N/A * Delete a Diffie-Hellman context that is pointed to by context.
2N/A * On a successfull return *context will be NULL.
2N/A */
2N/A
2N/AOM_uint32
2N/A__dh_gss_delete_sec_context(
2N/A OM_uint32 *minor, /* Mechanism status */
2N/A gss_ctx_id_t *context, /* GSS context */
2N/A gss_buffer_t token /* GSS token */)
2N/A{
2N/A dh_gss_context_t cntx;
2N/A
2N/A if (context == 0)
2N/A return (GSS_S_CALL_INACCESSIBLE_READ |
2N/A GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A /* context is a Diffie-Hellman context */
2N/A cntx = (dh_gss_context_t)*context;
2N/A
2N/A if (minor == 0)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A /*
2N/A * If token then set the length to zero value to zero to indicate
2N/A * We indicat a null token since we don't need to send a token to
2N/A * the other side.
2N/A */
2N/A
2N/A if (token) {
2N/A token->length = 0;
2N/A token->value = NULL;
2N/A }
2N/A
2N/A /* Deleting a null context is OK */
2N/A if (cntx == NULL)
2N/A return (GSS_S_COMPLETE);
2N/A
2N/A /* Validate the context */
2N/A if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
2N/A return (GSS_S_NO_CONTEXT);
2N/A
2N/A /* Zero out the session keys! */
2N/A memset(cntx->keys, 0, cntx->no_keys * sizeof (des_block));
2N/A
2N/A /* Unregister the context */
2N/A *minor = __dh_remove_context(cntx);
2N/A
2N/A /* Free storage */
2N/A __dh_destroy_seq_hist(cntx);
2N/A free(cntx->remote);
2N/A free(cntx->local);
2N/A Free(cntx->keys);
2N/A Free(cntx);
2N/A
2N/A /* Set context to NULL */
2N/A *context = NULL;
2N/A
2N/A return (GSS_S_COMPLETE);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Diffie-Hellman mechanism currently does not support exporting and importing
2N/A * gss contexts.
2N/A */
2N/A
2N/AOM_uint32
2N/A/*ARGSUSED*/
2N/A__dh_gss_export_sec_context(OM_uint32 *minor,
2N/A gss_ctx_id_t *context, gss_buffer_t token)
2N/A{
2N/A return (GSS_S_UNAVAILABLE);
2N/A}
2N/A
2N/AOM_uint32
2N/A/*ARGSUSED*/
2N/A__dh_gss_import_sec_context(OM_uint32 *minor,
2N/A gss_buffer_t token, gss_ctx_id_t *context)
2N/A{
2N/A return (GSS_S_UNAVAILABLE);
2N/A}
2N/A
2N/A/*
2N/A * Get the state of a Diffie-Hellman context
2N/A */
2N/A
2N/AOM_uint32
2N/A__dh_gss_inquire_context(
2N/A OM_uint32 *minor, /* Mechanism status */
2N/A gss_ctx_id_t context, /* GSS context */
2N/A gss_name_t *initiator, /* Name of initiator */
2N/A gss_name_t *acceptor, /* Name of acceptor */
2N/A OM_uint32 *time_rec, /* Amount of time left */
2N/A gss_OID *mech, /* return OID of mechanism */
2N/A OM_uint32 *flags_rec, /* flags set on context */
2N/A int *local, /* True if we're the initiator */
2N/A int *open /* True if the context is established */)
2N/A{
2N/A dh_gss_context_t cntx;
2N/A OM_uint32 stat = GSS_S_COMPLETE;
2N/A OM_uint32 t;
2N/A
2N/A /* context is a Diffie-Hellman */
2N/A cntx = (dh_gss_context_t)context;
2N/A
2N/A /* Validate the context */
2N/A if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
2N/A return (GSS_S_NO_CONTEXT);
2N/A
2N/A /* If the caller wants the mechanism OID set *mech to if we can */
2N/A if (mech) {
2N/A *mech = &OID;
2N/A }
2N/A
2N/A /* set t to be the time left on the context */
2N/A if (cntx->expire == GSS_C_INDEFINITE)
2N/A t = GSS_C_INDEFINITE;
2N/A else {
2N/A time_t now = time(0);
2N/A t = now > cntx->expire ? 0 : (OM_uint32)(cntx->expire - now);
2N/A }
2N/A
2N/A /* If the caller wants the initiator set *initiator to it. */
2N/A if (initiator) {
2N/A dh_principal p = cntx->initiate ? cntx->local : cntx->remote;
2N/A *initiator = (gss_name_t)strdup(p);
2N/A }
2N/A
2N/A /* If the callers wants the acceptor set *acceptor to it. */
2N/A if (acceptor) {
2N/A dh_principal p = cntx->initiate ? cntx->remote : cntx->local;
2N/A *acceptor = (gss_name_t)strdup(p);
2N/A }
2N/A
2N/A /* If the caller wants the time remaining set *time_rec to t */
2N/A if (time_rec)
2N/A *time_rec = t;
2N/A
2N/A
2N/A /* Return the flags in flags_rec if set */
2N/A if (flags_rec)
2N/A *flags_rec = cntx->flags;
2N/A
2N/A /* ditto for local */
2N/A if (local)
2N/A *local = cntx->initiate;
2N/A
2N/A /* ditto for open */
2N/A if (open)
2N/A *open = (cntx->state == ESTABLISHED);
2N/A
2N/A
2N/A /* return GSS_S_CONTEXT_EXPIRED if no time is left on the context */
2N/A return ((t == 0 ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE) | stat);
2N/A}
2N/A
2N/A/*
2N/A * __dh_gss_process_context_token.
2N/A * This routine is not implemented. It is depricated in version 2.
2N/A */
2N/A
2N/AOM_uint32
2N/A/*ARGSUSED*/
2N/A__dh_gss_process_context_token(OM_uint32 *minor,
2N/A gss_ctx_id_t context, gss_buffer_t token)
2N/A{
2N/A return (GSS_S_UNAVAILABLE);
2N/A}
2N/A
2N/A/*
2N/A * This implements the gss_wrap_size_limit entry point for Diffie-Hellman
2N/A * mechanism. See RFC 2078 for details. The idea here is for a context,
2N/A * qop, whether confidentiality is specified, and an output size, return
2N/A * the maximum input size that will fit in the given output size. Typically
2N/A * the output size would be the MTU of the higher level protocol using the
2N/A * GSS-API.
2N/A */
2N/A
2N/AOM_uint32
2N/A__dh_gss_wrap_size_limit(
2N/A OM_uint32 *minor, /* Mechanism status */
2N/A gss_ctx_id_t context, /* GSS context handle */
2N/A int conf_req, /* True if confidentiality is wanted */
2N/A gss_qop_t qop_req, /* Requested QOP */
2N/A OM_uint32 output_size, /* The maximum ouput size */
2N/A OM_uint32 *input_size /* Input size returned */)
2N/A{
2N/A OM_uint32 major, stat = GSS_S_COMPLETE;
2N/A unsigned int msgsize, sigsize, pad = 1, size;
2N/A dh_token_desc token;
2N/A dh_wrap_t wrap = &token.ver.dh_version_u.body.dh_token_body_desc_u.seal;
2N/A OM_uint32 left;
2N/A
2N/A if (input_size == 0)
2N/A stat = GSS_S_CALL_INACCESSIBLE_WRITE;
2N/A
2N/A /* We check for valid unexpired context by calling gss_context_time. */
2N/A if ((major = stat | __dh_gss_context_time(minor, context, &left))
2N/A != GSS_S_COMPLETE)
2N/A return (major | stat);
2N/A
2N/A /* Find the signature size for this qop. */
2N/A if ((*minor = __get_sig_size(qop_req, &sigsize)) != DH_SUCCESS)
2N/A return (GSS_S_BAD_QOP | stat);
2N/A
2N/A /* Just return if we can't give the caller what he ask for. */
2N/A if (stat)
2N/A return (stat);
2N/A
2N/A /*
2N/A * If we requested confidentiality, get the cipher pad for the
2N/A * requested qop. Since we can't support privacy the cipher pad
2N/A * is always 1.
2N/A */
2N/A if (conf_req)
2N/A pad = 1;
2N/A
2N/A /*
2N/A * Set up an empty wrap token to calculate header and signature
2N/A * overhead.
2N/A */
2N/A
2N/A token.ver.verno = DH_PROTO_VERSION;
2N/A token.ver.dh_version_u.body.type = DH_WRAP;
2N/A wrap->mic.qop = qop_req;
2N/A wrap->mic.seqnum = 0;
2N/A wrap->mic.client_flag = 0;
2N/A wrap->body.body_len = 0;
2N/A wrap->body.body_val = 0;
2N/A token.verifier.dh_signature_len = sigsize;
2N/A token.verifier.dh_signature_val = 0;
2N/A
2N/A /* This is the size of an empy wrap token */
2N/A size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)&token);
2N/A
2N/A /* This is the amount of space left to put our message. */
2N/A msgsize = (output_size > size) ? output_size - size : 0;
2N/A
2N/A /* XDR needs to pad to a four byte boundry */
2N/A msgsize = (msgsize / 4) * 4;
2N/A
2N/A /* We need to pad to pad bytes for encryption (=1 if conf_req = 0) */
2N/A msgsize = (msgsize / pad) * pad;
2N/A
2N/A /*
2N/A * The serialization of the inner message includes
2N/A * the original length.
2N/A */
2N/A
2N/A msgsize = (msgsize > sizeof (uint_t)) ? msgsize - sizeof (uint_t) : 0;
2N/A
2N/A /*
2N/A * We now have the space for the inner wrap message, which is also
2N/A * XDR encoded and is padded to a four byte boundry.
2N/A */
2N/A
2N/A msgsize = (msgsize / 4) * 4;
2N/A
2N/A *input_size = msgsize;
2N/A
2N/A return (GSS_S_COMPLETE);
2N/A}