static
void
import_names()
{
    OM_uint32                           major_status, minor_status;
    globus_gsi_cred_handle_t            handle;
    gss_buffer_desc                     buffer;
    X509 *                              cert;
    gss_OID_set                         name_types;
    globus_result_t                     result;
    globus_list_t                       *i, *j;
    compare_name_test_case_t *          test_case;
    int                                 present;

    major_status = gss_inquire_names_for_mech(
        &minor_status,
        (gss_OID) globus_i_gss_mech_globus_gssapi_openssl,
        &name_types);

    if (major_status == GSS_S_COMPLETE)
    {
        major_status = gss_test_oid_set_member(
                &minor_status,
                GLOBUS_GSS_C_NT_X509,
                name_types,
                &present);

        if (major_status == GSS_S_COMPLETE && present)
        {
            gss_l_x509_support = GLOBUS_TRUE;
        }

        major_status = gss_test_oid_set_member(
                &minor_status,
                GLOBUS_GSS_C_NT_HOST_IP,
                name_types,
                &present);

        if (major_status == GSS_S_COMPLETE && present)
        {
            gss_l_host_ip_support = GLOBUS_TRUE;
        }

        major_status = gss_release_oid_set(&minor_status, &name_types);
    }

    for (i = test_cases; !globus_list_empty(i); i = globus_list_rest(i))
    {
        test_case = globus_list_first(i);

        if (test_case->name1 == GSS_C_NO_NAME)
        {
            switch (test_case->name_type1)
            {
                case GSS_L_ANONYMOUS:
                    major_status = gss_import_name(&minor_status, &buffer, GSS_C_NT_ANONYMOUS, &test_case->name1);
                    if (major_status != GSS_S_COMPLETE)
                    {
                        fprintf(stderr, "Error importing <anonymous>\n");
                        globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                        exit(-1);
                    }
                    break;
                case GSS_L_NO_OID:
                    buffer.value = test_case->name_token1;
                    buffer.length = strlen(buffer.value);

                    major_status = gss_import_name(&minor_status, &buffer, GSS_C_NO_OID, &test_case->name1);
                    if (major_status != GSS_S_COMPLETE)
                    {
                        fprintf(stderr, "Error importing %s\n", test_case->name_token1);
                        globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                        exit(-1);
                    }
                    break;
                case GSS_L_HOSTBASED_SERVICE:
                    buffer.value = test_case->name_token1;
                    buffer.length = strlen(buffer.value);

                    major_status = gss_import_name(&minor_status, &buffer, GSS_C_NT_HOSTBASED_SERVICE, &test_case->name1);
                    if (major_status != GSS_S_COMPLETE)
                    {
                        fprintf(stderr, "Error importing %s\n", test_case->name_token1);
                        globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                        exit(-1);
                    }
                    break;
                case GSS_L_HOST_IP:
                    if (gss_l_host_ip_support)
                    {
                        buffer.value = test_case->name_token1;
                        buffer.length = strlen(buffer.value);

                        major_status = gss_import_name(&minor_status, &buffer, GLOBUS_GSS_C_NT_HOST_IP, &test_case->name1);
                        if (major_status != GSS_S_COMPLETE)
                        {
                            fprintf(stderr, "Error importing %s\n", test_case->name_token1);
                            globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                            exit(-1);
                        }
                    }
                    break;
                case GSS_L_X509:
                    if (gss_l_x509_support)
                    {
                        result = globus_gsi_cred_handle_init(&handle, NULL);
                        if (result != GLOBUS_SUCCESS)
                        {
                            globus_gsi_gssapi_test_print_result(stderr, result);
                            exit(-1);
                        }

                        result = globus_gsi_cred_read_cert(handle, test_case->name_token1);
                        if (result != GLOBUS_SUCCESS)
                        {
                            globus_gsi_gssapi_test_print_result(stderr, result);
                            exit(-2);
                        }

                        result = globus_gsi_cred_get_cert(handle, &cert);

                        buffer.value = cert;
                        buffer.length = sizeof(X509);

                        major_status = gss_import_name(&minor_status, &buffer, GLOBUS_GSS_C_NT_X509, &test_case->name1);
                        if (major_status != GSS_S_COMPLETE)
                        {
                            fprintf(stderr, "Error importing %s\n", test_case->name_token1);
                            globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                            exit(-1);
                        }
                        X509_free(cert);
                        globus_gsi_cred_handle_destroy(handle);
                    }
                    break;
            }

            for (j = i; !globus_list_empty(j); j = globus_list_rest(j))
            {
                compare_name_test_case_t *test_case2 = globus_list_first(j);

                if (test_case->name_type1 == test_case2->name_type1 &&
                    test_case->name_token1 && test_case2->name_token1 &&
                    strcmp(test_case->name_token1, test_case2->name_token1) == 0 &&
                    test_case2->name1 == GSS_C_NO_NAME)
                {
                    test_case2->name1 = test_case->name1;
                }
                if (test_case->name_type1 == test_case2->name_type2 &&
                    test_case->name_token1 && test_case2->name_token2 &&
                    strcmp(test_case->name_token1, test_case2->name_token2) == 0 &&
                    test_case2->name2 == GSS_C_NO_NAME)
                {
                    test_case2->name2 = test_case->name1;
                }
            }
        }
        if (test_case->name2 == GSS_C_NO_NAME)
        {
            switch (test_case->name_type2)
            {
                case GSS_L_ANONYMOUS:
                    major_status = gss_import_name(&minor_status, &buffer, GSS_C_NT_ANONYMOUS, &test_case->name2);
                    if (major_status != GSS_S_COMPLETE)
                    {
                        fprintf(stderr, "Error importing <anonymous>\n");
                        globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                        exit(-1);
                    }
                    break;
                case GSS_L_NO_OID:
                    buffer.value = test_case->name_token2;
                    buffer.length = strlen(buffer.value);

                    major_status = gss_import_name(&minor_status, &buffer, GSS_C_NO_OID, &test_case->name2);
                    if (major_status != GSS_S_COMPLETE)
                    {
                        fprintf(stderr, "Error importing %s\n", test_case->name_token2);
                        globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                        exit(-1);
                    }
                    break;
                case GSS_L_HOSTBASED_SERVICE:
                    buffer.value = test_case->name_token2;
                    buffer.length = strlen(buffer.value);

                    major_status = gss_import_name(&minor_status, &buffer, GSS_C_NT_HOSTBASED_SERVICE, &test_case->name2);
                    if (major_status != GSS_S_COMPLETE)
                    {
                        fprintf(stderr, "Error importing %s\n", test_case->name_token2);
                        globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                        exit(-1);
                    }
                    break;
                case GSS_L_HOST_IP:
                    if (gss_l_host_ip_support)
                    {
                        buffer.value = test_case->name_token2;
                        buffer.length = strlen(buffer.value);

                        major_status = gss_import_name(&minor_status, &buffer, GLOBUS_GSS_C_NT_HOST_IP, &test_case->name2);
                        if (major_status != GSS_S_COMPLETE)
                        {
                            fprintf(stderr, "Error importing %s\n", test_case->name_token2);
                            globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                            exit(-1);
                        }
                    }
                    break;
                case GSS_L_X509:
                    if (gss_l_x509_support)
                    {
                        result = globus_gsi_cred_handle_init(&handle, NULL);
                        if (result != GLOBUS_SUCCESS)
                        {
                            globus_gsi_gssapi_test_print_result(stderr, result);
                            exit(-1);
                        }

                        result = globus_gsi_cred_read_cert(handle, test_case->name_token2);
                        if (result != GLOBUS_SUCCESS)
                        {
                            globus_gsi_gssapi_test_print_result(stderr, result);
                            exit(-2);
                        }

                        result = globus_gsi_cred_get_cert(handle, &cert);

                        buffer.value = cert;
                        buffer.length = sizeof(X509);

                        major_status = gss_import_name(&minor_status, &buffer, GLOBUS_GSS_C_NT_X509, &test_case->name2);
                        if (major_status != GSS_S_COMPLETE)
                        {
                            fprintf(stderr, "Error importing %s\n", test_case->name_token2);
                            globus_gsi_gssapi_test_print_error(stderr, major_status, minor_status);
                            exit(-1);
                        }
                        X509_free(cert);
                        globus_gsi_cred_handle_destroy(handle);
                    }
                    break;
            }
            for (j = i; !globus_list_empty(j); j = globus_list_rest(j))
            {
                compare_name_test_case_t *test_case2 = globus_list_first(j);

                if (test_case->name_type2 == test_case2->name_type1 &&
                    test_case->name_token2 && test_case2->name_token1 &&
                    strcmp(test_case->name_token2, test_case2->name_token1) == 0 &&
                    test_case2->name1 == GSS_C_NO_NAME)
                {
                    test_case2->name1 = test_case->name2;
                }
                if (test_case->name_type2 == test_case2->name_type2 &&
                    test_case->name_token2 && test_case2->name_token2 &&
                    strcmp(test_case->name_token2, test_case2->name_token2) == 0 &&
                    test_case2->name2 == GSS_C_NO_NAME)
                {
                    test_case2->name2 = test_case->name2;
                }
            }
        }
    }
}
/**
 * @brief Initiate Delegation
 * @ingroup globus_gsi_gssapi_extensions_delegation
 * @details
 * This functions drives the initiating side of the credential
 * delegation process. It is expected to be called in tandem with the
 * gss_accept_delegation function.
 *
 * @param minor_status
 *        The minor status returned by this function. This parameter
 *        will be 0 upon success.
 * @param context_handle
 *        The security context over which the credential is
 *        delegated. 
 * @param cred_handle
 *        The credential to be delegated. May be GSS_C_NO_CREDENTIAL
 *        in which case the credential associated with the security
 *        context is used.
 * @param desired_mech
 *        The desired security mechanism. Currently not used. May be
 *        GSS_C_NO_OID. 
 * @param extension_oids
 *        A set of extension OIDs corresponding to buffers in the
 *        extension_buffers parameter below. The extensions specified
 *        will be added to the delegated credential. May be
 *        GSS_C_NO_BUFFER_SET. 
 * @param extension_buffers
 *        A set of extension buffers corresponding to OIDs in the
 *        extension_oids parameter above. May be
 *        GSS_C_NO_BUFFER_SET.
 * @param input_token
 *        The token that was produced by a prior call to
 *        gss_accept_delegation. This parameter will be ignored the
 *        first time this function is called.
 * @param req_flags
 *        Flags that modify the behavior of the function. Currently
 *        only GSS_C_GLOBUS_SSL_COMPATIBLE and
 *        GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG are checked for. The
 *        GSS_C_GLOBUS_SSL_COMPATIBLE  flag results in tokens that
 *        aren't wrapped and GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG
 *        causes the delegated proxy to be limited (requires that no
 *        extensions are specified.
 *        
 * @param time_req
 *        The requested period of validity (seconds) of the delegated
 *        credential. Passing a time_req of 0 cause the delegated credential to
 *        have the same lifetime as the credential that issued it. 
 * @param output_token
 *        A token that should be passed to gss_accept_delegation if the
 *        return value is GSS_S_CONTINUE_NEEDED.
 * @retval GSS_S_COMPLETE Success
 * @retval GSS_S_CONTINUE_NEEDED This function needs to be called again.
 * @retval GSS_S_FAILURE upon failure
 */
