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

static char * this_File GNU_UNUSED = __FILE__;

/************************************************************************/
/* $Id: //tools/src/freeware/gsstest/msg_prot.c#4 $
 ************************************************************************
 *
 * 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"


struct tmsg_job  stdjob[] = {
   { TMSG_ACT_A_MICONLY,    {  1000 } },
   { TMSG_ACT_A_WMIC,       {  1000 } },
   { TMSG_ACT_A_MICONLY,    {  1000 } },
   { TMSG_ACT_A_WCONF,      {  1000 } },
   { TMSG_ACT_B_MICONLY,    {   999 } },
   { TMSG_ACT_A_WCONF,      {  1000 } },
   { TMSG_ACT_A_WCONF,      {  1000 } },
   { TMSG_ACT_B_WCONF,      {   998 } },
   { TMSG_ACT_B_WCONF,      {   998 } },
   { TMSG_ACT_B_MICONLY,    {   999 } },
   { TMSG_ACT_B_TAKE_MSG,   {    1  } },
   { TMSG_ACT_B_WCONF,      {   998 } },
   { TMSG_ACT_B_WMIC,       {   997 } },
   { TMSG_ACT_B_WMIC,       {   997 } },
   { TMSG_ACT_A_WMIC,       {  1000 } },
   { TMSG_ACT_A_MICONLY,    {  1000 } },
   { TMSG_ACT_A_TAKE_MSG,   {    1  } },
   { TMSG_ACT_A_TAKE_MSG,   {    1  } },
   { TMSG_ACT_B_TAKE_MSG,   {    0  } },
   { TMSG_ACT_A_TAKE_MSG,   {    0  } },
   { TMSG_ACT_END_QUIT,     {    0  } },
   { 0,                     {    0  } }
};


struct tmsg_job  stdjob_xfer[] = {
   { TMSG_ACT_A_MICONLY,   {  1000 } },
   { TMSG_ACT_A_WMIC,      {  1000 } },
   { TMSG_ACT_A_MICONLY,   {  1000 } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_B_MICONLY,   {   999 } },
   { TMSG_ACT_B_CTXTRANS,  {    0  } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_B_WCONF,     {   998 } },
   { TMSG_ACT_B_WCONF,     {   998 } },
   { TMSG_ACT_A_CTXTRANS,  {    0  } },
   { TMSG_ACT_B_MICONLY,   {   999 } },
   { TMSG_ACT_B_TAKE_MSG,  {    1  } },
   { TMSG_ACT_B_WCONF,     {   998 } },
   { TMSG_ACT_B_WMIC,      {   997 } },
   { TMSG_ACT_B_CTXTRANS,  {    0  } },
   { TMSG_ACT_B_WMIC,      {   997 } },
   { TMSG_ACT_A_WMIC,      {  1000 } },
   { TMSG_ACT_A_MICONLY,   {  1000 } },
   { TMSG_ACT_LOC_FTRANS,  {    0  } },
   { TMSG_ACT_CHILD_FTRANS,{    0  } },
   { TMSG_ACT_A_MICONLY,   {  1000 } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_B_MICONLY,   {   999 } },
   { TMSG_ACT_B_WCONF,     {   998 } },
   { TMSG_ACT_A_TAKE_MSG,  {    1  } },
   { TMSG_ACT_A_CTXTRANS,  {    0  } },
   { TMSG_ACT_A_TAKE_MSG,  {    1  } },
   { TMSG_ACT_CTX_REFRESH, { GSS_C_INDEFINITE } },
   { TMSG_ACT_A_MICONLY,   {  1000 } },
   { TMSG_ACT_A_WMIC,      {  1000 } },
   { TMSG_ACT_A_MICONLY,   {  1000 } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_B_MICONLY,   {   999 } },
   { TMSG_ACT_B_CTXTRANS,  {    0  } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_A_WCONF,     {  1000 } },
   { TMSG_ACT_B_WCONF,     {   998 } },
   { TMSG_ACT_B_WCONF,     {   998 } },
   { TMSG_ACT_B_TAKE_MSG,  {    0  } },
   { TMSG_ACT_A_TAKE_MSG,  {    0  } },
   { TMSG_ACT_END_QUIT,    {    0  } },
   { TMSG_END_OF_LIST,     {    0  } }
};


struct tmsg_job  stdjob_early_xfer[] = {
   { TMSG_ACT_CHILD_FTRANS, {    0 } },
   { TMSG_ACT_LOC_FTRANS,   {    0 } },
   { TMSG_ACT_A_MICONLY,    { 1000 } },
   { TMSG_ACT_A_WMIC,       { 1000 } },
   { TMSG_ACT_A_MICONLY,    { 1000 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_B_MICONLY,    {  999 } },
   { TMSG_ACT_B_CTXTRANS,   {    0 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_B_WCONF,      {  998 } },
   { TMSG_ACT_B_WCONF,      {  998 } },
   { TMSG_ACT_A_CTXTRANS,   {    0 } },
   { TMSG_ACT_B_MICONLY,    {  999 } },
   { TMSG_ACT_B_TAKE_MSG,   {    1 } },
   { TMSG_ACT_B_WCONF,      {  998 } },
   { TMSG_ACT_B_WMIC,       {  997 } },
   { TMSG_ACT_B_CTXTRANS,   {    0 } },
   { TMSG_ACT_B_WMIC,       {  997 } },
   { TMSG_ACT_A_WMIC,       { 1000 } },
   { TMSG_ACT_A_MICONLY,    { 1000 } },
   { TMSG_ACT_LOC_FTRANS,   {    0 } },
   { TMSG_ACT_CHILD_FTRANS, {    0 } },
   { TMSG_ACT_A_MICONLY,    { 1000 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_B_MICONLY,    {  999 } },
   { TMSG_ACT_B_WCONF,      {  998 } },
   { TMSG_ACT_A_TAKE_MSG,   {    1 } },
   { TMSG_ACT_A_CTXTRANS,   {    0 } },
   { TMSG_ACT_A_TAKE_MSG,   {    1 } },
   { TMSG_ACT_CTX_REFRESH,  { GSS_C_INDEFINITE } },
   { TMSG_ACT_LOC_FTRANS,   {    0 } },
   { TMSG_ACT_A_MICONLY,    { 1000 } },
   { TMSG_ACT_A_WMIC,       { 1000 } },
   { TMSG_ACT_A_MICONLY,    { 1000 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_B_MICONLY,    {  999 } },
   { TMSG_ACT_B_CTXTRANS,   {    0 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_A_WCONF,      { 1000 } },
   { TMSG_ACT_B_WCONF,      {  998 } },
   { TMSG_ACT_B_WCONF,      {  998 } },
   { TMSG_ACT_B_TAKE_MSG,   {    0 } },
   { TMSG_ACT_A_TAKE_MSG,   {    0 } },
   { TMSG_ACT_END_QUIT,     {    0 } },
   { TMSG_END_OF_LIST,      {    0 } }
};


struct tmsg_job xfer_only [] = {
   { TMSG_ACT_A_CTXTRANS,  {  0   } },
   { TMSG_ACT_B_CTXTRANS,  {  0   } },
   { TMSG_ACT_LOC_FTRANS,  {  0   } },
   { TMSG_ACT_A_CTXTRANS,  {  0   } },
   { TMSG_ACT_A_CTXTRANS,  {  0   } },
   { TMSG_ACT_CTX_REFRESH, { GSS_C_INDEFINITE } },
   { TMSG_ACT_A_CTXTRANS,  {  0   } },
   { TMSG_ACT_CHILD_FTRANS,{  0   } },
   { TMSG_ACT_B_CTXTRANS,  {  0   } },
   { TMSG_ACT_CTX_REFRESH, { GSS_C_INDEFINITE } },
   { TMSG_ACT_B_CTXTRANS,  {  0   } },
   { TMSG_ACT_END_QUIT,    {  0   } },
   { TMSG_END_OF_LIST,     {  0   } }
};
   

static struct tmsg_job zerolength_msgs[] = {
   { TMSG_ACT_A_WCONF,     { 1000 } },
   { TMSG_ACT_B_MICONLY,   {   10 } },
   { TMSG_ACT_A_WMIC,      {   23 } },
   { TMSG_ACT_A_WCONF,     {  123 } },
   { TMSG_ACT_B_MICONLY,   {    5 } },
   { TMSG_ACT_B_TAKE_MSG,  {    0 } },
   { TMSG_ACT_A_WCONF,     {    0 } },
   { TMSG_ACT_A_MICONLY,   {    0 } },
   { TMSG_ACT_A_WMIC,      {    0 } },
   { TMSG_ACT_B_TAKE_MSG,  {    0 } },
   { TMSG_ACT_B_WMIC,      {    0 } },
   { TMSG_ACT_B_MICONLY,   {    0 } },
   { TMSG_ACT_B_WCONF,     {    0 } },
   { TMSG_ACT_A_TAKE_MSG,  {    0 } },
   { TMSG_ACT_A_WCONF,     { 1234 } },
   { TMSG_ACT_A_MICONLY,   { 2345 } },
   { TMSG_ACT_B_WMIC,      { 1432 } },
   { TMSG_ACT_A_WMIC,      {  543 } },
   { TMSG_ACT_B_WCONF,     {  556 } },
   { TMSG_ACT_B_MICONLY,   {  997 } },
   { TMSG_ACT_A_MICONLY,   {  297 } },
   { TMSG_ACT_AB_TAKE_MSG, {    0 } },
   { TMSG_ACT_END_QUIT,    {    0 } },
   { TMSG_END_OF_LIST,     {    0 } }
};


/*
 * msg_prot_tests()
 *
 *
 */
int
msg_prot_tests( DLL_GSSFP_T * p_gssfp_ini, DLL_GSSFP_T * p_gssfp_acc )
{
   int		  success  = FALSE;
   int		  rc       = 0;
   int		  rci      = 0;

   XVEB((V_SHOW, "====================\nMessage Protection functions ...\n----------\n"));

   if ( missing_msg_prot!=0  &&  options.try_msgprot_anyway==0 ) {

      XVEB((V_SHOW, "skipping -- GSS-API mechanism doesn't seem to support message protection.\n"));

   } else {

#if 0
      if ( options.show_timing_data!=FALSE ) {

	 XVEB((V_TEST, "Benchmarking message protection functions\n"));

	 rci  = benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_WCONF,     200 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_WCONF,     200 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_WCONF,    2000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_WCONF,    2000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_WCONF,   20000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_WCONF,   20000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_WMIC,      200 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_WMIC,      200 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_WMIC,     2000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_WMIC,     2000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_WMIC,    20000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_WMIC,    20000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_MICONLY,   200 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_MICONLY,   200 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_MICONLY,  2000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_MICONLY,  2000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, TRUE,  BENCH_MICONLY, 20000 );
	 rci += benchmark_msgprot( verbose_level, p_gssfp, FALSE, BENCH_MICONLY, 20000 );

	 XVEB((V_RESULT, rci, 0, NULL ));
	 /* rc  += rci;  it would be somewhat unfair to add any problems here */

      } /* endif (options.show_timing_data!=FALSE) */
#endif

      XVEB((V_TEST, "Testing Zero-length messages\n"));
      rci = sap_msg_exchange( verbose_level, SNC_CONTEXT_FLAGS,
			      GSS_C_INDEFINITE, &(zerolength_msgs[0]),
			      p_gssfp_ini, p_gssfp_acc,
			      SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			      p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len,
			      &success );

      if ( success==FALSE ) {
	 rci++;
      }
      XVEB((V_RESULT, rci, 0, NULL ));
      rc  += rci;


      XVEB((V_TEST, "gss_wrap_size_limit()/gss_wrap()/gss_getmic() token size (increase)\n"));
      rci = check_max_wrap_increase( verbose_level, SNC_CONTEXT_FLAGS,
				     p_gssfp_ini, p_gssfp_acc );
      XVEB((V_RESULT, rci, 0, NULL ));
      rc  += rci;


      if ( p_gssfp_ini->context_xferred>0 ) {
	 XVEB((V_TEST, "Testing immediate context transfer\n"));
	 rci = sap_msg_exchange( verbose_level, SNC_CONTEXT_FLAGS,
				 GSS_C_INDEFINITE, &(stdjob_early_xfer[0]),
				 p_gssfp_ini, p_gssfp_acc,
				 SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
				 p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len,
				 &success );

	 if ( success==FALSE ) {
	    rci++;
	 }
	 XVEB((V_RESULT, rci, 0, NULL ));
	 rc  += rci;
      }


      if ( options.test_errors!=FALSE ) {

	 XVEB((V_SHOW, "====================\nMessage Protection error handling ...\n----------\n"));

	 XVEB((V_TEST, "Reflection Attack\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_REFLECT, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Replay Attack\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_REPLAY, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Reordered Messages\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_REORDER, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message Loss\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_DELMSG, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message Truncation\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_TRUNC, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message Truncation #2\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_TRUNC_2, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message Truncation #3\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_TRUNC_3, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message with Trailing Garbage\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_TRAIL_GARB, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message modification (center)\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_MOD_MIDDLE, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

	 XVEB((V_TEST, "Message modification (tail)\n"));
	 rci = do_msg_prot_test( verbose_level, SNC_CONTEXT_FLAGS, GSS_C_INDEFINITE, "",
				 MP_TEST_MOD_TAIL, p_gssfp_ini, p_gssfp_acc, &success );
      
	 rc  += rci;

      } /* options.test_errors!=FALSE */

   } /* no message protection supported */
  
   return(rc);

} /* msg_prot_tests() */



/*
 * msg_prot_statistics()
 *
 *
 */
void
msg_prot_statistics( size_t          p_orimsg_size,
		     size_t	     p_out_size,
		     Ulong           p_microsec,
		     struct xtime  * p_xtime )
{
   Ulong    current;

   if ( p_out_size>0 && p_microsec>0 ) {

      if ( p_orimsg_size > 0 ) {
	 /* try to capture the smallest (observed) overhead */
	 if ( p_xtime->min==0 ) {
	    p_xtime->min = p_microsec;
	 } else {
	    if ( p_xtime->min > p_microsec ) { p_xtime->min = p_microsec; }
	 }
      }

      /* There should always be a non-zero output size ... (except for zero-length msgs) */
      if ( p_orimsg_size>=MINSIZE_FOR_STATISTICS
	   && p_orimsg_size<=MAXSIZE_FOR_STATISTICS ) {

	 if ( p_microsec < 12000
	      || p_microsec < 2*(p_xtime->min) ) {
	    /* we only want to consider operations that fall within a time slice        */
	    /* Hmmmm.  We still get quite a large variation of speed in this benchmark! */
	    /*									        */
	    /* less than 10 milliseconds will probably fail for Windows 95, because of  */
	    /* the apparent cheap implementation of QueryPerformanceCounter() on that   */
	    /* platform and even then it will be a useless result ...			*/
	    /*                                                                          */
	    /* Update: it is CPU-dependent whether QueryPerformanceCounter() has a      */
	    /* resolution below 10 milliseconds: Win95 doesn't know the Pentium Pro     */

	    /* For very slow implementations the 12 Milliseconds limit is a problem;        */
	    /* therefore we do accept all operations that do not exceed twice the minimum   */
	    /* measured time.  Normally, this minimum overhead should be less than          */
	    /* 6 Milliseconds and therefore this change should not affect regular tests ... */

	    current = (Ulong)((float)p_orimsg_size * (float)1000 / (float)p_microsec);

	    /* add statistics for this message protection to our statistics table */
	    p_xtime->count++;


	    p_xtime->sum += current;

	    if ( p_xtime->max <  current )   { p_xtime->max = current; }

	 }

      } 

   } /* p_out_size!=0 */

   return;

} /* msg_prot_statistics() */



/*
 * get_mic()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_get_mic()
 */
int
get_mic( int		p_trclevel,   DLL_GSSFP_T  * p_gssfp,
	 gss_ctx_id_t   p_ctx,	      gss_qop_t      p_qop,
	 gss_buffer_t   p_message,
	 gss_buffer_t   p_mic_token,  OM_uint32    * pp_maj_stat )
{
   char       * gss_call = "gss_get_mic";
   Ulong        microsec;
   OM_uint32    min_stat;
   OM_uint32    maj_stat = GSS_S_FAILURE;
   int          rc       = 0;

   BOGUS_INI_BUFFER( p_mic_token );
   BOGUS_INI_MINOR(  min_stat    );

   start_timer();
   maj_stat = (p_gssfp->gss_get_mic)( &min_stat, p_ctx, p_qop, p_message, p_mic_token );
   microsec = timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_GET_MIC, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER( p_mic_token, "mic_token" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( maj_stat==GSS_S_CONTEXT_EXPIRED
	   && p_mic_token->length>0 && p_mic_token->value!=NULL ) {

	 XVEB((V_HIDE, "  %s(): returns mic_token along with GSS_S_CONTEXT_EXPIRED!\n", gss_call));
	 rc += release_buffer( p_gssfp, p_mic_token );

      } else {

	 if ( p_mic_token->length>0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed but returned a mic_token!\n", gss_call));
	    print_buffer_head( 2, "mic_token", p_mic_token );
	    rc += release_buffer( p_gssfp, p_mic_token );
	 }

      }

   } else {

      /* gssapi success */
      if ( p_mic_token->length==0 || p_mic_token->value==NULL ) {
	 rc++;
	 XVEB((V_ERR, "%s() succeeded but forgot to return a mic_token!\n", gss_call));
	 print_buffer_head( 2, "mic_token", p_mic_token );
	 /* HEY! This was NO success: a missing token is an obvious failure ! */
	 maj_stat = GSS_S_FAILURE;
      }

      msg_prot_statistics( p_message->length, p_mic_token->length,
			   microsec, &(p_gssfp->getmic_speed) );
   }

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

   return(rc);

} /* get_mic() */





/*
 * verify_mic()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_verify_mic()
 */
int
verify_mic( int            p_trclevel,   DLL_GSSFP_T  * p_gssfp,
	    gss_ctx_id_t   p_ctx,	 gss_buffer_t   p_message,
	    gss_buffer_t   p_mic_token,
	    gss_qop_t	 * pp_qop,       OM_uint32    * pp_maj_stat )
{
   char       * gss_call = "gss_verify_mic";
   Ulong        microsec;
   OM_uint32    min_stat;
   OM_uint32    maj_stat = GSS_S_FAILURE;
   int		i;
   int          rc       = 0;


   BOGUS_INI_VALUE( pp_qop   );
   BOGUS_INI_MINOR( min_stat );

   start_timer();
   maj_stat = (p_gssfp->gss_verify_mic)( &min_stat, p_ctx, p_message, p_mic_token, pp_qop );
   microsec = timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_VERIFY_MIC, maj_stat, min_stat );

   BOGUS_CHECK_VALUE( pp_qop, "qop" );

   if ( maj_stat==GSS_S_COMPLETE ) {
      msg_prot_statistics( p_message->length, p_mic_token->length,
			   microsec, &(p_gssfp->verifymic_speed) );

      /* Let's do some statistics on non-default QOP values here */
      if ( pp_qop!=NULL && (*pp_qop)!=GSS_C_QOP_DEFAULT ) {

	 for ( i=0 ; i<MAX_QOP_VALUES ; i++ ) {
	    if ( p_gssfp->mic_qop[i]==0 ) {
	       p_gssfp->mic_qop[i] = (*pp_qop);
	       if ( i==(MAX_QOP_VALUES-1) ) {
		  XVEB((V_SHOW, "%s(): encountered %u distinct QOP values so far!\n",
		                gss_call, i+1 ));
	       }
	       break;
	    } else if ( p_gssfp->mic_qop[i]==(*pp_qop) ) {
	       break;
	    }
	 }

      } /* QOP-statistics */

   }

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

   return(rc);

} /* verify_mic() */




