Example #1
0
/**
 * Passes back the mech set of available mechs.
 * We only have one for now. 
 *
 * @param minor_status
 * @param mech_set
 *
 * @return
 */
OM_uint32 
GSS_CALLCONV gss_indicate_mechs(
    OM_uint32 *                         minor_status,
    gss_OID_set *                       mech_set)
{
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    gss_OID_set_desc  *                 set;
    static char *                       _function_name_ =
        "gss_indicate_mechs";
    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;

    if (minor_status == NULL || mech_set == NULL)
    {
        major_status = GSS_S_FAILURE;

        if (minor_status != NULL)
        {
            GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                    minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
                    (_GGSL("Invalid parameter")));
        }

        goto exit;
    }

    *minor_status = (OM_uint32) GLOBUS_SUCCESS;
    
    major_status = gss_create_empty_oid_set(&local_minor_status, 
                                            &set);
    if (GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_MECH);
        goto exit;
    }
    
    major_status = gss_add_oid_set_member(
        &local_minor_status, 
        (const gss_OID) gss_mech_globus_gssapi_openssl,
        &set);
    if (GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_OID);
        
        gss_release_oid_set(&local_minor_status, &set);
        goto exit;
    }
    
    *mech_set = set;

 exit:
    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}
/**
 * @brief Seal
 * @ingroup globus_gsi_gssapi
 * @details
 * Obsolete variant of gss_wrap for V1 compatibility
 *
 * @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_seal(
    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)
{
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    static char *                       _function_name_ =
        "gss_seal";
    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;

    major_status = gss_wrap(&local_minor_status,
                            context_handle,
                            conf_req_flag,
                            qop_req,
                            input_message_buffer,
                            conf_state,
                            output_message_buffer);
    
    if(GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_ENCRYPTING_MESSAGE);
    }

    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}
/**
 * @brief Free a Buffer Set
 * @ingroup globus_gsi_gssapi_buffer_set
 * @details
 * This function will free all memory associated with a buffer
 * set. Note that it will also free all memory associated with the
 * buffers int the buffer set.
 *
 * @param minor_status
 *        The minor status returned by this function. This parameter
 *        will be 0 upon success.
 * @param buffer_set
 *        Pointer to a buffer set structure. This pointer will point
 *        at a NULL value upon return.
 * 
 * @retval GSS_S_COMPLETE Success
 * @retval GSS_S_FAILURE Failure
 *
 * @see gss_create_empty_buffer_set
 * @see gss_add_buffer_set_member
 */
OM_uint32 
GSS_CALLCONV gss_release_buffer_set(
    OM_uint32 *                         minor_status,
    gss_buffer_set_t *                  buffer_set)
{
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    int                                 index;
    static char *                       _function_name_ =
        "gss_release_buffer_set";

    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;
    
    *minor_status = GLOBUS_SUCCESS;
        
    if (buffer_set == NULL || *buffer_set == GSS_C_NO_BUFFER_SET)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status, 
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("NULL parameters passed to function: %s"),
             _function_name_));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    for(index = 0; index < (*buffer_set)->count; index++)
    {
        major_status = gss_release_buffer(&local_minor_status,
                                          &(*buffer_set)->elements[index]);
        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_BUFFER);
            major_status = GSS_S_FAILURE;
            goto exit;
        }
    }

    free((*buffer_set)->elements);

    free(*buffer_set);

    *buffer_set = NULL;
    
 exit:
    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;

} 
/**
 * @brief Set Security Context Option
 * @ingroup globus_gsi_gssapi_extensions
 * @details
 * GSSAPI routine to initiate the sending of a security context
 * See: <draft-ietf-cat-gssv2-cbind-04.txt>
 *
 * @param minor_status
 * @param context_handle
 * @param option
 * @param value
 *
 * @return
 */
