#include "mysccs.h"
SCCSID("@(#)generic.c   %E%   SAP   %I%")

static char * this_File GNU_UNUSED = __FILE__;

/************************************************************************/
/* $Id: //tools/src/freeware/gsstest/generic.c#2 $
 ************************************************************************
 *
 * Copyright (c) 1998-2000  SAP AG.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by SAP AG"
 *
 * 4. The name "SAP AG" must not be used to endorse or promote products
 *    derived from this software without prior written permission.
 *    For written permission, please contact www.press@sap.com
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by SAP AG"
 *
 * THIS SOFTWARE IS PROVIDED BY SAP AG ``AS IS'' AND ANY EXPRESSED
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. SAP AG SHALL BE LIABLE FOR ANY DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE ONLY IF CAUSED BY SAP AG'S
 * INTENT OR GROSS NEGLIGENCE. IN CASE SAP AG IS LIABLE UNDER THIS
 * AGREEMENT FOR DAMAGES CAUSED BY SAP AG'S GROSS NEGLIGENCE SAP AG
 * FURTHER SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT, AND SHALL NOT BE LIABLE IN EXCESS OF THE AMOUNT OF
 * DAMAGES TYPICALLY FORESEEABLE FOR SAP AG, WHICH SHALL IN NO EVENT
 * EXCEED US$ 500.000.- 
 *
 ************************************************************************/


#include "gsstest.h"
/*
 * generic_tests()
 *
 *
 */
int
generic_tests( DLL_GSSFP_T * p_gssfp )
{
   int		  rc	    = 0;
   int		  rci	    = 0;
   gss_OID_set	  mech_list = GSS_C_NO_OID_SET;
   gss_OID_set	  nt_list   = GSS_C_NO_OID_SET;
   OM_uint32	  maj_stat;

   XVEB((V_SHOW, "====================\nTesting generic gssapi functions ...\n----------\n"));

   if ( p_gssfp->gss_export_name!=0 ) {
      /* for GSS-API v2 mechanism, we're going to do a simple verification  */
      /* if the mech_list from gss_indicate_mechs() can be safely passed    */
      /* to gss_release_oid_set() -- a mechanism implementing export_name() */
      /* should be "new" enough for that				    */
      XVEB((V_TEST, "passing mech_list from indicate_mechs() to release_oid_set()\n"));
      rci = indicate_mechs( 0, p_gssfp, &mech_list, &maj_stat );
      rci += release_oid_set( p_gssfp, &mech_list );
      XVEB((V_RESULT, rci, 0, NULL ));
      rc += rci;
   }

   if ( p_gssfp->gss_inquire_names_for_mech!=0 ) {
      XVEB((V_TEST, "passing name_types from inquire_names_for_mech() to release_oid_set()\n"));
      rci = inquire_names_for_mech( verbose_level, p_gssfp, &nt_list, &maj_stat );
      rci += release_oid_set( p_gssfp, &nt_list );
      XVEB((V_RESULT, rci, 0, NULL ));
      rc += rci;
   }

   return(rc);

} /* generic_tests() */





static char *
calling_error[] = {
      "",
      "GSS_S_CALL_INACCESSIBLE_READ",
      "GSS_S_CALL_INACCESSIBLE_WRITE",
      "GSS_S_CALL_BAD_STRUCTURE"
};

static char *
routine_error[] = {
      "GSS_S_COMPLETE",
      "GSS_S_BAD_MECH",
      "GSS_S_BAD_NAME",
      "GSS_S_BAD_NAMETYPE",
      "GSS_S_BAD_BINDINGS",
      "GSS_S_BAD_STATUS",
      "GSS_S_BAD_MIC",
      "GSS_S_NO_CRED",
      "GSS_S_NO_CONTEXT",
      "GSS_S_DEFECTIVE_TOKEN",
      "GSS_S_DEFECTIVE_CREDENTIAL",
      "GSS_S_CREDENTIALS_EXPIRED",
      "GSS_S_CONTEXT_EXPIRED",
      "GSS_S_FAILURE",
      "GSS_S_BAD_QOP",
      "GSS_S_UNAUTHORIZED",
      "GSS_S_UNAVAILABLE",
      "GSS_S_DUPLICATE_ELEMENT",
      "GSS_S_NAME_NOT_MN"
};

