text: /*
   * Copyright 1999-2006 University of Chicago
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   * http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
  /**
   * @file inquire_cred_by_oid.c
   * @author Sam Lang, Sam Meder
   */
  #endif
  
  #include "gssapi_openssl.h"
  #include "globus_i_gsi_gss_utils.h"
  #include <string.h>
  
  /* Only build if we have the extended GSSAPI */
  #ifdef _HAVE_GSI_EXTENDED_GSSAPI
  
  /**
   * @brief Inquire Cred By OID
   * @ingroup globus_gsi_gssapi_extensions
   * @details 
   * NOTE: Checks both the cert in the credential and 
   * the certs in the cert chain for a valid extension
   * that matches the desired OID.  The first one found
   * is used, starting with the endpoint cert, and then
   * searching the cert chain.
   *
   *
   * @param minor_status
   * @param cred_handle
   * @param desired_object
   * @param data_set
   *
   * @return
   */
  OM_uint32
  GSS_CALLCONV gss_inquire_cred_by_oid(
      OM_uint32 *                         minor_status,
      const gss_cred_id_t                 cred_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_cred_id_desc *                  cred;
      X509_EXTENSION *                    extension;
      X509 *                              cert = NULL;
      STACK_OF(X509) *                    cert_chain = NULL;
      ASN1_OBJECT *                       desired_asn1_obj;
      ASN1_OCTET_STRING *                 asn1_oct_string;
      gss_buffer_desc                     data_set_buffer;
      int                                 chain_index;
      int                                 found_index;
      globus_result_t                     local_result = GLOBUS_SUCCESS;
      static char *                       _function_name_ =
          "gss_inquire_cred_by_oid";
      GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER;
      
      if(minor_status == NULL)
      {
          major_status = GSS_S_FAILURE;
          goto exit;
      }
      
      *minor_status = (OM_uint32) GLOBUS_SUCCESS;
      cred = (gss_cred_id_desc *) cred_handle;
  
      /* parameter checking goes here */
  
      if(cred_handle == GSS_C_NO_CREDENTIAL)
      {
          GLOBUS_GSI_GSSAPI_ERROR_RESULT(
              minor_status,
              GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT,
              (_GGSL("Invalid credential handle passed to function")));
          major_status = GSS_S_FAILURE;
          goto exit;
      }
  
      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 passed to function")));
          major_status = GSS_S_FAILURE;
          goto exit;
      }
  
      local_result = globus_gsi_cred_get_cert_chain(
          ((gss_cred_id_desc *)cred_handle)->cred_handle,
          &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 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 exit;
      }
  
      local_result = globus_gsi_cred_get_cert(
          ((gss_cred_id_desc *)cred_handle)->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;
          goto exit;
      }
  
      /* create the ASN1_OBJECT from the gss_OID structure */
      desired_asn1_obj = ASN1_OBJECT_new();
      if(!desired_asn1_obj)
      {
          GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT(
              minor_status,
              GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL,
              (_GGSL("Couldn't create ASN1_OBJECT for the desired extension")));
          major_status = GSS_S_FAILURE;
          goto exit;
      }
  
      desired_asn1_obj->length = desired_object->length;
      desired_asn1_obj->data   = desired_object->elements;
  
      /* check the extensions in the cert first */
  
      chain_index = 0;
      found_index = -1;
  
      do
      {
          data_set_buffer.value = NULL;
          data_set_buffer.length = 0;
  
          found_index = X509_get_ext_by_OBJ(cert, desired_asn1_obj, found_index);
          if(found_index >= 0)
          {
              /* no extension with correct OID found */
              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 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 exit;
              }
  
              data_set_buffer.value = asn1_oct_string->data;
              data_set_buffer.length = asn1_oct_string->length;
          
              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 exit;
              }
          }
  
      } while(chain_index < sk_X509_num(cert_chain) &&
              (cert = sk_X509_value(cert_chain, chain_index++)));
  
   exit:
  
      if(cert_chain != NULL)
      {
          sk_X509_pop_free(cert_chain, X509_free);
      }
      
      GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT;
      return major_status;
  }
      
  #endif /* _HAVE_GSI_EXTENDED_GSSAPI */
  