OM_uint32
GSS_CALLCONV gss_init_delegation(
    OM_uint32 *                         minor_status,
    const gss_ctx_id_t                  context_handle,
    const gss_cred_id_t                 cred_handle,
    const gss_OID                       desired_mech,
    const gss_OID_set                   extension_oids,
    const gss_buffer_set_t              extension_buffers,
    const gss_buffer_t                  input_token,
    OM_uint32                           req_flags,
    OM_uint32                           time_req,
    gss_buffer_t                        output_token)
{
    BIO *                               bio = NULL;
    BIO *                               read_bio = NULL;
    BIO *                               write_bio = NULL;
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    gss_ctx_id_desc *                   context;
    gss_cred_id_desc *                  cred;
    X509 *                              cert = NULL;
    STACK_OF(X509) *                    cert_chain = NULL;
    PROXYCERTINFO *                     pci;
    globus_gsi_cert_utils_cert_type_t   cert_type;
    int                                 index;
    globus_result_t                     local_result = GLOBUS_SUCCESS;
    static char *                       _function_name_ =
        "init_delegation";
    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;
    
    if(minor_status == NULL)
    {
        major_status = GSS_S_FAILURE;
        goto exit;
    }
    
    *minor_status = (OM_uint32) GLOBUS_SUCCESS;

    if(context_handle == GSS_C_NO_CONTEXT)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid context_handle passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    context = (gss_ctx_id_desc *) context_handle;

    cred = (gss_cred_id_desc *) cred_handle;

    if (cred_handle == GSS_C_NO_CREDENTIAL)
    {
        cred = (gss_cred_id_desc *) context->cred_handle;
    }

    if(cred == GSS_C_NO_CREDENTIAL)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Couldn't initialize delegation credential handle")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    if(desired_mech != GSS_C_NO_OID &&
       desired_mech != (gss_OID) gss_mech_globus_gssapi_openssl)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid desired_mech passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    if(extension_oids != GSS_C_NO_OID_SET &&
       (extension_buffers == GSS_C_NO_BUFFER_SET ||
        extension_oids->count != extension_buffers->count))
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid extension parameters passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    if(output_token == GSS_C_NO_BUFFER)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid output_token passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    output_token->length = 0;

    if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE)
    {
        bio = BIO_new(BIO_s_mem());
        read_bio = bio;
        write_bio = bio;
    }
    else
    {
        bio = context->gss_sslbio;
    }

    /* lock the context mutex */
    globus_mutex_lock(&context->mutex);
    
    /* pass the input to the BIO */
    if(context->delegation_state != GSS_DELEGATION_START)
    {
        /*
         * first time there is no input token, but after that
         * there will always be one
         */
        if(input_token == GSS_C_NO_BUFFER)
        {
            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
                (_GGSL("Invalid input_token passed to function: "
                 "delegation is not at initial state")));
            major_status = GSS_S_FAILURE;
            goto mutex_unlock;
        }

        major_status = 
            globus_i_gsi_gss_put_token(&local_minor_status,
                                       context, 
                                       read_bio, 
                                       input_token);

        if (GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL);
            goto mutex_unlock;
        }
    }

    
    /* delegation state machine */
    
    switch (context->delegation_state)
    {

    case GSS_DELEGATION_START:

        /* start delegation by sending a "D" */
        BIO_write(bio, "D", 1); 
        context->delegation_state = GSS_DELEGATION_SIGN_CERT;
        break;

    case GSS_DELEGATION_SIGN_CERT:

        /* get the returned cert from the ssl BIO, make sure it is
         * correct and then sign it and place it in the output_token
         */
        local_result = globus_gsi_proxy_inquire_req(context->proxy_handle,
                                                    bio);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
            major_status = GSS_S_FAILURE;
            goto mutex_unlock;
        }

        local_result = globus_gsi_cred_get_cert_type(
            context->cred_handle->cred_handle,
            &cert_type);

        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;
        }
        /* set the requested time */

        if(time_req != 0)
        {
            if(time_req%60)
            {
                /* round up */
                time_req += 60;
            }
            
            local_result = globus_gsi_proxy_handle_set_time_valid(
                context->proxy_handle,
                time_req/60);

            if(local_result != GLOBUS_SUCCESS)
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
                major_status = GSS_S_FAILURE;
                context->delegation_state = GSS_DELEGATION_DONE;
                goto mutex_unlock;            
            }
        }
        
        /* clear the proxycertinfo */
        
        local_result =
            globus_gsi_proxy_handle_clear_cert_info(context->proxy_handle);
        
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;            
        }
        

        if(cert_type == GLOBUS_GSI_CERT_UTILS_TYPE_CA)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;            
        }
        
        local_result =
            globus_gsi_proxy_handle_set_type(
                context->proxy_handle,
                (req_flags & GSS_C_GLOBUS_DELEGATE_LIMITED_PROXY_FLAG)
                ? GLOBUS_GSI_CERT_UTILS_TYPE_LIMITED_PROXY 
                : GLOBUS_GSI_CERT_UTILS_TYPE_IMPERSONATION_PROXY);
        
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;            
        }    

        /* set the proxycertinfo here */
        if(extension_oids != GSS_C_NO_OID_SET)
        {
            if(GLOBUS_GSI_CERT_UTILS_IS_GSI_2_PROXY(cert_type))
            {
                GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                    minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
                    (_GGSL("A restricted globus proxy may not be created "
                     "from a legacy globus proxy")));
                context->delegation_state = GSS_DELEGATION_DONE;
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            
            for(index = 0; index < extension_oids->count; index++)
            {
                if(g_OID_equal((gss_OID) &extension_oids->elements[index],
                               gss_proxycertinfo_extension))
                {
                    pci = extension_buffers->elements[index].value;

                    local_result =
                        globus_gsi_proxy_handle_set_proxy_cert_info(
                            context->proxy_handle,
                            pci);
                    if(local_result != GLOBUS_SUCCESS)
                    {
                        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                            minor_status, local_result,
                            GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
                        major_status = GSS_S_FAILURE;
                        context->delegation_state = GSS_DELEGATION_DONE;
                        goto mutex_unlock;
                    }
                }
            }
        }

        local_result = globus_gsi_proxy_sign_req(
            context->proxy_handle,
            cred->cred_handle,
            bio);
        
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;
        }

        local_result = globus_gsi_cred_get_cert(cred->cred_handle, &cert);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;
        }
                
        /* push the cert used to sign the proxy */
        i2d_X509_bio(bio, cert);
        X509_free(cert);
        
        /* push the number of certs in the cert chain */
        local_result = globus_gsi_cred_get_cert_chain(cred->cred_handle,
                                                      &cert_chain);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL);
            major_status = GSS_S_FAILURE;
            context->delegation_state = GSS_DELEGATION_DONE;
            goto mutex_unlock;
        }

        for(index = 0; index < sk_X509_num(cert_chain); index++)
        {
            cert = sk_X509_value(cert_chain, index);
            if(!cert)
            {
                GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                    minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                    (_GGSL("Couldn't get cert from cert chain")));
                major_status = GSS_S_FAILURE;
                context->delegation_state = GSS_DELEGATION_DONE;
                goto mutex_unlock;
            }
            
            i2d_X509_bio(bio, cert);
        }
        sk_X509_pop_free(cert_chain, X509_free);

        /* reset state machine */
        context->delegation_state = GSS_DELEGATION_START; 
        break;

    case GSS_DELEGATION_COMPLETE_CRED:
    case GSS_DELEGATION_DONE:
        break;
    }
    
    major_status = globus_i_gsi_gss_get_token(&local_minor_status,
                                              context, 
                                              write_bio, 
                                              output_token);
    if(GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL);
        goto mutex_unlock;
    }

    if (context->delegation_state != GSS_DELEGATION_START)
    {
        major_status |= GSS_S_CONTINUE_NEEDED;
    }

 mutex_unlock:
    globus_mutex_unlock(&context->mutex);

 exit:

    if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE)
    {
        BIO_free(bio);
    }
        
    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}