static char *
suppl_information[] = {
      "GSS_S_CONTINUE_NEEDED",
      "GSS_S_DUPLICATE_TOKEN",
      "GSS_S_OLD_TOKEN",
      "GSS_S_UNSEQ_TOKEN",
      "GSS_S_GAP_TOKEN"
};


char * gss_call_names[GSS_CALL_ENUM_MAX];



/*
 * print_status()
 *
 *
 */
int
print_status( DLL_GSSFP_T  * p_gssfp,
	      gss_call_et    call_enum,
	      OM_uint32      major_status,
	      OM_uint32      minor_status )
{
   char		   * gss_call = "gss_display_status";
   gss_buffer_desc   message;
   gss_buffer_desc   message2;
   struct xtime    * xtm;
   char		     tmpbuf[1024];
   char		   * call_name;
   OM_uint32	     context;
   OM_uint32	     context2;
   OM_uint32	     min_stat, maj_stat;
   OM_uint32	     min_stat2, maj_stat2;
   OM_uint32	     calling;
   OM_uint32	     routine;
   OM_uint32	     supplinfo;
   Uint		     idx;
   Uint		     cntr;
   Uint		     len;
   Uint		     linecount1;
   Uint		     linecount2;
   Uint		     difference;
   int		     abbrev = 0;
   int		     rc     = 0;
   int		     rci    = 0;

   if ( gss_call_names[0]==NULL ) {
      gss_call_names[GSS_INIT_SEC_CONTEXT1]	= "gss_init_sec_context #1"; 
      gss_call_names[GSS_INIT_SEC_CONTEXT2]	= "gss_init_sec_context #2"; 
      gss_call_names[GSS_INIT_SEC_CONTEXT3]	= "gss_init_sec_context #3"; 
      gss_call_names[GSS_INIT_SEC_CONTEXT4]	= "gss_init_sec_context #4";
      gss_call_names[GSS_ACCEPT_SEC_CONTEXT1]	= "gss_accept_sec_context #1";
      gss_call_names[GSS_ACCEPT_SEC_CONTEXT2]	= "gss_accept_sec_context #2";
      gss_call_names[GSS_ACCEPT_SEC_CONTEXT3]	= "gss_accept_sec_context #3";
      gss_call_names[GSS_ACCEPT_SEC_CONTEXT4]	= "gss_accept_sec_context #4";
      gss_call_names[GSS_EXPORT_SEC_CONTEXT_I]	= "gss_export_sec_context Ini";
      gss_call_names[GSS_EXPORT_SEC_CONTEXT_A]	= "gss_export_sec_context Acc";
      gss_call_names[GSS_IMPORT_SEC_CONTEXT_I]	= "gss_import_sec_context Ini";
      gss_call_names[GSS_IMPORT_SEC_CONTEXT_A]	= "gss_import_sec_context Acc";
      gss_call_names[GSS_INQUIRE_CONTEXT]	= "gss_inquire_context";
      gss_call_names[GSS_CONTEXT_TIME]		= "gss_context_time";
      gss_call_names[GSS_DELETE_SEC_CONTEXT]	= "gss_delete_sec_context";
      gss_call_names[GSS_PROCESS_CONTEXT_TOKEN]	= "gss_process_context_token";

      gss_call_names[GSS_GET_MIC]		= "gss_get_mic";
      gss_call_names[GSS_VERIFY_MIC]		= "gss_verify_mic";
      gss_call_names[GSS_WRAP]			= "gss_wrap";
      gss_call_names[GSS_UNWRAP]		= "gss_unwrap";
      gss_call_names[GSS_WRAP_SIZE_LIMIT]	= "gss_wrap_size_limit";

      gss_call_names[GSS_ACQUIRE_CRED_I]	= "gss_acquire_cred Ini";
      gss_call_names[GSS_ACQUIRE_CRED_A]	= "gss_acquire_cred Acc";
      gss_call_names[GSS_ADD_CRED]		= "gss_add_cred";
      gss_call_names[GSS_INQUIRE_CRED_I]	= "gss_inquire_cred Ini";
      gss_call_names[GSS_INQUIRE_CRED_A]	= "gss_inquire_cred Acc";
      gss_call_names[GSS_INQUIRE_CRED_BY_MECH]	= "gss_inquire_cred_by_mech";
      gss_call_names[GSS_RELEASE_CRED]		= "gss_release_cred";

      gss_call_names[GSS_IMPORT_NAME]		= "gss_import_name";
      gss_call_names[GSS_CANONICALIZE_NAME]	= "gss_canonicalize_name";
      gss_call_names[GSS_COMPARE_NAME]		= "gss_compare_name";
      gss_call_names[GSS_DISPLAY_NAME]		= "gss_display_name";
      gss_call_names[GSS_DUPLICATE_NAME]	= "gss_duplicate_name";
      gss_call_names[GSS_EXPORT_NAME]		= "gss_export_name";
      gss_call_names[GSS_RELEASE_NAME]		= "gss_release_name";

      gss_call_names[GSS_DISPLAY_STATUS]	= "gss_display_status";
      gss_call_names[GSS_INDICATE_MECHS]	= "gss_indicate_mechs";

      gss_call_names[GSS_INQUIRE_MECHS_FOR_NAME] = "gss_inquire_mechs_for_name";
      gss_call_names[GSS_INQUIRE_NAMES_FOR_MECH] = "gss_inquire_names_for_mech";
      gss_call_names[GSS_RELEASE_BUFFER]	= "gss_release_buffer";

      gss_call_names[GSS_ADD_OID_SET_MEMBER]	= "gss_add_oid_set_member";
      gss_call_names[GSS_CREATE_EMPTY_OID_SET]	= "gss_create_empty_oid_set";
      gss_call_names[GSS_TEST_OID_SET_MEMBER]	= "gss_test_oid_set_member";
      gss_call_names[GSS_RELEASE_OID_SET]	= "gss_release_oid_set";
   }

   if ( call_enum>=0  ||  call_enum<GSS_CALL_ENUM_MAX ) {

      call_name = gss_call_names[call_enum];

      /* save elapsed time for the most recent successful gssapi call */
      if ( GSS_ERROR(major_status)==0 ) {
	 xtm = &(p_gssfp->tm[call_enum]);
	 if ( xtm->count == 0 ) {
	    xtm->min =  xtm->max = timedelay;
	 } else {
	    if ( timedelay<xtm->min ) { xtm->min = timedelay; }
	    if ( timedelay>xtm->max ) { xtm->max = timedelay; }
	 }
	 xtm->count++;
	 xtm->sum += timedelay;

	 xtm = &(p_gssfp->gtm[call_enum]);
	 if ( xtm->count == 0 ) {
	    xtm->min =  xtm->max = timedelay;
	 } else {
	    if ( timedelay<xtm->min ) { xtm->min = timedelay; }
	    if ( timedelay>xtm->max ) { xtm->max = timedelay; }
	 }
	 xtm->count++;
	 xtm->sum += timedelay;
      }

   } else {

      XVEB((V_BUG, "print_status(): call_enum out of range !!\n" ));
      call_name = "??";

   }

   call_name = gss_call_names[call_enum];

   if ( minor_status==BOGUS_VALUE ) {
      XVEB((V_ERR, "%s() FAILED to clear minor_status!\n", call_name));
      rc++;
      minor_status = 0;
   }

   if ( major_status==0 && minor_status!=0 ) {
      rc++;
      XVEB((V_ERR, "%s() unexpected non-zero minor_status (%08lx)!\n",
		   call_name, (Ulong) minor_status));
   }

   
   if ( verbose_level<2 ) {
      /* Verbosity level 1 doesn't list GSS_S_COMPLETE */
      if ( major_status==GSS_S_COMPLETE )
	 return(rc);
   }

   if ( call_enum==GSS_DISPLAY_STATUS  &&  major_status!=GSS_S_COMPLETE ) {
      /* prevent recursion! */
      return(rc);
   }

   if ( major_status==GSS_S_CONTINUE_NEEDED ) {
      if ( call_enum>=GSS_INIT_SEC_CONTEXT1 && call_enum<=GSS_INIT_SEC_CONTEXT4 )
	 abbrev = 1;
      if ( call_enum>=GSS_ACCEPT_SEC_CONTEXT1 && call_enum<=GSS_ACCEPT_SEC_CONTEXT4 )
	 abbrev = 1;
      if (abbrev!=0 && verbose_level<2)
	 return(rc);
   }

   message.length  = 0;
   message.value   = NULL;
   message2.length = 0;
   message2.value  = NULL;
   tmpbuf[0]       = '\0';

   calling   = (GSS_CALLING_ERROR(major_status)>>GSS_C_CALLING_ERROR_OFFSET);
   routine   = (GSS_ROUTINE_ERROR(major_status)>>GSS_C_ROUTINE_ERROR_OFFSET);
   supplinfo = GSS_SUPPLEMENTARY_INFO(major_status)>>GSS_C_SUPPLEMENTARY_OFFSET;

   strcat(tmpbuf, "(");
   cntr = 0;

   if ( major_status==GSS_S_COMPLETE ) {

      strcat(tmpbuf, routine_error[0]);

   } else if ( major_status==GSS_S_CONTINUE_NEEDED && minor_status==0 ) {

      strcat(tmpbuf, suppl_information[supplinfo-1] );

   } else {

      if ( calling!=0 ) {
	 if ( calling >= ARRAY_ELEMENTS(calling_error) ) {
	    XVEB((V_ERR, "%s() returned unknown calling error %u!\n",
			call_name, (Uint)calling));
	 } else {
	    strcat(tmpbuf, calling_error[calling]);
	    cntr++;
	 }
      }

      if ( routine!=0 ) {
	 if ( routine >= ARRAY_ELEMENTS(routine_error) ) {
	    XVEB((V_ERR, "%s() returned unknown routine error %u!\n",
		        call_name, (Uint)routine));
	 } else {
	    if (cntr>0)
	       strcat(tmpbuf, "|");
	    strcat(tmpbuf, routine_error[routine]);
	    cntr++;
	 }
      }

      if ( supplinfo!=0 ) {
	 for ( idx=0 ; idx<ARRAY_ELEMENTS(suppl_information) ; idx++ ) {
	    if ( (supplinfo&1)!=0 ) {
	       if (cntr>0)
		  strcat(tmpbuf, "|");
	       strcat(tmpbuf, suppl_information[idx]);
	       cntr++;
	    }
	    supplinfo /= 2;
	 }
	 if ( supplinfo!=0 ) {
	    XVEB((V_ERR, "%s() returned unknown supplementary information!\n",
			call_name ));
	 }
      }

   }

   strcat(tmpbuf,")");
   len = (Uint)strlen(tmpbuf);
   XVEB((V_STATUS, "%s() == %s%s\n",
	    call_name, len>50 ? "\n        " : "", tmpbuf ));

   /* don't print minor status when major_status == GSS_S_COMPLETE */
   if ( major_status!=GSS_S_COMPLETE
        && !(major_status==GSS_S_CONTINUE_NEEDED && abbrev!=0) ) {
      context = 0;
      XVEB((V_SHOW, "         %s(0x%08lx,GSS_S_GSS_CODE) =\n", gss_call, (Ulong)major_status));

      do {
	 message.value  = NULL;
	 message.length = 0;

	 rci = display_status( verbose_level-2, p_gssfp, major_status, GSS_C_GSS_CODE,
			       GSS_C_NO_OID, &context, &message, &maj_stat );
	 if (GSS_ERROR(maj_stat)==GSS_S_COMPLETE) {
	    if (message.value!=NULL && message.length>0 ) {
	       XVEB((V_SHOW, "           \"%.*s\"\n", (int)message.length, (char *)message.value ));
	    }
	 }
	 if ( message.value!=NULL && message.length>0 ) {
	    /* prevent recursion by calling gss_release_buffer() directly */
	    (void) (p_gssfp->gss_release_buffer)( &min_stat, &message );
	 }

      } while ( context!=0 && GSS_ERROR(maj_stat)==GSS_S_COMPLETE );

      /* don't print minor status information if there isn't any */
      if (minor_status!=0) {

         context    = 0;
	 linecount1 = 0;
         XVEB((V_SHOW, "         %s(0x%08lx,GSS_S_MECH_CODE) =\n", gss_call, (Ulong)minor_status));

	 do {
	    message.value  = NULL;
	    message.length = 0;

	    rci = display_status( verbose_level-2, p_gssfp, minor_status, GSS_C_MECH_CODE,
			          p_gssfp->mech, &context, &message, &maj_stat );
	    if (GSS_ERROR(maj_stat)==GSS_S_COMPLETE) {
	       if (message.value!=NULL || message.length>0 ) {
		  XVEB((V_SHOW, "           \"%.*s\"\n", (int)message.length, (char *)message.value ));
		  linecount1++;
	       }
	    }
	    if ( message.value!=NULL && message.length>0 ) {
	       (void) (p_gssfp->gss_release_buffer)( &min_stat, &message );
	    }

	 } while ( context!=0 && GSS_ERROR(maj_stat)==GSS_S_COMPLETE );

         /* If we have queried the minor status value with an explicit mech_oid */
         /* it is interesting to find out if we will get the same output when   */
         /* omitting the explicit mechanism oid 			        */
	 if ( p_gssfp->mech!=GSS_C_NO_OID ) {
	    context    = 0;
	    context2   = 0;
	    linecount2 = 0;
	    difference = FALSE;

	    do {
	       message.value  = message2.value  = NULL;
	       message.length = message2.length = 0;

	       maj_stat  = (p_gssfp->gss_display_status)( &min_stat, minor_status,
					    GSS_C_MECH_CODE, p_gssfp->mech,
					    &context, &message );
	       maj_stat2 = (p_gssfp->gss_display_status)( &min_stat2, minor_status,
					    GSS_C_MECH_CODE, GSS_C_NO_OID,
					    &context2, &message2 );

	       if ( maj_stat!=maj_stat2 || min_stat!=min_stat2
		    || context2!=context || compare_buffers( 0, &message, &message2 ) ) {
		  difference = TRUE;
	       }

	       if ( GSS_ERROR(maj_stat)==GSS_S_COMPLETE
		  && message.length>0 && message.value!=NULL ) {
		  linecount2++;
	       }

	       if ( message.value!=NULL && message.length>0 ) {
		  (void) (p_gssfp->gss_release_buffer)( &min_stat, &message );
	       }

	       if ( message2.value!=NULL && message2.length>0 ) {
		  (void) (p_gssfp->gss_release_buffer)( &min_stat, &message2 );
	       }

	    } while ( difference==FALSE && context!=0 && context2!=0
		      && GSS_ERROR(maj_stat )==GSS_S_COMPLETE
		      && GSS_ERROR(maj_stat2)==GSS_S_COMPLETE );

	    if ( difference || linecount1!=linecount2 ) {
	       XVEB((V_INFO, 0, "  %s() differs on minor_status for explicit/NULL mech_oid.\n", gss_call));
	    }

	 } /* endif (p_gssfp->mech_oid != GSS_C_NO_OID) */
	       
      } /* endif (minor_status!=0) */

   } /* endif (major_status!=0) */

   return(rc);

} /* print_status() */




