/**
 * @brief Init Sec Context
 * @ingroup globus_gsi_gssapi
 */
OM_uint32 
GSS_CALLCONV gss_init_sec_context(
    OM_uint32 *                         minor_status,
    const gss_cred_id_t                 initiator_cred_handle,
    gss_ctx_id_t *                      context_handle_P,
    const gss_name_t                    target_name,
    const gss_OID                       mech_type,
    OM_uint32                           req_flags,
    OM_uint32                           time_req,
    const gss_channel_bindings_t        input_chan_bindings,
    const gss_buffer_t                  input_token,
    gss_OID *                           actual_mech_type,
    gss_buffer_t                        output_token,
    OM_uint32 *                         ret_flags,
    OM_uint32 *                         time_rec) 
{
    gss_ctx_id_desc *                   context = NULL;
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    OM_uint32                           local_major_status;
    globus_result_t                     local_result;
    int                                 rc;
    char                                cbuf[1];
    globus_gsi_cert_utils_cert_type_t   cert_type;

    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;

    *minor_status = (OM_uint32) GLOBUS_SUCCESS;
    output_token->length = 0;

    context = *context_handle_P;

    /* module activation if not already done by calling
     * globus_module_activate
     */
    
    globus_thread_once(
        &once_control,
        globus_l_gsi_gssapi_activate_once);

    globus_mutex_lock(&globus_i_gssapi_activate_mutex);
    if (!globus_i_gssapi_active)
    {
        globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE);
    }
    globus_mutex_unlock(&globus_i_gssapi_activate_mutex);
    
    if(req_flags & GSS_C_ANON_FLAG &&
       req_flags & GSS_C_DELEG_FLAG)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Can't initialize a context to be both anonymous and "
             "provide delegation")));
        goto error_exit;
    }

    if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE &&
       req_flags & GSS_C_DELEG_FLAG)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Can't initialize a context to both use SSL compatible "
             "context establishment and provide delegation")));
        goto error_exit;
    }

    if(req_flags & GSS_C_DELEG_FLAG &&
       target_name == GSS_C_NO_NAME)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Need a target name for authorization prior "
             "to doing delegation")));
        goto error_exit;
    }

    
    if ((context == (gss_ctx_id_t) GSS_C_NO_CONTEXT) ||
        !(context->ctx_flags & GSS_I_CTX_INITIALIZED))
    {
        GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF(
            2, (globus_i_gsi_gssapi_debug_fstream, 
                "Creating context w/ %s.\n",
                (initiator_cred_handle == GSS_C_NO_CREDENTIAL) ?
                "GSS_C_NO_CREDENTIAL" :
                "Credentials provided"));

        major_status = 
            globus_i_gsi_gss_create_and_fill_context(&local_minor_status,
                                                     &context,
                                                     initiator_cred_handle,
                                                     GSS_C_INITIATE,
                                                     req_flags);
        if (GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT);
            goto error_exit;
        }

        *context_handle_P = context;

        if (actual_mech_type != NULL)
        {
            *actual_mech_type = (gss_OID) gss_mech_globus_gssapi_openssl;
        }

        if (ret_flags != NULL)
        {
            *ret_flags = 0 ;
        }
    }
    else
    {
        /* first time there is no input token, but after that
         * there will always be one
         */
    	major_status = globus_i_gsi_gss_put_token(&local_minor_status,
                                                  context, 
                                                  NULL, 
                                                  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 error_exit;
        }
    }

    switch (context->gss_state)
    {
    case(GSS_CON_ST_HANDSHAKE):
        
        /* do the handshake work */
        
        major_status = globus_i_gsi_gss_handshake(&local_minor_status,
                                                  context);
        
        if (major_status == GSS_S_CONTINUE_NEEDED)
        {
            break;
        }

        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_HANDSHAKE);
            context->gss_state = GSS_CON_ST_DONE;
            break;
        }

        /* make sure we are talking to the correct server */
        major_status = globus_i_gsi_gss_retrieve_peer(&local_minor_status,
                                                      context,
                                                      GSS_C_INITIATE);
        if (GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT);
            context->gss_state = GSS_CON_ST_DONE;
            break;
        }

        local_result = globus_gsi_callback_get_cert_type(
            context->callback_data,
            &cert_type);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA);
            major_status = GSS_S_FAILURE;
            goto error_exit;
        }

        /* 
         * Need to check if the server is using a limited proxy. 
         * And if that is acceptable here. 
         * Caller tells us if it is not acceptable to 
         * use a limited proxy. 
         */
        if ((context->req_flags & 
             GSS_C_GLOBUS_DONT_ACCEPT_LIMITED_PROXY_FLAG)
            && GLOBUS_GSI_CERT_UTILS_IS_LIMITED_PROXY(cert_type))
        {
            major_status = GSS_S_UNAUTHORIZED;
            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_PROXY_VIOLATION,
                (_GGSL("Function set to not accept limited proxies")));
            context->gss_state = GSS_CON_ST_DONE;
            break;
        }

        /* this is the mutual authentication test */
        if (target_name != NULL)
        {
            major_status = 
                gss_compare_name(&local_minor_status,
                                 context->peer_cred_handle->globusid,
                                 target_name,
                                 &rc);
            if (GSS_ERROR(major_status))
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_BAD_NAME);
                context->gss_state = GSS_CON_ST_DONE;
                break;
            }
            else if(rc == GSS_NAMES_NOT_EQUAL)
            {
                char *                  expected_name;
                char *                  actual_name;
                    
                if(g_OID_equal(((gss_name_desc*)  target_name)->name_oid,
                               GSS_C_NT_HOSTBASED_SERVICE))
                {
                    GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                        minor_status,
                        GLOBUS_GSI_GSSAPI_ERROR_AUTHZ_DENIED,
                        (_GGSL("The expected name for the remote host (%s%s%s) does not match the authenticated "
                               "name of the remote host (%s%s%s). This happens when the name in the host certificate does not match the information obtained from DNS and is often a DNS configuration problem."),
                         target_name->service_name
                            ? target_name->service_name : "",
                         target_name->service_name
                            ? "@" : "",
                         target_name->host_name
                            ? target_name->host_name : "unknown",
                         context->peer_cred_handle->globusid->service_name 
                            ? context->peer_cred_handle->globusid->service_name
                            : "",
                         context->peer_cred_handle->globusid->service_name 
                            ? "@" : "",
                         context->peer_cred_handle->globusid->host_name 
                            ?  context->peer_cred_handle->globusid->host_name 
                            : "unknown"));
                }
                else
                { 
                    expected_name =
                        ((gss_name_desc*)  target_name)->x509n_oneline;
                    actual_name = 
                        ((gss_name_desc*)
                         context->peer_cred_handle->globusid)->x509n_oneline;
                    
                    GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                        minor_status,
                        GLOBUS_GSI_GSSAPI_ERROR_AUTHZ_DENIED,
                        (_GGSL("The name of the remote entity (%s), and the expected "
                               "name for the remote entity (%s) do not match"),
                         actual_name, expected_name));
                }

                major_status = GSS_S_UNAUTHORIZED;
                context->gss_state = GSS_CON_ST_DONE;
                break;
            }
        }
    
        context->ret_flags |= GSS_C_MUTUAL_FLAG;
        context->ret_flags |= GSS_C_PROT_READY_FLAG; 
        context->ret_flags |= GSS_C_INTEG_FLAG
            | GSS_C_REPLAY_FLAG
            | GSS_C_SEQUENCE_FLAG
            | GSS_C_ANON_FLAG
            | GSS_C_DELEG_FLAG;
        if (GLOBUS_GSI_CERT_UTILS_IS_LIMITED_PROXY(cert_type))
        {
            context->ret_flags |= GSS_C_GLOBUS_RECEIVED_LIMITED_PROXY_FLAG;
        }
