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

static char * this_File GNU_UNUSED = __FILE__ ;

/************************************************************************/
/* $Id: //tools/src/freeware/gsstest/contexts.c#5 $
 ************************************************************************
 *
 * 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"
#include "non_ansi.h"

/*
 * context_tests()
 *
 *
 */
int
context_tests( DLL_GSSFP_T * p_gssfp_ini, DLL_GSSFP_T * p_gssfp_acc )
{
   int		  rc       = 0;
   int		  rci      = 0;

   XVEB((V_SHOW, "====================\nContext establishment functions ...\n----------\n"));

   XVEB((V_TEST, "Testing sec_context est.: ini_cred=SIMPLE, acc_cred=GSSNAMED\n"));

   rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_SIMPLE_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);

   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;


   XVEB((V_TEST, "Testing sec_context est.: ini_cred=CHECKED, acc_cred=GSSNAMED\n"));

   rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_CHECKED_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);

   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;


   XVEB((V_TEST, "Testing sec_context est.: ini_cred=GSSNAMED, acc_cred=GSSNAMED\n"));

   rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_GSSNAMED_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);

   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;


   XVEB((V_TEST, "Testing sec_context est.: ini_cred=PRNAMED, acc_cred=GSSNAMED\n"));

   rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_PRNAMED_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);

   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;


   XVEB((V_TEST, "Testing sec_context est.: ini_cred=PRNAMED_VIA_XP, acc_cred=GSSNAMED\n"));

   rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_PRNAMED_VIA_XP_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);

   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;


   if ( options.target!=NULL && options.target[0]!=0 ) {
      XVEB((V_TEST, "Testing sec_context est.: ini_cred=SIMPLE, acc_cred=PRNAMED\n"));

      rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_SIMPLE_CRED, SNC_PRNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);
   } else {
      XVEB((V_TEST, "Testing sec_context est.: ini_cred=SIMPLE, acc_cred=CHECKED\n"));

      rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_SIMPLE_CRED, SNC_CHECKED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len, 1);
   }
   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;



   XVEB((V_TEST, "Testing %u sec_context est.: ini_cred=CHECKED, acc_cred=GSSNAMED\n",
                 (Uint) options.num_parallel_contexts ));

   rci = sap_try_context(  verbose_level, SNC_CONTEXT_FLAGS,
			   GSS_C_INDEFINITE, p_gssfp_ini, p_gssfp_acc,
			   SNC_CHECKED_CRED, SNC_GSSNAMED_CRED,
			   p_gssfp_acc->acceptor, p_gssfp_acc->acceptor_len,
			   (size_t) options.num_parallel_contexts );

   XVEB((V_RESULT, rci, 0, NULL ));
   rc += rci;


   return(rc);

} /* context_tests() */




/*
 * delete_sec_context()
 *
 * Description:
 *   wrapper of gssapi v1 call     gss_delete_sec_context()
 */
int
delete_sec_context( int		    p_trclevel,    DLL_GSSFP_T  * p_gssfp,
		    gss_ctx_id_t  * pp_ctx,	   gss_buffer_t   p_fin_token,
		    OM_uint32	  * pp_maj_stat )
{
   char       * gss_call = "gss_delete_sec_context";
   OM_uint32    min_stat;
   OM_uint32    maj_stat = GSS_S_FAILURE;
   int          rc       = 0;

   if ( pp_ctx==NULL || *pp_ctx==GSS_C_NO_CONTEXT ) {

      if ( p_fin_token!=NULL ) {
	 p_fin_token->value  = NULL;
	 p_fin_token->length = 0;
      }

   } else {

      BOGUS_INI_BUFFER( p_fin_token );
      BOGUS_INI_MINOR(  min_stat    );

      start_timer();
      maj_stat = (p_gssfp->gss_delete_sec_context)( &min_stat, pp_ctx, p_fin_token );
      timedelay = read_timer();
      rc += print_status( p_gssfp, GSS_DELETE_SEC_CONTEXT, maj_stat, min_stat );

      BOGUS_CHECK_BUFFER( p_fin_token, "final_token" );

      if ( maj_stat!=GSS_S_COMPLETE ) {

	 /* gssapi failure */
	 if ( p_fin_token!=NULL ) {
	    if ( p_fin_token->length==0 && p_fin_token->value!=NULL ) {
	       XVEB((V_SHOW, "%s(): didn't clear value part of output_token.\n", gss_call));
	    } else if ( p_fin_token->length>0 ) {
	       rc++;
	       XVEB((V_ERR, "%s() failed but returned output_token!\n", gss_call));
	    }
	 }

      } else { /* maj_stat==GSS_S_COMPLETE */

	 /* gssapi success */
	 if ( *pp_ctx!=GSS_C_NO_CONTEXT ) {
	    rc++;
	    XVEB((V_ERR, "%s() succeeded but didn't clear context handle!\n", gss_call));
	    *pp_ctx = GSS_C_NO_CONTEXT;
	 }

	 if ( p_fin_token!=NULL ) {
	    if ( p_fin_token->length==0 && p_fin_token->value!=NULL ) {
	       XVEB((V_SHOW, "%s(): didn't clear value part of output_token.\n", gss_call));
	    } else if ( p_fin_token->length>0 ) {
	       XVEB((V_SHOW, "%s() returned final context output_token.\n", gss_call));
	       print_buffer_head( 2, "output_token", p_fin_token );
	    }
	 }

      } /* endif  maj_stat==GSS_S_COMPLETE */

   }

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

   return(rc);

} /* delete_sec_context() */





/*
 * context_time()
 *
 * Description:
 *   wrapper of gssapi v1 call     gss_context_time()
 */
int
context_time( int            p_trclevel,    DLL_GSSFP_T  * p_gssfp,
	      gss_ctx_id_t   p_ctx,
	      OM_uint32	   * pp_lifetime,   OM_uint32    * pp_maj_stat )
{
   char       * gss_call = "gss_context_time";
   OM_uint32    min_stat;
   OM_uint32    maj_stat = GSS_S_FAILURE;
   int          rc       = 0;


   BOGUS_INI_MINOR( min_stat );

   start_timer();
   maj_stat = (p_gssfp->gss_context_time)( &min_stat, p_ctx, pp_lifetime );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_CONTEXT_TIME, maj_stat, min_stat );

   BOGUS_CHECK_VALUE( pp_lifetime, "lifetime" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( maj_stat==GSS_S_CONTEXT_EXPIRED ) {
	 if ( *pp_lifetime!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): failed to clear \"lifetime\" for GSS_S_CONTEXT_EXPIRED!\n",
			 gss_call ));
	    *pp_lifetime = 0;
	 }
      }

   } else {

      /* gssapi success */
      if ( *pp_lifetime==0 ) {
	 rc++;
	 XVEB((V_ERR, "%s(): returned lifetime=0 with GSS_S_COMPLETE!\n", gss_call));
	 /* always return GSS_S_CONTEXT_EXPIRED for lifetime==0  */
	 maj_stat = GSS_S_CONTEXT_EXPIRED;
      }

   }

   if ( p_trclevel>2 ) {
      print_lifetime( p_trclevel, "remaining context lifetime", *pp_lifetime );
   }

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

   return(rc);

} /* context_time() */




/*
 * init_sec_context()
 *
 * Description:
 *   wrapper of gssapi v1 call     gss_init_sec_context()
 */