/*
 * print_lifetime_only()
 *
 * call with buffer[] of >=64 chars !!
 */
void
print_lifetime_only( char * p_buffer, OM_uint32 p_lifetime )
{
   Ulong      days, hours, mins, secs, remain;

   p_buffer[0] = '\0'; /* This isn't necessary, but ... */

   if ( p_lifetime==GSS_C_INDEFINITE ) {

      strcpy(p_buffer, "Indefinite");

   } else if ( p_lifetime==0 ) {

      strcpy(p_buffer, "Expired");

   } else {

      days = p_lifetime/86400;     remain = (p_lifetime - days  * 86400);
      hours= remain/3600;	   remain = (remain     - hours *  3600);
      mins = remain/60;		   remain = (remain     - mins  *    60);
      secs = remain;

      if ( days>0 ) {
	 sprintf(p_buffer,"%02lud %02luh %02lum %02lus",
	         (Ulong)days, (Ulong)hours, (Ulong)mins, (Ulong)secs);
      } else {
	 sprintf(p_buffer,"%02luh %02lum %02lus",
	         (Ulong)hours, (Ulong)mins, (Ulong)secs);
      }

   }

   return;

} /* print_lifetime_only() */




/*
 * print_lifetime()
 *
 *
 */
void
print_lifetime(int p_trclevel, char * prefix, OM_uint32 p_lifetime)
{
   char   buffer[128];

   buffer[0] = '\0';

   if ( p_trclevel<=0 )
      return;

   print_lifetime_only( buffer, p_lifetime );

   XVEB((V_SHOW, "%.150s: %s\n", prefix, buffer ));

   return;

} /* print_lifetime() */