#       if LINK_WITH_INTERNAL_OPENSSL_API
        context->ret_flags |= GSS_C_TRANS_FLAG;
#       endif

        /* 
         * IF we are talking to a real SSL server,
         * we don't want to do delegation, so we are done
         */

        if (context->req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE)
        {
            context->gss_state = GSS_CON_ST_DONE;
            break;
        }
            
        /*
         * If we have completed the handshake, but don't
         * have any more data to send, we can send the flag
         * now. i.e. fall through without break,
         * Otherwise, we will wait for the null byte
         * to get back in sync which we will ignore
         */

        if (output_token->length != 0)
        {
            context->gss_state=GSS_CON_ST_FLAGS;
            break;
        }

    case(GSS_CON_ST_FLAGS):

        if (input_token->length > 0)
        {   
            BIO_read(context->gss_sslbio, cbuf, 1);
        }

        /* send D if we want delegation, 0 otherwise */
        
        if (context->req_flags & GSS_C_DELEG_FLAG)
        {
            BIO_write(context->gss_sslbio, "D", 1); 
            context->gss_state = GSS_CON_ST_REQ;
        }
        else
        {
            BIO_write(context->gss_sslbio, "0", 1);
            context->gss_state = GSS_CON_ST_DONE;
        } 
        break;
            
    case(GSS_CON_ST_REQ):

        local_result = globus_gsi_proxy_inquire_req(
            context->proxy_handle,
            context->gss_sslbio);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_PROXY_NOT_RECEIVED);
            major_status = GSS_S_FAILURE;
            context->gss_state = GSS_CON_ST_DONE;
            goto error_exit;
        }
        
        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->gss_state = GSS_CON_ST_DONE;
            goto error_exit;
        }
        
        local_result =
            globus_gsi_proxy_handle_set_type(
                context->proxy_handle,
                (context->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->gss_state = GSS_CON_ST_DONE;
            goto exit;
        }

        
        local_result = globus_gsi_proxy_sign_req(
            context->proxy_handle,
            context->cred_handle->cred_handle,
            context->gss_sslbio);
        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->gss_state = GSS_CON_ST_DONE;
            goto error_exit;
        }

        context->gss_state = GSS_CON_ST_DONE;
        break;
            
    case(GSS_CON_ST_CERT): ;
    case(GSS_CON_ST_DONE): ;
    } /* end of switch for gss_con_st */

    local_major_status = globus_i_gsi_gss_get_token(&local_minor_status,
                                                    context, 
                                                    NULL, 
                                                    output_token);

    if(GSS_ERROR(local_major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL);
        major_status = GSS_S_FAILURE;
        context->gss_state = GSS_CON_ST_DONE;
        goto error_exit;
    }

    /* some error occurred during switch */
    if(GSS_ERROR(major_status))
    {
        goto error_exit;
    }

    if (context->gss_state != GSS_CON_ST_DONE)
    {
        major_status |= GSS_S_CONTINUE_NEEDED;
    }
    else if(time_rec != NULL)
    {
        time_t                          lifetime;
        time_t                          current_time;
        
        major_status = globus_i_gsi_gss_get_context_goodtill(
            &local_minor_status,
            context,
            &lifetime);
        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT);
            goto exit;
        }

        current_time = time(NULL);

        if(current_time > lifetime)
        {
            *time_rec = 0;
        }
        else
        {
            *time_rec = (OM_uint32) (lifetime - current_time);
        }
    }
       
    if (ret_flags != NULL)
    {
        *ret_flags = context->ret_flags;
    }

    GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF(
        2, (globus_i_gsi_gssapi_debug_fstream,
            "init_sec_context:major_status:%08x"
            ":gss_state:%d req_flags=%08x:ret_flags=%08x\n",
            (unsigned int) major_status, 
            context->gss_state,
            (unsigned int) req_flags, 
            (unsigned int) context->ret_flags));

    goto exit;

 error_exit:

    gss_delete_sec_context(&local_minor_status, 
                           (gss_ctx_id_t *) &context,
                           output_token);
    *context_handle_P = (gss_ctx_id_t) context;
 
 exit:

    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}