/*
 * wrap()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_wrap()
 */
int
wrap( int             p_trclevel,    DLL_GSSFP_T  * p_gssfp,
      OM_uint32	      p_ctx_flags,
      gss_ctx_id_t    p_ctx,	     gss_qop_t	    p_qop,
      int	      p_conf_req,    gss_buffer_t   p_message,
      int	    * pp_conf,	     gss_buffer_t   p_wrap_token,
      OM_uint32	    * pp_maj_stat )
{
   char       * gss_call   = "gss_wrap";
   Ulong        microsec;
   OM_uint32    min_stat;
   OM_uint32    maj_stat   = GSS_S_FAILURE;
   int          conf_state;
   int          rc         = 0;

   if ( pp_conf!=NULL ) { *pp_conf = FALSE; }

   BOGUS_INI_VALUE(  &conf_state  );
   BOGUS_INI_BUFFER( p_wrap_token );
   BOGUS_INI_MINOR(  min_stat     );

   start_timer();
   maj_stat = (p_gssfp->gss_wrap)( &min_stat, p_ctx, p_conf_req, p_qop,
				   p_message, &conf_state, p_wrap_token );
   microsec = timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_WRAP, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER( p_wrap_token, "wrap_token" );
   BOGUS_CHECK_VALUE(  &conf_state,  "conf_state" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( maj_stat==GSS_S_CONTEXT_EXPIRED
	   && p_wrap_token->length>0 && p_wrap_token->value!=NULL ) {

	 /* This really doesn't look right, but some implementations do it ... */
	 XVEB((V_HIDE, "%s(): returns wrap_token along with GSS_S_CONTEXT_EXPIRED!\n", gss_call));
	 rc += release_buffer( p_gssfp, p_wrap_token );

      } else {

	 if ( p_wrap_token->length>0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() returned a wrap token inspite of a fatal error!\n", gss_call));
	    print_buffer_head( 2, "wrap_token", p_wrap_token );
	    rc += release_buffer( p_gssfp, p_wrap_token );
	 }

      }

   } else {  /* else maj_stat==GSS_S_COMPLETE */

      /* gssapi success */
      if ( p_wrap_token->length==0 || p_wrap_token->value==NULL ) {
	 rc++;
	 XVEB((V_ERR, "%s() succeeded but forgot to return a wrap_token!\n", gss_call));
	 print_buffer_head( 2, "wrap_token", p_wrap_token );
	 /* HEY! This was NO success: a missing token is an obvious failure ! */
	 maj_stat = GSS_S_FAILURE;
      }

      msg_prot_statistics( p_message->length, p_wrap_token->length, microsec,
			   (conf_state==FALSE) ?   &(p_gssfp->wmic_speed)
						 : &(p_gssfp->wconf_speed) );

      if ( p_conf_req==0  &&  conf_state!=0 ) {

	 rc++;
	 XVEB((V_ERR, "%s(): inappropriately tries to \"enforce\" confidentiality!\n", gss_call));
	 if ( (p_ctx_flags&GSS_C_CONF_FLAG)==0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() enforces confidentiality that context doesn't offer?!\n", gss_call));
	 }

      } else if ( p_conf_req!=0  &&  conf_state==0 ) {

	 if ( (p_ctx_flags&GSS_C_CONF_FLAG)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): confidentiality inappropriately denied!\n", gss_call));
	    p_gssfp->conf_failure = TRUE;
	 } else {
	    /* that is ok for a mechanism that doesn't offer confidentiality */
	    /* XVEB((V_INFO, 2, "%s(): confidentiality was requested but not applied.\n", gss_call)); */
	 }

      } else {

	 if ( conf_state!=0 && (p_ctx_flags&GSS_C_CONF_FLAG)==0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): confidentiality applied which context didn't offer!\n", gss_call));
	 }
      }

      if ( pp_conf!=NULL )  { (*pp_conf) = conf_state; }

   }  /* endif maj_stat==GSS_S_COMPLETE */

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

   return(rc);

} /* wrap() */




/*
 * unwrap()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_unwrap()
 */
int
unwrap( int		p_trclevel,    DLL_GSSFP_T    * p_gssfp,
        OM_uint32	p_ctx_flags,
	int		p_expect_conf,  /* confidentiality protection indicator expected */
	int		p_expect_zlm,   /* zero-length output message expected           */
	gss_ctx_id_t	p_ctx,	       gss_buffer_t     p_wrap_token,
	gss_buffer_t	p_message,     int	      * pp_conf,
	gss_qop_t     * pp_qop,	       OM_uint32      * pp_maj_stat )
{
   char       * gss_call   = "gss_unwrap";
   Ulong        microsec;
   OM_uint32    min_stat;
   OM_uint32    maj_stat   = GSS_S_FAILURE;
   int	        conf_state;
   int	        i;
   int          rc         = 0;

   if ( pp_conf!=NULL ) { (*pp_conf) = FALSE; }

   BOGUS_INI_VALUE(  &conf_state );
   BOGUS_INI_VALUE(  pp_qop      );
   BOGUS_INI_BUFFER( p_message   );
   BOGUS_INI_MINOR(  min_stat    );

   start_timer();
   maj_stat = (p_gssfp->gss_unwrap)( &min_stat, p_ctx, p_wrap_token, p_message, &conf_state, pp_qop );
   microsec = timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_UNWRAP, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER( p_message,   "message"    );
   BOGUS_CHECK_VALUE(  &conf_state, "conf_state" );
   BOGUS_CHECK_VALUE(  pp_qop,      "qop"        );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( maj_stat==GSS_S_CONTEXT_EXPIRED
	   && p_message->length>0 && p_message->value!=NULL ) {

	 /* This really doesn't look right, but some implementations do it ... */
	 XVEB((V_HIDE, "%s(): returns a message along with GSS_S_CONTEXT_EXPIRED!\n", gss_call));
	 rc += release_buffer( p_gssfp, p_message );

      } else {

	 if ( p_message->length>0 ) {

	    if ( GSS_ERROR(maj_stat)==GSS_S_COMPLETE ) {
	       XVEB((V_HIDE, "%s() failed and returned a message.\n", gss_call));
	    } else {
	       rc++;
	       XVEB((V_ERR, "%s() returned a message inspite of a fatal error!\n", gss_call));
	    }
	    print_buffer_head( 2, "message", p_message );
	    rc += release_buffer( p_gssfp, p_message );

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

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

	 }

      }

   } else { /* else maj_stat==GSS_S_COMPLETE */

      /* Let's do some statistics on non-default QOP values here */
      if ( pp_qop!=NULL && (*pp_qop)!=GSS_C_QOP_DEFAULT ) {

	 for ( i=0 ; i<MAX_QOP_VALUES ; i++ ) {

	    if ( conf_state!=0 ) {

	       if ( p_gssfp->wconf_qop[i]==0 ) {

		  p_gssfp->wconf_qop[i] = (*pp_qop);
		  if ( i==(MAX_QOP_VALUES-1) ) {
		     XVEB((V_SHOW, "%s(conf=TRUE): encountered %u distinct QOP values so far!\n",
				   gss_call, i+1 ));
		  }
		  break;

	       } else if ( p_gssfp->wconf_qop[i]==(*pp_qop) ) {

		  break;

	       }

	    } else {

	       if ( p_gssfp->wmic_qop[i]==0 ) {

		  p_gssfp->wmic_qop[i] = (*pp_qop);
		  if ( i==(MAX_QOP_VALUES-1) ) {
		     XVEB((V_SHOW, "%s(conf=FALSE): encountered %u distinct QOP values so far!\n",
				   gss_call, i+1 ));
		  }
		  break;

	       } else if ( p_gssfp->wmic_qop[i]==(*pp_qop) ) {

		  break;

	       }

	    }

	 }

      } /* QOP-statistics */

      /* gssapi success */
      if ( p_expect_zlm==0 ) {
	 /* expecting a zero-length message */
         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 token is an obvious failure ! */
	    maj_stat = GSS_S_FAILURE;
	 }

      } else { /* p_expect_zlm!=0 */

	 if ( p_message->length>0 ) {
	    rc++;
	    XVEB((V_ERR, "%s should have returned a ZERO-length message but didn't!\n", gss_call));
	 } else if ( p_message->value!=NULL ) {
	    XVEB((V_HIDE, "%s(): didn't clear value-part of the ZERO-length message.\n", gss_call));
	 }

      }

      msg_prot_statistics( p_message->length, p_wrap_token->length, microsec,
			   (conf_state==FALSE) ?   &(p_gssfp->unwmic_speed)
						 : &(p_gssfp->unwconf_speed) );

      if ( p_expect_conf==0  && conf_state!=0 ) {
	 rc++;
	 XVEB((V_ERR, "%s() indicates UNexpected confidentiality protection!\n", gss_call));
      } else if ( p_expect_conf!=0 && conf_state==0 ) {
	 rc++;
	 XVEB((V_ERR, "%s() indicates missing confidentiality!\n", gss_call));
	 p_gssfp->conf_failure = TRUE;
      }

      if ( conf_state!=0  &&  (p_ctx_flags&GSS_C_CONF_FLAG)==0 ) {
	 rc++;
	 XVEB((V_ERR, "%s() indicates confidentiality which context doesn't offer!\n", gss_call));
      }

      if ( pp_conf!=NULL ) {  (*pp_conf) = conf_state; }

   }  /* endif maj_stat==GSS_S_COMPLETE */

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

   return(rc);

} /* unwrap() */




/*
 * wrap_size_limit()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_wrap_size_limit()
 *
 */
int
wrap_size_limit( int             p_trclevel,    DLL_GSSFP_T	* p_gssfp,
		 gss_ctx_id_t	 p_ctx,	        int		  p_conf_req,
		 gss_qop_t	 p_qop,	        OM_uint32	  p_req_total,
		 OM_uint32     * pp_max_input,  OM_uint32	* pp_maj_stat )
{
   char           * gss_call  = "gss_wrap_size_limit";
   OM_uint32	    maj_stat  = GSS_S_FAILURE;
   OM_uint32	    min_stat;
   int		    rc        = 0;

   BOGUS_INI_VALUE(  pp_max_input );
   BOGUS_INI_MINOR(  min_stat     );

   start_timer();
   maj_stat = (p_gssfp->gss_wrap_size_limit)( &min_stat, p_ctx, p_conf_req, p_qop, p_req_total, pp_max_input );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_WRAP_SIZE_LIMIT, maj_stat, min_stat );

   BOGUS_CHECK_VALUE(  pp_max_input, "max_input_size" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      (*pp_max_input) = 0;
      rc++;

   } else {

      if ( *pp_max_input > p_req_total ) {
	 rc++;
	 XVEB((V_ERR, "%s() returns max_input_size= %lu > req_output_size= %lu !\n",
		      gss_call, (Ulong) (*pp_max_input), (Ulong) p_req_total ));
	 (*pp_max_input) = 0;
      }

   }

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

   return(rc);

} /* wrap_size_limit() */