int
init_sec_context( int             p_trclevel,    DLL_GSSFP_T  * p_gssfp,
		  int		* pp_num_call,
		  gss_cred_id_t	  p_cred,	 gss_ctx_id_t * pp_ctx,
		  gss_name_t      p_target_name, gss_OID        p_mech,
		  OM_uint32       p_in_flags,	 OM_uint32	p_in_lifetime,
		  gss_channel_bindings_t  p_chbind,
		  gss_buffer_t	  p_in_token,	 gss_OID      * pp_actual_mech,
		  gss_buffer_t	  p_out_token,
		  OM_uint32	* pp_out_flags,  OM_uint32    * pp_out_lifetime,
		  OM_uint32     * pp_maj_stat )
{
   char         * gss_call = "gss_init_sec_context";
   OM_uint32      min_stat;
   OM_uint32      maj_stat = GSS_S_FAILURE;
   gss_ctx_id_t	  orig_ctx;
   int	          numcall;
   int            rc       = 0;

   if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
      *pp_num_call = 0;
      BOGUS_INI_VALUE( pp_out_lifetime );
      BOGUS_INI_OID(   pp_actual_mech  );
      BOGUS_INI_VALUE( pp_out_flags    );
   }

   BOGUS_INI_BUFFER(   p_out_token     );
   BOGUS_INI_MINOR(    min_stat        );

   numcall = ( *pp_num_call>=0 && *pp_num_call<=3 ) ? *pp_num_call : 3;

   orig_ctx = *pp_ctx; /* save context_handle value for later verification */

   start_timer();
   maj_stat = (p_gssfp
        ->gss_init_sec_context)( &min_stat, p_cred, pp_ctx, p_target_name,
				 p_mech, p_in_flags, p_in_lifetime, p_chbind,
				 p_in_token, pp_actual_mech,
				 p_out_token, pp_out_flags, pp_out_lifetime );

   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_INIT_SEC_CONTEXT1 + numcall, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER( p_out_token,   "output_token" );
   BOGUS_CHECK_VALUE( pp_out_flags,   "rec_flags"    );
   BOGUS_CHECK_OID(   pp_actual_mech, "actual_mech"  ); /* not set until COMPLETE */

   if ( maj_stat==GSS_S_COMPLETE ) {
      BOGUS_CHECK_VALUE( pp_out_lifetime, "time_rec"    );
   }

   if ( orig_ctx!=GSS_C_NO_CONTEXT ) {
      /* For continuation calls, the context_handle value is read_only for the mechanism */
      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
	 /* this is a different error which is handled later */
      } else if ( orig_ctx!=*pp_ctx ) {
	 XVEB((V_ERR, "%s() Mutating context_handle on continuation call!!\n", gss_call));
      }
   }

   (*pp_num_call)++;

   if ( maj_stat==GSS_S_CONTINUE_NEEDED || maj_stat==GSS_S_COMPLETE ) {

      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
	 XVEB((V_ERR, "%s() succeded but returned GSS_C_NO_CONTEXT!\n", gss_call));
	 rc++;
      }

      /* gssapi requires further context-level token exchanges */
      if ( p_out_token->length==0 ) {
	 if ( p_out_token->value!=NULL ) {
	    XVEB((V_HIDE, "%s() doesn't clear value part of gss_buffer_desc.\n", gss_call));
	 }

	 if ( maj_stat==GSS_S_CONTINUE_NEEDED ) {
	    rc++;
	    XVEB((V_ERR, "%s() fails to return token with GSS_S_CONTINUE_NEEDED!\n", gss_call));
	    /* this situation is a fatal eror, no matter what */
	    maj_stat = GSS_S_FAILURE;
	 } else {
	    XVEB((V_OK, p_trclevel-2, "  %s() completes with %u-way authentication\n", gss_call,
	                   (Uint)((*pp_num_call) - 1)*2 ));
	 }
			   
      } else { /* p_out_token->length > 0 */

	 if ( maj_stat==GSS_S_COMPLETE ) {
	    XVEB((V_OK, p_trclevel-2, "  %s() expected to complete with %u-way authentication\n", gss_call,
			   (Uint)((*pp_num_call)*2 - 1) ));

	    if ( pp_out_flags!=NULL ) {
	       if ( ((*pp_out_flags)&GSS_C_CONF_FLAG)!=0 && ((*pp_out_flags)&GSS_C_INTEG_FLAG)==0 ) {
		  XVEB((V_ERR, "%s() indicates confidentiality but lacks integrity!\n", gss_call));
		  rc++;
	       }
	    }
	 }

	 /* memorize size limits for context-level tokens returned by acceptor */
	 if ( p_out_token->length > p_gssfp->ctx_ini_token_max ) { p_gssfp->ctx_ini_token_max = p_out_token->length; }
	 if ( p_gssfp->ctx_ini_token_min==0 )                    { p_gssfp->ctx_ini_token_min = p_out_token->length; }
	 if ( p_out_token->length < p_gssfp->ctx_ini_token_min ) { p_gssfp->ctx_ini_token_min = p_out_token->length; }

	 if ( options.sap_constraints!=FALSE 
	      &&  p_out_token->length > SNC_MAX_CONTEXT_TOKEN ) {
	    rc++;
	    XVEB((V_ERR, "%s() returns huge token: %lu bytes (max=%lu)\n",
			 gss_call, (Ulong) (p_out_token->length), (Ulong) SNC_MAX_CONTEXT_TOKEN ));
	 }

      } /* p_out_token->length > 0 */


      if ( pp_out_lifetime!=NULL ) {
	 /* if it is neither of our "initialization" values, then let's */
	 /* assume it's a real lifetime value                           */
	 if ( *pp_out_lifetime!=BOGUS_VALUE && *pp_out_lifetime!=0 && p_trclevel>=2 ) {
	    XVEB((V_SHOW, "  %s(): %s security context ", gss_call,
			 (maj_stat==GSS_S_COMPLETE) ? "established" : "incomplete" ));
	    print_lifetime( 2, "lifetime", *pp_out_lifetime );
	 }
      }

      rc += print_ctx_flags( p_trclevel - 1, gss_call, "  req_flags", &p_in_flags  );
      rc += print_ctx_flags( p_trclevel - 1, gss_call, "  ret_flags", pp_out_flags );

      if ( pp_actual_mech!=NULL ) {
	 if ( (void *)(*pp_actual_mech)!=BOGUS_POINTER
	      &&  ( *pp_actual_mech!=GSS_C_NO_OID || maj_stat==GSS_S_COMPLETE ) ) {
	    rc += validate_oid( p_gssfp, OID_MECHANISM, gss_call, "actual_mech", *pp_actual_mech );
	 }
      }

   } else { /* maj_stat!=GSS_S_COMPLETE  &&  maj_stat!=GSS_S_CONTINUE_NEEDED */

      /* gssapi failure */
      if ( GSS_ERROR(maj_stat)==0 ) {
	 /* when gss_init_sec_context() returns DUPLICATE_TOKEN or OLD_TOKEN         */
	 /* informatory status, then these should be paired with a fatal error code. */
	 if ( (maj_stat&GSS_S_DUPLICATE_TOKEN)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed to indicate GSS_S_FAILURE along with GSS_S_DUPLICATE_TOKEN!\n", gss_call));
	 }
	 if ( (maj_stat&GSS_S_OLD_TOKEN)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed to indicate GSS_S_FAILURE along with GSS_S_OLD_TOKEN!\n", gss_call));
	 }
	 maj_stat |= GSS_S_FAILURE;
      }

      if ( pp_actual_mech!=NULL ) {
	 if ( min_stat!=0 && *pp_actual_mech==GSS_C_NO_OID ) {
	    XVEB((V_SHOW, "WARNING: %s() failed and returned min_stat but no mech_oid!\n", gss_call ));
	 }
      }

      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {

	 if ( *pp_num_call>1 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed and cleared context_handle on interation call!\n", gss_call));
	 }

      } else {

	 if ( *pp_num_call==1 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed on initial call but didn't clear context handle!\n", gss_call));
	 }

      }

      if ( p_out_token->length>0 ) {
	 XVEB((V_HIDE, "%s() failed AND returned a context output token on call #%u.\n",
		      gss_call, (Uint) *pp_num_call ));
	 /* EXCEPTION: we don't release this buffer here -- this behaviour is permitted! */
      } 
      
   } /* maj_stat!=GSS_S_COMPLETE  &&  maj_stat!=GSS_S_CONTINUE_NEEDED */

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

   return(rc);

} /* init_sec_context() */




/*
 * accept_sec_context()
 *
 * Description:
 *   wrapper of gssapi v1 call     gss_accept_sec_context()
 */
int
accept_sec_context( int             p_trclevel,    DLL_GSSFP_T     * p_gssfp,
		    int		  * pp_num_call,
		    gss_ctx_id_t  * pp_ctx,	   gss_cred_id_t     p_cred,
		    gss_buffer_t    p_in_token,	   gss_channel_bindings_t  p_chbind,
		    gss_name_t	  * pp_src_name,   gss_OID	   * pp_mech_type,
		    gss_buffer_t    p_out_token,   OM_uint32	   * pp_out_flags,
		    OM_uint32	  * pp_lifetime,   gss_cred_id_t   * pp_deleg_cred,
		    OM_uint32     * pp_maj_stat )
{
   char          * gss_call = "gss_accept_sec_context";
   OM_uint32       min_stat;
   OM_uint32       maj_stat = GSS_S_FAILURE;
   gss_ctx_id_t    orig_ctx;
   int	           numcall  = 0;
   int             rc       = 0;

   if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
      *pp_num_call = 0;
      BOGUS_INI_VALUE(  pp_lifetime   );
      BOGUS_INI_OID(    pp_mech_type  );
      BOGUS_INI_VALUE(  pp_out_flags  );
      BOGUS_INI_NAME(   pp_src_name   );
      BOGUS_INI_CRED(   pp_deleg_cred );
   }
   BOGUS_INI_BUFFER( p_out_token   );
   BOGUS_INI_MINOR(  min_stat      );

   numcall = ( *pp_num_call>=0 && *pp_num_call<=3 ) ? *pp_num_call : 3;

   orig_ctx = *pp_ctx; /* save context_handle value for later verification */

   start_timer();
   maj_stat = (p_gssfp->
	 gss_accept_sec_context)( &min_stat, pp_ctx, p_cred, p_in_token,
				  p_chbind, pp_src_name, pp_mech_type,
				  p_out_token, pp_out_flags, pp_lifetime,
				  pp_deleg_cred );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_ACCEPT_SEC_CONTEXT1 + numcall, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER(  p_out_token,   "output_token" );
   BOGUS_CHECK_VALUE(   pp_out_flags,  "ret_flags"    );
   BOGUS_CHECK_OID(     pp_mech_type,  "mech_type"    ); /* not set until COMPLETE */
   BOGUS_CHECK_NAME(    pp_src_name,   "src_name"     ); /* not set until COMPLETE */
   BOGUS_CHECK_CRED(    pp_deleg_cred, "deleg_cred"   ); /* not set until COMPLETE */

   if (maj_stat==GSS_S_COMPLETE ) {
      BOGUS_CHECK_VALUE( pp_lifetime,   "lifetime"   );
   }

   if ( orig_ctx!=GSS_C_NO_CONTEXT ) {
      /* For continuation calls, the context_handle value is read_only for the mechanism */
      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
	 /* this is a different error which is handled later */
      } else if ( orig_ctx!=*pp_ctx ) {
	 XVEB((V_ERR, "%s() Mutating context_handle on continuation call!!\n", gss_call));
      }
   }

   (*pp_num_call)++;

   if ( maj_stat==GSS_S_COMPLETE  ||  maj_stat==GSS_S_CONTINUE_NEEDED ) {

      /* gssapi success */
      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
	 XVEB((V_ERR, "%s() succeded but returned GSS_C_NO_CONTEXT!\n", gss_call));
	 rc++;

	 if ( pp_out_flags!=NULL ) {
	    if ( ((*pp_out_flags)&GSS_C_CONF_FLAG)!=0 && ((*pp_out_flags)&GSS_C_INTEG_FLAG)==0 ) {
	       XVEB((V_ERR, "%s() indicates confidentiality but lacks integrity!\n", gss_call));
	       rc++;
	    }
	 }
      }

      /* gssapi requires further context-level token exchanges */
      if ( p_out_token->length==0 ) {
	 if ( p_out_token->value!=NULL ) {
	    XVEB((V_HIDE, "%s() doesn't clear value part of gss_buffer_desc.\n", gss_call));
	 }

	 if ( maj_stat==GSS_S_CONTINUE_NEEDED ) {
	    rc++;
	    XVEB((V_ERR, "%s() fails to return token with GSS_S_CONTINUE_NEEDED!\n", gss_call));
	    /* this situation is a fatal eror, no matter what */
	    maj_stat = GSS_S_FAILURE;
	 } else {
	    XVEB((V_OK, p_trclevel-2, "  %s() completes with %u-way authentication\n", gss_call,
	                   (Uint)((*pp_num_call)*2 - 1) ));
	 }
			   
      } else { /* p_out_token->length > 0 */

	 if ( maj_stat==GSS_S_COMPLETE ) {
	    XVEB((V_OK, p_trclevel-2, "  %s() expected to complete with %u-way authentication\n", gss_call,
			   (Uint)((*pp_num_call)*2) ));
	 }

	 /* memorize size limits for context-level tokens returned by acceptor */
	 if ( p_out_token->length > p_gssfp->ctx_acc_token_max ) { p_gssfp->ctx_acc_token_max = p_out_token->length; }
	 if ( p_gssfp->ctx_acc_token_min==0 )                    { p_gssfp->ctx_acc_token_min = p_out_token->length; }
	 if ( p_out_token->length < p_gssfp->ctx_acc_token_min ) { p_gssfp->ctx_acc_token_min = p_out_token->length; }

	 if ( options.sap_constraints!=FALSE 
	      &&  p_out_token->length > SNC_MAX_CONTEXT_TOKEN ) {
	    rc++;
	    XVEB((V_ERR, "%s() returns huge token: %lu bytes (max=%lu)\n",
			 gss_call, (Ulong) (p_out_token->length), (Ulong) SNC_MAX_CONTEXT_TOKEN ));
	 }

      } /* p_out_token->length > 0 */


      if ( pp_lifetime!=NULL ) {
	 /* if it is neither of our "initialization" values, then let's */
	 /* assume it's a real lifetime value                           */
	 if ( *pp_lifetime!=BOGUS_VALUE && *pp_lifetime!=0 && p_trclevel>=2 ) {
	    XVEB((V_SHOW, "  %s(): %s security context ", gss_call,
			 (maj_stat==GSS_S_COMPLETE) ? "established" : "incomplete" ));
	    print_lifetime( 2, "lifetime", *pp_lifetime );
	 }
      }

      rc += print_ctx_flags( p_trclevel - 1, gss_call, "  ret_flags", pp_out_flags  );

      if ( pp_mech_type!=NULL ) {
	 if ( (void *)(*pp_mech_type)!=BOGUS_POINTER
	      &&  ( *pp_mech_type!=GSS_C_NO_OID || maj_stat==GSS_S_COMPLETE ) ) {
	    rc += validate_oid( p_gssfp, OID_MECHANISM, gss_call, "mech_type", *pp_mech_type );
	 }
      }

   } else { /* maj_stat!=GSS_S_COMPLETE  &&  maj_stat!=GSS_S_CONTINUE_NEEDED */

      /* gssapi failure */
      if ( GSS_ERROR(maj_stat)==0 ) {
	 /* when gss_accept_sec_context() returns DUPLICATE_TOKEN or OLD_TOKEN       */
	 /* informatory status, then these should be paired with a fatal error code. */
	 if ( (maj_stat&GSS_S_DUPLICATE_TOKEN)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed to indicate GSS_S_FAILURE along with GSS_S_DUPLICATE_TOKEN!\n", gss_call));
	 }
	 if ( (maj_stat&GSS_S_OLD_TOKEN)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed to indicate GSS_S_FAILURE along with GSS_S_OLD_TOKEN!\n", gss_call));
	 }
	 maj_stat |= GSS_S_FAILURE;
      }

      if ( pp_mech_type!=NULL ) {
	 if ( min_stat!=0 && *pp_mech_type==GSS_C_NO_OID ) {
	    XVEB((V_SHOW, "WARNING: %s() failed and returned min_stat but no mech_oid!\n", gss_call ));
	 }
      }

      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {

	 if ( *pp_num_call>1 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed and cleared context_handle on interation call!\n", gss_call));
	 }

      } else {

	 if ( *pp_num_call==1 ) {
	    rc++;
	    XVEB((V_ERR, "%s() failed on initial call but didn't clear context handle!\n", gss_call));
	 }

      }

      if ( p_out_token->length>0 ) {
	 XVEB((V_HIDE, "%s() failed AND returned a context output token on call #%u.\n",
		      gss_call, (Uint) *pp_num_call ));
	 /* EXCEPTION: we don't release this buffer here -- this behaviour is permitted! */
      } 


   } /* maj_stat!=GSS_S_COMPLETE  &&  maj_stat!=GSS_S_CONTINUE_NEEDED */

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

   return(rc);

} /* accept_sec_context() */