/*
 * check_lifetime_change()
 *
 *
 */
int
check_lifetime_change( int	    p_trclevel,    time_t      p_enter,
		       OM_uint32    p_lifetime1,   OM_uint32   p_lifetime2 )
{
   time_t     now = time(NULL);
   OM_uint32  diff;

   if ( p_enter > now ) {
      XVEB((V_ERR, "OUCH! Time moving backwards %lu seconds (or bug?).\n",
	            (Ulong) now - p_enter ));
      return(1);
   }

   if ( p_lifetime1==GSS_C_INDEFINITE ||  p_lifetime2==GSS_C_INDEFINITE ) {
      if ( p_lifetime1!=GSS_C_INDEFINITE  ||  p_lifetime2!=GSS_C_INDEFINITE ) {
	 XVEB((V_ERR, "strange lifetime change:\n", (Ulong) now-p_enter ));
	 print_lifetime(2, "          time1", p_lifetime1);
	 print_lifetime(2, "          time2", p_lifetime2);
      }
      return(0);
   }

   diff = (OM_uint32)(now - p_enter);

   if ( p_lifetime1 < p_lifetime2 ) {
      XVEB((V_ERR, "OUCH! Lifetime has increased by %lu sec while %lu sec passed!\n",
	    (Ulong) (p_lifetime2 - p_lifetime1), (Ulong)diff ));
      return(1);
   }

   if ( (OM_uint32)(p_lifetime1 - p_lifetime2) < diff + 5 ) {
      /* tolerable lifetime change */
      return(0);
   }

   XVEB((V_ERR, "lifetime jumped by %lu sec while %lu sec passed!\n",
	    (Ulong) (p_lifetime1 - p_lifetime2), (Ulong) diff ));

   return(1);

} /* check_lifetime_change() */