/*************************************************************************
 =========================================================================
 =====								     =====
 =====  Routines to predict via gss_wrap_size_limit and verify the   =====
 =====  token vs. message size increase when protecting messages     =====
 =====  with gss_wrap() as well as the size of gss_getmic() tokens.  =====
 =====								     =====
 =====  Since a full test of messages in the range [0..64000] would  =====
 =====  consume a considerable amount of time, only a small message  =====
 =====  size ranges are actually analyzed.                           =====
 =====								     =====
 =====  This is not a thorough test and it may easily miss certain   =====
 =====  implementation-specific boundary conditions.  However it     =====
 =====  should be sufficient to get a general idea about the size    =====
 =====  increase of gss_wrap() and the token size(s) of gss_getmic() =====
 =====  and be able to detect obvious bugs of gss_wrap_size_limit()  =====
 =====								     =====
 =========================================================================
 *************************************************************************/



#define MAX_WRAP_LENGTH   65000
#define MAX_WRAP_ERRORS      20
    

static struct range_list  fast_ranges[] = {
   {   1048,      0 },
   {   2068,   2028 },
   {   4110,   4088 },
   {   7130,   7120 },
   {   8202,   8182 },
   {  22000,  21990 },
   {  24586,  24566 },
   {  30010,  29930 },
   {  32778,  32758 },
   {  53300,  53290 },
   {  64010,  63990 },
   {  0,      0     }
};

static struct range_list  normal_ranges[] = {
   {   2096,      0 },
   {   4120,   4080 },
   {   7240,   7210 },
   {  15370,  15320 },
   {  16400,  16350 },
   {  23280,  23250 },
   {  30030,  29950 },
   {  31050,  31020 },
   {  32793,  32740 },
   {  41133,  41100 },
   {  53303,  53283 },
   {  64010,  63980 },
   {  0,      0     }
};

static struct range_list   slow_ranges[] = {
   {  8384,       0 },
   { 34000,   28000 },
   { 65000,   60000 },
   { 0,       0     }
};

static struct range_list   everything[] = {
   { 65000,       0 },
   { 0,       0     }
};

struct defined_ranges_s defined_ranges[] = {
   { &(fast_ranges[0]),   "fast"       },
   { &(normal_ranges[0]), "normal"     },
   { &(slow_ranges[0]),   "slow"       },
   { &(everything[0]),    "everything" }
};


/*
 * max_wrap_size_limit()
 *
 *
 */
int
max_wrap_size_limit( int		  p_trclevel,	 struct range_list  * p_ranges,
		     struct ctx_desc    * p_ctx,
		     size_t		* pp_min_conf,   size_t		    * pp_max_conf,
		     size_t		* pp_min_integ,  size_t		    * pp_max_integ,
		     int	        * pp_success )
{
   char		     * this_Call	= "max_wrap_size_limit";
   struct range_list * range;
   OM_uint32	       maj_stat	        = GSS_S_COMPLETE;
   size_t	       len;
   OM_uint32	       max_in;  /* gss_wrap_size_limit() wants OM_uint32 */
   size_t	       diff;
   size_t              first_conf_size  = 1000;
   size_t	       max_conf	        = 0;
   size_t	       max_conf_size	= 0;
   size_t	       min_conf	        = 65000;
   size_t              first_integ_size = 1000;
   size_t	       max_integ	= 0;
   size_t	       max_integ_size	= 0;
   size_t	       min_integ	= 65000;
   Ulong	       errors	        = 0;
   Ulong               conf_success     = 0;
   Ulong               integ_success    = 0;
   int		       success	        = FALSE;
   int		       rc	        = 0;


   for( range = p_ranges ; range->stop!=0 ; range++ ) {

      for ( len = range->start ; len<range->stop ; len++ ) {

	 wrap_size_limit( verbose_level-2, p_ctx->gssfp, p_ctx->ctx,
			  TRUE, p_ctx->qop, (OM_uint32)len, &max_in, &maj_stat );
	 if ( maj_stat!=GSS_S_COMPLETE ) {
	    if ( len>first_conf_size ) {
	       errors++;
	    }
	 } else {
	    conf_success++;
	    if ( len<first_conf_size ) {
		first_conf_size = len;
	    }
	    if ( max_in>0 ) {
	       diff = (len - max_in);
	       if ( diff > max_conf ) { max_conf = diff;  max_conf_size = max_in; }
	       if ( diff < min_conf ) { min_conf = diff; }
	    } else {
	       if ( len > max_conf ) { max_conf = len; }
	       if ( len > 1024 ) {
		  XVEB((V_ERR, "gss_wrap_size_limit(conf ): max_in==0  for  req_output_size=%lu ??\n",
			       (long) len ));
		  break;
	       }
	    }
	 }

   	 wrap_size_limit( verbose_level-2, p_ctx->gssfp, p_ctx->ctx,
			  FALSE, p_ctx->qop, (OM_uint32)len, &max_in, &maj_stat );
	 if ( maj_stat!=GSS_S_COMPLETE ) {
	    if ( len>first_integ_size ) {
	       errors++;
	    }
	 } else {
	    integ_success++;
	    if ( len<first_integ_size ) {
		first_integ_size = len;
	    }
	    if ( max_in>0 ) {
	       diff = (len - max_in);
	       if ( diff > max_integ ) { max_integ = diff;  max_integ_size = max_in; }
	       if ( diff < min_integ ) { min_integ = diff; }
	    } else {
	       if ( len > max_integ ) { max_integ = len; }
	       if ( len > 1024 ) {
		  XVEB((V_ERR, "gss_wrap_size_limit(integ): max_in==0  for  req_output_size=%lu ??\n",
			       (long) len ));
		  break;
	       }
	    }
	 }

	 if ( errors>MAX_WRAP_ERRORS ) {
	    rc++;
	    XVEB((V_ERR, "%s() >%lu errors already -- aborting!\n", this_Call, (Ulong)MAX_WRAP_ERRORS));
	    goto error;
	 }

      } /* for ( len=range->start ; len<range->stop ; len++ ) */

   } /* for ( range=p_ranges ; ... ; range++ ) */

   success = TRUE;

   /* This should only happen for broken implementations ... */
   if ( max_conf>0  && max_conf  < min_conf  )  { max_conf  = min_conf;  }
   if ( max_integ>0 && max_integ < min_integ )  { max_integ = min_integ; }

/*
 * we only test the real growth, not the predicted growth for our limits ...
 *
   if ( options.sap_constraints!=FALSE ) {
      if ( max_conf>=SNC_MAX_WRAP_INCREASE ) {
	 rc++;
	 XVEB((V_ERR, "excessive growth: gss_wrap_size_limit(conf,out=%lu)-->max_in=%lu !\n",
		      (Ulong) max_conf_size, (Ulong) (max_conf_size + max_conf) ));
      }
      if ( max_integ>=SNC_MAX_WRAP_INCREASE ) {
	 rc++;
	 XVEB((V_ERR, "excessive growth: gss_wrap_size_limit(integ,out=%lu)-->max_in=%lu !\n",
		      (Ulong) max_conf_size, (Ulong) (max_conf_size + max_conf) ));
      }
   }
*/
error:

   if ( pp_min_conf !=NULL ) { (*pp_min_conf)  = min_conf;  }
   if ( pp_min_integ!=NULL ) { (*pp_min_integ) = min_integ; }
   if ( pp_max_conf !=NULL ) { (*pp_max_conf)  = max_conf;  }
   if ( pp_max_integ!=NULL ) { (*pp_max_integ) = max_integ; }
   if ( pp_success  !=NULL ) { (*pp_success)   = success;   }

   return(rc);

} /* max_wrap_size_limit() */




/*
 * max_msg_increase()
 *
 *
 */