OM_uint32
GSS_CALLCONV gss_set_sec_context_option(
    OM_uint32 *                         minor_status,
    gss_ctx_id_t *                      context_handle,
    const gss_OID                       option,
    const gss_buffer_t                  value)
{
    gss_ctx_id_desc *                   context = NULL;
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    globus_result_t                     local_result = GLOBUS_SUCCESS;
    int                                 index;

    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 == NULL)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid context_handle passed to function - cannot be NULL")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    context = *context_handle;

    if(option == GSS_C_NO_OID)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid option passed to function with value: GSS_C_NO_OID")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }
    
    if (*context_handle == GSS_C_NO_CONTEXT)
    {
        /* for now just malloc and zero the context */
        context = (gss_ctx_id_desc *) malloc(sizeof(gss_ctx_id_desc));
        if (context == NULL)
        {
            GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status);
            major_status = GSS_S_FAILURE;
            goto exit;
        }

        *context_handle = context;
        memset(context, 0, sizeof(gss_ctx_id_desc));
        context->ctx_flags = 0;

        major_status = gss_create_empty_oid_set(
            &local_minor_status,
            (gss_OID_set *) &context->extension_oids);

        /* initialize the callback_data in the context.  This needs
         * to be done so the verify_callback func can be set later.
         */
        local_result = globus_gsi_callback_data_init(
            &context->callback_data);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT,
                (_GGSL("The callback data in the context "
                 "could not be initialized.")));
            major_status = GSS_S_FAILURE;
            goto exit;
        }
    }
    else if(context->ctx_flags & GSS_I_CTX_INITIALIZED)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT,
            (_GGSL("The context has already been initialized!  %s should be "
             "called on a context before initialization"), __func__));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    if(g_OID_equal(option, GSS_DISALLOW_ENCRYPTION))
    {
        context->ctx_flags |= GSS_I_DISALLOW_ENCRYPTION;
    }
    else if(g_OID_equal(option, GSS_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION))
    {
        context->ctx_flags |= GSS_I_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION;
    }
    else if(g_OID_equal(option, GSS_APPLICATION_WILL_HANDLE_EXTENSIONS))
    {
        if(value == GSS_C_NO_BUFFER)
        {
            GLOBUS_GSI_GSSAPI_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
                (_GGSL("Invalid buffer passed to function")));
            major_status = GSS_S_FAILURE;
            goto exit;
        }

        for(index = 0; 
            index < ((gss_OID_set_desc *) value->value)->count; 
            index++)
        {
            major_status = gss_add_oid_set_member(
                &local_minor_status,
                (gss_OID) 
                &((gss_OID_set_desc *) value->value)->elements[index],
                (gss_OID_set *) &context->extension_oids);

            if(GSS_ERROR(major_status))
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_OID);
                goto exit;
            }
        }

        local_result = globus_gsi_callback_set_extension_cb(
            context->callback_data,
            globus_i_gsi_gss_verify_extensions_callback);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA);
            major_status = GSS_S_FAILURE;
            goto exit;
        }

        /* if the extension_oids are set, 
         * then we set them in the callback data */
        local_result = globus_gsi_callback_set_extension_oids(
            context->callback_data,
            (void *) context->extension_oids);
        if(local_result != GLOBUS_SUCCESS)
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_result,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA);
            major_status = GSS_S_FAILURE;
            goto exit;
        }

        context->ctx_flags |= GSS_I_APPLICATION_WILL_HANDLE_EXTENSIONS;
    }
    else
    {
        /* unknown option */
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_UNKNOWN_OPTION,
            (NULL));
        major_status = GSS_S_FAILURE;
        goto exit;
    }

    *context_handle = context;

 exit:

    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}
/**
 * @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;
}
/**
 * @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;
}
Example #8
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;
}
/**
 * @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;
}
/**
 * @brief Inquire Sec Context by OID
 * @ingroup globus_gsi_gssapi_extensions
 */