/*
 * display_status()
 *
 *
 */
int
display_status( int            p_trclevel,      DLL_GSSFP_T    * p_gssfp,
	        OM_uint32      p_status_value,  int              p_status_type,
		gss_OID        p_oid,	        OM_uint32      * pp_context,
		gss_buffer_t   p_message,	OM_uint32      * pp_maj_stat )
{
   char        * gss_call = "gss_display_status";
   char        * ptr;
   size_t        prlen;
   OM_uint32     min_stat;
   OM_uint32     maj_stat = GSS_S_FAILURE;
   int		 dump_it  = 0;
   int	         rc       = 0;

   BOGUS_INI_MINOR(  min_stat  );
   BOGUS_INI_BUFFER( p_message );

   start_timer();
   maj_stat = (p_gssfp->gss_display_status)( &min_stat, p_status_value, p_status_type,
					     p_oid, pp_context, p_message );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_DISPLAY_STATUS, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER( p_message,  "message" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      if ( p_message->length>0 ) {

	 rc++;
	 XVEB((V_ERR, "%s() failed and returned a message.\n", gss_call));
	 (void) (p_gssfp->gss_release_buffer)( &min_stat, p_message );
	 p_message->value  = NULL;
	 p_message->length = 0;

      } else if ( p_message->value!=NULL ) {

	 XVEB((V_HIDE, "%s() failed but didn't clear value-part of message-buffer.\n", gss_call));

      }

   } else { /* maj_stat==GSS_S_COMPLETE */

      if ( p_message->length==0 || p_message->value==NULL ) {

	 rc++;
	 XVEB((V_ERR, "%s() succeeded but failed to return a message!\n", gss_call));
	 print_buffer_head( 2, "message", p_message );
	 /* HEY! This was NO success: a missing message is an obvious failure ! */
	 maj_stat = GSS_S_FAILURE;

      } else {

	 ptr = memchr( p_message->value, 0, p_message->length );
	 if ( ptr!=NULL ) {
	    rc++;
	    /* Uh-oh!  We found a '\0' char embedded in the message */
	    XVEB((V_ERR, "%s(%s,0x%08lx)\n    returned a string with ",
			 gss_call,
			 (p_status_type==GSS_C_GSS_CODE) ? "GSS_C_GSS_CODE" : "GSS_C_MECH_CODE",
			 (Ulong) p_status_value ));
	    prlen = (size_t)(ptr - (char *)p_message->value);
	    if ( prlen+1 == p_message->length ) {
	       (p_gssfp->dspstat_trailing_nul)++;
	       XVEB((V_SHOW, "trailing NUL char!\n"));
	    } else {
	       (p_gssfp->dspstat_embedded_nul)++;
	       XVEB((V_SHOW, "NUL at offset %lu!\n", (Ulong) prlen));
	       dump_it = 1;
	    }

	 } /* ptr!=NULL */

	 ptr = memchr( p_message->value, '\r', p_message->length );
	 if ( ptr==NULL ) {
	    ptr = memchr( p_message->value, '\n', p_message->length );
	 }
	 if ( ptr!=NULL ) {
	    (p_gssfp->dspstat_linebreak)++;
	    if ( options.sap_constraints!=FALSE ) {
	       XVEB((V_ERR, "%s(%s,0x%08lx)\n"
		            "    returned a string containing newline(s) at offset %lu!\n",
			    gss_call,
			    (p_status_type==GSS_C_GSS_CODE) ? "GSS_C_GSS_CODE" : "GSS_C_MECH_CODE",
			    (Ulong) (ptr - (char *)p_message->value) ));
	       dump_it = 1;
	    } else {
	       XVEB((V_HIDE, "%s(%s,0x%08lx)\n"
		             "    returned a string containing newline(s) at offset %lu!\n",
			     gss_call,
			     (p_status_type==GSS_C_GSS_CODE) ? "GSS_C_GSS_CODE" : "GSS_C_MECH_CODE",
			     (Ulong) (ptr - (char *)p_message->value) ));

	    }
	 }

	 if ( dump_it!=0 ) {
	    print_buffer_head( 2, "message", p_message );
	    print_buffer_content( 2, p_message );
	 }

      } /* message is a valid gss_buffer_t */

   } /* maj_stat==GSS_S_COMPLETE */

   if (GSS_ERROR(maj_stat)!=maj_stat) {
      rc++;
      XVEB((V_ERR, "%s() sets infomational bits! (0x%08lx)\n", gss_call, (Ulong) maj_stat));
   }

   if ( pp_maj_stat!=NULL ) { (*pp_maj_stat) = maj_stat; }

   return(rc);

} /* display_status() */