int
max_msg_increase( int	               p_trclevel,
		  struct range_list  * p_ranges,
		  struct ctx_desc    * p_ctx_a,
		  struct ctx_desc    * p_ctx_b,
		  size_t	     * pp_min_conf,  size_t * pp_max_conf,  size_t * pp_bigmax_conf,
		  size_t	     * pp_min_integ, size_t * pp_max_integ, size_t * pp_bigmax_integ,
		  size_t    	     * pp_min_mic,   size_t * pp_max_mic,   size_t * pp_bigmax_mic,
		  size_t	     * pp_max_len,
		  int		     * pp_success )
{
   char		      * this_Call	       = "max_msg_increase";
   Uchar	      * tmpbuf	       = NULL; /* locally malloc()ed memory */
   Uchar	      * msg	       = NULL; /* convenience pointer */
   struct range_list  * range;
   gss_buffer_desc      orimsg;		/* convenience struct, no alloc/release!! */
   gss_buffer_desc      witoken;	/* created by wrap(   p_ctx_a->gssfp, conf=FALSE, ...) */
   gss_buffer_desc      wctoken;	/* created by wrap(   p_ctx_a->gssfp, conf=TRUE,  ...) */
   gss_buffer_desc      mtoken;		/* created by getmic( p_ctx_a->gssfp, ...) */
   gss_buffer_desc      newmsg1;	/* created by unwrap( p_ctx_a->gssfp, ...) */
   gss_buffer_desc      newmsg2;	/* created by unwrap( p_ctx_a->gssfp, ...) */
   size_t	        len;
   size_t		conf_new_len    = 0; /* don't report inaccuracies for messages up to this size */
   size_t		integ_new_len   = 0; /* don't report inaccuracies for messages up to this size */
   gss_qop_t	        qop;
   Ulong		one;
   Ulong	        two;
   size_t	        diff;
   OM_uint32		max_in;
   size_t	        max_conf	= 0;
   size_t		bigmax_conf	= 0;
   size_t	        min_conf	= MAX_WRAP_LENGTH;
   size_t	        max_integ	= 0;
   size_t		bigmax_integ	= 0;
   size_t	        min_integ	= MAX_WRAP_LENGTH;
   size_t	        max_mic		= 0;
   size_t		bigmax_mic	= 0;
   size_t	        min_mic		= MAX_WRAP_LENGTH;
   size_t		max_len		= 0;
   OM_uint32		maj_stat        = GSS_S_COMPLETE;
   OM_uint32	        maj_stat1	= GSS_S_COMPLETE;
   OM_uint32	        maj_stat2	= GSS_S_COMPLETE;
   OM_uint32	        maj_stat3	= GSS_S_COMPLETE;
   OM_uint32	        maj_stat4	= GSS_S_COMPLETE;
   OM_uint32	        maj_stat5	= GSS_S_COMPLETE;
   OM_uint32	        maj_stat6	= GSS_S_COMPLETE;
   int		        success		= FALSE;
   int		        conf_state;
   Ulong	        errors		= 0;
   int		        rc		= 0;

   witoken.value  = wctoken.value  = mtoken.value  = NULL;
   witoken.length = wctoken.length = mtoken.length = 0;

   orimsg.value  = newmsg1.value  = newmsg2.value  = NULL;
   orimsg.length = newmsg1.length = newmsg2.length = 0;

   tmpbuf = malloc( MAX_WRAP_LENGTH );

   if ( tmpbuf==NULL ) {
      
      rc++;
      XVEB((V_ERR, "%s(): malloc(%lu) failed -- aborting!\n",
		   this_Call, (Ulong) MAX_WRAP_LENGTH ));

   } else { /* tmpbuf!=NULL */

      /* fill some pseudo-random pattern into the buffer */
      one  = read_timer();
      two  = (Ulong)(time(NULL) & 0xff);
      for ( len = 0 ; len <MAX_WRAP_LENGTH ; len++ ) {
	 one = (Ulong)((one + len + two) & 0xffffff);
	 tmpbuf[len] = (Uchar) (one & 0xff);
      }

      orimsg.length = MAX_WRAP_LENGTH;

      for ( range=p_ranges ; range->stop!=0 ; range++ ) {

	 conf_new_len  = 0; /* (re-)enable error reporting for gss_wrap()!=gss_wrap_size_limit() */
	 integ_new_len = 0; /* (re-)enable error reporting for gss_wrap()!=gss_wrap_size_limit() */

	 for ( len=range->start ; len < range->stop && len<MAX_WRAP_LENGTH ; len++ ) {

	    msg  = &(tmpbuf[ MAX_WRAP_LENGTH - len - 1 ]);

	    if ( ((p_ctx_a->flags)&GSS_C_CONF_FLAG)!=0 ) {
	       /* try wrap(conf_req=TRUE) when available for context */
	       orimsg.length = len;
	       orimsg.value  = msg;
	       rc += wrap( verbose_level - 1, p_ctx_a->gssfp,
			   p_ctx_a->flags, p_ctx_a->ctx, p_ctx_a->qop,
			   TRUE, &orimsg, &conf_state, &wctoken, &maj_stat1 );
	       if ( maj_stat1!=GSS_S_COMPLETE ) { errors++; }

	       if ( len>conf_new_len && (p_ctx_a->gssfp)->gss_wrap_size_limit!=0 ) {
		  rc += wrap_size_limit( verbose_level - 2, p_ctx_a->gssfp,
				         p_ctx_a->ctx, TRUE, p_ctx_a->qop,
					 (OM_uint32)(wctoken.length - 1), &max_in, &maj_stat );
		  if ( maj_stat!=GSS_S_COMPLETE ) {
		     errors++;
		  } else {
		     if ( max_in>=len ) {
			rc++;
			XVEB((V_ERR, "wrap_size_limit(conf ,out=%lu)->max_in=%lu != wrap(in=%lu)->out=%lu\n",
				      (Ulong) (wctoken.length - 1), (Ulong) max_in,
				      (Ulong) len, (Ulong) (wctoken.length) ));
			if ( max_in>conf_new_len ) { conf_new_len = len + 128; } /* suppress some errors */
		     }
		  }
	       }
	       if ( p_trclevel>3 ) {
		  rc += wrap_size_limit( verbose_level - 2, p_ctx_a->gssfp,
					 p_ctx_a->ctx, TRUE, p_ctx_a->qop,
					 (OM_uint32)wctoken.length, &max_in, &maj_stat );
		  if ( maj_stat!=GSS_S_COMPLETE ) {
		     errors++;
		  } else {
		     if ( max_in < len ) {
			XVEB((V_SHOW, "  wrap_size_limit(conf ,out=%lu)->max_in=%lu != wrap(in=%lu)->out=%lu\n",
				      (Ulong) wctoken.length, (Ulong) max_in,
				      (Ulong) len, (Ulong) wctoken.length ));
		     }
		  }
	       }
	    }

	    /* try wrap(conf_req=FALSE) */
	    orimsg.length = len;
	    orimsg.value  = msg;
	    rc += wrap( verbose_level - 1, p_ctx_a->gssfp,
			p_ctx_a->flags, p_ctx_a->ctx, p_ctx_a->qop,
			FALSE, &orimsg, &conf_state, &witoken, &maj_stat2 );
	    if ( maj_stat2!=GSS_S_COMPLETE ) { errors++; }

	    if ( len>integ_new_len && (p_ctx_a->gssfp)->gss_wrap_size_limit!=0 ) {
	       rc += wrap_size_limit( verbose_level - 2, p_ctx_a->gssfp,
				      p_ctx_a->ctx, FALSE, p_ctx_a->qop,
				      (OM_uint32)(witoken.length - 1), &max_in, &maj_stat );
	       if ( maj_stat!=GSS_S_COMPLETE ) {
		  errors++;
	       } else {
		  if ( max_in>=len ) {
		     rc++;
		     XVEB((V_ERR, "wrap_size_limit(integ,out=%lu)->max_in=%lu != wrap(in=%lu)->out=%lu\n",
				  (Ulong) (witoken.length - 1), (Ulong) max_in,
				  (Ulong) len, (Ulong) (witoken.length) ));
		     if ( max_in>integ_new_len ) { integ_new_len = len + 128; } /* suppress some errors */
		  }
	       }

	       if ( p_trclevel>3 ) {
		  rc += wrap_size_limit( verbose_level - 2, p_ctx_a->gssfp,
					 p_ctx_a->ctx, TRUE, p_ctx_a->qop,
					 (OM_uint32)witoken.length, &max_in, &maj_stat );
		  if ( maj_stat!=GSS_S_COMPLETE ) {
		     errors++;
		  } else {
		     if ( max_in < len ) {
			XVEB((V_SHOW, "  wrap_size_limit(integ,out=%lu)->max_in=%lu != wrap(in=%lu)->out=%lu\n",
				      (Ulong) witoken.length, (Ulong) max_in,
				      (Ulong) len, (Ulong) witoken.length ));
		     }
		  }
	       }

	    }

	    /* try getmic() */
	    orimsg.length = len;
	    orimsg.value  = msg;
	    rc += get_mic( verbose_level - 1, p_ctx_a->gssfp, p_ctx_a->ctx,
			   p_ctx_a->qop, &orimsg, &mtoken, &maj_stat3 );
	    if ( maj_stat3!=GSS_S_COMPLETE ) { errors++; }


	    /* if wrap(conf_req=TRUE) succeded, then meter and verify result */
	    if ( wctoken.length>0 ) {

	       diff = wctoken.length - len; /* size increase from message to wrap()-token */
	       if ( len>BIG_MESSAGE_SIZE ) {
		   if ( diff>bigmax_conf ) { bigmax_conf = diff; }
	       } else {
		   if ( diff>max_conf ) { max_conf = diff; }  /* remember new maximum */
	       }
	       if ( diff<min_conf ) { min_conf = diff; }  /* remember new minimum */
	       if ( len>max_len ) { max_len = len; }

	       rc += unwrap( verbose_level - 1, p_ctx_b->gssfp,
			     p_ctx_b->flags, TRUE, (len==0),  p_ctx_b->ctx,
			     &wctoken, &newmsg1, &conf_state, &qop, &maj_stat4 );

	       if ( maj_stat4!=GSS_S_COMPLETE ) {
		  errors++;
	       } else {
		  if ( conf_state==FALSE ) {
		     errors++;
		  }
		  if ( ( len==0 && newmsg1.length==len)
		      || (newmsg1.length==len && newmsg1.value!=NULL &&
			!memcmp(newmsg1.value, msg, len) ) ) {
		     /* output buffer and original message verifies ok */
		  } else {
		     rc++;
		     errors++;
		     XVEB((V_ERR, "%s(): unwrap()ed message failed verification (orimsg.length=%lu)\n",
				  this_Call, (Ulong) len ));
		  }
	       }
	    }

	    /* if wrap(conf_req=FALSE) succeeded, then meter and verify result */
	    if ( witoken.length>0 ) {

	       diff = witoken.length - len; /* size increase from message to wrap()-token */
	       if ( len>BIG_MESSAGE_SIZE ) {
		   if ( diff>bigmax_integ ) { bigmax_integ = diff; }
	       } else {
		   if ( diff>max_integ ) { max_integ = diff; }
	       }
	       if ( diff<min_integ ) { min_integ = diff; }

	       rc += unwrap( verbose_level - 1, p_ctx_b->gssfp,
			     p_ctx_b->flags, FALSE, (len==0),  p_ctx_b->ctx,
			     &witoken, &newmsg2, &conf_state, &qop, &maj_stat5 );

	       if ( maj_stat5!=GSS_S_COMPLETE ) {
		  errors++;
	       } else {
		  if ( ( len==0 && newmsg2.length==len)
		       || (newmsg2.length==len && newmsg2.value!=NULL &&
			!memcmp(newmsg2.value, msg, len) ) ) {
		     /* output buffer and original message verifies ok */
		  } else {
		     rc++;
		     errors++;
		     XVEB((V_ERR, "%s(): unwrap()ed message failed verification (orimsg.length=%lu)\n",
				  this_Call, (Ulong) len ));
		  }
	       }
	    }

	    /* if get_mic() succeded, then meter and verify result */
	    if ( mtoken.length>0 ) {

		if ( len>=BIG_MESSAGE_SIZE ) {
		   if ( mtoken.length > bigmax_mic ) { bigmax_mic = mtoken.length; }
	       } else {
    		   if ( mtoken.length > max_mic ) { max_mic = mtoken.length; } /* memorize max. MIC token length */
	       }
	       if ( mtoken.length < min_mic ) { min_mic = mtoken.length; } /* memorize min. MIC token length */
	       if ( len>max_len ) { max_len = len; }

	       orimsg.length = len;
	       orimsg.value  = msg;
	       rc += verify_mic( verbose_level - 1, p_ctx_b->gssfp,
				 p_ctx_b->ctx, &orimsg, &mtoken, &qop, &maj_stat6 );
	       if ( maj_stat6!=GSS_S_COMPLETE ) {
		  errors++;
	       }

	    } /* mtoken.length>0    i.e. we have a MIC token */

	    rc += release_buffer( p_ctx_a->gssfp, &witoken );
	    rc += release_buffer( p_ctx_a->gssfp, &wctoken );
	    rc += release_buffer( p_ctx_a->gssfp, &mtoken  );
	    rc += release_buffer( p_ctx_b->gssfp, &newmsg1 );
	    rc += release_buffer( p_ctx_b->gssfp, &newmsg2 );

	    if ( errors>MAX_WRAP_ERRORS ) {
	       rc++;
	       XVEB((V_ERR, "%s(): >%lu errors already -- aborting!\n",
			    this_Call, (Ulong)MAX_WRAP_ERRORS ));
	       goto error;
	    }

	 } /* for ( len=range->start ; range->stop!=0 ; len++ ) */
	 

      } /* for( size<MAX_WRAP_SIZE ) */


      if ( errors>0 ) {
	 XVEB((V_ERR, "%s() encountered %lu errors!\n", this_Call, (Ulong)errors));
      }

      success = TRUE;

   } /* tmpbuf!=NULL */

error:
   if ( tmpbuf!=NULL ) { free( tmpbuf ); tmpbuf= NULL; }

   /* For safety, we repeat the release calls here -- that doesn't hurt */
   rc += release_buffer( p_ctx_a->gssfp, &witoken );
   rc += release_buffer( p_ctx_a->gssfp, &wctoken );
   rc += release_buffer( p_ctx_a->gssfp, &mtoken  );
   rc += release_buffer( p_ctx_b->gssfp, &newmsg1 );
   rc += release_buffer( p_ctx_b->gssfp, &newmsg2 );

   if ( pp_min_conf    !=NULL ) { (*pp_min_conf)     = min_conf;     }
   if ( pp_min_integ   !=NULL ) { (*pp_min_integ)    = min_integ;    }
   if ( pp_max_conf    !=NULL ) { (*pp_max_conf)     = max_conf;     }
   if ( pp_bigmax_conf !=NULL ) { (*pp_bigmax_conf)  = bigmax_conf;  }
   if ( pp_max_integ   !=NULL ) { (*pp_max_integ)    = max_integ;    }
   if ( pp_bigmax_integ!=NULL ) { (*pp_bigmax_integ) = bigmax_integ; }
   if ( pp_min_mic     !=NULL ) { (*pp_min_mic)      = min_mic;      }
   if ( pp_max_mic     !=NULL ) { (*pp_max_mic)      = max_mic;      }
   if ( pp_bigmax_mic  !=NULL ) { (*pp_bigmax_mic)   = bigmax_mic;   }
   if ( pp_max_len     !=NULL ) { (*pp_max_len)      = max_len;      }
   if ( pp_success     !=NULL ) { (*pp_success)      = success;      }

   return(rc);

} /* max_msg_increase() */



static char * msg_notavail  = "  wrap(conf)= N.A.  ";
static char * msg_formatstr = "  wrap(conf)= [%3lu ..%3lu] ";
/*
 * check_max_wrap_increase()
 *
 *
 */
int
check_max_wrap_increase( int   p_trclevel,	      OM_uint32	     p_flags,
			 DLL_GSSFP_T  * p_gssfp_ini,  DLL_GSSFP_T  * p_gssfp_acc )
{
   struct ctx_desc   * ini            = NULL;
   struct ctx_desc   * acc            = NULL;
   char		     * label1         = NULL;
   char		     * label2         = NULL;
   char		     * rlabel         = NULL;
   struct range_list * ranges         = NULL;
   char                msg[128];
   int		       success        = FALSE;
   size_t	       ini_max_conf      = 0;
   size_t	       ini_bigmax_conf   = 0;
   size_t	       ini_max_conf2     = 0;
   size_t	       ini_min_conf      = 0;
   size_t	       ini_min_conf2     = 0;
   size_t	       ini_max_integ     = 0;
   size_t	       ini_bigmax_integ  = 0;
   size_t	       ini_max_integ2    = 0;
   size_t	       ini_min_integ     = 0;
   size_t	       ini_min_integ2    = 0;
   size_t	       acc_max_conf      = 0;
   size_t	       acc_bigmax_conf   = 0;
   size_t	       acc_max_conf2     = 0;
   size_t	       acc_min_conf      = 0;
   size_t	       acc_min_conf2     = 0;
   size_t	       acc_max_integ     = 0;
   size_t	       acc_bigmax_integ  = 0;
   size_t	       acc_max_integ2    = 0;
   size_t	       acc_min_integ     = 0;
   size_t	       acc_min_integ2    = 0;
   size_t	       ini_min_mic       = 0;
   size_t	       ini_max_mic       = 0;
   size_t	       ini_bigmax_mic    = 0;
   size_t	       acc_min_mic       = 0;
   size_t	       acc_max_mic       = 0;
   size_t	       acc_bigmax_mic    = 0;
   size_t	       max_len		 = 0;
   int		       have_limit     = FALSE;
   int		       twice;
   int		       rc             = 0;

   twice  = (p_gssfp_acc==p_gssfp_ini) ? FALSE : TRUE;

   ranges = defined_ranges[options.wrap_range_level].ranges;
   rlabel = defined_ranges[options.wrap_range_level].rlabel;

   rc += sap_establish_context( verbose_level-1, p_flags, GSS_C_INDEFINITE,
			        p_gssfp_ini, p_gssfp_acc, &ini, &acc,
				SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
				p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len, NULL );

   if ( ini!=NULL && acc!=NULL ) {

      /* try gss_wrap_size_limit() for initiator context */
      if ( p_gssfp_ini->gss_wrap_size_limit!=0 ) {
	 rc += max_wrap_size_limit( verbose_level, ranges, ini, &ini_min_conf2, &ini_max_conf2,
				    &ini_min_integ2, &ini_max_integ2, &success );
	 label1     = " for initiator context";
	 have_limit = TRUE;
      }

      /* try gss_wrap_size_limit() for acceptor_context */
      if ( p_gssfp_acc->gss_wrap_size_limit!=0  &&  success!=FALSE ) {
	 rc += max_wrap_size_limit( verbose_level, ranges, acc, &acc_min_conf2, &acc_max_conf2,
				    &acc_min_integ2, &acc_max_integ2, &success );
	 if ( acc_min_conf2!=ini_min_conf2 || acc_min_integ2!=ini_min_integ2
	    || acc_max_conf2!=ini_max_conf2 || acc_max_integ2!=ini_max_integ2 ) {
	    label2 = " for acceptor context";
	 } else {
	    label1 = "";
	 }
	 have_limit = TRUE;
      }
      
      if ( have_limit!=FALSE  &&  success!=FALSE ) {
	 /* if we have results, show them */
	 sprintf(msg, (ini_max_conf2>0 ? msg_formatstr : msg_notavail),
		      (Ulong)ini_min_conf2, (Ulong) ini_max_conf2);
	 XVEB((V_SHOW, "  gss_wrap_size_limit() growth predictions%s:\n", label1));
	 XVEB((V_SHOW, "%s  wrap(integ)= [%3lu ..%3lu ]   bytes\n",
		       msg, (Ulong)ini_min_integ2, (Ulong)ini_max_integ2  ));
	 if ( label2!=NULL ) {
	    sprintf(msg, (acc_max_conf2>0 ? msg_formatstr : msg_notavail),
		         (Ulong)acc_min_conf2, (Ulong) acc_max_conf2);
	    XVEB((V_SHOW, "  gss_wrap_size_limit() growth predictions%s:\n", label2));
	    XVEB((V_SHOW, "%s  wrap(integ)=[%3lu ..%3lu ]   bytes\n",
			  msg, (Ulong)acc_min_integ2, (Ulong)acc_max_integ2  ));
	 }

      } /* have_limit!=FALSE */

      XVEB((V_SHOW, "  Measuring true Mic token size & Wrap size increase %s-- PATIENCE\n",
		    (twice==FALSE) ? "" : "for initiator " ));
      rc += max_msg_increase( p_trclevel, ranges, ini, acc,
			      &ini_min_conf,  &ini_max_conf,	&ini_bigmax_conf,
			      &ini_min_integ, &ini_max_integ,	&ini_bigmax_integ,
			      &ini_min_mic,   &ini_max_mic,	&ini_bigmax_mic,
			      &max_len,       &success );
      if ( success!=FALSE ) {
	 if ( ini_bigmax_integ>ini_max_integ+10 || ini_bigmax_conf>ini_max_conf+10 ) {
	     XVEB((V_SHOW, "  (for messages sizes <= %ld bytes)\n", (long)BIG_MESSAGE_SIZE));
	     sprintf(msg, (ini_max_conf>0 ? msg_formatstr : msg_notavail),
			  (Ulong)ini_min_conf, (Ulong) ini_max_conf);
	     XVEB((V_SHOW, "%s  wrap(integ)=[%3lu ..%3lu ]   getmic()=[%3lu ..%3lu ]\n",
			    msg, (Ulong)ini_min_integ, (Ulong)ini_max_integ,
			   (Ulong)ini_min_mic,   (Ulong)ini_max_mic ));
	 }
	 XVEB((V_SHOW, "  (for messages sizes <= %ld bytes)\n", (long)max_len));
	 sprintf(msg, (ini_max_conf>0 ? msg_formatstr : msg_notavail),
		      (Ulong)ini_min_conf, (Ulong) ini_bigmax_conf);
	 XVEB((V_SHOW, "%s  wrap(integ)=[%3lu ..%3lu ]   getmic()=[%3lu ..%3lu ]\n",
			msg, (Ulong)ini_min_integ, (Ulong)ini_bigmax_integ,
		       (Ulong)ini_min_mic,   (Ulong)ini_bigmax_mic ));
	 p_gssfp_ini->mic_size_min      = ini_min_mic;
	 p_gssfp_ini->mic_size_max      = ini_max_mic;
	 p_gssfp_ini->mic_bigsize_max   = ini_bigmax_mic;
	 p_gssfp_ini->wmic_size_min     = ini_min_integ;
	 p_gssfp_ini->wmic_size_max     = ini_max_integ;
	 p_gssfp_ini->wmic_bigsize_max  = ini_bigmax_integ;
	 p_gssfp_ini->wconf_size_min    = ini_min_conf;
	 p_gssfp_ini->wconf_size_max    = ini_max_conf;
	 p_gssfp_ini->wconf_bigsize_max = ini_bigmax_conf;
      }

      if ( twice!=FALSE ) {
	 XVEB((V_SHOW, "  Measuring true Mic token size & Wrap size increase for acceptor -- PATIENCE\n"));
	 rc += max_msg_increase( p_trclevel, ranges, acc, ini,
				 &acc_min_conf,  &acc_max_conf,	    &acc_bigmax_conf,
				 &acc_min_integ, &acc_max_integ,    &acc_bigmax_integ,
				 &acc_min_mic,   &acc_max_mic,	    &acc_bigmax_mic,
				 &max_len,       &success );
	 if ( success!=FALSE ) {
	    if ( acc_bigmax_conf>acc_max_conf+10 || acc_bigmax_integ>acc_bigmax_integ+10 ) {
	        XVEB((V_SHOW, "  (for messages sizes <= %ld bytes)\n", (long)BIG_MESSAGE_SIZE));
	        sprintf(msg, (acc_max_conf>0 ? msg_formatstr : msg_notavail),
		             (Ulong)acc_min_conf, (Ulong) acc_max_conf);
	        XVEB((V_SHOW, "%s  wrap(integ)=[%3lu ..%3lu ]   getmic()=[%3lu ..%3lu ]\n",
			      msg, (Ulong)acc_min_integ, (Ulong)acc_max_integ,
			      (Ulong)acc_min_mic,   (Ulong)acc_max_mic ));

	    }
	    XVEB((V_SHOW, "  (for messages sizes <= %ld bytes)\n", (long)max_len));
	    sprintf(msg, (acc_max_conf>0 ? msg_formatstr : msg_notavail),
		         (Ulong)acc_min_conf, (Ulong) acc_bigmax_conf);
	    XVEB((V_SHOW, "%s  wrap(integ)=[%3lu ..%3lu ]   getmic()=[%3lu ..%3lu ]\n",
			  msg, (Ulong)acc_min_integ, (Ulong)acc_bigmax_integ,
			  (Ulong)acc_min_mic,   (Ulong)acc_bigmax_mic ));
	    p_gssfp_acc->mic_size_min      = acc_min_mic;
	    p_gssfp_acc->mic_size_max      = acc_max_mic;
	    p_gssfp_acc->mic_bigsize_max   = acc_bigmax_mic;
	    p_gssfp_acc->wmic_size_min     = acc_min_integ;
	    p_gssfp_acc->wmic_size_max     = acc_max_integ;
	    p_gssfp_acc->wmic_bigsize_max  = acc_bigmax_integ;
	    p_gssfp_acc->wconf_size_min    = acc_min_conf;
	    p_gssfp_acc->wconf_size_max    = acc_max_conf;
	    p_gssfp_acc->wconf_bigsize_max = acc_bigmax_conf;
	 }
      }

      if ( options.sap_constraints!=FALSE ) {
	 if ( ini_max_conf>=SNC_MAX_WRAP_INCREASE
	      || ini_max_integ>=SNC_MAX_WRAP_INCREASE
	      || acc_max_conf>=SNC_MAX_WRAP_INCREASE
	      || acc_max_integ>=SNC_MAX_WRAP_INCREASE ) {
	    XVEB((V_ERR, "excessive message size increase of gss_wrap(); must be <%u f\n",
			 (Uint)SNC_MAX_WRAP_INCREASE ));
	 }
	 if ( ini_max_mic>=SNC_MAX_MIC_TOKEN
	      || acc_max_mic>=SNC_MAX_MIC_TOKEN ) {
	    XVEB((V_ERR, "excessive MIC token size from gss_getmic(): must be <%u\n",
			 (Uint)SNC_MAX_MIC_TOKEN ));
	 }

      } /* options.sap_constraints!=0 */

   } /* ini!=NULL && acc!=NULL */

   rc += release_ctx_desc( &ini );
   rc += release_ctx_desc( &acc );

   return(rc);

} /* check_max_wrap_increase() */