OM_uint32
GSS_CALLCONV gss_inquire_sec_context_by_oid(
    OM_uint32 *                         minor_status,
    const gss_ctx_id_t                  context_handle,
    const gss_OID                       desired_object,
    gss_buffer_set_t *                  data_set)
{
    OM_uint32                           major_status = GSS_S_COMPLETE;
    OM_uint32                           local_minor_status;
    gss_ctx_id_desc *                   context = NULL;
    int                                 found_index;
    int                                 chain_index;
    int                                 cert_count;
    X509_EXTENSION *                    extension;
    X509 *                              cert = NULL;
    STACK_OF(X509) *                    cert_chain = NULL;
    ASN1_OBJECT *                       asn1_desired_obj = NULL;
    ASN1_OCTET_STRING *                 asn1_oct_string;
    gss_buffer_desc                     data_set_buffer = GSS_C_EMPTY_BUFFER;
    globus_result_t                     local_result = GLOBUS_SUCCESS;
    unsigned char *                     tmp_ptr;
    static char *                       _function_name_ =
        "gss_inquire_sec_context_by_oid";
    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;

    /* parameter checking goes here */

    if(minor_status == NULL)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid minor_status (NULL) passed to function")));
        major_status = GSS_S_FAILURE;
        goto exit;
    }
    
    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;
    }

    *minor_status = (OM_uint32) GLOBUS_SUCCESS;
    context = (gss_ctx_id_desc *) context_handle;

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

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

    *data_set = NULL;

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

    local_result = 
        globus_gsi_callback_get_cert_depth(context->callback_data,
                                           &cert_count);
    if(local_result != GLOBUS_SUCCESS)
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_result,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA);
        major_status = GSS_S_FAILURE;
        goto unlock_exit;
    }

    if(cert_count == 0)
    {
        goto unlock_exit;
    }
    
    major_status = gss_create_empty_buffer_set(&local_minor_status, data_set);

    if(GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_BUFFER);
        goto unlock_exit;
    }
    
    local_result = globus_gsi_callback_get_cert_chain(
        context->callback_data,
        &cert_chain);
    if(local_result != GLOBUS_SUCCESS)
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_result,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA);
        major_status = GSS_S_FAILURE;
        cert_chain = NULL;
        goto unlock_exit;
    }

    if(((gss_OID_desc *)desired_object)->length !=
       gss_ext_x509_cert_chain_oid->length ||
       memcmp(((gss_OID_desc *)desired_object)->elements,
              gss_ext_x509_cert_chain_oid->elements,
              gss_ext_x509_cert_chain_oid->length))
    {
        /* figure out what object was asked for */
        
        asn1_desired_obj = ASN1_OBJECT_new();
        if(!asn1_desired_obj)
        {
            GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                (_GGSL("Couldn't create ASN1 object")));
            major_status = GSS_S_FAILURE;
            goto unlock_exit;
        }

        asn1_desired_obj->length = ((gss_OID_desc *)desired_object)->length;
        asn1_desired_obj->data = ((gss_OID_desc *)desired_object)->elements;

        found_index = -1;

        for(chain_index = 0; chain_index < cert_count; chain_index++)
        {
            cert = sk_X509_value(cert_chain, chain_index);

            data_set_buffer.value = NULL;
            data_set_buffer.length = 0;

            found_index = X509_get_ext_by_OBJ(cert, 
                                              asn1_desired_obj, 
                                              found_index);
        
            if(found_index >= 0)
            {
                extension = X509_get_ext(cert, found_index);
                if(!extension)
                {
                    GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                        minor_status,
                        GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                        (_GGSL("Couldn't get extension at index %d "
                         "from cert in credential."), found_index));
                    major_status = GSS_S_FAILURE;
                    goto unlock_exit;
                }

                asn1_oct_string = X509_EXTENSION_get_data(extension);
                if(!asn1_oct_string)
                {
                    GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                        minor_status,
                        GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                        (_GGSL("Couldn't get cert extension in the form of an "
                         "ASN1 octet string.")));
                    major_status = GSS_S_FAILURE;
                    goto unlock_exit;
                }

                asn1_oct_string = ASN1_OCTET_STRING_dup(asn1_oct_string);

                if(!asn1_oct_string)
                {
                    GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                        minor_status,
                        GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                        (_GGSL("Failed to make copy of extension data")));
                    major_status = GSS_S_FAILURE;
                    goto unlock_exit;
                }

                data_set_buffer.value = asn1_oct_string->data;
                data_set_buffer.length = asn1_oct_string->length;

                OPENSSL_free(asn1_oct_string);
            
                major_status = gss_add_buffer_set_member(
                    &local_minor_status,
                    &data_set_buffer,
                    data_set);
                if(GSS_ERROR(major_status))
                {
                    GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                        minor_status, local_minor_status,
                        GLOBUS_GSI_GSSAPI_ERROR_WITH_BUFFER);
                    goto unlock_exit;
                }
            }
        } 
    }
    else
    {
        for(chain_index = 0; chain_index < cert_count; chain_index++)
        {
            int certlen;
            cert = sk_X509_value(cert_chain, chain_index);

            certlen = i2d_X509(cert, NULL);
            data_set_buffer.length = certlen;

            if (certlen < 0)
            {
                GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                    minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                    (_GGSL("Failed to serialize certificate")));
                major_status = GSS_S_FAILURE;
                goto unlock_exit;                
            }
            
            tmp_ptr = realloc(data_set_buffer.value,
                              data_set_buffer.length);

            if(tmp_ptr == NULL)
            {
                GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status);
                major_status = GSS_S_FAILURE;
                goto unlock_exit;                
            }

            data_set_buffer.value = tmp_ptr;
            
            if(i2d_X509(cert,&tmp_ptr) < 0)
            {
                free(data_set_buffer.value);
                GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
                    minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
                    (_GGSL("Failed to serialize certificate")));
                major_status = GSS_S_FAILURE;
                goto unlock_exit;                
            }

            major_status = gss_add_buffer_set_member(
                &local_minor_status,
                &data_set_buffer,
                data_set);
            if(GSS_ERROR(major_status))
            {
                GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                    minor_status, local_minor_status,
                    GLOBUS_GSI_GSSAPI_ERROR_WITH_BUFFER);
                goto unlock_exit;
            }
        }
        
        if(data_set_buffer.value != NULL)
        {
            free(data_set_buffer.value);
        }
    }