Beispiel #2
0
/**
 * @brief Export a GSSAPI credential
 * @ingroup globus_gsi_gssapi_extensions
 * @details
 * Saves the credential so it can be checkpointed and 
 * imported by gss_import_cred
 *
 * @param minor_status
 * @param cred_handle
 * @param desired_mech
 *        Should either be gss_mech_globus_gssapi_openssl or
 *        NULL (in which case gss_mech_globus_gssapi_openssl is
 *        assumed).
 * @param option_req
 * @param export_buffer
 *
 * @return
 */
OM_uint32 
GSS_CALLCONV gss_export_cred(
    OM_uint32 *                         minor_status,
    const gss_cred_id_t                 cred_handle,
    const gss_OID                       desired_mech,
    OM_uint32                           option_req,
    gss_buffer_t                        export_buffer)
{
    OM_uint32                           major_status = GLOBUS_SUCCESS;
    BIO *                               bp = NULL;
    gss_cred_id_desc *                  cred_desc = NULL;
    globus_result_t                     local_result;
    char *                              proxy_filename = NULL;

    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;

    cred_desc = (gss_cred_id_desc *) cred_handle;
    
    *minor_status = (OM_uint32) GLOBUS_SUCCESS;

    if (export_buffer == NULL ||
        export_buffer == GSS_C_NO_BUFFER)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("NULL or empty export_buffer parameter passed to function: %s"),
             __func__));
        goto exit;
    }

    export_buffer->length = 0;
    export_buffer->value = NULL;

    if (cred_handle == NULL)
    { 
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("NULL or empty export_buffer parameter passed to function: %s"),
             __func__));
        goto exit;
    }

    if(desired_mech != NULL &&
       g_OID_equal(desired_mech, (gss_OID) gss_mech_globus_gssapi_openssl))
    {
        major_status = GSS_S_BAD_MECH;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_MECH,
            (_GGSL("The desired mechanism of: %s, is not supported by this "
             "GSS implementation"), desired_mech->elements));
        goto exit;
    }

    if(option_req == GSS_IMPEXP_OPAQUE_FORM)
    {
        /* When option_req is equal to EXPORT_OPAQUE_FORM (0), it exports
         * an opaque buffer suitable for storage in memory or on  
         * disk or passing to another process, which 
         * can import the buffer with gss_import_cred().
         */
        bp = BIO_new(BIO_s_mem());
        if(bp == NULL)
        {
            GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                (_GGSL("Couldn't initialize IO bio for exporting credential")));
            major_status = GSS_S_FAILURE;
            goto exit;
        }

	local_result = globus_gsi_cred_write(cred_desc->cred_handle, bp);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_IMPEXP_BIO_SSL);
            major_status = GSS_S_FAILURE;
            goto exit;
        }            
		
        export_buffer->length = BIO_pending(bp);
		
        if (export_buffer->length > 0)
        {
            export_buffer->value = (char *) malloc(export_buffer->length);
            if (export_buffer->value == NULL)
            {
                export_buffer->length = 0;
                GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status);
                major_status = GSS_S_FAILURE;
                goto exit;
            }
			
            BIO_read(bp, export_buffer->value, export_buffer->length);
        }
        else
        {
            export_buffer->value = NULL;
        }

        major_status = GSS_S_COMPLETE;
    }
    else if(option_req == GSS_IMPEXP_MECH_SPECIFIC)
    {
        /* With option_req is equal to EXPORT_MECH_SPECIFIC (1), 
         * it exports a buffer filled with mechanism-specific 
         * information that the calling application can use 
         * to pass the credentials to another process that 
         * is not written to the GSS-API.
         */
        local_result = 
            GLOBUS_GSI_SYSCONFIG_GET_UNIQUE_PROXY_FILENAME(&proxy_filename);
        if(local_result != GLOBUS_SUCCESS)
        {
            proxy_filename = NULL;
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY);
            major_status = GSS_S_FAILURE;
            goto exit;
        }

        GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF(
            3, (globus_i_gsi_gssapi_debug_fstream,
                "Writing exported cred to: %s\n", proxy_filename));

        local_result = globus_gsi_cred_write_proxy(cred_desc->cred_handle,
                                                   proxy_filename);
        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 exit;
        }                                       

        export_buffer->value = globus_common_create_string(
            "X509_USER_PROXY=%s",
            proxy_filename);
        export_buffer->length = strlen(export_buffer->value)+1;
    }
    else
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Unrecognized option_req of: %d"), option_req));
        goto exit;
    }

 exit:

    if(proxy_filename != NULL)
    { 
        free(proxy_filename);
    }
    
    if (bp) 
    {
        BIO_free(bp);
    }

    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;
}
/**
 * @brief Wrap
 * @ingroup globus_gsi_gssapi
 * @details 
 * Wrap a message for integrity and protection.
 * We do this using the SSLv3 routines, by writing to the
 * SSL bio, and pulling off the buffer from the back 
 * of the write BIO.  But we can't do everything SSL 
 * might want, such as control messages, or segment the messages
 * here, since we are forced to using the GSSAPI tokens,
 * and can not communicate directly with our peer. 
 * So there maybe some failures which would work with true
 * SSL. 
 *
 * @param minor_status
 * @param context_handle
 * @param conf_req_flag
 * @param qop_req
 * @param input_message_buffer
 * @param conf_state
 * @param output_message_buffer
 *
 * @return
 */