/*************************************************************************
 =========================================================================
 =====								     =====
 =====  Routines to process simple lists (arrays of struct tmsg_job) =====
 =====  describing sequences of message protection/unprotection      =====
 =====  as well as security context transfer and refresh operations  =====
 =====  for the two ends of an established security context.         =====
 =====								     =====
 =========================================================================
 *************************************************************************/




/*
 * STATUS: internal support function for managing message protection jobs
 *
 * queue_flush()
 *
 * Description:
 *   Flush a queue with protected messages (for error cleanups)
 *
 */
int
queue_flush( struct tmsg_entry ** pp_queue )
{
   struct  tmsg_entry  * next;
   struct  tmsg_entry  * cur;
   int                   rc  = 0;

   for ( cur = *pp_queue ; cur!=NULL ; cur = next ) {
      next = cur->next;
      if ( cur->ori_msg.value!=NULL ) {
	 free( cur->ori_msg.value );
      }
      cur->ori_msg.value  = NULL;
      cur->ori_msg.length = 0;
      if ( cur->gssfp_token!=NULL ) {
	 rc += release_buffer( cur->gssfp_token, &(cur->gss_token)   );
      }
      if ( cur->gssfp_message!=NULL ) {
	 rc += release_buffer( cur->gssfp_message, &(cur->gss_ret_msg) );
      }
      cur->next = NULL;
      free( cur );
   }

   *pp_queue = NULL;

   return(rc);

} /* queue_flush() */



/*
 * STATUS: internal support function for managing message protection jobs
 *
 * queue_add()
 *
 * Description:
 *   Add "pp_entry" to the tail of the linked list p_queue (FIFO)
 *   upon return pp_entry is cleared so that the caller doesn't
 *   try to free/release associated resources.
 *
 */
void
queue_add( tmsg_type_et         p_type,	     gss_qop_t     p_qop,
	   struct tmsg_entry  **pp_entry,
	   struct tmsg_entry  **pp_queue )
{
   struct    tmsg_entry   * pe;

   (*pp_entry)->next      = NULL;
   (*pp_entry)->msg_type  = p_type;
   (*pp_entry)->qop	  = p_qop;

   if ( *pp_queue==NULL ) {
      *pp_queue = *pp_entry;
      *pp_entry = NULL;
   } else {
      for ( pe = *pp_queue ; pe->next!=NULL ; pe = pe->next );
      pe->next = *pp_entry;
      *pp_entry = NULL;
   }

   return;

} /* queue_add() */




/*
 * STATUS: internal support function for managing message protection jobs
 *
 * queue_remove()
 *
 * Description:
 *   Remove and return the first entry from the given
 *   linked list pp_queue (FIFO)
 *
 */
struct tmsg_entry *
queue_remove( struct tmsg_entry  ** pp_queue )
{
   struct tmsg_entry   * entry = NULL;

   if ( *pp_queue!=NULL ) {
      entry       = *pp_queue;

      *pp_queue   = entry->next;
      entry->next = NULL;
   }

   return(entry);

} /* queue_remove() */




/*
 * STATUS: internal support function for managing message protection jobs
 *
 * queue_new_entry()
 *
 * Description:
 *   create a new entry for the queue and allocate/prepare a
 *   plaintext message of the requested size
 *
 */
struct tmsg_entry *
queue_new_entry( size_t p_size )
{
   Uchar               * sptr;
   struct tmsg_entry   * pe;
   void		       * ptr;
   size_t		 len;
   size_t		 i;
   Uchar                 uch;

   len = sizeof(*pe);
   pe  = malloc( len );
   memset( pe, 0, len );

   if ( pe!=NULL ) {
      len = p_size;
      if ( len>0 ) {
	 ptr = malloc( len );
	 if ( ptr!=NULL ) {
	    pe->ori_msg.value  = ptr;
	    pe->ori_msg.length = len;

	    sptr = (Uchar *) ptr;
	    for ( uch=0 , i=0 ; i<len ; i++ , sptr++ ) {
	       *sptr = uch;
	       uch++;
	       if (uch>254) { uch = 0; }
	    }

	 } else {
	    free(pe);
	    pe = NULL;
	 }
      }
   }

   if ( pe==NULL ) {
      XVEB((V_ERR, "queue_new_entry(): malloc(1,%lu) failed!\n",
		   (Ulong) sizeof(*pe) ));
   }

   return(pe);

} /* queue_new_entry() */




/*
 * STATUS: internal support function for managing message protection jobs
 *
 * tstm_process_entry()
 *
 * Description:
 *   Process a queue-entry, i.e. unprotect a message using the
 *   given security context handle.
 *
 */
int
tstm_process_entry( struct ctx_desc   * p_ctx,
		    struct tmsg_entry * p_entry,
		    OM_uint32	 * pp_maj_stat )
{
   char	     * this_Call GNU_UNUSED = "tstm_process_message";
   gss_qop_t   qop;
   OM_uint32   maj_stat  = GSS_S_COMPLETE;
   int	       conf;
   int	       expect_conf;
   int	       zlen_flag;
   int	       rc        = 0;

   switch ( p_entry->msg_type ) {

      case TMSG_TYPE_MICONLY:
	 rc += verify_mic( verbose_level, p_ctx->gssfp, p_ctx->ctx,
			   &(p_entry->ori_msg), &(p_entry->gss_token),
			   &qop, &maj_stat );
	 if ( p_entry->qop!=GSS_C_QOP_DEFAULT
	      &&  qop==GSS_C_QOP_DEFAULT ) {
	    rc++;
	    XVEB((V_ERR, "verify_mic() returns qop 0x%08lx for msg get_mic()ed with qop 0x%08lx!\n",
			 (Ulong) qop, (Ulong) p_entry->qop ));
	 }
	 break;


      case TMSG_TYPE_WMIC:
      case TMSG_TYPE_WCONF:
	 /* expect_conf = (p_entry->msg_type==TMSG_TYPE_WCONF) ? TRUE : FALSE; */
	 expect_conf = p_entry->conf_flag;
	 zlen_flag   = (p_entry->ori_msg.length==0 )    ? TRUE : FALSE;
	 p_entry->gssfp_message = p_ctx->gssfp; /* need this for later release of buffer */
	 rc += unwrap( verbose_level, p_ctx->gssfp, p_ctx->flags,
		       expect_conf, zlen_flag, p_ctx->ctx,
		       &(p_entry->gss_token), &(p_entry->gss_ret_msg),
		       &conf, &qop, &maj_stat );
	 if ( p_entry->qop!=GSS_C_QOP_DEFAULT
	      &&  qop==GSS_C_QOP_DEFAULT ) {
	    rc++;
	    XVEB((V_ERR, "unwrap() returns qop 0x%08lx for msg wrap()ed with qop 0x%08lx!\n",
			 (Ulong) qop, (Ulong) p_entry->qop ));
	 }
	 break;


      default:
	 XVEB((V_BUG, "tstm_process_entry(): strange queue entry type: 0x%08lx!\n",
		      (Ulong) p_entry->msg_type ));
	 break;

   }

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

   return(rc);

} /* tstm_process_entry() */




/*
 * tstm_act_entry()
 *
 *
 */