/*
 * export_sec_context()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_export_sec_context()
 */
int
export_sec_context( int                p_trclevel,    DLL_GSSFP_T     * p_gssfp,
		    ctx_type_et	       p_usagetype,
		    gss_ctx_id_t     * pp_ctx,	      gss_buffer_t	p_export_token,
		    OM_uint32	     * pp_maj_stat )
{
   char	     * gss_call = "gss_export_sec_context";
   OM_uint32   min_stat;
   OM_uint32   maj_stat = GSS_S_FAILURE;
   int         rc       = 0;
   gss_call_et callno;

   callno = (p_usagetype==CTX_ACCEPTOR) ? GSS_EXPORT_SEC_CONTEXT_A : GSS_EXPORT_SEC_CONTEXT_I;

   BOGUS_INI_BUFFER( p_export_token );
   BOGUS_INI_MINOR(  min_stat       );

   start_timer();
   maj_stat = (p_gssfp->gss_export_sec_context)( &min_stat, pp_ctx, p_export_token );
   timedelay = read_timer();
   rc += print_status( p_gssfp, callno, maj_stat, min_stat );

   BOGUS_CHECK_BUFFER( p_export_token, "export_token" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( p_export_token->length>0 ) {
	 rc++;
	 XVEB((V_ERR, "%s() failed but returned exported context token!\n", gss_call));
	 rc += release_buffer( p_gssfp, p_export_token );

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

	 XVEB((V_HIDE, "%s() doesn't clear value part of gss_buffer_desc.\n", gss_call));

      }

      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
	 rc++;
	 XVEB((V_ERR, "%s() failed but deleted security context!\n", gss_call ));
      }

   } else {

      /* gssapi success */
      if ( p_export_token->length==0 || p_export_token->value==NULL ) {
	 rc++;
	 XVEB((V_ERR, "%s() succeeded but did NOT return an exported context token!\n", gss_call));
	 maj_stat = GSS_S_FAILURE;
      }

      if ( *pp_ctx!=GSS_C_NO_CONTEXT ) {
	 rc++;
	 XVEB((V_ERR, "%s() succeded but did NOT clean (delete?) security context handle!\n", gss_call));
	 rc += delete_sec_context( p_trclevel - 1, p_gssfp, pp_ctx, NULL, NULL );
      }

   }

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

   return(rc);

} /* export_sec_context() */




/*
 * import_sec_context()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_import_sec_context()
 */
int
import_sec_context( int             p_trclevel,	      DLL_GSSFP_T    * p_gssfp,
		    ctx_type_et	    p_usagetype,
		    gss_buffer_t    p_export_token,   gss_ctx_id_t   * pp_ctx,
		    OM_uint32     * pp_maj_stat )
{
   char       * gss_call = "gss_import_sec_context";
   OM_uint32    min_stat;
   OM_uint32    maj_stat = GSS_S_FAILURE;
   int          rc       = 0;
   gss_call_et  callno;

   callno = (p_usagetype==CTX_ACCEPTOR) ? GSS_IMPORT_SEC_CONTEXT_A : GSS_IMPORT_SEC_CONTEXT_I;

   BOGUS_INI_CONTEXT(  pp_ctx   );
   BOGUS_INI_MINOR(    min_stat );

   start_timer();
   maj_stat = (p_gssfp->gss_import_sec_context)( &min_stat, p_export_token, pp_ctx );
   timedelay = read_timer();
   rc += print_status( p_gssfp, callno, maj_stat, min_stat );

   BOGUS_CHECK_CONTEXT( pp_ctx, "context_handle" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( maj_stat==GSS_S_CONTEXT_EXPIRED ) {
	 rc++;
	 XVEB((V_ERR, "%s() returned GSS_S_CONTEXT_EXPIRED!\n", gss_call ));
      }

      if ( *pp_ctx!=GSS_C_NO_CONTEXT ) {
	 rc++;
	 XVEB((V_ERR, "%s() failed AND returned a non-NULL security context handle!\n", gss_call));
      }

   } else {

      /* gssapi success */
      if ( *pp_ctx==GSS_C_NO_CONTEXT ) {
	 rc++;
	 XVEB((V_ERR, "%s() succeded but did NOT return a security context handle!\n", gss_call));
	 maj_stat = GSS_S_FAILURE;
      }

   }

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

   return(rc);

} /* import_sec_context() */




/*
 * inquire_context()
 *
 * Description:
 *   wrapper of gssapi v2 call     gss_inquire_context()
 */
int
inquire_context( int		   p_trclevel,	   DLL_GSSFP_T * p_gssfp,
		 gss_ctx_id_t	   p_ctx,
		 gss_name_t	 * pp_src_name,	   gss_name_t  * pp_targ_name,
		 OM_uint32	 * pp_lifetime,	   gss_OID     * pp_mech_type,
		 OM_uint32	 * pp_ctx_flags,   int	       * pp_local_ini,
		 int		 * pp_open,	   OM_uint32   * pp_maj_stat )
{
   char       * gss_call = "gss_inquire_context";
   OM_uint32    min_stat;
   OM_uint32    maj_stat = GSS_S_FAILURE;
   int	        open     = 0;
   int          rc       = 0;

   BOGUS_INI_VALUE( pp_local_ini );
   BOGUS_INI_VALUE( pp_ctx_flags );
   BOGUS_INI_VALUE( pp_lifetime  );
   BOGUS_INI_OID(   pp_mech_type );
   BOGUS_INI_NAME(  pp_src_name  );
   BOGUS_INI_NAME(  pp_targ_name );
   BOGUS_INI_MINOR( min_stat     );
   BOGUS_INI_VALUE( &open        );

   start_timer();
   maj_stat = (p_gssfp->gss_inquire_context)( &min_stat, p_ctx, pp_src_name, pp_targ_name,
					      pp_lifetime, pp_mech_type, pp_ctx_flags,
					      pp_local_ini, &open );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_INQUIRE_CONTEXT, maj_stat, min_stat );

   BOGUS_CHECK_VALUE( &open,          "open"         );
   BOGUS_CHECK_VALUE( pp_local_ini,   "local_ini"    );
   BOGUS_CHECK_VALUE( pp_ctx_flags,   "ctx_flags"    );
   if ( open!=0 ) {
      BOGUS_CHECK_VALUE( pp_lifetime, "lifetime_rec" );
   }
   BOGUS_CHECK_OID(   pp_mech_type,   "mech_type"    );
   BOGUS_CHECK_NAME(  pp_src_name,    "src_name"     );
   BOGUS_CHECK_NAME(  pp_targ_name,   "targ_name"    );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      if ( pp_src_name!=NULL && *pp_src_name!=GSS_C_NO_NAME ) {
	 rc++;
	 XVEB((V_ERR, "%s() failed and returned src_name!\n", gss_call));
      }
      if ( pp_targ_name!=NULL && *pp_targ_name!=GSS_C_NO_NAME ) {
	 rc++;
	 XVEB((V_ERR, "%s() failed and returned targ_name!\n", gss_call));
      }
      if ( pp_mech_type!=NULL && *pp_mech_type!=GSS_C_NO_OID ) {
	 rc += validate_oid( p_gssfp, OID_MECHANISM, gss_call, "mech_type", *pp_mech_type );
      }

   } else { /* maj_stat==GSS_S_COMPLETE */

      /* gssapi success */
      if ( open!=0 ) {

	 if ( pp_src_name!=NULL  &&  *pp_src_name==GSS_C_NO_NAME ) {
	    rc++;
	    XVEB((V_ERR, "%s() succeeded but failed to return src_name!\n", gss_call));
	 }

	 if ( pp_targ_name!=NULL  &&  *pp_targ_name==GSS_C_NO_NAME ) {
	    rc++;
	    XVEB((V_ERR, "%s() succeeded but failed to return targ_name!\n", gss_call));
	 }

	 if ( pp_mech_type!=NULL  &&  *pp_mech_type==GSS_C_NO_OID ) {
	    rc++;
	    XVEB((V_ERR, "%s() succeeded but failed to return mech_type!\n", gss_call));
	 } /* pp_mech_type!=NULL */

      }

      rc += print_ctx_flags( p_trclevel - 1, gss_call, "ret_flags", pp_ctx_flags  );

      if ( pp_mech_type!=NULL  &&  *pp_mech_type!=GSS_C_NO_OID ) {
	 rc += validate_oid( p_gssfp, OID_MECHANISM, gss_call, "mech_type", *pp_mech_type);
      }

   } /* maj_stat==GSS_S_COMPLETE */

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

   return(rc);

} /* inquire_context() */








static char * ctx_flag_names[] = {
      "DELEG",
      "MUTUAL",
      "REPLAY",
      "SEQUENCE",
      "CONF",
      "INTEG",
      "ANON",
      "PROT_READY",
      "TRANS",
      (char *)0
};

/*
 * print_ctx_flags()
 *
 * Description:
 *   Verbose description of a "rec_flags"/"req_flags"
 *   gssapi security context attributes value
 *
 * Returns:
 *   rc   error count (for indicated but unknown flag bits)
 *
 */
