root/usr/src/lib/gss_mechs/mech_dummy/mech/dmech.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright (c) 2016 by Delphix. All rights reserved.
 *
 * A module that implements a dummy security mechanism.
 * It's mainly used to test GSS-API application. Multiple tokens
 * exchanged during security context establishment can be
 * specified through dummy_mech.conf located in /etc.
 *
 */
#ifndef lint
#define dummy_gss_accept_sec_context \
                dummy_867227349
#define dummy_gss_acquire_cred \
                dummy_352458907
#define dummy_gss_add_cred \
                dummy_911432290
#define dummy_gss_compare_name \
                dummy_396663848
#define dummy_gss_context_time \
                dummy_955669998
#define dummy_gss_delete_sec_context \
                dummy_440868788
#define dummy_gss_display_name \
                dummy_999874939
#define dummy_gss_display_status \
                dummy_485073729
#define dummy_gss_export_sec_context \
                dummy_1044079879
#define dummy_gss_import_name \
                dummy_529311438
#define dummy_gss_import_sec_context \
                dummy_14542996
#define dummy_gss_indicate_mechs \
                dummy_573516378
#define dummy_gss_init_sec_context \
                dummy_58780705
#define dummy_gss_inquire_context \
                dummy_617721319
#define dummy_gss_inquire_cred \
                dummy_102985645
#define dummy_gss_inquire_cred_by_mech \
                dummy_661926260
#define dummy_gss_inquire_names_for_mech \
                dummy_147190586
#define dummy_gss_internal_release_oid \
                dummy_706163968
#define dummy_gss_process_context_token \
                dummy_191395526
#define dummy_gss_release_cred \
                dummy_750368909
#define dummy_gss_release_name \
                dummy_235600467
#define dummy_gss_seal \
                dummy_794573849
#define dummy_gss_sign \
                dummy_279838176
#define dummy_gss_unseal \
                dummy_838778790
#define dummy_gss_verify \
                dummy_324010348
#define dummy_gss_wrap_size_limit \
                dummy_882983731
#define dummy_pname_to_uid \
                dummy_345475423
#endif

#include <stdio.h>
#include <stdlib.h>
#include <gssapiP_dummy.h>
#include <mechglueP.h>
#include <gssapi_err_generic.h>

#define dummy_context_name_len  19
/* private routines for dummy_mechanism */
static dummy_token_t make_dummy_token(char *name);
static void free_dummy_token(dummy_token_t *token);
static gss_buffer_desc make_dummy_token_buffer(char *name);
static gss_buffer_desc make_dummy_token_msg(void *data, int datalen);
static int der_length_size(int length);
static void der_write_length(unsigned char ** buf, int length);
static int der_read_length(unsigned char **buf, int *bufsize);
static int g_token_size(gss_OID mech, unsigned int body_size);
static void g_make_token_header(gss_OID mech, int body_size,
                                unsigned char **buf, int tok_type);
static int g_verify_token_header(gss_OID mech, int *body_size,
                                unsigned char **buf_in, int tok_type,
                                int toksize);


/* private global variables */
static char dummy_srcname[] = "dummy source";
static OM_uint32 dummy_flags;
static int token_nums;

/*
 * The Mech OID:
 * { iso(1) org(3) internet(6) dod(1) private(4) enterprises(1) sun(42)
 *  products(2) gssapi(26) mechtypes(1) dummy(2) }
 */
static struct gss_config dummy_mechanism =
        {{10, "\053\006\001\004\001\052\002\032\001\002"},
        NULL,
        dummy_gss_acquire_cred,
        dummy_gss_release_cred,
        dummy_gss_init_sec_context,
        dummy_gss_accept_sec_context,
        dummy_gss_unseal,
        dummy_gss_process_context_token,
        dummy_gss_delete_sec_context,
        dummy_gss_context_time,
        dummy_gss_display_status,
        dummy_gss_indicate_mechs,
        dummy_gss_compare_name,
        dummy_gss_display_name,
        dummy_gss_import_name,
        dummy_gss_release_name,
        dummy_gss_inquire_cred,
        dummy_gss_add_cred,
        dummy_gss_seal,
        dummy_gss_export_sec_context,
        dummy_gss_import_sec_context,
        dummy_gss_inquire_cred_by_mech,
        dummy_gss_inquire_names_for_mech,
        dummy_gss_inquire_context,
        dummy_gss_internal_release_oid,
        dummy_gss_wrap_size_limit,
        dummy_pname_to_uid,
        NULL,   /* __gss_userok */
        NULL,   /* _export name */
        dummy_gss_sign,
        dummy_gss_verify,
        NULL,   /* _store_cred */
};