int
tstm_act_entry( int		     p_trclevel,    char		* p_test_title,
	        int		     p_timing,	    struct tmsg_job	* p_step,
		struct ctx_desc   ** pp_ini,        struct ctx_desc    ** pp_acc,
		struct tmsg_entry ** pp_queue_ini,  struct tmsg_entry  ** pp_queue_acc,
		OM_uint32	   * pp_maj_stat )
{
   char		      * this_Call   = "tstm_act_entry";
   OM_uint32		maj_stat    = GSS_S_COMPLETE;
   char		      * gss_call    = NULL;     /* ref only */
   struct tmsg_entry  * entry	    = NULL;     /* dynamic  */
   struct tmsg_job    * xfer_job    = NULL;	/* ref only */
   tmsg_type_et		type;
   OM_uint32		lifetime    = 0;
   int			conf_req    = 0;
   int                  rc	    = 0;
   int			success;

   if ( ((p_step->action)&TMA_CTXTIME)!=0 ) {

      if ( ((p_step->action)&TMA_INI)!=0 ) {
	 rc += context_time( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->ctx, &lifetime, &maj_stat );
	 if ( (*pp_ini)->enter>0  &&  maj_stat==GSS_S_COMPLETE ) {
	    rc += check_lifetime_change( p_trclevel, (*pp_ini)->enter,
					 (*pp_ini)->lifetime, lifetime );
	 }
      }

      if ( ((p_step->action)&TMA_ACC)!=0 ) {
	 rc += context_time( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->ctx, &lifetime, &maj_stat );
	 if ( (*pp_acc)->enter>0  &&  maj_stat==GSS_S_COMPLETE ) {
	    rc += check_lifetime_change( p_trclevel, (*pp_acc)->enter,
					 (*pp_acc)->lifetime, lifetime );
	 }
      }

   } /* endif !TMA_CTXTIME */

   if ( pp_queue_acc==NULL  ||  pp_queue_ini==NULL ) {
      XVEB((V_BUG, "%s(): &queue_ini=%p and &queue_acc=%p!\n",
		   this_Call, pp_queue_ini, pp_queue_acc ));
      goto fatal_error_2;
   }

   if ( ((p_step->action) & TMA_ADDMSG)!=0 ) {
      entry = queue_new_entry( p_step->u.size );
      if ( entry==NULL ) {
	 maj_stat = GSS_S_FAILURE;
	 goto fatal_error_2;
      }
   }

   switch( p_step->action ) {

      case TMSG_ACT_A_MICONLY:
	    entry->gssfp_token = (*pp_ini)->gssfp; /* needed for later release of buffer */
	    rc += get_mic( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->ctx, (*pp_ini)->qop,
			   &(entry->ori_msg), &(entry->gss_token),
			   &maj_stat );
	    if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE	
	         ||  entry->gss_token.length==0 ) {
	       gss_call = "gss_get_mic";
	       goto fatal_error;
	    }
	    queue_add( TMSG_TYPE_MICONLY, (*pp_ini)->qop, &entry, pp_queue_acc );
	    break;

	       
      case TMSG_ACT_A_WMIC:
      case TMSG_ACT_A_WCONF:
	    entry->gssfp_token = (*pp_ini)->gssfp; /* needed for later release of buffer */
	    if ( p_step->action==TMSG_ACT_A_WCONF ) {
	       conf_req = TRUE;	    type = TMSG_TYPE_WCONF;
	    } else {
	       conf_req = FALSE;    type = TMSG_TYPE_WMIC;
	    }
	    rc += wrap( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->flags,
		        (*pp_ini)->ctx, (*pp_ini)->qop, conf_req,
			&(entry->ori_msg),   &(entry->conf_flag),
			&(entry->gss_token), &maj_stat );
	    if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE
	         ||  entry->gss_token.length==0 ) {
	       gss_call = "gss_wrap";
	       goto fatal_error;
	    }
	    queue_add( type, (*pp_ini)->qop, &entry, pp_queue_acc );
	    break;


      case TMSG_ACT_B_MICONLY:
	    entry->gssfp_token = (*pp_acc)->gssfp; /* needed for later release of buffer */
	    rc += get_mic( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->ctx, (*pp_acc)->qop,
			   &(entry->ori_msg), &(entry->gss_token),
			   &maj_stat );
	    if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE	
	         ||  entry->gss_token.length==0 ) {
	       gss_call = "gss_get_mic";
	       goto fatal_error;
	    }
	    queue_add( TMSG_TYPE_MICONLY, (*pp_acc)->qop, &entry, pp_queue_ini );
	    break;


      case TMSG_ACT_B_WMIC:
      case TMSG_ACT_B_WCONF:
	    entry->gssfp_token = (*pp_acc)->gssfp; /* needed for later release of buffer */
	    if ( p_step->action==TMSG_ACT_B_WCONF ) {
	       conf_req = TRUE;	    type = TMSG_TYPE_WCONF;
	    } else {
	       conf_req = FALSE;    type = TMSG_TYPE_WMIC;
	    }
	    rc += wrap( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->flags,
			(*pp_acc)->ctx, (*pp_acc)->qop, conf_req,
			&(entry->ori_msg),   &(entry->conf_flag),
			&(entry->gss_token), &maj_stat );
	    if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE
	         ||  entry->gss_token.length==0 ) {
	       gss_call = "gss_wrap";
	       goto fatal_error;
	    }
	    queue_add( type, (*pp_acc)->qop, &entry, pp_queue_ini );
	    break;


      case TMSG_ACT_A_TAKE_MSG:
	    if ( *pp_queue_ini!=NULL ) {
	       entry = queue_remove( pp_queue_ini );
	       rc += tstm_process_entry( (*pp_ini), entry, &maj_stat );
	       rc += queue_flush( &entry );
	    }
	    break;


      case TMSG_ACT_B_TAKE_MSG:
	    if ( *pp_queue_acc!=NULL ) {
	       entry = queue_remove( pp_queue_acc );
	       rc += tstm_process_entry( (*pp_acc), entry, &maj_stat );
	       rc += queue_flush( &entry );
	    }
	    break;


      case TMSG_ACT_A_CTXTRANS:
	    if ( ((*pp_ini)->flags&GSS_C_TRANS_FLAG)!=0  ||  options.force_xfer!=FALSE ) {
	       rc += ctx_transfer_cycle( p_trclevel, (*pp_ini) );
	       if ( (*pp_ini)->ctx==GSS_C_NO_CONTEXT )
		  maj_stat = GSS_S_FAILURE;
	    }
	    break;


      case TMSG_ACT_B_CTXTRANS:
	    if ( ((*pp_acc)->flags&GSS_C_TRANS_FLAG)!=0  ||  options.force_xfer!=FALSE ) {
	       rc += ctx_transfer_cycle( p_trclevel, (*pp_acc) );
	       if ( (*pp_acc)->ctx==GSS_C_NO_CONTEXT )
		  maj_stat = GSS_S_FAILURE;
	    }
	    break;


      case TMSG_ACT_LOC_FTRANS:
	    if ( ((*pp_acc)->flags & (*pp_ini)->flags & GSS_C_TRANS_FLAG)!=0
		 || options.force_xfer!=FALSE ) {

	       rc += transfer_via_file( p_trclevel, PARENT_CTX_FILE, NULL,
				        pp_ini, pp_acc, &success );
	       if ( success==FALSE )
		  maj_stat = GSS_S_FAILURE;
	    }
	    break;


      case TMSG_ACT_CHILD_FTRANS:
	    if ( options.child_process!=FALSE ) { break; }
	    if ( options.cross_process_xfer==FALSE ) { break; }

	    if ( ((*pp_ini)->flags&GSS_C_TRANS_FLAG)!=0  ||  options.force_xfer!=FALSE ) {
	       if ( ((*pp_ini)->flags&(GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG))!=0
		    ||  options.try_msgprot_anyway!=FALSE ) {
		  xfer_job = stdjob_xfer;
	       } else {
		  xfer_job = xfer_only;
	       }

	       rc += transfer_via_child( p_trclevel, xfer_job, pp_ini, pp_acc, &success );
	       if ( success==FALSE )
		  maj_stat = GSS_S_FAILURE;

	    }
	    break;


      case TMSG_ACT_CTX_REFRESH:
	    rc += refresh_context( p_trclevel, (*pp_ini)->lifetime_req, pp_ini, pp_acc, &success );
	    if ( success==FALSE )
	       maj_stat = GSS_S_FAILURE;

	    break;


      case TMSG_ACT_MSG_TIMING:
	    print_timing_data( p_timing, (*pp_ini)->gssfp, p_step->u.label,
			       GSS_GET_MIC, GSS_UNWRAP, TRUE );
	    if ( (*pp_ini)->gssfp!=(*pp_acc)->gssfp ) {
	       /* print the timing data for acceptor seperately when not sharing gssfp */
	       print_timing_data( p_timing, (*pp_acc)->gssfp, p_step->u.label,
				  GSS_GET_MIC, GSS_UNWRAP, TRUE );
	    }
	    break;


      case TMSG_ACT_CLEAR_TIMING:
	    clear_timing_data( 0, (*pp_ini)->gssfp, GSS_GET_MIC, GSS_UNWRAP );
	    clear_timing_data( 0, (*pp_ini)->gssfp, GSS_EXPORT_SEC_CONTEXT_I, GSS_IMPORT_SEC_CONTEXT_A );
	    if ( (*pp_ini)->gssfp!=(*pp_acc)->gssfp ) {
	       /* clear other gssfp as well -- if different for ini and acc */
	       clear_timing_data( 0, (*pp_acc)->gssfp, GSS_GET_MIC, GSS_UNWRAP );
	       clear_timing_data( 0, (*pp_acc)->gssfp, GSS_EXPORT_SEC_CONTEXT_I, GSS_IMPORT_SEC_CONTEXT_A );
	    }
	    break;


      case TMSG_ACT_XFER_TIMING:
	    if ( ((*pp_ini)->flags&GSS_C_TRANS_FLAG)!=0  ||  options.force_xfer!=FALSE ) {
	       print_timing_data( p_timing, (*pp_ini)->gssfp, p_step->u.label,
			       GSS_EXPORT_SEC_CONTEXT_I, GSS_IMPORT_SEC_CONTEXT_A, TRUE );
	       if ( (*pp_ini)->gssfp!=(*pp_acc)->gssfp ) {
		  /* print the timing data for acceptor seperately when not sharing gssfp */
		  print_timing_data( p_timing, (*pp_acc)->gssfp, p_step->u.label,
			       GSS_EXPORT_SEC_CONTEXT_I, GSS_IMPORT_SEC_CONTEXT_A, TRUE );
	       }
	    }
	    break;


      case TMSG_ACT_INVALID:
      default:
	    XVEB((V_BUG, "%s() invalid action in job: %u!\n",
			 this_Call, (Uint)p_step->action ));
	    maj_stat = GSS_S_FAILURE;
	    rc++;
	    break;

   } /* end   switch(step->action ) */

   if ( 0 ) {
fatal_error:
      if ( entry!=NULL && entry->ori_msg.length==0 ) {
	 XVEB((V_ERR, "%s failed for ZERO-length message, terminating msg exchange!\n", gss_call));
      } else {
	 XVEB((V_ERR, "%s failed, terminating this message exchange!\n", gss_call));
      }
   }

fatal_error_2:
   if ( entry!=NULL ) {
      /* this should only happen in case of an error ... */
      rc += queue_flush( &entry );
   }

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

   return(rc);

} /* tstm_act_entry() */




/*
 * tstm_act_queueloop()
 *
 *
 */
int
tstm_act_queueloop( int			 p_trclevel,    char		    * p_test_title,
		    int			 p_timing,
		    tmsg_action_et       p_action,      size_t		      p_loops,
		    struct ctx_desc   ** pp_ini,        struct ctx_desc    ** pp_acc,
		    struct tmsg_entry ** pp_queue_ini,  struct tmsg_entry  ** pp_queue_acc,
		    OM_uint32	       * pp_maj_stat )
{
   struct tmsg_job   localstep;
   OM_uint32	     maj_stat = GSS_S_COMPLETE;
   size_t	     loopcnt  = 0;
   int		     rc	      = 0;

   localstep.u.size = 0;

   while( GSS_ERROR(maj_stat)==GSS_S_COMPLETE
          && ( ( (*pp_queue_ini)!=NULL  &&  ((p_action)&TMA_INI)!=0 )
	       || ( (*pp_queue_acc)!=NULL  &&  ((p_action)&TMA_ACC)!=0 ) ) ) {

      if ( ((p_action)&TMA_INI)!=0  &&  (*pp_queue_ini)!=NULL ) {
	 localstep.action = TMSG_ACT_A_TAKE_MSG;
	 rc += tstm_act_entry( p_trclevel, p_test_title, p_timing,
			       &localstep, pp_ini, pp_acc,
			       pp_queue_ini, pp_queue_acc, &maj_stat );
      }

      if ( GSS_ERROR(maj_stat)==GSS_S_COMPLETE ) {
	 if ( ((p_action)&TMA_ACC)!=0  &&  (*pp_queue_acc)!=NULL ) {
	    localstep.action = TMSG_ACT_B_TAKE_MSG;
	    rc += tstm_act_entry( p_trclevel, p_test_title, p_timing,
				  &localstep, pp_ini, pp_acc,
				  pp_queue_ini, pp_queue_acc, &maj_stat );
	 }
      }

      loopcnt++;

      if ( p_loops>0 && loopcnt>=p_loops ) {
	 /* exit loop when counter defined and reached */
	 break;
      }

   } /* while(queue-entries and no error) */

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

   return(rc);

} /* tstm_act_queueloop() */




/*
 * test_message_exchange()
 *
 *
 */
int
test_message_exchange( int	            p_trclevel,    char		       * p_test_title,
		       int		    p_timing,      struct tmsg_job     * p_job,
		       struct ctx_desc   ** pp_ini,        struct ctx_desc    ** pp_acc,
		       struct tmsg_entry ** pp_queue_ini,  struct tmsg_entry  ** pp_queue_acc,
		       int	          * pp_success )
{
   char		      * this_Call GNU_UNUSED = "test_message_exchange";
   struct tmsg_entry  * queue_ini  = (struct tmsg_entry *)0;
   struct tmsg_entry  * queue_acc  = (struct tmsg_entry *)0;
   struct tmsg_entry ** pqueue_ini;
   struct tmsg_entry ** pqueue_acc;
   struct tmsg_job    * step	   = NULL;
   OM_uint32	        maj_stat   = GSS_S_COMPLETE;
   int                  leave      = FALSE;
   int                  rc         = 0;

   (*pp_success) = FALSE;

   if ( pp_queue_ini!=NULL && pp_queue_acc!=NULL ) {
      pqueue_ini = pp_queue_ini;
      pqueue_acc = pp_queue_acc;
   } else {
      pqueue_ini = &queue_ini;
      pqueue_acc = &queue_acc;
   }

   for ( step=p_job ; leave==FALSE ; step++ ) {

      switch( step->action ) {

	 default:
		  rc += tstm_act_entry( p_trclevel, p_test_title, p_timing,
				        step, pp_ini, pp_acc,
				        pqueue_ini, pqueue_acc, &maj_stat );
		  if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE ) {
		     goto fatal_error;
		  }
		  break;


	 case TMSG_ACT_A_TAKE_MSG:
	 case TMSG_ACT_B_TAKE_MSG:
	 case TMSG_ACT_AB_TAKE_MSG:
		  rc += tstm_act_queueloop( p_trclevel, p_test_title, p_timing,
					    step->action, step->u.size, pp_ini, pp_acc,
 					    pqueue_ini, pqueue_acc, &maj_stat );
		  if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE ) {
		     goto fatal_error;
		  }

		  break;


	 case TMSG_ACT_CHILD_FTRANS:
	 case TMSG_ACT_CTX_REFRESH:
		  /* first, process all remaining messages in the queues */
		  rc += tstm_act_queueloop( p_trclevel, p_test_title, p_timing,
					    TMSG_ACT_AB_TAKE_MSG, 0, pp_ini, pp_acc,
					    pqueue_ini, pqueue_acc, &maj_stat );
		  if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE ) {
		     goto fatal_error;
		  }
		  /* when the queues are processed/flushed, the refresh */
		  /* or security context transfer can be done           */
		  rc += tstm_act_entry( p_trclevel, p_test_title, p_timing,
				        step, pp_ini, pp_acc,
				        pqueue_ini, pqueue_acc, &maj_stat );
		  if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE ) {
		     goto fatal_error;
		  }
		  break;


	 case TMSG_ACT_END_QUIT:
		  leave = TRUE;
		  break;


      }

      if ( (*pp_ini)==NULL || (*pp_acc)==NULL ) {
	 /* This may happen when a security context transfer fails ... */
	 goto fatal_error;
      }

   }
		  
   rc += tstm_act_queueloop( p_trclevel, p_test_title, p_timing,
			     TMSG_ACT_AB_TAKE_MSG, 0, pp_ini, pp_acc,
			     &queue_ini, &queue_acc, &maj_stat );
   if ( GSS_ERROR(maj_stat)!=GSS_S_COMPLETE ) {
      goto fatal_error;
   }

   (*pp_success) = TRUE;

fatal_error:

   rc += queue_flush( &queue_ini );
   rc += queue_flush( &queue_acc );

   return(rc);

} /* test_message_exchange() */




/*
 * sap_msg_exchange()
 *
 *
 */