int
print_ctx_flags( int   p_trclevel, char * p_gss_call, char * p_parm_name, OM_uint32 * pp_flags )
{
   char         tmpbuf[256];
   OM_uint32    flags;
   Uint         i, cnt;
   int		rc      = 0;

   if ( pp_flags==NULL )
      return(rc);

   flags = ~((OM_uint32)KNOWN_GSS_CONTEXT_FLAGS);

   if ( (*pp_flags & flags) != 0  &&  p_gss_call!=NULL ) {
      XVEB((V_SHOW, "WARNING: %s() returns unknown context attributes: 0x%08lx!\n",
		   p_gss_call, (Ulong) *pp_flags));
      /* rc++; */ /* a gssapi mechanism SHOULD clear all undefined flag bits */
   }

   if ( p_trclevel<1 )
      return(rc);

   flags = *pp_flags;
   tmpbuf[0]= '\0';

   for ( cnt=0 , i=0 ; ctx_flag_names[i]!=0 ; i++ ) {
      if ( (flags&0x01)!=0 ) {
	 if (cnt==0) {
	    strcat(tmpbuf, "(");
	 } else {
	    strcat(tmpbuf, ",");
	 }
	 strcat(tmpbuf, ctx_flag_names[i]);
	 cnt++;
      }
      flags /= 2;
   }

   if ( tmpbuf[0]=='\0' ) {
      strcat(tmpbuf,"(no flags)");
   } else {
      strcat(tmpbuf, ")");
   }

   if ( p_gss_call==NULL ) {
      XVEB((V_SHOW, "%s = %s\n", p_parm_name, tmpbuf));
   } else {
      XVEB((V_SHOW, "%s(): %s = %s\n", p_gss_call, p_parm_name, tmpbuf));
   }

   return(rc);

} /* print_ctx_flags() */




/*
 * ctx_flags_statistics()
 *
 * Description:
 *   keep a record about the security context attributes that were returned/granted
 *   (subset from the requested set, except for GSS_C_TRANS_FLAG). 
 *
 *   Specifically, keep a record whether attributes were denied/donated/enforced
 */
void
ctx_flags_statistics( DLL_GSSFP_T  * p_gssfp,
		      OM_uint32      req_flags,   OM_uint32   ini_flags,   OM_uint32   acc_flags )
{

   Uint32   expected_flags = req_flags | GSS_C_TRANS_FLAG;

   p_gssfp->ctx_flags_inconsistent |= ((ini_flags ^ acc_flags) & SYMMETRIC_GSS_CONTEXT_FLAGS);
   p_gssfp->ctx_flags_donated      |= ((ini_flags ^ req_flags) & ini_flags & KNOWN_GSS_CONTEXT_FLAGS);
   p_gssfp->ctx_flags_denied       |= ((ini_flags ^ expected_flags) & expected_flags & KNOWN_GSS_CONTEXT_FLAGS);
   p_gssfp->ctx_flags_sometimes    |= ((ini_flags | acc_flags ) & KNOWN_GSS_CONTEXT_FLAGS);
   p_gssfp->ctx_flags_requested    |= (req_flags & KNOWN_GSS_CONTEXT_FLAGS );

   p_gssfp->ctx_flags_always  = ( (p_gssfp->ctx_flags_sometimes) & ~(p_gssfp->ctx_flags_denied) );

   return;

} /* ctx_flags_statistics() */




/*
 * ctx_lifetime_statistics()
 *
 *
 */
void
ctx_lifetime_statistics( DLL_GSSFP_T  * p_gssfp,
			 OM_uint32      p_ctx_lifetime_ini,   OM_uint32 p_ctx_lifetime_acc,  
			 OM_uint32      p_cred_lifetime_ini,  OM_uint32 p_cred_lifetime_acc )
{
   OM_uint32    lifetime_diff;

   p_gssfp->ctx_lifetime_counter  ++;
   p_gssfp->cred_lifetime_counter ++;

   lifetime_diff = (p_ctx_lifetime_ini>p_ctx_lifetime_acc)
		   ? p_ctx_lifetime_ini - p_ctx_lifetime_acc : p_ctx_lifetime_acc - p_ctx_lifetime_ini ;

   p_gssfp->ctx_lifetime_stat_end = (Ulong) time(NULL);
   if ( p_gssfp->ctx_lifetime_stat_begin==0 ) {
      p_gssfp->ctx_lifetime_stat_begin = p_gssfp->ctx_lifetime_stat_end;
      p_gssfp->cred_lifetime_ini_final = p_gssfp->cred_lifetime_ini_initial = p_cred_lifetime_ini;
      p_gssfp->cred_lifetime_acc_final = p_gssfp->cred_lifetime_acc_initial = p_cred_lifetime_acc;
      p_gssfp->ctx_lifetime_ini_final  = p_gssfp->ctx_lifetime_ini_initial  = p_ctx_lifetime_ini;
      p_gssfp->ctx_lifetime_acc_final  = p_gssfp->ctx_lifetime_acc_initial  = p_ctx_lifetime_acc;
      p_gssfp->ctx_lifetime_ini_min    = p_ctx_lifetime_ini;
      p_gssfp->ctx_lifetime_acc_min    = p_ctx_lifetime_acc;
   }

   p_gssfp->cred_lifetime_acc_final = p_cred_lifetime_acc;

   /* record final value and unexpected increase of context lifetime on initiator side */
   if ( p_ctx_lifetime_ini > p_gssfp->ctx_lifetime_ini_final
        && (p_ctx_lifetime_ini - p_gssfp->ctx_lifetime_ini_final)>1 ) {
      p_gssfp->ctx_lifetime_ini_growth += (p_ctx_lifetime_ini - p_gssfp->ctx_lifetime_ini_final);
   }
   p_gssfp->ctx_lifetime_ini_final = p_ctx_lifetime_ini;

   if ( p_ctx_lifetime_ini < p_gssfp->ctx_lifetime_ini_min ) {
      p_gssfp->ctx_lifetime_ini_min = p_ctx_lifetime_ini;
   }
   if ( p_ctx_lifetime_ini > p_gssfp->ctx_lifetime_ini_max ) {
      p_gssfp->ctx_lifetime_ini_max = p_ctx_lifetime_ini;
   }

   /* record final value and unexpected increase of credentials lifetime on initiator side */
   if ( p_cred_lifetime_ini > p_gssfp->cred_lifetime_ini_final ) {
      p_gssfp->cred_lifetime_ini_growth += p_cred_lifetime_ini;
   }
   p_gssfp->cred_lifetime_ini_final = p_cred_lifetime_ini;

   /* record final value and unexpected increase of context lifetime on acceptor side */
   if ( p_ctx_lifetime_acc > p_gssfp->ctx_lifetime_acc_final
        && (p_ctx_lifetime_acc - p_gssfp->ctx_lifetime_acc_final)>1 ) {
      p_gssfp->ctx_lifetime_acc_growth += (p_ctx_lifetime_acc - p_gssfp->ctx_lifetime_acc_final);
   }
   p_gssfp->ctx_lifetime_acc_final = p_ctx_lifetime_acc;

   if ( p_ctx_lifetime_acc < p_gssfp->ctx_lifetime_acc_min ) {
      p_gssfp->ctx_lifetime_acc_min = p_ctx_lifetime_acc;
   }
   if ( p_ctx_lifetime_acc > p_gssfp->ctx_lifetime_acc_max ) {
      p_gssfp->ctx_lifetime_acc_max = p_ctx_lifetime_acc;
   }

   /* record final value and unexpected increase of credentials lifetime on acceptor side */
   if ( p_cred_lifetime_acc > p_gssfp->cred_lifetime_acc_final ) {
      p_gssfp->cred_lifetime_acc_growth += p_cred_lifetime_acc;
   }
   p_gssfp->cred_lifetime_acc_final = p_cred_lifetime_acc;

   /* check consistency for initiator and acceptor context lifetimes */
   if ( p_ctx_lifetime_ini==GSS_C_INDEFINITE  &&  p_ctx_lifetime_acc==GSS_C_INDEFINITE ) {

      /* OK: both, initiator and acceptor are indicating INDEFINITE lifetime */
      p_gssfp->ctx_lifetime_indefinite++;

   } else if ( p_ctx_lifetime_ini==GSS_C_INDEFINITE || p_ctx_lifetime_acc==GSS_C_INDEFINITE ) {

      /* BAD: one is indicating INDEFINITE, the other is indicating limited lifetime */
      /* this will very probably prevent correct detection of context expiration     */
      /* no further lifetime evaluation(s)                                           */
      p_gssfp->ctx_lifetime_inconsistent++;

   } else {

      /* OK: limited security context lifetime: how will this affect security context refresh */
      /*     at the application level (in relation to the credentials lifetime)               */
      if ( lifetime_diff > 20 ) {
	 p_gssfp->ctx_lifetime_skew++;
      }

      if ( p_cred_lifetime_ini!=GSS_C_INDEFINITE ) {
	 if ( p_cred_lifetime_ini < p_ctx_lifetime_ini ) {
	    if ( (p_ctx_lifetime_ini - p_cred_lifetime_ini) >= SNC_MAX_CRCTX_LIFETIME_DIFF ) {
	       p_gssfp->cred_ctx_lifetime_ini_mismatch++;
	    }
	 }
      }

      if ( p_cred_lifetime_acc!=GSS_C_INDEFINITE ) {
	 if ( p_cred_lifetime_acc < p_ctx_lifetime_acc ) {
	    if ( (p_ctx_lifetime_acc - p_cred_lifetime_acc) >= SNC_MAX_CRCTX_LIFETIME_DIFF ) {
	       p_gssfp->cred_ctx_lifetime_acc_mismatch++;
	    }
	 }
      }

   }

   return;

} /* ctx_lifetime_statistics() */




/*
 * establish_context()
 *
 * Description:
 *   generic gssapi security context establishment loop
 *
 * Returns:
 *   both ends of the established security context and all of the requested
 *   resulting objects&attributes from both ends
 */