gss_mechanism
gss_mech_initialize(oid)
const gss_OID oid;
{
        FILE *fp;

        dprintf("Entering gss_mech_initialize\n");

        if (oid == NULL ||
                !g_OID_equal(oid, &dummy_mechanism.mech_type)) {
                fprintf(stderr, "invalid dummy mechanism oid.\n");
                return (NULL);
        }

        fp = fopen("/etc/dummy_mech_token.conf", "rF");
        if (fp == NULL) {
                fprintf(stderr, "dummy_mech.conf is not found.\n");
                fprintf(stderr, "Setting number tokens exchanged to 1\n");
                token_nums = 1;
        } else {
                fscanf(fp, "%d", &token_nums);
                fclose(fp);
                dprintf("dummy_mech.conf is found.\n");
                dprintf1("Setting number tokens exchanged to %d\n", token_nums);
        }

        if (token_nums == 1)
                dummy_flags = GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
        else
                dummy_flags = GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG
                                | GSS_C_MUTUAL_FLAG;

        dprintf("Leaving gss_mech_initialize\n");
        return (&dummy_mechanism);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_acquire_cred(ctx, minor_status, desired_name, time_req, desired_mechs,
                        cred_usage, output_cred_handle,
                        actual_mechs, time_rec)
        void *ctx;
        OM_uint32 *minor_status;
        gss_name_t desired_name;
        OM_uint32 time_req;
        gss_OID_set desired_mechs;
        gss_cred_usage_t cred_usage;
        gss_cred_id_t *output_cred_handle;
        gss_OID_set *actual_mechs;
        OM_uint32 *time_rec;
{
        dprintf("Entering dummy_gss_acquire_cred\n");

        if (actual_mechs)
                *actual_mechs = NULL;
        if (time_rec)
                *time_rec = 0;

        *output_cred_handle = (gss_cred_id_t)
                                make_dummy_token("dummy_gss_acquire_cred");
        if (time_rec)  /* user may pass a null pointer */
                *time_rec = GSS_C_INDEFINITE;
        if (actual_mechs) {
                if (gss_copy_oid_set(minor_status, gss_mech_set_dummy,
                                actual_mechs) == GSS_S_FAILURE) {
                        return (GSS_S_FAILURE);
                }
        }

        dprintf("Leaving dummy_gss_acquire_cred\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_release_cred(ctx, minor_status, cred_handle)
        void *ctx;
        OM_uint32 *minor_status;
        gss_cred_id_t *cred_handle;
{
        dprintf("Entering dummy_gss_release_cred\n");

        free_dummy_token((dummy_token_t *)(cred_handle));
        *cred_handle = NULL;

        dprintf("Leaving dummy_gss_release_cred\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_init_sec_context(ct, minor_status, claimant_cred_handle,
                                context_handle, target_name, mech_type,
                                req_flags, time_req, input_chan_bindings,
                                input_token, actual_mech_type, output_token,
                                ret_flags, time_rec)
        void *ct;
        OM_uint32 *minor_status;
        gss_cred_id_t claimant_cred_handle;
        gss_ctx_id_t *context_handle;
        gss_name_t target_name;
        gss_OID mech_type;
        OM_uint32 req_flags;
        OM_uint32 time_req;
        gss_channel_bindings_t input_chan_bindings;
        gss_buffer_t input_token;
        gss_OID *actual_mech_type;
        gss_buffer_t output_token;
        OM_uint32 *ret_flags;
        OM_uint32 *time_rec;
{
        dummy_gss_ctx_id_t ctx;
        char token_string[64];
        OM_uint32 ret;
        OM_uint32 aret;
        int send_token = 0;

        dprintf("Entering init_sec_context\n");

        output_token->length = 0;
        output_token->value = NULL;
        if (actual_mech_type)
                *actual_mech_type = NULL;

        if (*context_handle == GSS_C_NO_CONTEXT) {

                if (input_token != NULL && input_token->value != NULL)
                        return (GSS_S_FAILURE);

                ctx = (dummy_gss_ctx_id_t)malloc(sizeof (dummy_gss_ctx_id_rec));
                ctx->established = 0;
                ctx->last_stat = 0xffffffff;
                *context_handle = (gss_ctx_id_t)ctx;
                /*
                 * Initiator interpretation of config file. If 2 or more
                 * the client returns CONTINUE_NNED on the first call.
                 */
                if (token_nums >= 2) {
                        ret = GSS_S_CONTINUE_NEEDED;
                } else {
                        ret = GSS_S_COMPLETE;
                }
                send_token = 1;
        } else {
                unsigned char *ptr;
                int bodysize;
                int err;

                if (input_token == NULL || input_token->value == NULL) {
                        ctx->last_stat = GSS_S_FAILURE;
                        return (GSS_S_FAILURE);
                }

                ctx = (dummy_gss_ctx_id_t)(*context_handle);


                ptr = (unsigned char *) input_token->value;
                if (err = g_verify_token_header((gss_OID)gss_mech_dummy,
                    &bodysize, &ptr, 0, input_token->length)) {

                        *minor_status = err;
                        ctx->last_stat = GSS_S_DEFECTIVE_TOKEN;
                        return (GSS_S_DEFECTIVE_TOKEN);
                }

                if (sscanf((char *)ptr, "%d", &aret) < 1) {
                        *minor_status = 1;
                        ctx->last_stat = GSS_S_DEFECTIVE_TOKEN;
                        return (GSS_S_DEFECTIVE_TOKEN);
                }

                if (aret == GSS_S_CONTINUE_NEEDED) {
                        if (ctx->last_stat == GSS_S_COMPLETE) {
                                /*
                                 * RFC 2078, page 36, under GSS_S_COMPLETE
                                 * says that acceptor (target) has sufficient
                                 * information to perform per-message
                                 * processing. So if initiator previously
                                 * returned GSS_S_COMPLETE, and acceptor
                                 * says it needs more, then we have
                                 * a problem.
                                 */
                                ctx->last_stat = GSS_S_FAILURE;
                                return (GSS_S_FAILURE);
                        }
                        ret = GSS_S_CONTINUE_NEEDED;
                        send_token = 1;
                } else {
                        ret = GSS_S_COMPLETE;
                        send_token = 0;
                }
        }
        if (ret_flags)  /* user may pass a null pointer */
                *ret_flags = dummy_flags;
        if (time_rec)  /* user may pass a null pointer */
                *time_rec = GSS_C_INDEFINITE;
        if (actual_mech_type)
                *actual_mech_type = (gss_OID) gss_mech_dummy;

        if (send_token == 1) {
                sprintf(token_string, "%d", ret);

                *output_token = make_dummy_token_msg(
                                token_string, strlen(token_string) + 1);
        } else {
                *output_token = make_dummy_token_msg(NULL, 0);
        }

        if (ret == GSS_S_COMPLETE)
                ctx->established = 1;

        ctx->last_stat = ret;
        return (ret);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_accept_sec_context(ct, minor_status, context_handle,
                                verifier_cred_handle, input_token,
                                input_chan_bindings, src_name, mech_type,
                                output_token, ret_flags, time_rec,
                                delegated_cred_handle)
        void *ct;
        OM_uint32 *minor_status;
        gss_ctx_id_t *context_handle;
        gss_cred_id_t verifier_cred_handle;
        gss_buffer_t input_token;
        gss_channel_bindings_t input_chan_bindings;
        gss_name_t *src_name;
        gss_OID *mech_type;
        gss_buffer_t output_token;
        OM_uint32 *ret_flags;
        OM_uint32 *time_rec;
        gss_cred_id_t *delegated_cred_handle;
{
        dummy_gss_ctx_id_t ctx;
        char token_string[64];
        gss_buffer_desc name;
        OM_uint32 status;
        gss_name_t temp;
        unsigned char *ptr;
        int bodysize;
        int err;
        OM_uint32 iret;
        int return_token = 0;

        dprintf("Entering accept_sec_context\n");

        if (src_name)
                *src_name = (gss_name_t)NULL;
        output_token->length = 0;
        output_token->value = NULL;
        if (mech_type)
                *mech_type = GSS_C_NULL_OID;
        /* return a bogus cred handle */
        if (delegated_cred_handle)
                *delegated_cred_handle = GSS_C_NO_CREDENTIAL;

        /* Check for defective input token. */
        ptr = (unsigned char *) input_token->value;
        if (err = g_verify_token_header((gss_OID)gss_mech_dummy, &bodysize,
                                        &ptr, 0,
                                        input_token->length)) {
                *minor_status = err;
                return (GSS_S_DEFECTIVE_TOKEN);
        }

        if (sscanf((char *)ptr, "%d", &iret) < 1) {
                *minor_status = 1;
                return (GSS_S_DEFECTIVE_TOKEN);
        }

        if (*context_handle == GSS_C_NO_CONTEXT) {
                ctx = (dummy_gss_ctx_id_t)malloc(sizeof (dummy_gss_ctx_id_rec));
                ctx->token_number = token_nums;
                ctx->established = 0;
                *context_handle = (gss_ctx_id_t)ctx;
        } else {
                ctx = (dummy_gss_ctx_id_t)(*context_handle);
        }

        if (ret_flags)  /* user may pass a null pointer */
                *ret_flags = dummy_flags;
        if (time_rec)  /* user may pass a null pointer */
                *time_rec = GSS_C_INDEFINITE;
        if (mech_type)
                *mech_type = (gss_OID)gss_mech_dummy;

        /*
         * RFC 2078, page 36, under GSS_S_COMPLETE, GSS_S_CONTINUE_NEEDED
         * tells us whether to return a token or not.
         */

        if (iret == GSS_S_CONTINUE_NEEDED)
                return_token = 1;
        else
                return_token = 0;


        if (ctx->token_number > 1) {
                /*
                 * RFC 2078, page 36, under GSS_S_COMPLETE, says that if
                 * initiator is done, the target (us) has what it needs, so
                 * it must return GSS_S_COMPLETE;
                 */
                if (iret == GSS_S_CONTINUE_NEEDED)
                        status = GSS_S_CONTINUE_NEEDED;
                else
                        status = GSS_S_COMPLETE;

        } else
                status = GSS_S_COMPLETE;

        /* source name is ready at GSS_S_COMPLELE */
        if ((status == GSS_S_COMPLETE) && src_name) {
                name.length = strlen(dummy_srcname);
                name.value = dummy_srcname;
                status = dummy_gss_import_name(ct, minor_status, &name,
                                (gss_OID)GSS_C_NT_USER_NAME, &temp);
                if (status != GSS_S_COMPLETE) {
                        free(*context_handle);
                        *context_handle = GSS_C_NO_CONTEXT;
                        return (status);
                }
                *src_name = temp;
        }

        if (status == GSS_S_COMPLETE) {
                ctx->established = 1;
        }

        if (return_token == 1) {
                sprintf(token_string, "%d", status);

                *output_token = make_dummy_token_msg(
                                token_string, strlen(token_string) + 1);
        } else {
                *output_token = make_dummy_token_msg(NULL, 0);
        }

        if (ctx->token_number > 0)
                ctx->token_number--;

        return (status);
}


/*ARGSUSED*/
OM_uint32
dummy_gss_process_context_token(ct, minor_status, context_handle, token_buffer)
        void *ct;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        gss_buffer_t token_buffer;
{
        dprintf("In process_sec_context\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_delete_sec_context(ct, minor_status, context_handle, output_token)
        void *ct;
        OM_uint32 *minor_status;
        gss_ctx_id_t *context_handle;
        gss_buffer_t output_token;
{
        dummy_gss_ctx_id_t ctx;

        dprintf("Entering delete_sec_context\n");

        /* Make the length to 0, so the output token is not sent to peer */
        if (output_token) {
                output_token->length = 0;
                output_token->value = NULL;
        }

        if (*context_handle == GSS_C_NO_CONTEXT) {
                *minor_status = 0;
                return (GSS_S_COMPLETE);
        }

        ctx = (dummy_gss_ctx_id_t)*context_handle;
        free(ctx);
        *context_handle = GSS_C_NO_CONTEXT;

        dprintf("Leaving delete_sec_context\n");
        return (GSS_S_COMPLETE);
}


/*ARGSUSED*/
OM_uint32
dummy_gss_context_time(ct, minor_status, context_handle, time_rec)
        void *ct;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        OM_uint32 *time_rec;
{
        dprintf("In context_time\n");
        if (time_rec)  /* user may pass a null pointer */
                return (GSS_S_FAILURE);
        else
                *time_rec = GSS_C_INDEFINITE;
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_sign(ctx, minor_status, context_handle,
                qop_req, message_buffer, message_token)
        void *ctx;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        int qop_req;
        gss_buffer_t message_buffer;
        gss_buffer_t message_token;
{
        char token_string[] = "dummy_gss_sign";
        dummy_gss_ctx_id_t context;

        dprintf("Entering gss_sign\n");

        context = (dummy_gss_ctx_id_t)(context_handle);
        if (context_handle == GSS_C_NO_CONTEXT)
                return (GSS_S_NO_CONTEXT);
        if (!context->established)
                return (GSS_S_NO_CONTEXT);

        *message_token = make_dummy_token_msg(
                        token_string, strlen(token_string));

        dprintf("Leaving gss_sign\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_verify(ctx, minor_status, context_handle,
                message_buffer, token_buffer, qop_state)
        void *ctx;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        gss_buffer_t message_buffer;
        gss_buffer_t token_buffer;
        int *qop_state;
{
        unsigned char *ptr;
        int bodysize;
        int err;
        dummy_gss_ctx_id_t context;

        dprintf("Entering gss_verify\n");

        context = (dummy_gss_ctx_id_t)(context_handle);
        if (context_handle == GSS_C_NO_CONTEXT)
                return (GSS_S_NO_CONTEXT);
        if (!context->established)
                return (GSS_S_NO_CONTEXT);

        /* Check for defective input token. */
        ptr = (unsigned char *) token_buffer->value;
        if (err = g_verify_token_header((gss_OID)gss_mech_dummy, &bodysize,
                                        &ptr, 0,
                                        token_buffer->length)) {
                *minor_status = err;
                return (GSS_S_DEFECTIVE_TOKEN);
        }

        if (qop_state)
                *qop_state = GSS_C_QOP_DEFAULT;

        dprintf("Leaving gss_verify\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_seal(ctx, minor_status, context_handle, conf_req_flag,
                qop_req, input_message_buffer, conf_state,
                output_message_buffer)
        void *ctx;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        int conf_req_flag;
        int qop_req;
        gss_buffer_t input_message_buffer;
        int *conf_state;
        gss_buffer_t output_message_buffer;
{
        gss_buffer_desc output;
        dummy_gss_ctx_id_t context;

        dprintf("Entering gss_seal\n");

        context = (dummy_gss_ctx_id_t)(context_handle);
        if (context_handle == GSS_C_NO_CONTEXT)
                return (GSS_S_NO_CONTEXT);
        if (!context->established)
                return (GSS_S_NO_CONTEXT);

        /* Copy the input message to output message */
        output = make_dummy_token_msg(
                input_message_buffer->value, input_message_buffer->length);

        if (conf_state)
                *conf_state = 1;

        *output_message_buffer = output;

        dprintf("Leaving gss_seal\n");
        return (GSS_S_COMPLETE);
}




/*ARGSUSED*/
OM_uint32
dummy_gss_unseal(ctx, minor_status, context_handle,
                input_message_buffer, output_message_buffer,
                conf_state, qop_state)
        void *ctx;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        gss_buffer_t input_message_buffer;
        gss_buffer_t output_message_buffer;
        int *conf_state;
        int *qop_state;
{
        gss_buffer_desc output;
        unsigned char *ptr;
        int bodysize;
        int err;
        dummy_gss_ctx_id_t context;

        dprintf("Entering gss_unseal\n");

        context = (dummy_gss_ctx_id_t)(context_handle);
        if (context_handle == GSS_C_NO_CONTEXT)
                return (GSS_S_NO_CONTEXT);
        if (!context->established)
                return (GSS_S_NO_CONTEXT);

        ptr = (unsigned char *) input_message_buffer->value;
        if (err = g_verify_token_header((gss_OID)gss_mech_dummy, &bodysize,
                                        &ptr, 0,
                                        input_message_buffer->length)) {
                *minor_status = err;
                return (GSS_S_DEFECTIVE_TOKEN);
        }
        output.length = bodysize;
        output.value = (void *)malloc(output.length);
        memcpy(output.value, ptr, output.length);

        *output_message_buffer = output;
        if (qop_state)
                *qop_state = GSS_C_QOP_DEFAULT;
        if (conf_state)
                *conf_state = 1;

        dprintf("Leaving gss_unseal\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_display_status(ctx, minor_status, status_value, status_type,
                        mech_type, message_context, status_string)
        void *ctx;
        OM_uint32 *minor_status;
        OM_uint32 status_value;
        int status_type;
        gss_OID mech_type;
        OM_uint32 *message_context;
        gss_buffer_t status_string;
{
        dprintf("Entering display_status\n");

        *message_context = 0;
        *status_string = make_dummy_token_buffer("dummy_gss_display_status");

        dprintf("Leaving display_status\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_indicate_mechs(ctx, minor_status, mech_set)
        void *ctx;
        OM_uint32 *minor_status;
        gss_OID_set *mech_set;
{
        dprintf("Entering indicate_mechs\n");

        *minor_status = 0;
        if (mech_set) {
                if (gss_copy_oid_set(minor_status, gss_mech_set_dummy,
                                mech_set) == GSS_S_FAILURE) {
                        return (GSS_S_FAILURE);
                }
        }

        dprintf("Leaving indicate_mechs\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_compare_name(ctx, minor_status, name1, name2, name_equal)
        void *ctx;
        OM_uint32 *minor_status;
        gss_name_t name1;
        gss_name_t name2;
        int *name_equal;
{
        dummy_name_t name_1 = (dummy_name_t)name1;
        dummy_name_t name_2 = (dummy_name_t)name2;

        dprintf("Entering compare_name\n");

        if (g_OID_equal(name_1->type, name_2->type) &&
        (name_1->buffer->length == name_2->buffer->length) &&
        !memcmp(name_1->buffer->value, name_2->buffer->value,
        name_1->buffer->length))
                *name_equal = 1;
        else
                *name_equal = 0;

        dprintf("Leaving compare_name\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_display_name(ctx, minor_status, input_name, output_name_buffer,
                        output_name_type)
        void *ctx;
        OM_uint32 *minor_status;
        gss_name_t input_name;
        gss_buffer_t output_name_buffer;
        gss_OID *output_name_type;
{
        OM_uint32 status = GSS_S_COMPLETE;
        dummy_name_t name = (dummy_name_t)input_name;

        dprintf("Entering display_name\n");

        if (g_OID_equal(name->type, GSS_C_NT_USER_NAME) ||
        g_OID_equal(name->type, GSS_C_NT_MACHINE_UID_NAME) ||
        g_OID_equal(name->type, GSS_C_NT_STRING_UID_NAME) ||
        g_OID_equal(name->type, GSS_C_NT_HOSTBASED_SERVICE)) {
/*
 *              output_name_buffer = (gss_buffer_t)
 *                                      malloc(sizeof (gss_buffer_desc));
 */
                if (output_name_buffer == NULL)
                        return (GSS_S_FAILURE);

                output_name_buffer->length = name->buffer->length;
                output_name_buffer->value = (void *)
                                                malloc(name->buffer->length);
                if (output_name_buffer->value == NULL)
                        return (GSS_S_FAILURE);

                memcpy(output_name_buffer->value, name->buffer->value,
                        name->buffer->length);
                if (output_name_type)
                        *output_name_type = name->type;

                dprintf("Leaving display_name\n");
                return (status);
        }

        dprintf("Leaving display_name\n");
        return (GSS_S_BAD_NAMETYPE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_import_name(ctx, minor_status, input_name_buffer,
                        input_name_type, output_name)
        void *ctx;
        OM_uint32 *minor_status;
        gss_buffer_t input_name_buffer;
        gss_OID input_name_type;
        gss_name_t *output_name;
{
        OM_uint32 status;

        dprintf("Entering import_name\n");

        *output_name = NULL;
        *minor_status = 0;

        if (input_name_type == GSS_C_NULL_OID)
                return (GSS_S_BAD_NAMETYPE);

        if (g_OID_equal(input_name_type, GSS_C_NT_USER_NAME) ||
        g_OID_equal(input_name_type, GSS_C_NT_MACHINE_UID_NAME) ||
        g_OID_equal(input_name_type, GSS_C_NT_STRING_UID_NAME) ||
        g_OID_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE)) {
                dummy_name_t name = (dummy_name_t)
                                        malloc(sizeof (dummy_name_desc));
                name->buffer = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
                name->buffer->length = input_name_buffer->length;
                name->buffer->value = (void *)malloc(input_name_buffer->length);
                if (name->buffer->value == NULL)
                        return (GSS_S_FAILURE);

                memcpy(name->buffer->value, input_name_buffer->value,
                                input_name_buffer->length);

                status = generic_gss_copy_oid(minor_status,
                input_name_type, &(name->type));
                *output_name = (gss_name_t)name;
                dprintf("Leaving import_name\n");
                return (status);
        }
        dprintf("Leaving import_name\n");
        return (GSS_S_BAD_NAMETYPE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_release_name(ctx, minor_status, input_name)
        void *ctx;
        OM_uint32 *minor_status;
        gss_name_t *input_name;
{
        dummy_name_t name = (dummy_name_t)*input_name;

        dprintf("Entering release_name\n");
        free(name->buffer->value);
        generic_gss_release_oid(minor_status, &(name->type));
        free(name->buffer);
        free(name);
        dprintf("Leaving release_name\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret,
                        cred_usage, mechanisms)
        void *ctx;
        OM_uint32 *minor_status;
        gss_cred_id_t cred_handle;
        gss_name_t *name;
        OM_uint32 *lifetime_ret;
        gss_cred_usage_t *cred_usage;
        gss_OID_set *mechanisms;
{
        dprintf("Entering inquire_cred\n");
        if (name)
                *name = (gss_name_t)make_dummy_token
                                ("dummy gss credential");
        if (lifetime_ret)
                *lifetime_ret = GSS_C_INDEFINITE;
        if (cred_usage)
                *cred_usage = GSS_C_BOTH;
        if (mechanisms) {
                if (gss_copy_oid_set(minor_status, gss_mech_set_dummy,
                                mechanisms) == GSS_S_FAILURE)
                        return (GSS_S_FAILURE);
        }

        dprintf("Leaving inquire_cred\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_add_cred(ctx, minor_status, input_cred_handle,
                        desired_name, desired_mech, cred_usage,
                        initiator_time_req, acceptor_time_req,
                        output_cred_handle, actual_mechs,
                        initiator_time_rec, acceptor_time_rec)
        void *ctx;
        OM_uint32 *minor_status;
        gss_cred_id_t input_cred_handle;
        gss_name_t desired_name;
        gss_OID desired_mech;
        gss_cred_usage_t cred_usage;
        OM_uint32 initiator_time_req;
        OM_uint32 acceptor_time_req;
        gss_cred_id_t *output_cred_handle;
        gss_OID_set *actual_mechs;
        OM_uint32 *initiator_time_rec;
        OM_uint32 *acceptor_time_rec;
{
        dprintf("Entering add_cred\n");

        if ((desired_mech != GSS_C_NULL_OID) &&
        (g_OID_equal(desired_mech, gss_mech_dummy)))
                return (GSS_S_BAD_MECH);
        *minor_status = 0;

        dprintf("Leaving add_cred\n");

        /* This routine likes in kerberos V5 is never be used / called by */
        /* the GSS_API. It simply returns GSS_S_DUPLICATE_ELEMENT to indicate */
        /* this error */

        return (GSS_S_DUPLICATE_ELEMENT);
}

/* Should I add the token structure to deal with import/export */
/* of sec_context. For now, I just create dummy interprocess token, and when */
/* the peer accept it, it calls the import_sec_context.The import_sec_context */
/* creates new sec_context with status established. (rather than get it */
/* from interprocess token. it can be done because the sec context in dummy */
/* mechanism is very simple (contains only status if it's established). */
/*ARGSUSED*/
OM_uint32
dummy_gss_export_sec_context(ct, minor_status, context_handle,
                                interprocess_token)
        void *ct;
        OM_uint32 *minor_status;
        gss_ctx_id_t *context_handle;
        gss_buffer_t interprocess_token;
{
        char str[] = "dummy_gss_export_sec_context";

        dprintf("Entering export_sec_context\n");

        *interprocess_token = make_dummy_token_msg(str, strlen(str));
        free(*context_handle);
        *context_handle = GSS_C_NO_CONTEXT;

        dprintf("Leaving export_sec_context\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_import_sec_context(ct, minor_status, interprocess_token,
                                context_handle)
void *ct;
OM_uint32 *minor_status;
gss_buffer_t interprocess_token;
gss_ctx_id_t *context_handle;
{
        /* Assume that we got ctx from the interprocess token. */
        dummy_gss_ctx_id_t ctx;

        dprintf("Entering import_sec_context\n");

        ctx = (dummy_gss_ctx_id_t)malloc(sizeof (dummy_gss_ctx_id_rec));
        ctx->token_number = 0;
        ctx->established = 1;

        *context_handle = (gss_ctx_id_t)ctx;

        dprintf("Leaving import_sec_context\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_inquire_cred_by_mech(ctx, minor_status, cred_handle,
                                mech_type, name, initiator_lifetime,
                                acceptor_lifetime, cred_usage)
        void *ctx;
        OM_uint32 *minor_status;
        gss_cred_id_t cred_handle;
        gss_OID mech_type;
        gss_name_t *name;
        OM_uint32 *initiator_lifetime;
        OM_uint32 *acceptor_lifetime;
        gss_cred_usage_t *cred_usage;
{
        dprintf("Entering inquire_cred_by_mech\n");
        if (name)
                *name = (gss_name_t)make_dummy_token("dummy credential name");
        if (initiator_lifetime)
                *initiator_lifetime = GSS_C_INDEFINITE;
        if (acceptor_lifetime)
                *acceptor_lifetime = GSS_C_INDEFINITE;
        if (cred_usage)
                *cred_usage = GSS_C_BOTH;

        dprintf("Leaving inquire_cred_by_mech\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_inquire_names_for_mech(ctx, minor_status, mechanism, name_types)
        void            *ctx;
        OM_uint32       *minor_status;
        gss_OID         mechanism;
        gss_OID_set     *name_types;
{
        OM_uint32   major, minor;

        dprintf("Entering inquire_names_for_mech\n");
        /*
         * We only know how to handle our own mechanism.
         */
        if ((mechanism != GSS_C_NULL_OID) &&
        !g_OID_equal(gss_mech_dummy, mechanism)) {
                *minor_status = 0;
                return (GSS_S_FAILURE);
        }

        major = gss_create_empty_oid_set(minor_status, name_types);
        if (major == GSS_S_COMPLETE) {
                /* Now add our members. */
                if (((major = gss_add_oid_set_member(minor_status,
                        (gss_OID) GSS_C_NT_USER_NAME, name_types))
                == GSS_S_COMPLETE) &&
                ((major = gss_add_oid_set_member(minor_status,
                        (gss_OID) GSS_C_NT_MACHINE_UID_NAME, name_types))
                == GSS_S_COMPLETE) &&
                ((major = gss_add_oid_set_member(minor_status,
                        (gss_OID) GSS_C_NT_STRING_UID_NAME, name_types))
                == GSS_S_COMPLETE)) {
                        major = gss_add_oid_set_member(minor_status,
                        (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, name_types);
                }

                if (major != GSS_S_COMPLETE)
                        (void) gss_release_oid_set(&minor, name_types);
        }

        dprintf("Leaving inquire_names_for_mech\n");
        return (major);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_inquire_context(ct, minor_status, context_handle, initiator_name,
                        acceptor_name, lifetime_rec, mech_type, ret_flags,
                        locally_initiated, open)
        void *ct;
        OM_uint32 *minor_status;
        gss_ctx_id_t context_handle;
        gss_name_t *initiator_name;
        gss_name_t *acceptor_name;
        OM_uint32 *lifetime_rec;
        gss_OID *mech_type;
        OM_uint32 *ret_flags;
        int *locally_initiated;
        int *open;
{
        dummy_gss_ctx_id_t ctx;
        dummy_name_t name1, name2;
        OM_uint32 status;

        dprintf("Entering inquire_context\n");

        ctx = (dummy_gss_ctx_id_t)(context_handle);
        name1 = (dummy_name_t)
                                malloc(sizeof (dummy_name_desc));
        name1->buffer = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
        name1->buffer->length = dummy_context_name_len;
        name1->buffer->value = make_dummy_token("dummy context name");
        status = generic_gss_copy_oid(minor_status,
                (gss_OID) GSS_C_NT_USER_NAME, &(name1->type));
        if (status != GSS_S_COMPLETE)
                return (status);
        if (initiator_name)
                *initiator_name = (gss_name_t)name1;

        name2 = (dummy_name_t)
                                malloc(sizeof (dummy_name_desc));
        name2->buffer = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
        name2->buffer->length = dummy_context_name_len;
        name2->buffer->value = make_dummy_token("dummy context name");
        status = generic_gss_copy_oid(minor_status,
                (gss_OID) GSS_C_NT_USER_NAME, &(name2->type));
        if (status != GSS_S_COMPLETE)
                return (status);
        if (acceptor_name)
                *acceptor_name = (gss_name_t)name2;

        if (lifetime_rec)  /* user may pass a null pointer */
                *lifetime_rec = GSS_C_INDEFINITE;
        if (mech_type)
                *mech_type = (gss_OID)gss_mech_dummy;
        if (ret_flags)
                *ret_flags = dummy_flags;
        if (open)
        *open = ctx->established;

        dprintf("Leaving inquire_context\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_internal_release_oid(ct, minor_status, oid)
        void            *ct;
        OM_uint32       *minor_status;
        gss_OID         *oid;
{
        dprintf("Entering internal_release_oid\n");

        /* Similar to krb5_gss_internal_release_oid */

        if (*oid != gss_mech_dummy)
                return (GSS_S_CONTINUE_NEEDED); /* We don't know this oid */

        *minor_status = 0;
        *oid = GSS_C_NO_OID;

        dprintf("Leaving internal_release_oid\n");
        return (GSS_S_COMPLETE);
}

/*ARGSUSED*/
OM_uint32
dummy_gss_wrap_size_limit(ct, minor_status, context_handle, conf_req_flag,
                                qop_req, req_output_size, max_input_size)
        void            *ct;
        OM_uint32       *minor_status;
        gss_ctx_id_t    context_handle;
        int             conf_req_flag;
        gss_qop_t       qop_req;
        OM_uint32       req_output_size;
        OM_uint32       *max_input_size;
{
        dprintf("Entering wrap_size_limit\n");
        *max_input_size = req_output_size;
        dprintf("Leaving wrap_size_limit\n");
        return (GSS_S_COMPLETE);
}

/* ARGSUSED */
OM_uint32
dummy_pname_to_uid(ct, minor_status, name, uidOut)
        void *ct;
        OM_uint32 *minor_status;
        const gss_name_t name;
        uid_t *uidOut;
{
        dprintf("Entering pname_to_uid\n");
        *minor_status = 0;
        *uidOut = 60001;
        dprintf("Leaving pname_to_uid\n");
        return (GSS_S_COMPLETE);
}

static dummy_token_t
make_dummy_token(char *name)
{
        dummy_token_t token;

        token = (dummy_token_t)malloc(strlen(name)+1);
        strcpy(token, name);
        return (token);
}

static void
free_dummy_token(dummy_token_t *token)
{
        free(*token);
        *token = NULL;
}

static gss_buffer_desc
make_dummy_token_buffer(char *name)
{
        gss_buffer_desc buffer;

        if (name == NULL) {
                buffer.length = 0;
                buffer.value = NULL;
        } else {
                buffer.length = strlen(name)+1;
                buffer.value = make_dummy_token(name);
        }
        return (buffer);
}

static gss_buffer_desc
make_dummy_token_msg(void *data, int dataLen)
{
        gss_buffer_desc buffer;
        int tlen;
        unsigned char *t;
        unsigned char *ptr;

        if (data == NULL) {
                buffer.length = 0;
                buffer.value = NULL;
                return (buffer);
        }

        tlen = g_token_size((gss_OID)gss_mech_dummy, dataLen);
        t = (unsigned char *) malloc(tlen);
        ptr = t;

        g_make_token_header((gss_OID)gss_mech_dummy, dataLen, &ptr, 0);
        memcpy(ptr, data, dataLen);

        buffer.length = tlen;
        buffer.value = (void *) t;
        return (buffer);
}

static int
der_length_size(length)
        int length;
{
        if (length < (1<<7))
                return (1);
        else if (length < (1<<8))
                return (2);
        else if (length < (1<<16))
                return (3);
        else if (length < (1<<24))
                return (4);
        else
                return (5);
}

static void
der_write_length(buf, length)
        unsigned char **buf;
        int length;
{
        if (length < (1<<7)) {
                *(*buf)++ = (unsigned char) length;
        } else {
                *(*buf)++ = (unsigned char) (der_length_size(length)+127);
                if (length >= (1<<24))
                        *(*buf)++ = (unsigned char) (length>>24);
                if (length >= (1<<16))
                        *(*buf)++ = (unsigned char) ((length>>16)&0xff);
                if (length >= (1<<8))
                        *(*buf)++ = (unsigned char) ((length>>8)&0xff);
                *(*buf)++ = (unsigned char) (length&0xff);
        }
}

static int
der_read_length(buf, bufsize)
unsigned char **buf;
int *bufsize;
{
        unsigned char sf;
        int ret;

        if (*bufsize < 1)
                return (-1);

        sf = *(*buf)++;
        (*bufsize)--;
        if (sf & 0x80) {
                if ((sf &= 0x7f) > ((*bufsize)-1))
                        return (-1);

                if (sf > DUMMY_SIZE_OF_INT)
                        return (-1);
                ret = 0;
                for (; sf; sf--) {
                ret = (ret<<8) + (*(*buf)++);
                (*bufsize)--;
        }
        } else {
                ret = sf;
        }

        return (ret);
}

static int
g_token_size(mech, body_size)
        gss_OID mech;
        unsigned int body_size;
{
        /* set body_size to sequence contents size */
        body_size += 4 + (int)mech->length;     /* NEED overflow check */
        return (1 + der_length_size(body_size) + body_size);
}

static void
g_make_token_header(mech, body_size, buf, tok_type)
        gss_OID mech;
        int body_size;
        unsigned char **buf;
        int tok_type;
{
        *(*buf)++ = 0x60;
        der_write_length(buf, 4 + mech->length + body_size);
        *(*buf)++ = 0x06;
        *(*buf)++ = (unsigned char) mech->length;
        TWRITE_STR(*buf, mech->elements, ((int)mech->length));
        *(*buf)++ = (unsigned char) ((tok_type>>8)&0xff);
        *(*buf)++ = (unsigned char) (tok_type&0xff);
}

static int
g_verify_token_header(mech, body_size, buf_in, tok_type, toksize)
gss_OID mech;
int *body_size;
unsigned char **buf_in;
int tok_type;
int toksize;
{
        unsigned char *buf = *buf_in;
        int seqsize;
        gss_OID_desc toid;
        int ret = 0;

        if ((toksize -= 1) < 0)
                return (G_BAD_TOK_HEADER);
        if (*buf++ != 0x60)
                return (G_BAD_TOK_HEADER);

        if ((seqsize = der_read_length(&buf, &toksize)) < 0)
                return (G_BAD_TOK_HEADER);

        if (seqsize != toksize)
                return (G_BAD_TOK_HEADER);

        if ((toksize -= 1) < 0)
                return (G_BAD_TOK_HEADER);
        if (*buf++ != 0x06)
                return (G_BAD_TOK_HEADER);

        if ((toksize -= 1) < 0)
                return (G_BAD_TOK_HEADER);
        toid.length = *buf++;

        if ((toksize -= toid.length) < 0)
                return (G_BAD_TOK_HEADER);
        toid.elements = buf;
        buf += toid.length;

        if (!g_OID_equal(&toid, mech))
                ret = G_WRONG_MECH;

        /*
         * G_WRONG_MECH is not returned immediately because it's more important
         * to return G_BAD_TOK_HEADER if the token header is in fact bad
         */

        if ((toksize -= 2) < 0)
                return (G_BAD_TOK_HEADER);

        if ((*buf++ != ((tok_type>>8)&0xff)) ||
            (*buf++ != (tok_type&0xff)))
                return (G_BAD_TOK_HEADER);

        if (!ret) {
                *buf_in = buf;
                *body_size = toksize;
        }

        return (ret);
}