/*
 * indicate_mechs()
 *
 *
 */
int
indicate_mechs( int	        p_trclevel,	DLL_GSSFP_T    * p_gssfp,
	        gss_OID_set   * pp_oid_set,	OM_uint32      * pp_maj_stat )
{
   char      * gss_call = "gss_indicate_mechs";
   OM_uint32   min_stat;
   OM_uint32   maj_stat = GSS_S_FAILURE;
   int	       rc	= 0;

   (*pp_oid_set) = GSS_C_NO_OID_SET;

   BOGUS_INI_MINOR(   min_stat    );
   BOGUS_INI_OID_SET( pp_oid_set );
   start_timer();
   maj_stat = (p_gssfp->gss_indicate_mechs)( &min_stat, pp_oid_set );
   timedelay = read_timer();
   rc += print_status(p_gssfp, GSS_INDICATE_MECHS, maj_stat, min_stat);

   BOGUS_CHECK_OID_SET( pp_oid_set, "mech_list" );

   if (maj_stat!=GSS_S_COMPLETE) {

      rc++;

      if ( (*pp_oid_set)!=GSS_C_NO_OID_SET ) {

	 rc++;
	 XVEB((V_ERR, "gss_indicate_mechs() failed AND returned an gss_OID_set!\n"));

	 if ( (p_gssfp->flags & GSS_DL_V2)!=0 ) {
	    rc += release_oid_set( p_gssfp, pp_oid_set );
	 }
      }

   } else { /* maj_stat==GSS_S_COMPLETE */

      rc += print_oid_set( p_trclevel, "mech_list from gss_indicate_mechs()", *pp_oid_set );

   } /* maj_stat==GSS_S_COMPLETE */

   if ( pp_maj_stat!=NULL ) { *pp_maj_stat = maj_stat; }

   return(rc);

} /* indicate_mechs() */