unlock_exit:
    /* unlock the context mutex */
    globus_mutex_unlock(&context->mutex);

exit:
    if (asn1_desired_obj != NULL)
    {
        ASN1_OBJECT_free(asn1_desired_obj);
    }
    if(cert_chain != NULL)
    {
        sk_X509_pop_free(cert_chain, X509_free);
    }
    
    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}
Example #11
0
/**
 * Get the proxy group from a GSS name.
 *
 * This function will get the proxy group from a GSS name structure. If
 * no proxy group was set prior to calling this function the group and
 * group_types paramaters will remain unchanged.
 *
 * @param minor_status
 *        The minor status returned by this function. This paramter
 *        will be 0 upon success.
 * @param name
 *        The GSS name from which the group information is extracted.
 * @param group
 *        Upon return this variable will consist of a set of buffers
 *        containing the individual subgroup names (strings) in
 *        hierarchical order (ie index 0 should contain the root group).
 * @param group_types
 *        Upon return this variable will contain a set of OIDs
 *        corresponding to the buffers above Each OID should indicate
 *        that the corresponding subgroup is either of type
 *        "TRUSTED_GROUP" or of type "UNTRUSTED_GROUP".
 *
 * @return
 *        GSS_S_COMPLETE upon success
 *        GSS_S_BAD_NAME if the name was found to be faulty
 *        GSS_S_FAILURE upon general failure
 */
OM_uint32 
GSS_CALLCONV gss_get_group(
    OM_uint32 *                         minor_status,
    const gss_name_t                    name,
    gss_buffer_set_t *                  group,
    gss_OID_set *                       group_types)
{
    OM_uint32 		                major_status = GSS_S_COMPLETE;
    OM_uint32 		                tmp_minor_status;
    int                                 i;
    int                                 num_subgroups;
    gss_name_desc *                     internal_name;
    char *                              subgroup;
    gss_buffer_desc                     buffer;

    static char *                       _function_name_ =
        "gss_get_group";

    GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;

    internal_name = (gss_name_desc *) name;

    if(minor_status == NULL)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status, major_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("NULL parameter minor_status passed to function: %s"),
             _function_name_));
        goto exit;
    }
        
    *minor_status = (OM_uint32) GLOBUS_SUCCESS;

    if(name == GSS_C_NO_NAME)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status, major_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid group name passed to function: %s"),
             _function_name_));
        goto exit;
    }

    if(group == NULL)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status, major_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid group passed to function: %s"),
             _function_name_));
        goto exit;
    }

    if(group_types == NULL)
    {
        major_status = GSS_S_FAILURE;
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status, major_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
            (_GGSL("Invalid group types passed to function: %s"),
             _function_name_));
        goto exit;
    }

    num_subgroups = sk_num(internal_name->group);
    
    if(internal_name->group == NULL || num_subgroups == 0)
    {
        goto exit;
    }
    
    if(internal_name->group_types == NULL)
    {
        GLOBUS_GSI_GSSAPI_ERROR_RESULT(
            minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_BAD_NAME);
        major_status = GSS_S_BAD_NAME;
        goto exit;
    }

    major_status = gss_create_empty_buffer_set(local_minor_status, group);
    if(GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP);
        goto exit;
    }

    major_status = gss_create_empty_oid_set(local_minor_status, group_types);

    if(GSS_ERROR(major_status))
    {
        GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
            minor_status, local_minor_status,
            GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP);
        goto release_buffer;
    }

    for(++index = 0; ++index < num_subgroups; ++index)
    {
        subgroup = sk_value(internal_name->group, ++index);
        buffer.value = (void *) subgroup;
        buffer.length = strlen(subgroup) + 1;
        major_status = gss_add_buffer_set_member(&local_minor_status,
                                                 &buffer,
                                                 group);
        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP);
            goto release_oid;
        }

        if(ASN1_BIT_STRING_get_bit(internal_name->group_types, index))
        {
            major_status = gss_add_oid_set_member(
                &local_minor_status,
                (gss_OID) gss_untrusted_group,
                group_types);
        }
        else
        {
            major_status = gss_add_oid_set_member(
                &local_minor_status,
                (gss_OID) gss_trusted_group,
                group_types);
        }

        if(GSS_ERROR(major_status))
        {
            GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT(
                minor_status, local_minor_status,
                GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP);
            goto release_oid;
        }
    }
    
    goto exit;

 release_oid:
    gss_release_oid_set(&local_minor_status, group_types);

 release_buffer:
    gss_release_buffer_set(&local_minor_status, group);

 exit:
    GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
    return major_status;
}