OM_uint32 
GSS_CALLCONV gss_wrap(
    OM_uint32 *                         minor_status,
    const gss_ctx_id_t                  context_handle,
    int                                 conf_req_flag,
    gss_qop_t                           qop_req,
    const gss_buffer_t                  input_message_buffer,
    int *                               conf_state,
    gss_buffer_t                        output_message_buffer)
{
    gss_ctx_id_desc *                   context =
        (gss_ctx_id_desc *)context_handle; 
    gss_buffer_desc                     mic_buf_desc;
    gss_buffer_t                        mic_buf =
        (gss_buffer_desc *) &mic_buf_desc;
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    unsigned char *                     message_value;
    time_t                              context_goodtill;
    static char *                       _function_name_ =
        "gss_wrap";
    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;
    
    *minor_status = (OM_uint32) GLOBUS_SUCCESS;

    if(GLOBUS_I_GSI_GSSAPI_DEBUG(3))
    {
        BIO *                           debug_bio;
        fprintf(globus_i_gsi_gssapi_debug_fstream,
                "input message: length = %u\n"
                "               value = \n",
                (unsigned) input_message_buffer->length);

        debug_bio = BIO_new_fp(globus_i_gsi_gssapi_debug_fstream,
                               BIO_NOCLOSE);
        BIO_dump(debug_bio,
                 input_message_buffer->value,
                 input_message_buffer->length);
    }

    output_message_buffer->value = NULL;
    output_message_buffer->length = 0;

    GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF(
        2, (globus_i_gsi_gssapi_debug_fstream,
            "gss_wrap conf_req_flag=%d qop_req=%d\n",
            conf_req_flag, (int) qop_req));

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

    /* lock the context mutex */
    
    globus_mutex_lock(&context->mutex);

    if(context->ctx_flags & GSS_I_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION)
    {
        time_t                          current_time;

        current_time = time(NULL);

        major_status = globus_i_gsi_gss_get_context_goodtill(
            &local_minor_status,
            context,
            &context_goodtill);
        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT);
            goto unlock_mutex_error;
        }

        if(current_time > context_goodtill)
        {
            major_status = GSS_S_CONTEXT_EXPIRED;
            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_EXPIRED_CREDENTIAL,
                (_GGSL("Expired credential: %s < %s"), 
                 ctime(&context_goodtill), ctime(&current_time)));
            goto unlock_mutex_error;
        }
    }

    if (conf_req_flag == GSS_INTEGRITY_ONLY &&
        qop_req == GSS_C_QOP_GLOBUS_GSSAPI_OPENSSL_BIG)
    {
        /* unlock the context mutex */
        globus_mutex_unlock(&context->mutex);
        
        major_status = gss_get_mic(&local_minor_status,
                                   context_handle,
                                   qop_req,
                                   input_message_buffer,
                                   mic_buf);
        if (GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_MIC);
            goto unlock_mutex_error;
        }

        /* lock the context mutex */
        globus_mutex_lock(&context->mutex);
        
        output_message_buffer->value = 
            (char *) malloc(5 + mic_buf->length + 
                           input_message_buffer->length);
        if (output_message_buffer->value == NULL)
        {
            GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status);
            gss_release_buffer(&local_minor_status, mic_buf);
            major_status = GSS_S_FAILURE;
            goto unlock_mutex_error;
        }

        output_message_buffer->length = 5 + mic_buf->length + 
            input_message_buffer->length;
        message_value = output_message_buffer->value;
        *message_value++ = SSL3_RT_GSSAPI_OPENSSL;
        *message_value++ = 3;
        *message_value++ = 0;
        S2N(mic_buf->length, (char *) message_value);
        message_value += 2;
        memcpy(message_value, mic_buf->value, mic_buf->length);
        message_value = message_value + mic_buf->length;
        memcpy(message_value, input_message_buffer->value,
               input_message_buffer->length);
        
        if (conf_state)
        {
            *conf_state = GSS_INTEGRITY_ONLY;
        }
    } 
    else
    {
        int rc;
        rc = SSL_write(context->gss_ssl,
                       input_message_buffer->value,
                       input_message_buffer->length);
        if (rc != input_message_buffer->length)
        {
            /* problem, did not take the whole buffer */

            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WRAP_BIO,
                (_GGSL("SSL failed wrapping entire message: "
                 "SSL_write wrote %d bytes, should be %d bytes"),
                 rc, input_message_buffer->length));
            major_status = GSS_S_FAILURE;
            goto unlock_mutex_error;
        }
        if (conf_state)
        {
            if (SSL_CIPHER_get_bits(
                    SSL_get_current_cipher(context->gss_ssl), NULL) == 0)
            {
                *conf_state = GSS_INTEGRITY_ONLY;
            }
            else
            {
                *conf_state = GSS_CONFIDENTIALITY;
            }
        }

        /* get the data from the write BIO */
        major_status =  globus_i_gsi_gss_get_token(&local_minor_status,
                                                   context,
                                                   NULL,
                                                   output_message_buffer);
        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL);
            goto unlock_mutex_error;
        }
    }

 unlock_mutex_error:

    globus_mutex_unlock(&context->mutex);

 exit:

    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}