/**
 * Accept a delegated credential.
 *
 * This functions drives the accepting side of the credential
 * delegation process. It is expected to be called in tandem with the
 * gss_init_delegation function.
 *
 * @param minor_status
 *        The minor status returned by this function. This paramter
 *        will be 0 upon success.
 * @param context_handle
 *        The security context over which the credential is
 *        delegated. 
 * @param extension_oids
 *        A set of extension oids corresponding to buffers in the
 *        extension_buffers paramter below. May be
 *        GSS_C_NO_BUFFER_SET. Currently not used.
 * @param extension_buffers
 *        A set of extension buffers corresponding to oids in the
 *        extension_oids paramter above. May be
 *        GSS_C_NO_BUFFER_SET. Currently not used. 
 * @param input_token
 *        The token that was produced by a prior call to
 *        gss_init_delegation. 
 * @param req_flags
 *        Flags that modify the behavior of the function. Currently
 *        only GSS_C_GLOBUS_SSL_COMPATIBLE is checked for. This flag
 *        results in tokens that aren't wrapped.
 * @param time_req
 *        The requested period of validity (seconds) of the delegated
 *        credential. Currently a noop.
 * @param time_rec
 *        This parameter will contain the received period of validity
 *        of the delegated credential upon success. May be NULL.
 * @param delegated_cred_handle
 *        This parameter will contain the delegated credential upon
 *        success. 
 * @param mech_type
 *        Returns the security mechanism upon success. Currently not
 *        implemented. May be NULL.
 * @param output_token
 *        A token that should be passed to gss_init_delegation if the
 *        return value is GSS_S_CONTINUE_NEEDED.
 * @return
 *        GSS_S_COMPLETE upon successful completion
 *        GSS_S_CONTINUE_NEEDED if the function needs to be called
 *                              again.
 *        GSS_S_FAILURE upon failure
 */