/*
 * inquire_names_for_mech()
 *
 *
 */
int
inquire_names_for_mech( int	         p_trclevel,	 DLL_GSSFP_T	* p_gssfp,
			gss_OID_set    * pp_nt_list,	 OM_uint32	* pp_maj_stat )
{
   char      * gss_call = "gss_inquire_names_for_mech";
   OM_uint32   min_stat;
   OM_uint32   maj_stat = GSS_S_FAILURE;
   int	       rc	= 0;

   if ( p_gssfp->gss_inquire_names_for_mech==0 ) {
      XVEB((V_SHOW, "gss_inquire_names_for_mech() not available.\n"));
      ERROR_RETURN_RC(1);
   }

   BOGUS_INI_OID_SET( pp_nt_list );
   BOGUS_INI_MINOR(   min_stat   );

   start_timer();
   maj_stat = (p_gssfp->gss_inquire_names_for_mech)( &min_stat,
						     p_gssfp->mech, pp_nt_list );
   timedelay = read_timer();

   rc += print_status(p_gssfp, GSS_INQUIRE_NAMES_FOR_MECH, maj_stat, min_stat);

   BOGUS_CHECK_OID_SET( pp_nt_list, "nametype_list" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      rc++;
      if ( (*pp_nt_list)!=GSS_C_NO_OID_SET ) {

	 rc++;

	 XVEB((V_ERR, "gss_inquire_names_for_mech() failed AND returned an gss_OID_set!\n"));
	 rc += release_oid_set( p_gssfp, pp_nt_list );

      }

   } else {

      if ( (*pp_nt_list)==GSS_C_NO_OID_SET ) {

	 rc++;
	 XVEB((V_ERR, "gss_inquire_names_for_mech() failed to return an gss_OID_set!\n"));

      } else {

	 rc += print_oid_set( p_trclevel-1, "name_types", (*pp_nt_list) );

      }

   }

error:
   if ( pp_maj_stat!=NULL ) { *pp_maj_stat = maj_stat; }

   return(rc);

} /* inquire_names_for_mech() */