int
sap_msg_exchange( int                p_trclevel,      OM_uint32         p_flags,
		  OM_uint32          p_lifetime,      struct tmsg_job * p_job,
		  DLL_GSSFP_T      * p_gssfp_ini,     DLL_GSSFP_T     * p_gssfp_acc,
		  sc_variants_et     p_ini_cred_type, sc_variants_et    p_acc_cred_type,
		  char             * p_target,	      size_t            p_target_len,
		  int		   * pp_success )
{
   struct ctx_desc  * ini     = NULL;
   struct ctx_desc  * acc     = NULL;
   int		      success = FALSE;
   int                rc      = 0;

   rc += sap_establish_context( verbose_level-1, p_flags, p_lifetime,
				p_gssfp_ini, p_gssfp_acc, &ini, &acc,
				p_ini_cred_type, p_acc_cred_type,
				p_target, p_target_len, NULL );

   if ( ini!=NULL && acc!=NULL ) {

      if ( (ini->flags&(GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG))==0
	   &&  options.try_msgprot_anyway==FALSE ) {

	 XVEB((V_HIDE, "sap_msg_exchange(): skipping -- message protection not available!\n"));

      } else {

	 rc += test_message_exchange( p_trclevel, "", TRUE, p_job,
				      &ini, &acc, NULL, NULL, &success );

      }

   } /* ini_ctx!=GSS_C_NO_CONTEXT  &&  acc_ctx!=GSS_C_NO_CONTEXT */

   rc += release_ctx_desc( &ini );
   rc += release_ctx_desc( &acc );

   if ( pp_success!=NULL ) { (*pp_success) = success; }

   return(rc);

} /* sap_msg_exchange() */




/*
 * benchmark_msgprot()
 *
 * Description:
 *   Attempt to benchmark the message protection and unprotection calls.
 *   A new security context will be temporarily established for this
 *   purpose.
 */
int
benchmark_msgprot( int      p_trclevel,	  DLL_GSSFP_T  * p_gssfp,
		   int      atob,	  int		 p_type,
		   size_t   p_size )
{
   struct tmsg_job  bm_job[30];
   char		    title[128];
   char           * sdir;
   char		  * how;
   tmsg_action_et   protect;
   tmsg_action_et   unprotect;
   Uint		    step;
   Uint		    i;
   Uint		    msgcount;
   int		    success = FALSE;
   int		    rc	    = 0;

   if ( atob==FALSE ) {

      unprotect = TMSG_ACT_A_TAKE_MSG;
      sdir      = "ini==>acc";
      switch(p_type) {
	 case BENCH_WCONF:	 protect = TMSG_ACT_B_WCONF;   how = "conf"; break;
	 case BENCH_WMIC:	 protect = TMSG_ACT_B_WMIC;    how = "MIC";  break;
	 case BENCH_MICONLY:
	 default:		 protect = TMSG_ACT_B_MICONLY; how = "MIC";    break;
      }

   } else { /* atob!=FALSE */

      unprotect = TMSG_ACT_B_TAKE_MSG;
      sdir      = "acc==>ini";
      switch(p_type) {
	 case BENCH_WCONF:	 protect = TMSG_ACT_A_WCONF;   how = "conf"; break;
	 case BENCH_WMIC:	 protect = TMSG_ACT_A_WMIC;    how = "MIC";  break;
	 case BENCH_MICONLY:
	 default:		 protect = TMSG_ACT_A_MICONLY; how = "MIC";  break;
      }


   } /* atob!=FALSE */

   sprintf(title, "%.54s (%.10s) %lu Bytes", sdir, how, (Ulong)p_size );

   step = 0;
   bm_job[step].action = TMSG_ACT_CLEAR_TIMING;
   bm_job[step].u.size   = 0;
   step++;

   if ( p_size<=10000 ) {
      msgcount = 20;  /* limited: see definition of bm_job[] above! */
   } else if ( p_size <=100000 ) {
      msgcount = 10;  /* limited: see definition of bm_job[] above! */
   } else {
      msgcount = 5;   /* limited: see definition of bm_job[] above! */
   }

   for ( i=0 ; i<msgcount ; i++ ) {
      bm_job[step].action = protect;
      bm_job[step].u.size = p_size;
      step++;
   }

   bm_job[step].action  = unprotect;
   bm_job[step].u.size  = 0;
   step++;

   bm_job[step].action  = TMSG_ACT_MSG_TIMING;
   bm_job[step].u.label = title;
   step++;

   bm_job[step].action  = TMSG_ACT_END_QUIT;
   bm_job[step].u.size  = 0;

   rc += sap_msg_exchange( p_trclevel, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, &(bm_job[0]),
			   p_gssfp, p_gssfp,
			   SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp->acceptor, p_gssfp->acceptor_len,
			   &success );

   return(rc);   

} /* benchmark_msgprot() */





struct tmsg_job  prepare_mic[] = {
   { TMSG_ACT_A_MICONLY,   { 1000 } },
   { TMSG_ACT_A_MICONLY,   { 1013 } },
   { TMSG_ACT_A_MICONLY,   { 1025 } },
   { TMSG_ACT_A_MICONLY,   { 1037 } },
   { TMSG_ACT_A_MICONLY,   { 1049 } },
   { TMSG_ACT_A_MICONLY,   { 1051 } },
   { TMSG_ACT_B_MICONLY,   { 1089 } },
   { TMSG_ACT_B_MICONLY,   { 1101 } },
   { TMSG_ACT_B_MICONLY,   { 1113 } },
   { TMSG_ACT_B_MICONLY,   { 1115 } },
   { TMSG_ACT_B_MICONLY,   { 1127 } },
   { TMSG_ACT_B_MICONLY,   { 1139 } },
   { TMSG_ACT_END_QUIT,	   {    0 } }
};


struct tmsg_job  prepare_wmic[] = {
   { TMSG_ACT_A_WMIC,	   { 1000 } },
   { TMSG_ACT_A_WMIC,	   { 1013 } },
   { TMSG_ACT_A_WMIC,	   { 1025 } },
   { TMSG_ACT_A_WMIC,	   { 1037 } },
   { TMSG_ACT_A_WMIC,	   { 1049 } },
   { TMSG_ACT_A_WMIC,	   { 1051 } },
   { TMSG_ACT_B_WMIC,	   { 1089 } },
   { TMSG_ACT_B_WMIC,	   { 1101 } },
   { TMSG_ACT_B_WMIC,	   { 1113 } },
   { TMSG_ACT_B_WMIC,	   { 1115 } },
   { TMSG_ACT_B_WMIC,	   { 1127 } },
   { TMSG_ACT_B_WMIC,	   { 1139 } },
   { TMSG_ACT_END_QUIT,	   {    0 } }
};


struct tmsg_job  prepare_wconf[] = {
   { TMSG_ACT_A_WCONF,	   { 1000 } },
   { TMSG_ACT_A_WCONF,	   { 1013 } },
   { TMSG_ACT_A_WCONF,	   { 1025 } },
   { TMSG_ACT_A_WCONF,	   { 1037 } },
   { TMSG_ACT_A_WCONF,	   { 1049 } },
   { TMSG_ACT_A_WCONF,	   { 1051 } },
   { TMSG_ACT_B_WCONF,	   { 1089 } },
   { TMSG_ACT_B_WCONF,	   { 1101 } },
   { TMSG_ACT_B_WCONF,	   { 1113 } },
   { TMSG_ACT_B_WCONF,	   { 1115 } },
   { TMSG_ACT_B_WCONF,	   { 1127 } },
   { TMSG_ACT_B_WCONF,	   { 1139 } },
   { TMSG_ACT_END_QUIT,	   {    0 } }
};


struct tmsg_job preprocess_job[] = {
   { TMSG_ACT_A_TAKE_MSG,  {    1 } },
   { TMSG_ACT_B_TAKE_MSG,  {    1 } },
   { TMSG_ACT_A_TAKE_MSG,  {    1 } },
   { TMSG_ACT_B_TAKE_MSG,  {    1 } },
   { TMSG_ACT_A_TAKE_MSG,  {    1 } },
   { TMSG_ACT_B_TAKE_MSG,  {    1 } },
   { TMSG_ACT_END_QUIT,	   {    0 } }
};


struct tmsg_job mp_final_messages[] = {
   { TMSG_ACT_A_WCONF,	   { 1234 } },
   { TMSG_ACT_A_WCONF,	   { 1432 } },
   { TMSG_ACT_A_WCONF,	   { 1001 } },
   { TMSG_ACT_A_WCONF,	   { 1009 } },
   { TMSG_ACT_A_WMIC,	   {  991 } },
   { TMSG_ACT_A_WMIC,	   {  878 } },
   { TMSG_ACT_A_WMIC,	   {  779 } },
   { TMSG_ACT_A_WMIC,	   {  679 } },
   { TMSG_ACT_A_MICONLY,   { 1007 } },
   { TMSG_ACT_A_MICONLY,   { 1035 } },
   { TMSG_ACT_A_MICONLY,   { 1117 } },
   { TMSG_ACT_A_MICONLY,   { 1017 } },
   { TMSG_ACT_END_QUIT,	   {    0 } }
};


struct tmsg_job mp_finish[] = {
   { TMSG_ACT_A_TAKE_MSG,  {    0 } },
   { TMSG_ACT_END_QUIT,	   {    0 } }
};


/*
 * describe_mp_test()
 *
 *
 */
void
describe_mp_test( struct tmsg_entry  * p_entry,
		  char               * p_fmt,
		  char               * p_s1,      char   * p_s2 )
{
   char   * this_Call = "describe_mp_test";
   char   * gss_call = "";
   char     msg[256];

   sprintf(msg, p_fmt, p_s1, p_s2 );

   switch( p_entry->msg_type ) {

      case TMSG_TYPE_MICONLY:	 gss_call = "gss_verifymic()";
				 break;

      case TMSG_TYPE_WMIC:	 gss_call = "gss_unwrap(conf=FALSE)";
				 break;

      case TMSG_TYPE_WCONF:	 gss_call = "gss_unwrap(conf=TRUE)";
				 break;

      default:
	    XVEB((V_BUG, "%s(): invalid message type %u when trying %s\n",
			 this_Call, (Uint)(p_entry->msg_type), msg ));
	    return;

   }

			      
   XVEB((V_SHOW, "%s for %s (tok_len=%lu)\n",
	         msg, gss_call, (Ulong)((p_entry->gss_token).length) ));
   return;

} /* describe_mp_test() */



char *mp_victim[] = { "acceptor", "initiator" };

/*
 * msg_prot_test()
 *
 * Status: under construction ...
 *
 */