int
establish_context(
		   int		       p_trclevel,	 OM_uint32	     p_flags_req,
		   OM_uint32	       p_lifetime_req,	 gss_OID	     p_ini_mech,
		   struct ctx_desc   * p_ini,		 struct ctx_desc   * p_acc,
		   gss_OID	     * pp_ini_mech,	 gss_OID	   * pp_acc_mech,
		   OM_uint32	     * pp_maj_stat )
{
   char	           * this_Call = "establish_context";
   gss_buffer_desc   ini_token;
   gss_buffer_desc   acc_token;
   gss_buffer_t	     pacc_token;
   OM_uint32	     maj_stat	 = GSS_S_FAILURE;
   OM_uint32	     maj_stat2   = GSS_S_FAILURE;
   OM_uint32	     missing_flags;
   OM_uint32	     donated_flags;
   OM_uint32	     flagmask;
   int		     ini_numcall = 0;
   int		     acc_numcall = 0;
   int		     rc		 = 0;
   int		     dsp_ini_lifetime = 0;
   int		     dsp_acc_lifetime = 0;

   ini_token.value  = NULL;
   ini_token.length = 0;
   acc_token.value  = NULL;
   acc_token.length = 0;

   p_ini->flags_req    = p_flags_req;
   p_ini->lifetime_req = p_lifetime_req;
   p_acc->flags_req    = p_flags_req;
   p_acc->lifetime_req = p_lifetime_req;

   /* initialize some return values in case we fail immediately  */
   /* during first call to gss_init_sec_context().  Later on all */
   /* return values will have non-critical values. (if any)      */
   if ( p_acc->src_name!=GSS_C_NO_NAME ) {
      XVEB((V_BUG, "%s(): Huh?! p_acc->src_name = %p !\n", this_Call, p_acc->src_name));
      p_acc->src_name = GSS_C_NO_NAME;
   }

   p_ini->ctx = p_acc->ctx = GSS_C_NO_CONTEXT; /* on your marks ... */

   p_ini->type = CTX_INITIATOR;
   p_acc->type = CTX_ACCEPTOR;

   for(;;) {

      if ( ini_numcall==0  &&  (options.zap_trailing_nuls!=FALSE ) ) {
	 pacc_token = GSS_C_NO_BUFFER; /* OpenVision GSS-API bug */
      } else {
	 pacc_token = &acc_token;
      }


      rc += init_sec_context( verbose_level, p_ini->gssfp, &ini_numcall,
			      p_ini->cred, &(p_ini->ctx), p_ini->targ_name,
			      p_ini_mech, p_flags_req, p_lifetime_req,
			      GSS_C_NO_CHANNEL_BINDINGS,
			      pacc_token, pp_ini_mech, &ini_token,
			      &(p_ini->flags), &(p_ini->lifetime),
			      &maj_stat );

      rc += release_buffer( p_acc->gssfp, &acc_token );

      if ( maj_stat==GSS_S_COMPLETE ) {

	 if ( ini_token.length==0 ) {
	    /* this was the final message exchange, break out here! */
	    break;
	 }

      } else if ( maj_stat!=GSS_S_CONTINUE_NEEDED ) {

	 /* all failures abort here and await cleanup */
	 break;

      }


      rc += accept_sec_context( verbose_level, p_acc->gssfp, &acc_numcall,
				&(p_acc->ctx), p_acc->cred, &ini_token,
				GSS_C_NO_CHANNEL_BINDINGS, &(p_acc->src_name),
				pp_acc_mech, &acc_token, &(p_acc->flags),
				&(p_acc->lifetime), NULL, &maj_stat2 );

      rc += release_buffer( p_ini->gssfp, &ini_token );

      if ( maj_stat2==GSS_S_COMPLETE ) {

	 if ( acc_token.length==0 ) {
	    /* this was the final message exchange, break out here! */
	    break;
	 }

      } else if ( maj_stat2!=GSS_S_CONTINUE_NEEDED ) {

	 /* DebugBreak(); */
	 /* all failures abort here and await cleanup */
	 break;

      }

      if ( ini_numcall>3 ) {
	 rc++;
	 XVEB((V_ERR, "%s(): Something is wrong! we've already done 4 roundtrips!\n", this_Call));
	 maj_stat2=GSS_S_FAILURE;
	 break;
      }

   } /* for(;;)  context establishment loop */

   
   if ( maj_stat==GSS_S_COMPLETE && maj_stat2==GSS_S_COMPLETE ) {

      /* OK, everything went smooth! */
      /* no let's do some sanity / consistency checks on the return values! */

      /* (1)  if both calls return a mechanism OID, it MUST be the same! */
      if ( pp_acc_mech!=NULL && pp_ini_mech!=NULL ) {
	 if ( compare_oid(*pp_acc_mech, *pp_ini_mech)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): differing mechanism OIDs for Initiator and Acceptor:\n", this_Call));
	    print_oid( 2, "initiator mech_type", *pp_ini_mech );
	    print_oid( 2, "acceptor  mech_type", *pp_acc_mech );
	 }
      }

      /* count the number of successfully established security contexts  */
      (p_ini->gssfp)->context_counter++;

      /**************************************************************/
      /* analyze information about security context attributes and  */
      /* availability of message protection services on the context */
      /**************************************************************/

      ctx_flags_statistics( (p_ini->gssfp), p_flags_req, (p_ini->flags), (p_acc->flags) );
      (p_ini->gssfp)->context_conf     += ((p_ini->flags&GSS_C_CONF_FLAG) !=0 ? 1 : 0 );
      (p_ini->gssfp)->context_integ    += ((p_ini->flags&GSS_C_INTEG_FLAG)!=0 ? 1 : 0 );
      (p_ini->gssfp)->context_xferable += ((p_ini->flags&GSS_C_TRANS_FLAG)!=0 ? 1 : 0 );

      if ( (p_ini->gssfp) != (p_acc->gssfp) ) {
	 ctx_flags_statistics( (p_acc->gssfp), p_flags_req, (p_ini->flags), (p_acc->flags) );
	 (p_acc->gssfp)->context_counter++;
	 (p_acc->gssfp)->context_conf     += ((p_acc->flags&GSS_C_CONF_FLAG )!=0 ? 1 : 0 );
	 (p_acc->gssfp)->context_integ    += ((p_acc->flags&GSS_C_INTEG_FLAG)!=0 ? 1 : 0 );
         (p_acc->gssfp)->context_xferable += ((p_acc->flags&GSS_C_TRANS_FLAG)!=0 ? 1 : 0 );
      }

      /*****************************************************************************/
      /* different gssapi implementations may implement replay/sequence protection */
      /* in a different way and therefore different pairing                        */
      /* i.e. simple implementations may use a single&simple sequence number and   */
      /*      therefore enforce sequencing when replay protection is requested     */
      /*      more sophisticated implementations may be using a replay cache and   */
      /*      offer replay and sequencing protection independently                 */
      /*****************************************************************************/
      if ( p_ini->gssfp != p_acc->gssfp ) {
	  int   iniprot = ((p_ini->flags)&(GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG))!=0 ? TRUE : FALSE ;
	  int   accprot = ((p_acc->flags)&(GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG))!=0 ? TRUE : FALSE ;

	  if ( iniprot != accprot ) {
	      XVEB((V_ERR, "%s(): differing sequence/replay protection for Initiator and Acceptor:\n", this_Call));
	      print_ctx_flags( 2, "init_sec_context",   "ret_flags", &(p_ini->flags) );
	      print_ctx_flags( 2, "accept_sec_context", "ret_flags", &(p_acc->flags) );
	  }
      }

      flagmask = (p_ini == p_acc ) ? SYMMETRIC_GSS_CONTEXT_FLAGS : SYMMETRIC_GSS_CONTEXT_FLAGS_2_LIBS;

      /* (2) all (most) attributes returned by both calls need to be consistent */
      if ( ((p_ini->flags) & flagmask) != ((p_acc->flags) & flagmask) ) {
	 rc++;
	 XVEB((V_ERR, "%s(): differing context attributes for Initiator and Acceptor:\n", this_Call));
	 print_ctx_flags( 2, "init_sec_context",   "ret_flags", &(p_ini->flags) );
	 print_ctx_flags( 2, "accept_sec_context", "ret_flags", &(p_acc->flags) );
      }

      /* (3) check whether the received flags differ from those requested */
      missing_flags = ( p_flags_req ^ (p_ini->flags) ) & p_flags_req;
      donated_flags = ( p_flags_req ^ (p_ini->flags) ) & (p_ini->flags);

      if ( missing_flags!=0 ) {
	 print_ctx_flags( p_trclevel, "gss_init_sec_context", "  requested context attributes", &p_flags_req );
	 print_ctx_flags( p_trclevel, "gss_init_sec_context", "  missing   context attributes", &missing_flags );
      }

      if ( donated_flags!=0 ) {
         print_ctx_flags( p_trclevel-1, "gss_init_sec_context", "  donated   context attributes", &donated_flags );
      }

      if ( (p_flags_req&(GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG))!=0
	   && ((p_ini->flags)&(GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG))==0 ) {
	 /* this gssapi mech seems to be lacking message protection services */
	 missing_msg_prot++;
      }


      /*******************************************************************/
      /* analyze lifetimes of the resulting security context (both ends) */
      /* and the lifetime of the credentials that were involved in       */
      /* establishing this security context				 */
      /*******************************************************************/

      ctx_lifetime_statistics( p_ini->gssfp, p_ini->lifetime, p_acc->lifetime,
			       p_ini->cred_lifetime, p_acc->cred_lifetime );
      if ( p_ini->gssfp != p_acc->gssfp ) {
	 ctx_lifetime_statistics( p_acc->gssfp, p_ini->lifetime, p_acc->lifetime,
				  p_ini->cred_lifetime, p_acc->cred_lifetime );
      }
      /* (4) if both calls return context lifetimes, these should align! */
      if ( (p_ini->lifetime)==GSS_C_INDEFINITE && (p_acc->lifetime)!=GSS_C_INDEFINITE) {

	 rc++;
	 XVEB((V_ERR, "%s():", this_Call));
	 print_lifetime( 2, "  one-sided acceptor context lifetime limit", p_acc->lifetime );
	 dsp_acc_lifetime = 1;

      } else if ( p_ini->lifetime!=GSS_C_INDEFINITE && p_acc->lifetime==GSS_C_INDEFINITE ) {

	 rc++;
	 XVEB((V_ERR, "%s():", this_Call));
	 print_lifetime( 2, "  one-sided initiator context lifetime limit", p_ini->lifetime );
	 dsp_ini_lifetime = 1;

      } else {

	 if ( p_ini->lifetime != p_acc->lifetime ) {
	    OM_uint32  diff;

	    diff = (p_ini->lifetime > p_acc->lifetime )
		   ? (p_ini->lifetime - p_acc->lifetime) : (p_acc->lifetime - p_ini->lifetime);
	    if ( diff > MAX_CTX_LIFETIME_SKEW ) {
	       /* we tolerate a 20 seconds difference in lifetime between        */
	       /* initiator and acceptor -- busy machine and slow smartcards :(  */
	       rc++;
	       XVEB((V_ERR, "%s(): Acceptor and Initiator context lifetimes differ:\n", this_Call));
	       print_lifetime( 2, "initiator context lifetime", p_ini->lifetime );
	       print_lifetime( 2, "acceptor context lifetime",  p_acc->lifetime );
	       dsp_acc_lifetime = dsp_ini_lifetime = 1;
	    }

	 }

      }

      /* display security context lifetimes if it was requested */
      if ( p_trclevel>=1  && dsp_ini_lifetime==0 ) {
	 XVEB((V_SHOW, "  %s() completed, ", this_Call));
	 print_lifetime( 2, "initiator lifetime", p_ini->lifetime );
      }

      if ( p_trclevel>=1  && dsp_acc_lifetime==0 ) {
	 XVEB((V_SHOW, "  %s() completed, ", this_Call));
	 print_lifetime( 2, "acceptor  lifetime", p_acc->lifetime );
      }

      print_timing_data( p_trclevel-1, p_ini->gssfp, "initiator context performance",
			 GSS_INIT_SEC_CONTEXT1, GSS_INIT_SEC_CONTEXT4, TRUE );

      print_timing_data( p_trclevel-1, p_acc->gssfp, "acceptor  context performance",
			 GSS_ACCEPT_SEC_CONTEXT1, GSS_ACCEPT_SEC_CONTEXT4, TRUE );

   } else { /* maj_stat!=GSS_S_COMPLETE  ||  maj_stat2!=GSS_S_COMPLETE */

      /* there was a gssapi failure.					      */
      /* Release all GSS-API dynamic objects here that may have been created. */
      rc += delete_sec_context( p_trclevel-1, p_ini->gssfp, &(p_ini->ctx), NULL, NULL );
      rc += delete_sec_context( p_trclevel-1, p_acc->gssfp, &(p_acc->ctx), NULL, NULL );
      if ( (p_acc->src_name)!=NULL   ) { release_name( p_acc->gssfp, &(p_acc->src_name) );   }
      if ( pp_ini_mech!=NULL   ) { *pp_ini_mech = GSS_C_NO_OID;            }
      if ( pp_acc_mech!=NULL   ) { *pp_acc_mech = GSS_C_NO_OID;            }

   } /* maj_stat!=GSS_S_COMPLETE */

   rc += release_buffer( p_acc->gssfp, &acc_token ); /* play safe: don't leak buffers */
   rc += release_buffer( p_ini->gssfp, &ini_token ); /* play safe: don't leak buffers */

   if ( pp_maj_stat!=NULL ) {
       if ( GSS_ERROR(maj_stat) || maj_stat2==GSS_S_COMPLETE ) {
	   (*pp_maj_stat) = maj_stat;
       } else if ( GSS_ERROR(maj_stat2) || maj_stat==GSS_S_COMPLETE ) {
	   (*pp_maj_stat) = maj_stat2;
       } else {
	   (*pp_maj_stat) = (maj_stat!=GSS_S_CONTINUE_NEEDED) ? maj_stat : maj_stat2;
       }
   }

   return(rc);

} /* establish_context() */