OM_uint32
GSS_CALLCONV gss_accept_delegation(
    OM_uint32 *                         minor_status,
    const gss_ctx_id_t                  context_handle,
    const gss_OID_set                   extension_oids,
    const gss_buffer_set_t              extension_buffers,
    const gss_buffer_t                  input_token,
    OM_uint32                           req_flags,
    OM_uint32                           time_req,
    OM_uint32 *                         time_rec,
    gss_cred_id_t *                     delegated_cred_handle,
    gss_OID *                           mech_type, 
    gss_buffer_t                        output_token)
{
    BIO *                               bio = NULL;
    BIO *                               read_bio = NULL;
    BIO *                               write_bio = NULL;
    X509 *                              peer_cert = NULL;
    const EVP_MD *                      peer_digest;
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    globus_result_t                     local_result = GLOBUS_SUCCESS;
    gss_ctx_id_desc *                   context;
    globus_gsi_cred_handle_t            delegated_cred = NULL;
    char                                dbuf[1];

    static char *                       _function_name_ =
        "gss_accept_delegation";
    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;
    
    /* parameter checking goes here */
    if(minor_status == NULL)
    {
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    *minor_status = (OM_uint32) GLOBUS_SUCCESS;

    if(context_handle == GSS_C_NO_CONTEXT)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status, 
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid context_handle passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    context = (gss_ctx_id_desc *) context_handle;

    if(delegated_cred_handle == NULL)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid delegated_cred_handle passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    if(extension_oids != GSS_C_NO_OID_SET &&
       (extension_buffers == GSS_C_NO_BUFFER_SET ||
        extension_oids->count != extension_buffers->count))
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid restriction parameters passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    if(output_token == GSS_C_NO_BUFFER)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid output token passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    output_token->length = 0;

    if(input_token == GSS_C_NO_BUFFER)
    {
        major_status |= GSS_S_CONTINUE_NEEDED;
        goto exit;
    }

    if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE)
    {
        bio = BIO_new(BIO_s_mem());
        read_bio = bio;
        write_bio = bio;
    }
    else
    {
        bio = context->gss_sslbio;
    }
    
    /* lock the context mutex */    
    globus_mutex_lock(&context->mutex);

    major_status = globus_i_gsi_gss_put_token(&local_minor_status,
                                              context, 
                                              read_bio, 
                                              input_token);

    if (GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL);
        goto mutex_unlock;
    }

    switch(context->delegation_state)
    {

    case GSS_DELEGATION_START:

        /* generate the proxy */
        BIO_read(bio, dbuf, 1);

        GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF(
            2, (globus_i_gsi_gssapi_debug_fstream,
                "delegation flag: %.1s\n", dbuf));

        if (dbuf[0] == 'D')
        {
            globus_gsi_proxy_handle_attrs_t     proxy_handle_attrs;
            int                                 key_bits;
           
            /* 
             * The delegated credential always generated 512 bit keys 
             * irrespective of the key strength of the peer credential (bug
             * 3794). Fix for that is added below.
             */
            local_result = globus_gsi_cred_get_key_bits(
                            context->peer_cred_handle->cred_handle, &key_bits);
            if(local_result != GLOBUS_SUCCESS)
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            local_result = globus_gsi_proxy_handle_attrs_init(
                                                        &proxy_handle_attrs);
            if(local_result != GLOBUS_SUCCESS)
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            local_result = globus_gsi_proxy_handle_attrs_set_keybits(
                                               proxy_handle_attrs, key_bits);
            if(local_result != GLOBUS_SUCCESS)
            {
                globus_gsi_proxy_handle_attrs_destroy(proxy_handle_attrs);
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            local_result = globus_gsi_cred_get_cert(
                        context->peer_cred_handle->cred_handle,
                        &peer_cert);
            if (local_result != GLOBUS_SUCCESS)
            {
                globus_gsi_proxy_handle_attrs_destroy(proxy_handle_attrs);
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            peer_digest = EVP_get_digestbynid(
                    OBJ_obj2nid(peer_cert->sig_alg->algorithm));

            local_result = globus_gsi_proxy_handle_attrs_set_signing_algorithm(
                    proxy_handle_attrs, (EVP_MD *) peer_digest);
            if (local_result != GLOBUS_SUCCESS)
            {
                globus_gsi_proxy_handle_attrs_destroy(proxy_handle_attrs);
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
                    

            if(context->proxy_handle)
            {
                globus_gsi_proxy_handle_destroy(context->proxy_handle);
            }
            local_result = globus_gsi_proxy_handle_init(
                                &context->proxy_handle, proxy_handle_attrs);
            if(local_result != GLOBUS_SUCCESS)
            {
                globus_gsi_proxy_handle_attrs_destroy(proxy_handle_attrs);
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            local_result = globus_gsi_proxy_handle_attrs_destroy(
                                                proxy_handle_attrs);
            if(local_result != GLOBUS_SUCCESS)
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }
            local_result = 
                globus_gsi_proxy_create_req(context->proxy_handle, bio);
            if(local_result != GLOBUS_SUCCESS)
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }

            context->delegation_state = GSS_DELEGATION_COMPLETE_CRED;
        }
        else
        {
            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_DELEGATION,
                (_GGSL("Invalid initial hello message, expecting: 'D', "
                 "received: '%c'"), dbuf[0]));
            major_status = GSS_S_FAILURE;
            goto mutex_unlock;
        }
        
        break;

    case GSS_DELEGATION_COMPLETE_CRED:

        /* get the signed cert and the key chain and insert them into
         * the cred structure
         */

        local_result = globus_gsi_proxy_assemble_cred(
            context->proxy_handle,
            &delegated_cred,
            bio);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
            major_status = GSS_S_FAILURE;
            goto mutex_unlock;
        }

        major_status = globus_i_gsi_gss_create_cred(&local_minor_status,
                                                    GSS_C_BOTH,
                                                    delegated_cred_handle,
                                                    &delegated_cred);
        if(GSS_ERROR(major_status))
        {
            globus_gsi_cred_handle_destroy(delegated_cred);
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL);
            goto mutex_unlock;
        }

        /* reset state machine */
        context->delegation_state = GSS_DELEGATION_START;

        if (time_rec != NULL)
        {
            time_t                      lifetime;
            
            local_result = globus_gsi_cred_get_lifetime(
                ((gss_cred_id_desc *)(*delegated_cred_handle))->cred_handle,
                &lifetime);
            if(local_result != GLOBUS_SUCCESS)
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_result,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL);
                major_status = GSS_S_FAILURE;
                goto mutex_unlock;
            }

            *time_rec = (OM_uint32) lifetime;
        }
        
    case GSS_DELEGATION_SIGN_CERT:
    case GSS_DELEGATION_DONE:
        break;
        
    }

    /* returns empty token when there is no output */
    
    major_status = globus_i_gsi_gss_get_token(&local_minor_status,
                                              context, 
                                              write_bio, 
                                              output_token);
    if(GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL);
        goto mutex_unlock;
    }

    if (context->delegation_state != GSS_DELEGATION_START)
    {
        major_status |= GSS_S_CONTINUE_NEEDED;
    }
    
 mutex_unlock:
    if (peer_cert)
    {
        X509_free(peer_cert);
    }
    
    globus_mutex_unlock(&context->mutex);

 exit:

    if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE)
    {
        BIO_free(bio);
    }

    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}