int
msg_prot_test( int	          p_trclevel,       OM_uint32	      p_flags,
	       OM_uint32          p_lifetime,	    char	    * p_test_title,
	       mp_test_et	  p_mptype,
	       DLL_GSSFP_T      * p_gssfp_ini,      DLL_GSSFP_T	    * p_gssfp_acc,
	       sc_variants_et     p_ini_cred_type,  sc_variants_et    p_acc_cred_type,
	       void	        * p_target,	    size_t	      p_target_len,
	       int	        * pp_success )
{
   char		      * this_Call = "msg_prot_test";
   char		      * tmpbuf    = NULL;
   char		      * ptr;
   struct ctx_desc    * ini	  = NULL;
   struct ctx_desc    * acc	  = NULL;
   struct ctx_desc   ** a         = NULL;
   struct ctx_desc   ** b         = NULL;
   struct tmsg_job    * job       = NULL;
   struct tmsg_job    * prepare   = NULL;
   struct tmsg_entry  * queue_ini = NULL;
   struct tmsg_entry  * queue_acc = NULL;
   struct tmsg_entry    save;
   struct tmsg_entry  * entry     = NULL;
   struct tmsg_entry ** aqueue    = NULL;
   struct tmsg_entry ** bqueue    = NULL;
   size_t		len;
   OM_uint32		maj_stat  = GSS_S_COMPLETE;
   int		        success	  = FALSE;
   int		        success2  = FALSE;
   int			success3  = FALSE;
   int			success4  = FALSE;
   int		        success5  = FALSE;
   int                  victim    = 0;
   int		        rc	  = 0;
   char			savech;

   rc += sap_establish_context( verbose_level-1, p_flags, p_lifetime,
				p_gssfp_ini, p_gssfp_acc, &ini, &acc,
				p_ini_cred_type, p_acc_cred_type,
				p_target, p_target_len, NULL );

   if( ini!=NULL  &&  acc!=NULL
          &&  ini->ctx!=GSS_C_NO_CONTEXT  &&  acc->ctx!=GSS_C_NO_CONTEXT ) {

      if ( (p_mptype&MP_TEST_WCONF)!=0 ) {

	 prepare = &(prepare_wconf[0]);

      } else if ( (p_mptype&MP_TEST_WMIC)!=0 ) {

	 prepare = &(prepare_wmic[0]);

      } else {

	 prepare = &(prepare_mic[0]);

      }

      rc += test_message_exchange( verbose_level-1, p_test_title,
				   FALSE, prepare, &ini, &acc,
				   &queue_ini, &queue_acc, &success );

      if ( success==FALSE )
	 goto error;

      rc += test_message_exchange( verbose_level-1, p_test_title,
				   FALSE, &(preprocess_job[0]), &ini, &acc,
				   &queue_ini, &queue_acc, &success );
      if ( success==FALSE )
	 goto error;

      
      if ( (p_mptype&MP_TEST_ACC)!=0 ) {
	 victim    = 0;
	 a         = &acc;
	 aqueue    = &queue_acc;
	 b         = &ini;
	 bqueue    = &queue_ini;
      } else {
	 victim    = 1;
	 a         = &ini;
	 aqueue    = &queue_ini;
	 b         = &acc;
	 bqueue    = &queue_acc;
      }

      success = FALSE;

      switch( (p_mptype&MP_TEST_MASK) ) {

	 case MP_TEST_REFLECT:
	       if (*bqueue!=NULL) {
		  entry = (*bqueue);
		  describe_mp_test( entry, "Message Reflection on %s", mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  /* we may have to release a message token here ... */
		  if ( (entry->gssfp_message)!=NULL ) {
		     rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		  }
		  if ( maj_stat==GSS_S_COMPLETE ) {
		     if ( ((*a)->flags&GSS_C_REPLAY_FLAG)!=0 ) {
			rc++;
			XVEB((V_ERR, "Reflection of message NOT detected!\n"));
		     } else {
			XVEB((V_SHOW, "INFO:  Reflection of message NOT detected!\n"));
		     }
		  } else {
		     success = TRUE;
		  }
	       }
	       break;


      	 case MP_TEST_REPLAY:
	       if (*aqueue!=NULL) {
		  entry = queue_remove( aqueue );
		  describe_mp_test( entry, "Message Replay on %s", mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  /* we may have to release a message token here ... */
		  if ( (entry->gssfp_message)!=NULL ) {
		     rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		  }
		  if ( maj_stat==GSS_S_COMPLETE ) {
		     rc += tstm_process_entry( *a, entry, &maj_stat );
		     if ( maj_stat==GSS_S_COMPLETE ) {
			if ( ((*a)->flags&GSS_C_REPLAY_FLAG)!=0 ) {
			   rc++;
			   XVEB((V_ERR, "Message replay NOT detected!\n"));
			} else {
			   XVEB((V_SHOW, "INFO:  Message replay not detected!\n"));
			}
		     } else {
			success = TRUE;
		     }
		  }
		  rc += queue_flush( &entry );
	       }
	       break;


	 case MP_TEST_REORDER:
	       if ( *aqueue!=NULL  &&  (*aqueue)->next!=NULL ) {

		  entry           = (*aqueue)->next;
		  (*aqueue)->next = entry->next;
		  entry->next     = NULL;
		  describe_mp_test( entry, "Reordered Messages on %s", mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  if ( ((*a)->flags&GSS_C_SEQUENCE_FLAG)!=0  &&  maj_stat==GSS_S_COMPLETE ) {
		     rc++;
		     XVEB((V_ERR, "Context with sequence protection failed to detect skipped message!\n"));
		  } else {
		     success = TRUE;
		  }
		  rc += queue_flush( &entry );
	       }
	       break;


	 case MP_TEST_DELMSG:
	       if ( *aqueue!=NULL  &&  (*aqueue)->next!=NULL ) {
		  entry   = queue_remove( aqueue );
		  queue_flush( &entry );
		  entry   = queue_remove( aqueue );
		  describe_mp_test( entry, "Message loss on %s", mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  if ( ((*a)->flags&GSS_C_SEQUENCE_FLAG)!=0  &&  maj_stat==GSS_S_COMPLETE ) {
		     rc++;
		     XVEB((V_ERR, "Context with sequence protection failed to detect skipped message!\n"));
		  } else {
		     success = TRUE;
		  }
		  rc += queue_flush( &entry );
	       }
	       break;

		  
	 case MP_TEST_TRUNC:
	       /* Message truncation, first variant:  new buffer, copy only truncated data */
	       if ( *aqueue!=NULL ) {
		  entry  = (*aqueue); /* leave message in queue */
		  save   = (*entry);
		  len    = (entry->gss_token).length - 1;
		  tmpbuf = malloc( len );  /* We copy the entry so that Purify can detect UMRs */
		  if ( tmpbuf==NULL ) {
		     rc++;
		     XVEB((V_ERR, "OOOPS! malloc(%lu) failed!\n", (Ulong) len));
		  } else {
		     /* modify gss_token and length in entry */
		     memcpy( tmpbuf, (entry->gss_token).value, len);
		     (entry->gss_token).length = len;
		     (entry->gss_token).value  = tmpbuf;
		     describe_mp_test( entry, "Message truncation on %s (-1 Byte)",
				       mp_victim[victim], "" );
		     rc += tstm_process_entry( *a, entry, &maj_stat );
		     /* we may have to release a message token here ... */
		     if ( (entry->gssfp_message)!=NULL ) {
			rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		     }
		     /* restore original length in entry->gss_token */
		     (entry->gss_token).value  = (save.gss_token).value;
		     (entry->gss_token).length = (save.gss_token).length;

		     if ( maj_stat==GSS_S_COMPLETE ) {
			entry = queue_remove( aqueue );
			rc   += queue_flush( &entry );
			rc++;
			XVEB((V_ERR, "Truncation of message NOT detected !\n"));
		     } else {
			success = TRUE;
		     }
		     if ( tmpbuf!=NULL ) { free(tmpbuf); tmpbuf = NULL; }
		  }
	       }
	       break;


	 case MP_TEST_TRUNC_2:
	       /* Message truncation, second variant:  original buffer, just pass a shorter length field */
	       if ( *aqueue!=NULL ) {
		  entry  = (*aqueue); /* leave message in queue */
		  save   = (*entry);
		  (entry->gss_token).length -= 1;  /* reduce length only, but leave the data in place */
		  describe_mp_test( entry, "Message truncation (2) on %s (-1 Byte)",
				    mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  /* we may have to release a message token here ... */
		  if ( (entry->gssfp_message)!=NULL ) {
		     rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		  }
		  /* restore original length in entry->gss_token */
		  (entry->gss_token).length = (save.gss_token).length;

		  if ( maj_stat==GSS_S_COMPLETE ) {
		     entry = queue_remove( aqueue );
		     rc   += queue_flush( &entry );
		     rc++;
		     XVEB((V_ERR, "Truncation of message NOT detected !\n"));
		  } else {
		     success = TRUE;
		  }
	       }
	       break;


	 case MP_TEST_TRUNC_3:
	       /* Message truncation, third variant:  new buffer, truncate aggressively, copy only truncated data */
	       if ( *aqueue!=NULL ) {
		  entry  = (*aqueue); /* leave message in queue */
		  save   = (*entry);
		  len    = 18;
		  tmpbuf = malloc( len );  /* We copy the entry so that Purify can detect UMRs */
		  if ( tmpbuf==NULL ) {
		     rc++;
		     XVEB((V_ERR, "OOOPS! malloc(%lu) failed!\n", (Ulong) len));
		  } else {
		     /* modify gss_token and length in entry */
		     memcpy( tmpbuf, (entry->gss_token).value, len);
		     (entry->gss_token).length = len;
		     (entry->gss_token).value  = tmpbuf;
		     describe_mp_test( entry, "Message truncation (3) on %s (18 Byte)",
				       mp_victim[victim], "" );
		     rc += tstm_process_entry( *a, entry, &maj_stat );
		     /* we may have to release a message token here ... */
		     if ( (entry->gssfp_message)!=NULL ) {
			rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		     }
		     /* restore original length in entry->gss_token */
		     (entry->gss_token).value  = (save.gss_token).value;
		     (entry->gss_token).length = (save.gss_token).length;

		     if ( maj_stat==GSS_S_COMPLETE ) {
			entry = queue_remove( aqueue );
			rc   += queue_flush( &entry );
			rc++;
			XVEB((V_ERR, "Truncation of message NOT detected !\n"));
		     } else {
			success = TRUE;
		     }
		     if ( tmpbuf!=NULL ) { free(tmpbuf); tmpbuf = NULL; }
		  }
	       }
	       break;


	 case MP_TEST_TRAIL_GARB:
	       if ( *aqueue!=NULL ) {
		  entry  = (*aqueue); /* leave message in queue */
		  save   = (*entry);
		  len    = (entry->gss_token).length + 1;
		  tmpbuf = malloc( len );
		  if ( tmpbuf==NULL ) {
		     rc++;
		     XVEB((V_ERR, "OOOPS! malloc(%lu) failed!\n", (Ulong) len));
		  } else {
		     /* modify gss_token in entry */
		     memcpy( tmpbuf, (entry->gss_token).value, (entry->gss_token).length );
		     tmpbuf[len-1]='A';
		     (entry->gss_token).length = len;
		     (entry->gss_token).value  = tmpbuf;
		     describe_mp_test( entry, "Message with trailing garbage on %s",
				       mp_victim[victim], "" );
		     rc += tstm_process_entry( *a, entry, &maj_stat );
		     /* we may have to release a message token here ... */
		     if ( (entry->gssfp_message)!=NULL ) {
			rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		     }
		     /* restore original entry */
		     (entry->gss_token).length = (save.gss_token).length;
		     (entry->gss_token).value  = (save.gss_token).value;

		     if ( maj_stat==GSS_S_COMPLETE ) {
			entry = queue_remove( aqueue );
			rc   += queue_flush( &entry );
			rc++;
			XVEB((V_ERR, "Trailing garbage for token not detected!\n"));
		     } else {
			success = TRUE;
		     }
		     if ( tmpbuf!=NULL ) { free(tmpbuf); tmpbuf = NULL; }
		  }
	       }
	       break;


	 case MP_TEST_MOD_MIDDLE:
	       if ( *aqueue!=NULL ) {
		  entry    = (*aqueue);
		  len      = (entry->gss_token).length>>2;
		  ptr      = (entry->gss_token).value;
		  savech   = ptr[len];
		  /* modify gss_token in entry */
		  ptr[len] = savech ^ 0x41;
		  describe_mp_test( entry, "Modified message (center) on %s",
					   mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  /* we may have to release a message token here ... */
		  if ( (entry->gssfp_message)!=NULL ) {
		     rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		  }
		  /* restore original message */
		  ptr[len] = savech;
		  if ( maj_stat==GSS_S_COMPLETE ) {
		     entry  = queue_remove( aqueue );
		     rc    += queue_flush( &entry );
		     rc++;
		     XVEB((V_ERR, "Modified message (at offset %lu) NOT detected!\n",
				  (Ulong) len ));
		  } else {
		     success = TRUE;
		  }
	       }
	       break;


	 case MP_TEST_MOD_TAIL:
	       if ( *aqueue!=NULL ) {
		  entry    = (*aqueue);
		  len      = (entry->gss_token).length-10;
		  ptr      = (entry->gss_token).value;
		  savech   = ptr[len];
		  /* modify gss_token in entry */
		  ptr[len] = savech ^ 0x41;
		  describe_mp_test( entry, "Modified message (tail) on %s",
					   mp_victim[victim], "" );
		  rc += tstm_process_entry( *a, entry, &maj_stat );
		  /* we may have to release a message token here ... */
		  if ( (entry->gssfp_message)!=NULL ) {
		     rc += release_buffer( entry->gssfp_message, &(entry->gss_ret_msg) );
		  }
		  /* restore original message */
		  ptr[len] = savech;
		  if ( maj_stat==GSS_S_COMPLETE ) {
		     entry  = queue_remove( aqueue );
		     rc    += queue_flush( &entry );
		     rc++;
		     XVEB((V_ERR, "Modified message (at offset %lu) NOT detected!\n",
				  (Ulong) len ));
		  } else {
		     success = TRUE;
		  }
	       }
	       break;


	 case MP_TEST_SIMUL_FRAME:
	       {
		  /* char   testframe[1024]; */
		  /* create a custom frame with the generic gssapi framing */
		  /* and throw it onto the message protection call ....    */
	       }
	       break;


	 default:
	       XVEB((V_BUG, "%s(): wrong mptype : %u -- aborting!\n", this_Call, (Uint)p_mptype));
	       goto error;


      } /* (p_mptype&MP_TEST_MASK) */


      /* new message protections on security context a (the attacked context) */
      rc += test_message_exchange( p_trclevel - 1 , "", FALSE, &(mp_final_messages[0]),
				   a, b, aqueue, bqueue, &success2 );
      if ( success==TRUE && success2==FALSE ) {
	 rc++;
	 XVEB((V_ERR, "Problems to protect further messages with the attacked context!\n" ));
      }

      /* new message protections on security context b for processing by a */
      rc += test_message_exchange( p_trclevel - 1 , "", FALSE, &(mp_final_messages[0]),
				   b, a, bqueue, aqueue, &success3 );

      /* process all queue entries for context a (the attacked context) */
      rc += test_message_exchange( p_trclevel - 1, "", FALSE, &(mp_finish[0]),
				   a, b, aqueue, bqueue, &success4 );
      if ( success3==TRUE && success4==FALSE ) {
	 rc++;
	 if ( ((*a)->flags&GSS_C_SEQUENCE_FLAG)!=0 && (p_mptype&MP_TEST_DELMSG)!=0 ) {
	    /* With sequencing, it is possible that a message loss causes this behaviour */
	    XVEB((V_SHOW, "INFO: Failing to unprotect further messages with the attacked context!\n"));
	 } else {
	    XVEB((V_ERR, "Problems to unprotect further messages with the attacked context!\n"));
	 }
      }

      /* process all queue entries for context b (the other side context) */
      rc += test_message_exchange( p_trclevel - 1, "", FALSE, &(mp_finish[0]),
				   b, a, bqueue, aqueue, &success5 );
      

      if ( success2!=FALSE && success3!=FALSE && success4!=FALSE && success5!=FALSE ) {

	 job = NULL;

	 if ( (ini->flags&GSS_C_TRANS_FLAG)!=0  ||  options.force_xfer!=FALSE ) {
	    job = stdjob_xfer;
	 } else {
	    job = stdjob;
	 }

	 rc += test_message_exchange( p_trclevel - 1, "", FALSE, job, &ini, &acc, &queue_ini, &queue_acc, &success );

      }

   }

error:

   if ( tmpbuf!=NULL ) { free(tmpbuf);   tmpbuf = NULL; }

   rc += release_ctx_desc( &ini );
   rc += release_ctx_desc( &acc );
   rc += queue_flush( &queue_ini );
   rc += queue_flush( &queue_acc );

   if ( pp_success!=NULL ) { (*pp_success) = success; }

   return(rc);

} /* msg_prot_test() */





int
do_msg_prot_test( int	           p_trclevel,       OM_uint32	      p_flags,
		  OM_uint32        p_lifetime,	     char	    * p_test_name,
		  mp_test_et	   p_mptype,
		  DLL_GSSFP_T    * p_gssfp_ini,      DLL_GSSFP_T    * p_gssfp_acc,
		  int	         * pp_success )
{
   int	     xsuccess = FALSE;
   int       success  = TRUE;
   int	     rc       = 0;
   int	     rci      = 0;

   p_mptype &= MP_TEST_MASK;

   rci = msg_prot_test( p_trclevel, p_flags, p_lifetime, "",
		        p_mptype|MP_TEST_INI|MP_TEST_WCONF,
			p_gssfp_ini, p_gssfp_acc,
			SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len,
			&xsuccess );
   success &= xsuccess;
   XVEB((V_RESULT, rci, 0, NULL ));
   rc  += rci;


   rci = msg_prot_test( p_trclevel, p_flags, p_lifetime, "",
		        p_mptype|MP_TEST_INI|MP_TEST_WMIC,
		        p_gssfp_ini, p_gssfp_acc,
			SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len,
			&xsuccess );
   success &= xsuccess;
   XVEB((V_RESULT, rci, 0, NULL ));
   rc  += rci;


   rci = msg_prot_test( p_trclevel, p_flags, p_lifetime, "",
		        p_mptype|MP_TEST_INI|MP_TEST_MICONLY,
		        p_gssfp_ini, p_gssfp_acc,
			SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len,
			&xsuccess );
   success &= xsuccess;
   XVEB((V_RESULT, rci, 0, NULL ));
   rc  += rci;


   rci = msg_prot_test( p_trclevel, p_flags, p_lifetime, "",
		        p_mptype|MP_TEST_ACC|MP_TEST_WCONF,
		        p_gssfp_ini, p_gssfp_acc,
			SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len,
			&xsuccess );
   success &= xsuccess;
   XVEB((V_RESULT, rci, 0, NULL ));
   rc  += rci;


   rci = msg_prot_test( p_trclevel, p_flags, p_lifetime, "",
		        p_mptype|MP_TEST_ACC|MP_TEST_WMIC,
		        p_gssfp_ini, p_gssfp_acc,
			SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len,
			&xsuccess );
   success &= xsuccess;
   XVEB((V_RESULT, rci, 0, NULL ));
   rc  += rci;


   rci = msg_prot_test( p_trclevel, p_flags, p_lifetime, "",
		        p_mptype|MP_TEST_ACC|MP_TEST_MICONLY,
		        p_gssfp_ini, p_gssfp_acc,
			SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			p_gssfp_ini->acceptor, p_gssfp_ini->acceptor_len,
			&xsuccess );
   success &= xsuccess;
   XVEB((V_RESULT, rci, 0, NULL ));
   rc  += rci;


   if ( pp_success!=NULL ) { *pp_success = success; }

   return(rc);

} /* do_msg_prot_test() */