/*
 * sap_establish_context()
 *
 * Description:
 *   Establish a security context using the specified pair of
 *   SNC-typical credentials with the given context attributes,
 *   lifetime and target name.
 *
 * Returns:
 *   initiator and acceptor handle of established security context
 *   all other requested attributes of this security context
 */
int
sap_establish_context( int	            p_trclevel,       OM_uint32		  p_flags,
		       OM_uint32	    p_lifetime,
		       DLL_GSSFP_T	  * p_gssfp_ini,      DLL_GSSFP_T       * p_gssfp_acc,
		       struct ctx_desc   ** pp_ini,           struct ctx_desc  ** pp_acc,
		       sc_variants_et	    p_ini_cred_type,  sc_variants_et      p_acc_cred_type,
		       void		  * p_target,	      size_t		  p_target_len,
		       OM_uint32	  * pp_maj_stat )
{
   char           * this_Call    = "sap_establish_context";
   gss_OID	    ini_mech_oid = GSS_C_NO_OID;
   gss_OID	    acc_mech_oid = GSS_C_NO_OID;
   OM_uint32	    maj_stat     = GSS_S_COMPLETE;
   OM_uint32	    maj_stat_c	 = GSS_S_COMPLETE;
   int		    rc           = 0;
   int              rci;

   *pp_ini = create_ctx_desc( p_gssfp_ini, "initiator",
			      p_gssfp_ini->initiator, p_gssfp_ini->initiator_len,
			      p_gssfp_ini->can_nametype,
			      p_gssfp_acc->acceptor,  p_gssfp_acc->acceptor_len,
			      p_gssfp_acc->can_nametype );

   *pp_acc = create_ctx_desc( p_gssfp_acc, "acceptor",
			      p_gssfp_ini->initiator, p_gssfp_ini->initiator_len,
			      p_gssfp_ini->can_nametype,
			      p_gssfp_acc->acceptor,  p_gssfp_acc->acceptor_len,
			      p_gssfp_acc->can_nametype );

   if ( *pp_ini==NULL || *pp_acc==NULL ) {
      rc++;
      XVEB((V_ERR, "%s(): creation of struct ctx_desc failed--aborting!\n", this_Call));
      goto error;
   }

   if ( p_target!=NULL && p_target_len!=0 ) {
      rc += import_and_canonicalize( verbose_level, (*pp_ini)->gssfp,
				     p_target, p_target_len,
				     ((*pp_ini)->gssfp)->can_nametype,
				     &((*pp_ini)->targ_name), &maj_stat );
      if ( maj_stat!=GSS_S_COMPLETE || (*pp_ini)->targ_name==GSS_C_NO_NAME )
	 goto error;
   }

   (*pp_ini)->flags_req    = p_flags;
   (*pp_ini)->lifetime_req = p_lifetime;
   (*pp_ini)->cred_type    = p_ini_cred_type;
   (*pp_acc)->cred_type	   = p_acc_cred_type;

   rc += sap_acquire_cred( p_trclevel, (*pp_ini)->gssfp, (*pp_ini)->cred_type,
			   GSS_C_INITIATE, (*pp_ini)->ini_name,
			   (*pp_ini)->ini_prname.value, (*pp_ini)->ini_prname.length,
			   ((*pp_ini)->gssfp)->can_nametype, &((*pp_ini)->cred_lifetime),
			   &((*pp_ini)->cred), NULL );

   if ( (*pp_ini)->cred!=GSS_C_NO_CREDENTIAL ) {
      rc += sap_acquire_cred( p_trclevel, (*pp_acc)->gssfp, (*pp_acc)->cred_type,
			      GSS_C_ACCEPT, (*pp_acc)->acc_name,
			      (*pp_acc)->acc_prname.value, (*pp_acc)->acc_prname.length,
			      GSS_C_NO_OID, &((*pp_acc)->cred_lifetime),
			      &((*pp_acc)->cred), NULL );

      if ( (*pp_acc)->cred!=GSS_C_NO_CREDENTIAL ) {

	 rc += establish_context( p_trclevel, p_flags, p_lifetime,
				  p_gssfp_ini->mech, *pp_ini, *pp_acc,
				  &ini_mech_oid, &acc_mech_oid, &maj_stat_c );

	 if ( (*pp_acc)->ctx!=GSS_C_NO_CONTEXT
	      &&  ((*pp_acc)->flags&GSS_C_MUTUAL_FLAG)!=0 ) {
	    rci = verify_same_aclkey( p_trclevel, "",
				      "target_name",     TRUE,  (*pp_ini)->gssfp, (*pp_ini)->targ_name,
				      "acceptor's name", FALSE, (*pp_acc)->gssfp, (*pp_acc)->acc_name );
	    rc += rci;
	    if ( rci>0 ) {
		XVEB((V_ERR, "%s(): Aborting test!\n", this_Call));
		if ( p_gssfp_ini->context_counter>0 ) {
		    p_gssfp_ini->context_counter--;
		}
		goto error;
	    }
	 }

	 if ( (*pp_ini)->ctx!=GSS_C_NO_CONTEXT
	      &&  ((*pp_acc)->src_name)!=GSS_C_NO_NAME ) {
	    rci = verify_same_aclkey( p_trclevel, "",
				    "initiator's name",   TRUE,  (*pp_ini)->gssfp, (*pp_ini)->ini_name,
				    "authenticated src_name", FALSE, (*pp_acc)->gssfp, (*pp_acc)->src_name );
	    rc += rci;
	    if ( rci>0 ) {
		XVEB((V_ERR, "%s(): Aborting test!\n", this_Call));
		if ( p_gssfp_ini->context_counter>0 ) {
		    p_gssfp_ini->context_counter--;
		}
		goto error;
	    }
	 }
      
      }
   
   }

   rc += release_cred( (*pp_ini)->gssfp, &((*pp_ini)->cred) );
   rc += release_cred( (*pp_acc)->gssfp, &((*pp_acc)->cred) );

   if ( *pp_ini==NULL || (*pp_ini)->ctx==GSS_C_NO_CONTEXT
        || *pp_acc==NULL || (*pp_acc)->ctx==GSS_C_NO_CONTEXT ) {
error:
      rc += release_ctx_desc( pp_ini );
      rc += release_ctx_desc( pp_acc );

   }

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

   return(rc);

} /* sap_establish_context() */


  


/*
 * sap_try_context()
 *
 * Description:
 *   Check SNC-typical credential combinations and possibilities to create
 *   multiple parallel security contexts.  All security contexts
 *   and credentials are released upon return.
 *
 *   The validity of the security context is verified by performing
 *   a common set of message protection/unprotection and context transfer
 *   operations.
 *
 * Returns:
 *   rc    error count
 *
 */
int
sap_try_context( int              p_trclevel,	   OM_uint32       p_flags,
		 OM_uint32        p_lifetime,
		 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,
		 size_t		  p_count )
{
   char		     *	this_Call   = "sap_try_context";
   OM_uint32            maj_stat    = GSS_S_FAILURE;
   struct ctx_desc  **	x_acc	    = NULL;
   struct ctx_desc  **	x_ini	    = NULL;
   struct tmsg_job   *  job	    = NULL;
   size_t		count, i;
   int			success	    = FALSE;
   int			rc	    = 0;

   x_acc = (struct ctx_desc **) calloc( p_count, sizeof(void *) );
   x_ini = (struct ctx_desc **) calloc( p_count, sizeof(void *) );
   
   if ( x_acc==NULL  ||  x_ini==NULL ) {

      XVEB((V_ERR, "%s(): calloc( %lu, %u ) failed!\n",
		   this_Call, (Ulong) p_count, (Uint) sizeof(void *) ));

   } else {

      for ( count=i=0 ; i<p_count ; i++,count++ ) {
	 rc += sap_establish_context( verbose_level, p_flags, p_lifetime,
				      p_gssfp_ini, p_gssfp_acc, &(x_ini[i]), &(x_acc[i]),
				      p_ini_cred_type, p_acc_cred_type,
				      p_target, p_target_len, &maj_stat );

	 if ( maj_stat!=GSS_S_COMPLETE || x_ini[i]==NULL || x_acc[i]==NULL ) {
	    /* missing security context handle --                */
	    /* security context establishment failed in some way */
	    rc++;
	    break;
	 }

	 if ( ((x_ini[i]->flags)&(GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG))!=0
	      ||  options.try_msgprot_anyway!=FALSE ) {

	    if ( p_gssfp_ini->context_transfer_calls!=FALSE
	       && p_gssfp_acc->context_transfer_calls!=FALSE ) {

	       job = &(stdjob_xfer[0]);

	    } else { /* NO context transfer available */

	       job = &(stdjob[0]);

	    } /* NO context transfer available */

	 } else { /* NO message protection available */

	    if ( p_gssfp_ini->context_transfer_calls!=FALSE
	       && p_gssfp_acc->context_transfer_calls!=FALSE ) {

	       job = &(xfer_only[0]);
	    } /* context transfer available */

	 } /* NO message protection available */

	 if ( job!=NULL ) {

	       rc += test_message_exchange( p_trclevel, "", TRUE, job,
					    &(x_ini[i]), &(x_acc[i]), NULL, NULL, &success );
	       if ( success==FALSE ) {
		  break;
	       }
	 }

      } /* end for(i<p_count) */

      if ( success!=FALSE ) {
	 for ( i=0 ; i<p_count ; i++ ) {
	    if ( x_ini[i]!=NULL && x_acc[i]!=NULL && job!=NULL ) {
	       rc += test_message_exchange( p_trclevel, "", TRUE, job,
					    &(x_ini[i]), &(x_acc[i]), NULL, NULL, &success );
	       if ( success==FALSE ) {
		  break;
	       }
	    }
	 } /* end for(i<p_count) */
      } /* success!=FALSE */

      if ( count<p_count ) {
	 /* rc++; */
	 XVEB((V_ERR, "%s(): context establishment error after #%lu contexts!\n",
		       this_Call, (Ulong)count ));
      }

      for ( i=0 ; i<p_count ; i++ ) {
	 rc += release_ctx_desc( &(x_ini[i]) );
	 rc += release_ctx_desc( &(x_acc[i]) );
      }

   }

   if ( x_ini!=NULL  ) { free( x_ini ); }
   if ( x_acc!=NULL  ) { free( x_acc ); }

   return(rc);

} /* sap_try_context() */




/*
 * ctx_transfer_cycle()
 *
 * Description:
 *   Export an re-import one end (one handle) of a gssapi security context
 *   within the current process space.  Compare the characteristics of the
 *   security context before the export and after the re-import.
 *
 * Returns:
 *   The "transferred" security context handle
 *
 */
int
ctx_transfer_cycle( int  p_trclevel,  struct  ctx_desc  * p_ctx )
{
   char		   * this_Call     = "ctx_transfer_cycle";
   gss_name_t	     ini_name_in   = GSS_C_NO_NAME;
   gss_name_t	     acc_name_in   = GSS_C_NO_NAME;
   gss_OID	     mech_in	   = GSS_C_NO_OID;
   gss_name_t	     ini_name_out  = GSS_C_NO_NAME;
   gss_name_t	     acc_name_out  = GSS_C_NO_NAME;
   gss_OID	     mech_out	   = GSS_C_NO_OID;
   gss_buffer_desc   exp_ctx;
   gss_buffer_desc   tmp_buf;
   OM_uint32	     maj_stat;
   OM_uint32	     flags_in;
   OM_uint32	     flags_out;
   OM_uint32	     lifetime_in;
   OM_uint32	     lifetime_out;
   int		     local_ini_in;
   int		     local_ini_out;
   int		     open_in;
   int		     open_out;
   int		     rc		   = 0;
   time_t	     enter         = time(NULL);


   exp_ctx.value  = tmp_buf.value  = NULL;
   exp_ctx.length = tmp_buf.length = 0;

   if ( p_ctx->gssfp->context_transfer_calls==FALSE ) {
      XVEB((V_ERR, "%s(): context transfer calls not available!\n", this_Call));
      rc++;
      return(rc);
   }

   rc += inquire_context( verbose_level, p_ctx->gssfp, p_ctx->ctx,
			  &ini_name_in, &acc_name_in, &lifetime_in, &mech_in,
			  &flags_in, &local_ini_in, &open_in, &maj_stat );

   if ( open_in!=FALSE ) {

      if ( compare_oid((p_ctx->gssfp)->mech, mech_in)!=0 ) {
	 rc++;
	 XVEB((V_ERR, "%s(): mechanism of security context mutated:\n", this_Call));
	 print_oid( 2, "mech_type should be",  (p_ctx->gssfp)->mech );
	 print_oid( 2, "mech_type is instead", mech_in );
      }

      if ( p_ctx->flags!=flags_in ) {
	 rc++;
	 XVEB((V_ERR, "%s(): mutating context attributes!\n", this_Call));
	 print_ctx_flags( p_trclevel, "inquire_context", "  original context attributes", &p_ctx->flags  );
	 print_ctx_flags( p_trclevel, "inquire_context", "  current  context attributes", &flags_in );
      }

      rc += export_sec_context( verbose_level, p_ctx->gssfp, p_ctx->type, &(p_ctx->ctx), &exp_ctx, &maj_stat );

      if ( GSS_ERROR(maj_stat)==GSS_S_COMPLETE && exp_ctx.length>0 ) {

	 /* accumulate statistics on the interprocess token size */
	 (p_ctx->gssfp)->ctx_exported_count[p_ctx->type]++;
	 if ( (p_ctx->gssfp)->ctx_exported_min[p_ctx->type]==0
	      ||  (p_ctx->gssfp)->ctx_exported_min[p_ctx->type] > exp_ctx.length ) {
	    (p_ctx->gssfp)->ctx_exported_min[p_ctx->type] = exp_ctx.length;
	 }
	 if ( (p_ctx->gssfp)->ctx_exported_max[p_ctx->type] < exp_ctx.length ) {
	    (p_ctx->gssfp)->ctx_exported_max[p_ctx->type] = exp_ctx.length;
	 }

	 VERBOSE_DUPMEM( exp_ctx.value, exp_ctx.length, tmp_buf.value, tmp_buf.length );
	 if ( tmp_buf.value!=NULL ) {
	    rc += release_buffer( p_ctx->gssfp, &exp_ctx );

	    rc += import_sec_context( verbose_level, p_ctx->gssfp, p_ctx->type,
				   &tmp_buf, &(p_ctx->ctx), &maj_stat );

	    if ( GSS_ERROR(maj_stat)==GSS_S_COMPLETE && p_ctx->ctx!=GSS_C_NO_CONTEXT ) {

	       /* count successful security context transfers in general */
	       (p_ctx->gssfp)->context_xferred++;

	       rc += inquire_context( verbose_level, p_ctx->gssfp, p_ctx->ctx,
			     &ini_name_out, &acc_name_out, &lifetime_out, &mech_out,
			     &flags_out, &local_ini_out, &open_out, &maj_stat );

	       /* consistency checks of the security context attributes     */
	       /* that were inquired before and after the context transfer  */
	       rc += verify_same_name( p_trclevel, p_ctx->gssfp, "verifying initiator name",
			      "before context transfer", ini_name_in,
			      "after  context transfer", ini_name_out );
      
	       rc += verify_same_name( p_trclevel, p_ctx->gssfp, "verifying acceptor name",
			      "before context transfer", acc_name_in,
			      "after  context transfer", acc_name_out );

	       if ( compare_oid(mech_in, mech_out)!=0 ) {
		  rc++;
		  XVEB((V_ERR, "%s(): mechanism of security context mutated:\n", this_Call));
		  print_oid( 2, "mech_type before xfer", mech_in  );
		  print_oid( 2, "mech_type after  xfer", mech_out );
	       }

	       rc += check_lifetime_change( p_trclevel, enter, lifetime_in, lifetime_out );

	       if ( flags_out!=flags_in ) {
		  rc++;
		  XVEB((V_ERR, "%s(): mutating context attributes!\n", this_Call));
		  print_ctx_flags( p_trclevel, "inquire_context", "  before context transfer", &flags_in  );
		  print_ctx_flags( p_trclevel, "inquire_context", "  after  context transfer", &flags_out );
	       }

	       if ( (local_ini_in!=0 && local_ini_out==0)
		    || ( local_ini_in==0 && local_ini_out!=0) ) {
		  rc++;
		  XVEB((V_ERR, "%s(): \"local_ini\" from inquire_context mutated %d->%d !\n",
			    this_Call, (int)local_ini_in, (int)local_ini_out ));
	       }

	       if ( (open_in!=0 && open_out==0) || (open_in==0 && open_out!=0) ) {
		  rc++;
		  XVEB((V_ERR, "%s(): \"open\" from inquire_context mutated %d->%d !\n",
			    this_Call, (int)open_in, (int)open_out ));
	       }

	    } /* copy of exported context token succeded */

	 } /* import_context() succeeded */

      } /* export_context() succeeded */
      
   } /* security context is fully established (i.e. transferable) */

   rc += release_name(   p_ctx->gssfp, &ini_name_in  );
   rc += release_name(   p_ctx->gssfp, &acc_name_in  );
   rc += release_name(   p_ctx->gssfp, &ini_name_out );
   rc += release_name(   p_ctx->gssfp, &acc_name_out );
   
   rc += release_buffer( p_ctx->gssfp, &exp_ctx      );
   if ( tmp_buf.value!=NULL ) {
      verbose_freemem( &(tmp_buf.value), &(tmp_buf.length) );
   }

   return(rc);

} /* ctx_transfer_cycle() */
		    



/*
 * refresh_context()
 *
 * Description:
 *   Simulate an application level security context refresh.
 *   A new security context is established and the resulting characteristics
 *   are compared with those of the original security context.
 *   When the establishment of the new security context is successful,
 *   the old context will be deleted and the handles of the new context
 *   will be returned instead.
 *
 * Returns:
 *   Both handles of a new ("refreshed") security context
 *
 */
int
refresh_context( int		     p_trclevel,   OM_uint32	       p_lifetime,
		 struct ctx_desc  ** pp_ini,	   struct ctx_desc  ** pp_acc,
		 int		   * pp_success )
{
   char		   * this_Call      = "refresh_context";
   gss_name_t	     ini_name_i_in  = GSS_C_NO_NAME;
   gss_name_t	     acc_name_i_in  = GSS_C_NO_NAME;
   gss_name_t	     ini_name_a_in  = GSS_C_NO_NAME;
   gss_name_t	     acc_name_a_in  = GSS_C_NO_NAME;
   gss_name_t	     ini_name_i_out = GSS_C_NO_NAME;
   gss_name_t	     acc_name_i_out = GSS_C_NO_NAME;
   gss_name_t	     ini_name_a_out = GSS_C_NO_NAME;
   gss_name_t	     acc_name_a_out = GSS_C_NO_NAME;
   struct ctx_desc * ini	    = NULL;
   struct ctx_desc * acc	    = NULL;
   gss_OID	     mech_i_in	    = GSS_C_NO_OID;
   gss_OID	     mech_i_out     = GSS_C_NO_OID;
   gss_OID	     mech_a_in	    = GSS_C_NO_OID;
   gss_OID	     mech_a_out	    = GSS_C_NO_OID;
   OM_uint32	     flags_i_in;
   OM_uint32	     flags_i_out;
   OM_uint32	     flags_a_in;
   OM_uint32	     flags_a_out;
   OM_uint32	     lifetime_i_out;
   OM_uint32	     lifetime_a_out;
   OM_uint32	     maj_stat;
   OM_uint32	     maj_stat2;
   int	 	     local_ini_i;
   int		     local_ini_a;
   int		     success        = FALSE;
   int		     rc		    = 0;


   /* attempt an application level security context refresh by */
   /* establishing a new security context with the exact same  */
   /* characteristics as the existing one.		       */
   /* well, ideally one would request and expect to receive    */
   /* a security context with a longer lifetime than the       */
   /* existing one ...					       */

   /* First, figure out the characteristics of both ends of the */
   /* existing security context				        */
   rc += inquire_context( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->ctx,
			  &ini_name_i_in, &acc_name_i_in, NULL, &mech_i_in,
			  &flags_i_in, &local_ini_i, NULL, &maj_stat );

   if ( compare_oid(mech_i_in, ((*pp_ini)->gssfp)->mech)!=0 ) {
      rc++;
      XVEB((V_ERR, "%s(): HUH? invalid mechanism OID for ini context:\n", this_Call));
      print_oid( 2, "inquire_context() returns", mech_i_in     );
      print_oid( 2, "mech_type ought to be    ", ((*pp_ini)->gssfp)->mech );
   }

   rc += inquire_context( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->ctx,
			  &ini_name_a_in, &acc_name_a_in, NULL, &mech_a_in,
			  &flags_a_in, &local_ini_a, NULL, &maj_stat2 );

   if ( compare_oid(mech_a_in, ((*pp_acc)->gssfp)->mech)!=0 ) {
      rc++;
      XVEB((V_ERR, "%s(): HUH? invalid mechanism OID for acc context:\n", this_Call));
      print_oid( 2, "inquire_context() returns", mech_i_in     );
      print_oid( 2, "mech_type ought to be    ", ((*pp_acc)->gssfp)->mech );
   }

   if ( maj_stat==GSS_S_COMPLETE  &&  maj_stat2==GSS_S_COMPLETE ) {

      /* attempt to establish a new security context	        */
      /* for now, we reuse the explicitly passed flags&lifetime */
      /* that were used to create the existing context          */
      /* ideally, we should be able to simply reuse the inquired() */
      /* characteristics...				        */

      rc += sap_establish_context( p_trclevel, (*pp_ini)->flags_req, p_lifetime,
				   (*pp_ini)->gssfp, (*pp_acc)->gssfp,
				   &ini, &acc,
				   SNC_GSSNAMED_CRED, SNC_GSSNAMED_CRED,
				   ((*pp_ini)->acc_prname.value),
				   ((*pp_ini)->acc_prname.length), &maj_stat );

      if ( GSS_S_COMPLETE!=maj_stat || NULL==ini || NULL==acc ) {
	 rc++;
      } else {
	 /* we seem to have established a new security context */
	 /* let's find out its characteristics and do several  */
	 /* comparisons with those of the original context     */
	 rc += inquire_context( verbose_level, ini->gssfp, ini->ctx,
				&ini_name_i_out, &acc_name_i_out, &lifetime_i_out, &mech_i_out,
				&flags_i_out, &local_ini_i, NULL, &maj_stat );

	 rc += inquire_context( verbose_level, acc->gssfp, acc->ctx,
				&ini_name_a_out, &acc_name_a_out, &lifetime_a_out, &mech_a_out,
			        &flags_a_out, &local_ini_a, NULL, &maj_stat2 );

	 if ( maj_stat==GSS_S_COMPLETE  &&  maj_stat2==GSS_S_COMPLETE ) {

	    success = TRUE;

	    XVEB((V_HIDE, "   %s() completed security context refresh.\n", this_Call));

	    rc += check_lifetime_change( p_trclevel, ini->enter, ini->lifetime, lifetime_i_out );
	    rc += check_lifetime_change( p_trclevel, acc->enter, acc->lifetime, lifetime_a_out );

	    rc += verify_same_name( p_trclevel, ini->gssfp, "verifying initiator name",
			      "original ini context", ini_name_i_in,
			      "new      ini context", ini_name_i_out );
      
	    rc += verify_same_name( p_trclevel, acc->gssfp, "verifying initiator name",
			      "original acc context", ini_name_a_in,
			      "new      acc context", ini_name_a_out );

	    rc += verify_same_aclkey( p_trclevel, "verifying initiator name",
			      "new      ini context", TRUE,  ini->gssfp, ini_name_i_out,
			      "new      acc context", FALSE, acc->gssfp, ini_name_a_out );

	    rc += verify_same_name( p_trclevel, ini->gssfp, "verifying acceptor name",
			      "original ini context", acc_name_i_in,
			      "new      ini context", acc_name_i_out );

	    rc += verify_same_name( p_trclevel, acc->gssfp, "verifying acceptor name",
			      "original acc context", acc_name_a_in,
			      "new      acc context", acc_name_a_out );

	    rc += verify_same_aclkey( p_trclevel, "verifying acceptor name",
			      "new      ini context", TRUE, ini->gssfp, acc_name_i_out,
			      "new      acc context", TRUE, acc->gssfp, acc_name_a_out );

	    if ( flags_i_in!=flags_i_out ) {
	       rc++;
	       XVEB((V_ERR, "%s(): differing context attributes after refresh!\n", this_Call));
	       print_ctx_flags( 2, "original ini context", "flags", &flags_i_in   );
	       print_ctx_flags( 2, "new      ini context", "flags", &flags_i_out );
	    }

	    if ( ini->flags!=flags_i_out ) {
	       rc++;
	       XVEB((V_ERR, "%s(): differing context attributes after refresh!\n", this_Call));
	       print_ctx_flags( 2, "new ini context (init_ctx)", "flags", &(ini->flags) );
	       print_ctx_flags( 2, "new ini context (inquire )", "flags", &flags_i_out );
	    }

	    if ( flags_a_in!=flags_a_out ) {
	       rc++;
	       XVEB((V_ERR, "%s(): differing context attributes after refresh!\n", this_Call));
	       print_ctx_flags( 2, "original acc context", "flags", &flags_a_in   );
	       print_ctx_flags( 2, "new      acc context", "flags", &flags_a_out );
	    }

	    if ( acc->flags!=flags_a_out ) {
	       rc++;
	       XVEB((V_ERR, "%s(): differing context attributes after refresh!\n", this_Call));
	       print_ctx_flags( 2, "new acc context (init_ctx)", "flags", &acc->flags );
	       print_ctx_flags( 2, "new acc context (inquire )", "flags", &flags_a_out );
	    }

	    if ( compare_oid(mech_i_out, (ini->gssfp)->mech)!=0 ) {
	       rc++;
	       XVEB((V_ERR, "%s(): unexpected mechanism for new ini context:\n", this_Call));
	       print_oid( 2, "inquire_context returns", mech_i_out    );
	       print_oid( 2, "mech_type ought to be  ", (ini->gssfp)->mech );
	    }

	    if ( compare_oid(mech_a_out, (acc->gssfp)->mech)!=0 ) {
	       rc++;
	       XVEB((V_ERR, "%s(): unexpected mechanism for new acc context:\n", this_Call));
	       print_oid( 2, "inquire_context returns", mech_a_out    );
	       print_oid( 2, "mech_type ought to be  ", (acc->gssfp)->mech );
	    }

	    if ( compare_oid(mech_a_out, mech_i_out)!=0 ) {
	       rc++;
	       XVEB((V_ERR, "%s(): HUH?? inconsisten mech_type for ini and acc!\n", this_Call));
	       print_oid( 2, "ini context mech_type", mech_i_out );
	       print_oid( 2, "acc context mech_type", mech_a_out );
	    }

	    /* delete original security contexts now */
	    rc += release_ctx_desc( pp_ini );
	    rc += release_ctx_desc( pp_acc );

	    /* move (temporary) context handles into output parameters         */
	    /* and prevent the temporary handles from getting released on exit */
	    (*pp_ini) = ini;     ini = NULL;
	    (*pp_acc) = acc;	 acc = NULL;

	 } /* inquire_context(both new context handles )==GSS_S_COMPLETE */

      } /* endif   context establishment succeeded */

   } /* inquire_context( both old context handles )==GSS_S_COMPLETE */

   rc += release_name( (*pp_ini)->gssfp, &ini_name_i_in     );
   rc += release_name( (*pp_ini)->gssfp, &ini_name_i_out    );
   rc += release_name( (*pp_acc)->gssfp, &ini_name_a_in     );
   rc += release_name( (*pp_acc)->gssfp, &ini_name_a_out    );
   rc += release_name( (*pp_ini)->gssfp, &acc_name_i_in     );
   rc += release_name( (*pp_ini)->gssfp, &acc_name_i_out    );
   rc += release_name( (*pp_acc)->gssfp, &acc_name_a_in     );
   rc += release_name( (*pp_acc)->gssfp, &acc_name_a_out    );

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

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

} /* refresh_context() */





/*
 * create_ctx_desc()
 *
 *
 */
struct ctx_desc  *
create_ctx_desc( DLL_GSSFP_T        * p_gssfp,
	         char               * p_label,
	         void		    * p_initiator,
	         size_t		      p_initiator_len,
		 gss_OID	      p_ini_nt,
	         void		    * p_acceptor,
	         size_t		      p_acceptor_len,
		 gss_OID	      p_acc_nt )
{
   char             * this_Call = "create_ctx_desc";
   OM_uint32	      maj_stat;
   struct ctx_desc  * ctx       = NULL;

   ctx = (struct ctx_desc *) calloc( 1, sizeof( *ctx ) );

   if ( ctx!=NULL ) {

      ctx->gssfp = p_gssfp;     /* currently we simply copy this handle */
      ctx->label = Strmaxdup( p_label, 128 );

      ctx->ctx       = GSS_C_NO_CONTEXT;
      ctx->cred      = GSS_C_NO_CREDENTIAL;
      ctx->src_name  = GSS_C_NO_NAME;
      ctx->targ_name = GSS_C_NO_NAME;

      VERBOSE_DUPMEM( p_initiator, p_initiator_len, ctx->ini_prname.value, ctx->ini_prname.length );
      VERBOSE_DUPMEM( p_acceptor,  p_acceptor_len,  ctx->acc_prname.value, ctx->acc_prname.length );

      if ( ctx->ini_prname.value==NULL || ctx->ini_prname.length==0 ) {

	 ctx->ini_name = GSS_C_NO_NAME;

      } else {

	 import_and_canonicalize( verbose_level, ctx->gssfp,
				        ctx->ini_prname.value, ctx->ini_prname.length,
				        p_ini_nt, &(ctx->ini_name), &maj_stat );
	 if ( maj_stat!=GSS_S_COMPLETE  ||  ctx->ini_name==GSS_C_NO_NAME ) {
	    XVEB((V_ERR, "%s(): failed to import initiator name!\n", this_Call ));
	    goto error;
	 }

      }

      if ( ctx->acc_prname.value==NULL || ctx->acc_prname.length==0 ) {

	 ctx->acc_name = GSS_C_NO_NAME;

      } else {

	 import_and_canonicalize( verbose_level, ctx->gssfp,
				        ctx->acc_prname.value, ctx->acc_prname.length,
					p_acc_nt, &(ctx->acc_name), &maj_stat );
	 if ( maj_stat!=GSS_S_COMPLETE  ||  ctx->acc_name==GSS_C_NO_NAME ) {
	    XVEB((V_ERR, "%s(): failed to import acceptor name!\n", this_Call ));
	    goto error;
	 }

      }

   }

   return(ctx);


error:
   release_ctx_desc( &ctx );

   return(NULL);

} /* create_ctx_desc() */




/*
 * release_ctx_desc()
 *
 *
 */
int
release_ctx_desc( struct ctx_desc   ** pp_ctx )
{
   char          * this_Call GNU_UNUSED = "release_ctx_desc";
   int             rc        = 0;

   if ( pp_ctx!=NULL ) {

      if ( *pp_ctx!=NULL ) {

	 if ( (*pp_ctx)->gssfp!=NULL ) {
	    rc += delete_sec_context( verbose_level, (*pp_ctx)->gssfp, &((*pp_ctx)->ctx), NULL, NULL );
	    rc += release_name( (*pp_ctx)->gssfp, &((*pp_ctx)->ini_name)  );
	    rc += release_name( (*pp_ctx)->gssfp, &((*pp_ctx)->acc_name)  );
	    rc += release_cred( (*pp_ctx)->gssfp, &((*pp_ctx)->cred)      );
	    rc += release_name( (*pp_ctx)->gssfp, &((*pp_ctx)->src_name)  );
	    rc += release_name( (*pp_ctx)->gssfp, &((*pp_ctx)->targ_name) );
	 }

	 verbose_freemem( &((*pp_ctx)->ini_prname.value), &((*pp_ctx)->ini_prname.length) );
	 verbose_freemem( &((*pp_ctx)->acc_prname.value), &((*pp_ctx)->acc_prname.length)  );

	 if ( (*pp_ctx)->label!=NULL ) {
	    free( (*pp_ctx)->label );
	    (*pp_ctx)->label  = NULL;
	 }

	 (*pp_ctx)->gssfp = NULL;  /* don't release this handle, it was only copied */

	 free( *pp_ctx );
	 (*pp_ctx) = NULL;
      }
   }

   return(rc);

} /* release_ctx_desc() */


