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

static char * this_File GNU_UNUSED = __FILE__;

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



/*
 * name_tests()
 *
 *
 */
int
name_tests( DLL_GSSFP_T * p_gssfp )
{
   gss_name_t  tmp_ini_name   = GSS_C_NO_NAME;
   gss_name_t  tmp_acc_name   = GSS_C_NO_NAME;
   gss_name_t  tmp_hbs_name   = GSS_C_NO_NAME;
   gss_name_t  tmp_hbsx_name  = GSS_C_NO_NAME;
   gss_OID     nt_oid;
   char      * hostname;
   char        hbs_name[256];
   OM_uint32   maj_stat;
   int	       rc  = 0;
   int	       rci = 0;

   XVEB((V_SHOW, "====================\nTesting names management functions ...\n----------\n"));

   if ( p_gssfp->canonical_names_calls==FALSE ) {

      rc++;
      XVEB((V_SHOW, "ERROR: canonicalize_name/export_name not available!\n" ));

      rc += import_and_canonicalize( verbose_level, p_gssfp,
				     p_gssfp->acceptor, p_gssfp->acceptor_len,
				     p_gssfp->can_nametype, &(p_gssfp->acc_name), NULL );

      rc += import_and_canonicalize( verbose_level, p_gssfp,
				     p_gssfp->initiator, p_gssfp->initiator_len,
				     GSS_C_NO_OID, &tmp_ini_name, NULL );

   } else {

      if ( p_gssfp->initiator!=NULL ) {

	 XVEB((V_TEST, "Testing consistency of gss_name_t conversions\n"));
	 rci  = name_transformations( verbose_level, p_gssfp, NULL,
				      p_gssfp->initiator, p_gssfp->initiator_len,
				      p_gssfp->can_nametype, NULL );

	 rci += import_and_canonicalize( verbose_level, p_gssfp,
				         p_gssfp->initiator, p_gssfp->initiator_len,
					 p_gssfp->can_nametype, &(p_gssfp->ini_name),
					 NULL );

	 rci += name_transformations( verbose_level, p_gssfp, NULL,
				      p_gssfp->initiator, p_gssfp->initiator_len,
				      GSS_C_NO_OID, NULL );

	 rci += import_and_canonicalize( verbose_level, p_gssfp,
				         p_gssfp->initiator, p_gssfp->initiator_len,
					 GSS_C_NO_OID, &tmp_ini_name, NULL );

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

      }

      if ( p_gssfp->acceptor!=NULL ) {

	 XVEB((V_TEST, "Testing consistency of gss_name_t conversions\n"));
	 rci += name_transformations( verbose_level, p_gssfp, NULL,
				      p_gssfp->acceptor, p_gssfp->acceptor_len,
				      p_gssfp->can_nametype, NULL );

	 rci += import_and_canonicalize( verbose_level, p_gssfp,
				         p_gssfp->acceptor, p_gssfp->acceptor_len,
					 p_gssfp->can_nametype, &(p_gssfp->acc_name), NULL );

	 rci  = name_transformations( verbose_level, p_gssfp, NULL,
				      p_gssfp->acceptor, p_gssfp->acceptor_len,
				      GSS_C_NO_OID, NULL );

	 rci += import_and_canonicalize( verbose_level, p_gssfp,
				         p_gssfp->acceptor, p_gssfp->acceptor_len,
					 GSS_C_NO_OID, &tmp_acc_name, NULL );

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

      }

   } 


   hostname = sys_get_hostname();
   if ( hostname!=NULL && *hostname!=0 ) {
      if ( p_gssfp->nt_list!=GSS_C_NO_OID_SET ) {
	 (p_gssfp->nt_list_has_hb_service)     = !( check_oid_in_oidset(verbose_level, "GSS_C_NT_HOSTBASED_SERVICE", nt_hb_service,
								   "nametype list of mechanism", p_gssfp->nt_list) );
         (p_gssfp->nt_list_has_hb_service_alt) = !( check_oid_in_oidset(verbose_level, "GSS_C_NT_HOSTBASED_SERVICE_X", nt_hb_service_alt,
								   "nametype list of mechanism", p_gssfp->nt_list) );
      }
      sprintf( hbs_name, "ftp@%.128s", hostname );
      XVEB((V_TEST, "Testing support of hostbased service name \"%s\"\n", hbs_name));
      if ( p_gssfp->nt_list_has_hb_service==0 ) {
	 XVEB((V_SHOW, "  (expecting failure: names_for_mech() didn't show GSS_C_NT_HOSTBASED_SERVICE)\n"));
      }
      rci += import_and_canonicalize( verbose_level, p_gssfp, hbs_name, 0,
				      nt_hb_service, &tmp_hbs_name, &maj_stat );
      if ( rci>0 ) {
	 if (maj_stat==GSS_S_BAD_NAMETYPE) {
	    XVEB((V_SHOW, "Nametype \"GSS_C_NT_HOSTBASED_SERVICE\" is rejected.\n"));
	    rci = (rci>0) ? rci-1 : 0 ;
	 } else {
	    XVEB((V_SHOW, "  Hostbased service names may not work.\n"));
	 }

      } else {
	 XVEB((V_SHOW, "  Hostbased service name is recognized and transformed to\n"));
	 rci = print_name(1, p_gssfp, "this name", tmp_hbs_name, NULL, &nt_oid, &maj_stat );
	 if ( rci==0 ) {
	    (p_gssfp->hb_service_looks_ok) = TRUE;
	 }
      }

      if ( (p_gssfp->nt_list_has_hb_service_alt) ) {
	 if ( (p_gssfp->nt_list_has_hb_service)==FALSE ) {
	    XVEB((V_ERR, "Mechanism supports GSS_C_NT_HOSTBASED_SERVICE_X but NOT GSS_C_NT_HOSTBASED_SERVICE!\n"));
	    rci++;
	 }
	 /* Test the alternative (deprecated) nametype OID for hostbased service name only */
	 /* when it is contained in the list of nametypes supported by the mechanism       */
	 rci += import_and_canonicalize( verbose_level, p_gssfp, hbs_name, 0,
				         nt_hb_service_alt, &tmp_hbsx_name, &maj_stat );
	 if ( rci>0 ) {
	    if (maj_stat==GSS_S_BAD_NAMETYPE) {
	       XVEB((V_SHOW, "Alternative Nametype \"GSS_C_NT_HOSTBASED_SERVICE_X\" is rejected.\n"));
	       rci = (rci>0) ? rci-1 : 0 ;
	    } else {
	       XVEB((V_SHOW, "  The alternative nametype OID for Hostbased service names may not work.\n"));
	    }

	 } else {
	    XVEB((V_SHOW, "  With alternative nametype OID hostbased service name is transformed to\n"));
	    rci = print_name(1, p_gssfp, "this name", tmp_hbsx_name, NULL, &nt_oid, &maj_stat );
	    if ( rci==0 ) {
	       (p_gssfp->hb_service_alt_looks_ok) = TRUE;
	    }
	 }
      }

      XVEB((V_RESULT, rci, 0, NULL ));

   } else {

      XVEB((V_SHOW, "\nOuch!  I don't know my own hostname -- skipping hostbased service names\n"));

   }

   rc += release_name( p_gssfp, &tmp_hbs_name  );
   rc += release_name( p_gssfp, &tmp_hbsx_name );
   rc += release_name( p_gssfp, &tmp_ini_name  );
   rc += release_name( p_gssfp, &tmp_acc_name  );

   return(rc);

} /* name_tests() */




/*
 * release_name()
 *
 */
int
release_name( DLL_GSSFP_T * p_gssfp, gss_name_t * pp_name ) 
{
   OM_uint32    min_stat;
   OM_uint32    maj_stat;
   int		rc	 = 0;

   if ( *pp_name==BOGUS_NAME ) {

      *pp_name = GSS_C_NO_NAME;

   } else if ( *pp_name!=GSS_C_NO_NAME ) {

      BOGUS_INI_MINOR( min_stat );
      start_timer();
      maj_stat = (p_gssfp->gss_release_name)( &min_stat, pp_name );
      timedelay = read_timer();
      rc += print_status(p_gssfp, GSS_RELEASE_NAME, maj_stat, min_stat);

      if ( maj_stat!=GSS_S_COMPLETE ) {
	 rc++;
      }
   }

   if ( *pp_name!=GSS_C_NO_NAME ) {
     XVEB((V_ERR, "(gss_name_t)out_name   was not zeroed by gss_release_name()!\n"));
     *pp_name = GSS_C_NO_NAME;
     rc++;
   }

   return(rc);

} /* release_name() */




/* 
 * import_name()
 *
 *
 */
int
import_name( int	   p_trclevel,	  DLL_GSSFP_T	       * p_gssfp,
	     char	 * p_name,	  size_t		 p_name_len,
	     gss_OID	   p_nt_oid,
	     gss_name_t	 * pp_gssname,	  OM_uint32	       * pp_maj_stat )
{
   char            * gss_call = "gss_import_name";
   gss_buffer_desc   input_name;
   OM_uint32	     min_stat;
   OM_uint32	     maj_stat = GSS_S_COMPLETE;
   int               exported_name = FALSE;
   int		     rc	      = 0;

   (*pp_gssname) = GSS_C_NO_NAME;

   input_name.value = p_name;
   if (p_name_len==0 && p_name!=NULL ) {
      input_name.length = strlen(p_name);
   } else {
      input_name.length = p_name_len;
   }

   /* check whether this is a printable or exported name               */
   /* by comparing the supplied nametype OID to GSS_C_NT_EXPORTED_NAME */
   if ( p_nt_oid!=GSS_C_NO_OID
	&&  p_nt_oid->length==nt_export_name->length
	&&  !memcmp(p_nt_oid->elements, nt_export_name->elements, p_nt_oid->length) ) {
      exported_name = TRUE;

      if ( ignore_name_for_statistics==FALSE ) {
         /* collect some name length statistics */
	 if ( p_name_len < (p_gssfp->bcname_size_min)  ||  (p_gssfp->bcname_size_min)==0 ) { (p_gssfp->bcname_size_min) = p_name_len; }
	 if ( p_name_len > (p_gssfp->bcname_size_max)                                    ) { (p_gssfp->bcname_size_max) = p_name_len; }
      }

   } else { /* p_nt_oid is not GSS_C_NT_EXPORTED_NAME */

      if ( ignore_name_for_statistics==FALSE ) {
	 /* collect some name length statistics */
	 if ( p_name_len < (p_gssfp->prname_size_min)  ||  (p_gssfp->prname_size_min)==0 ) { (p_gssfp->prname_size_min) = p_name_len; }
	 if ( p_name_len > (p_gssfp->prname_size_max)				      ) { (p_gssfp->prname_size_max) = p_name_len; }
      }

   } /* endif checking for GSS_C_NT_EXPORTED_NAME */


   /* Hack for option "-z", work around a Kerberos5-beta (OpenVision) gssapi bug */
   if ( exported_name==FALSE
        &&  options.zap_trailing_nuls!=FALSE ) {
      /* some gssapi implementations return names with trailing NULs, */
      /* but reject trailing NULs for gss_import_name()               */
      /* This is definitely broken behaviour!                         */
      /* exported names are binary and are allowed to contain trailing NUL bytes */
      char   * ptr;
      size_t   len;

      /* otherwise assume a printable name, which shall not contain trailing NUL chars */
      ptr = (char *) input_name.value;
      len = input_name.length;

      /* chop off trailing NUL characters */
      while ( len>0 && ptr[len-1]=='\0' ) { len--; }
      input_name.length = len;

   } /* exported_name==FALSE  &&  options.correct_trailing_nul!=FALSE */

   
   if ( p_trclevel>=2 ) {
      print_buffer_head(    2, "input name buffer", &input_name );
      print_buffer_content( 2, &input_name );
      print_oid( 2, "nametype oid", p_nt_oid );
   }

   BOGUS_INI_NAME(  pp_gssname );
   BOGUS_INI_MINOR( min_stat   );

   start_timer();
   maj_stat = (p_gssfp->gss_import_name)( &min_stat, &input_name,
					  p_nt_oid, pp_gssname );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_IMPORT_NAME, maj_stat, min_stat );

   BOGUS_CHECK_NAME( pp_gssname, "output_name" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      rc++;

      if ( *pp_gssname!=GSS_C_NO_NAME ) {
	 rc++;

	 XVEB((V_ERR, "%s() failed but didn't clean output name!\n", gss_call));
	 rc += release_name( p_gssfp, pp_gssname );
      }

   } else {

      if ( *pp_gssname==GSS_C_NO_NAME ) {

	 XVEB((V_ERR, "%s() succeeded but didn't produce (gss_name_t)!\n", gss_call));
	 rc++;

      } else {
	 
	 rc += print_name( p_trclevel-1, p_gssfp, "newly imported", *pp_gssname, NULL, NULL, NULL );

      }

   }

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

   return(rc);

} /* import_name() */




/*
 * display_name()
 *
 *
 */
int
display_name( int	      p_trclevel,    DLL_GSSFP_T    * p_gssfp,
	      gss_name_t      p_name,
	      gss_buffer_t    p_buffer,	     gss_OID	    * pp_oid,
	      OM_uint32	    * pp_maj_stat )
{
   char            * gss_call  = "gss_display_name";
   OM_uint32	     min_stat;
   OM_uint32	     maj_stat  = GSS_S_FAILURE;
   char            * ptr;
   int		     rc	      = 0;
   Ulong	     prlen;

   p_buffer->value   = NULL;
   p_buffer->length  = 0;

   BOGUS_INI_BUFFER( p_buffer );
   BOGUS_INI_OID(    pp_oid   );
   BOGUS_INI_MINOR(  min_stat );

   start_timer();
   maj_stat = (p_gssfp->gss_display_name)( &min_stat, p_name, p_buffer, pp_oid );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_DISPLAY_NAME, maj_stat, min_stat );

   BOGUS_CHECK_OID(    pp_oid,   "nametype"       );
   BOGUS_CHECK_BUFFER( p_buffer, "printable name" );

   if ( ignore_name_for_statistics==FALSE
        &&  options.sap_constraints!=0 && p_buffer->length>SNC_NAME_MAXLEN ) {
      rc++;
      XVEB((V_ERR, "%s() printable name output length = %lu (max=%u)!\n",
		   gss_call, (Ulong) p_buffer->length, (Uint)SNC_NAME_MAXLEN ));
   }

   if ( maj_stat==GSS_S_COMPLETE ) {

      if ( pp_oid!=NULL ) {
	 if ( *pp_oid==GSS_C_NO_OID ) {
	    XVEB((V_HIDE, "  %s() returned nametype==GSS_C_NO_OID.\n", gss_call));
	 } else {
	    rc += validate_oid( p_gssfp, OID_NAMETYPE, gss_call, "nametype", *pp_oid );
	 }
      } 

      if ( p_buffer->length==0  ||  p_buffer->value==NULL ) {
	 rc++;
	 XVEB((V_ERR, "%s() returned empty buffer %s?!\n", gss_call,
	       (pp_oid!=NULL && compare_oid(*pp_oid, nt_anonymous)==0) ? "for anonymous" : "" ));
      } else {

	 if ( ignore_name_for_statistics==FALSE ) {
	    /* collect some name length statistics */
	    if ( (p_buffer->length) < (p_gssfp->prname_size_min)  ||  (p_gssfp->prname_size_min)==0 ) { (p_gssfp->prname_size_min) = (p_buffer->length); }
	    if ( (p_buffer->length) > (p_gssfp->prname_size_max)                                    ) { (p_gssfp->prname_size_max) = (p_buffer->length); }
	 }

	 if ( p_trclevel>2 ) {
	    XVEB((V_SHOW, "        %s() returned \"%.*s\"\n", gss_call,
		  (int)p_buffer->length, p_buffer->value ));
	 }

	 /******************************************************************/
	 /* search for embedded or trailing NUL characters in output names */
	 /******************************************************************/
	 ptr = memchr( p_buffer->value, 0, p_buffer->length);
	 if ( ptr!=NULL ) {
	    prlen = (Ulong)(ptr - (char *)(p_buffer->value));
	 }

	 if ( p_trclevel>2 || (ptr!=NULL && options.zap_trailing_nuls==FALSE)  ) {
	    print_buffer_head(    2, "printable name buffer", p_buffer );
	    print_buffer_content( 2, p_buffer );
	    if ( pp_oid!=NULL ) { print_oid( 2, "nametype", *pp_oid ); }
	 }

	 if ( ptr!=NULL ) {
	    if ( prlen+1 == p_buffer->length ) {
	       (p_gssfp->dspname_trailing_nul)++;
	       if ( options.zap_trailing_nuls==FALSE ) {
		  rc++;
		  XVEB((V_ERR, "%s() returned printable name with trailing NUL char!\n",
			       gss_call ));
	       }
	    } else {
	       (p_gssfp->dspname_embedded_nul)++;
	       if ( options.zap_trailing_nuls==FALSE ) {
		  rc++;
		  XVEB((V_ERR, "%s() returned printable name with NUL at offset %lu!\n",
			       gss_call, (Ulong) prlen ));
	       }
	    }
	 } /* ptr!=NULL */


	 /**********************************************************************/
	 /* search for trailing/embedded line break characters in output names */
	 /**********************************************************************/
	 ptr = memchr( p_buffer->value, '\n', p_buffer->length );
	 if ( ptr==NULL ) {
	    ptr = memchr( p_buffer->value, '\r', p_buffer->length );
	 }
	 if ( ptr!=NULL ) {
	    (p_gssfp->dspname_linebreak)++;
	    if ( options.sap_constraints!=FALSE ) {
	       rc++;
	       XVEB((V_ERR, "%s() returned printable name with line break at offset %lu!\n",
			    gss_call, (Ulong)(ptr - (char *)p_buffer->value) ));
	    }
	 }

	 /************************************************************************/
	 /* search for leading or trailing whitespace characters in output names */
	 /************************************************************************/
	 ptr = (char *) p_buffer->value;
	 if ( ptr[0]==' ' || ptr[0]=='\t' ) {
	    (p_gssfp->dspname_leading_wsp)++;
	 }
	 if ( ptr[p_buffer->length-1]==' ' || ptr[p_buffer->length-1]=='\t' ) {
	    (p_gssfp->dspname_trailing_wsp)++;
	 }

      }

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

      rc++;

      if ( p_buffer!=NULL || p_buffer->length!=0 ) {

	 rc++;
	 XVEB((V_ERR, "%s() failed but didn't clear buffer!\n", gss_call));

	 rc += release_buffer( p_gssfp, p_buffer );

      }

   }

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

   return(rc);  

} /* display_name() */




/*
 * export_name()
 *
 *
 */
int
export_name( int	      p_trclevel,    DLL_GSSFP_T       * p_gssfp,
	     gss_name_t	      p_name,
	     gss_buffer_t     p_expname,     OM_uint32	       * pp_maj_stat )
{
   char      * gss_call = "gss_export_name";
   OM_uint32   min_stat;
   OM_uint32   maj_stat = GSS_S_FAILURE;
   int	       rc	= 0;

   if ( p_gssfp->gss_export_name == 0 ) {
      XVEB((V_ERR, "%s() function pointer not defined!\n", gss_call));
      abort();
   }

   if ( p_name==GSS_C_NO_NAME ) {
      XVEB((V_BUG, "%s(): was given a GSS_C_NO_NAME!\n", gss_call));
      abort();
   }

   BOGUS_INI_BUFFER( p_expname );
   BOGUS_INI_MINOR(  min_stat  );

   start_timer();
   maj_stat = (p_gssfp->gss_export_name)( &min_stat, p_name, p_expname );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_EXPORT_NAME, maj_stat, min_stat);

   BOGUS_CHECK_BUFFER( p_expname, "exported name" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      rc++;
      if ( p_expname->length>0 ) {

	 XVEB((V_ERR, "%s() failed but didn't clean output buffer!\n", gss_call));
	 rc += release_buffer( p_gssfp, p_expname );

      }

   } else { /* maj_stat==GSS_S_COMPLETE */

      /* gssapi success */
      if ( p_expname->length==0 || p_expname->value==NULL ) {
	 XVEB((V_ERR, "%s() succeeded but didn't produce (gss_buffer_t)!\n", gss_call));
	 rc++;

      } else {

	 if ( ignore_name_for_statistics==FALSE ) {
	    /* collect some name length statistics */
	    if ( (p_expname->length) < (p_gssfp->bcname_size_min)  ||  (p_gssfp->bcname_size_min)==0 ) { (p_gssfp->bcname_size_min) = (p_expname->length); }
	    if ( (p_expname->length) > (p_gssfp->bcname_size_max)                                    ) { (p_gssfp->bcname_size_max) = (p_expname->length); }
	 }

      }

      if ( options.sap_constraints!=0 && p_expname->length>SNC_ACLKEY_MAXLEN ) {
	 rc++;
	 XVEB((V_ERR, "%s() exported name output length = %lu (max=%u)!\n",
		   gss_call, (Ulong) p_expname->length, (Uint)SNC_ACLKEY_MAXLEN ));
      }

      if ( p_trclevel>=2 && p_expname!=NULL ) {
	 print_buffer_head(    2, "exported name buffer", p_expname );
	 print_buffer_content( 2, p_expname );
      }

   } /* endif (maj_stat) */

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

   return(rc);

} /* export_name() */




/*
 * compare_names()
 *
 *
 */
int
compare_names( int	     p_trclevel,  DLL_GSSFP_T    * p_gssfp,
	       gss_name_t    p_name1,	  gss_name_t	   p_name2,
	       int	   * pp_result,	  OM_uint32	 * pp_maj_stat )
{
   char        * gss_call = "gss_compare_name";
   OM_uint32     min_stat;
   OM_uint32     maj_stat = GSS_S_FAILURE;
   int           rc       = 0;

   (*pp_result) = FALSE;

   if ( p_name1==GSS_C_NO_NAME  ||  p_name2==GSS_C_NO_NAME ) {

      /* In case we accidentally supply a GSS_C_NO_NAME ... */
      XVEB((V_OK, verbose_level-2, "compare_names() called with GSS_C_NO_NAME.\n"));
      maj_stat = GSS_S_COMPLETE;
      (*pp_result) = (p_name1==p_name2) ? TRUE : FALSE;

   } else {

      BOGUS_INI_VALUE( pp_result );
      BOGUS_INI_MINOR( min_stat );

      start_timer();
      maj_stat = (p_gssfp->gss_compare_name)( &min_stat, p_name1, p_name2, pp_result );
      timedelay = read_timer();
      rc += print_status( p_gssfp, GSS_COMPARE_NAME, maj_stat, min_stat );

      BOGUS_CHECK_VALUE( pp_result, "result" );

      if ( maj_stat!=GSS_S_COMPLETE ) {

	 (*pp_result) = FALSE;
	 rc++;

      }

   }

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

   return(rc);

} /* compare_names() */



/*
 * canonicalize_name()
 *
 *
 */
int
canonicalize_name( int		  p_trclevel,	DLL_GSSFP_T  * p_gssfp,
		   gss_name_t	  p_name,	gss_OID	       p_mech_oid,
		   gss_name_t	* pp_cname,	OM_uint32    * pp_maj_stat )
{
   char	       * gss_call = "gss_canonicalize_name";
   OM_uint32	 min_stat;
   OM_uint32	 maj_stat = GSS_S_FAILURE;
   int		 rc       = 0;

   if ( p_gssfp->gss_canonicalize_name == 0 ) {
      XVEB((V_ERR, "%s() function pointer not defined!\n", gss_call ));
      abort();
   }

   if ( p_name==GSS_C_NO_NAME ) {
      XVEB((V_BUG, "%s(): was given a GSS_C_NO_NAME!\n", gss_call ));
      abort();
   }

   if ( p_mech_oid==GSS_C_NO_OID ) {
      XVEB((V_BUG, "%s(): was given GSS_C_NO_OID!\n", gss_call ));
      abort();
   }

   BOGUS_INI_NAME(  pp_cname );
   BOGUS_INI_MINOR( min_stat );

   start_timer();
   maj_stat = (p_gssfp->gss_canonicalize_name)( &min_stat, p_name, p_mech_oid, pp_cname );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_CANONICALIZE_NAME, maj_stat, min_stat );

   BOGUS_CHECK_NAME( pp_cname, "output_name" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      rc++;
      if ( *pp_cname!=GSS_C_NO_NAME ) {
	 XVEB((V_ERR, "%s() failed but returned output_name!\n", gss_call));
	 rc += print_name(2, p_gssfp, "output_name", *pp_cname, NULL, NULL, NULL );
	 rc += release_name( p_gssfp, pp_cname );
      }

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

      if ( *pp_cname==GSS_C_NO_NAME ) {
	 rc++;
	 maj_stat = GSS_S_FAILURE;
	 XVEB((V_ERR, "%s() succeeded but failed to return output_name!\n"));
      }

   } /* endif (maj_stat==GSS_S_COMPLETE) */

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

   return(rc);

} /* canonicalize_name() */




/*
 * duplicate_name()
 *
 *
 */
int
duplicate_name( int	      p_trclevel,    DLL_GSSFP_T    * p_gssfp,
		gss_name_t    p_srcname,
		gss_name_t  * pp_dstname,    OM_uint32	    * pp_maj_stat )
{
   char	       * gss_call = "gss_duplicate_name";
   OM_uint32	 min_stat;
   OM_uint32	 maj_stat = GSS_S_FAILURE;
   int		 rc       = 0;

   if ( p_gssfp->gss_duplicate_name == 0 ) {
      XVEB((V_ERR, "%s() function pointer not defined!\n", gss_call ));
      abort();
   }

   if ( p_srcname==GSS_C_NO_NAME ) {
      XVEB((V_BUG, "%s(): was given a GSS_C_NO_NAME srcname!\n", gss_call ));
      abort();
   }

   BOGUS_INI_NAME(  pp_dstname );
   BOGUS_INI_MINOR( min_stat   );

   start_timer();
   maj_stat = (p_gssfp->gss_duplicate_name)( &min_stat, p_srcname, pp_dstname );
   timedelay = read_timer();
   rc += print_status( p_gssfp, GSS_DUPLICATE_NAME, maj_stat, min_stat );

   BOGUS_CHECK_NAME( pp_dstname, "dest_name" );

   if ( maj_stat!=GSS_S_COMPLETE ) {

      /* gssapi failure */
      rc++;
      if ( *pp_dstname!=GSS_C_NO_NAME ) {
	 XVEB((V_ERR, "%s() failed but returned output_name!\n", gss_call));
	 rc += print_name(2, p_gssfp, "dest_name", *pp_dstname, NULL, NULL, NULL );
	 rc += release_name( p_gssfp, pp_dstname );
      }

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

      if ( *pp_dstname==GSS_C_NO_NAME ) {
	 rc++;
	 maj_stat = GSS_S_FAILURE;
	 XVEB((V_ERR, "%s() succeeded but failed to return dest_name!\n"));
      }

   } /* endif (maj_stat==GSS_S_COMPLETE) */

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

   return(rc);

} /* duplicate_name() */




/*
 * import_and_canonicalize()
 *
 *
 */
int
import_and_canonicalize( int	       p_trclevel,    DLL_GSSFP_T  * p_gssfp,
			 void	     * p_prname,      size_t	     p_prname_len,
			 gss_OID       p_nametype,
			 gss_name_t  * pp_out_name,   OM_uint32	   * pp_maj_stat )
{
   OM_uint32     maj_stat = GSS_S_FAILURE;
   gss_name_t    tmp_name = GSS_C_NO_NAME;
   int           rc       = 0;

   *pp_out_name = GSS_C_NO_NAME;

   rc += import_name( verbose_level, p_gssfp, p_prname, p_prname_len,
		      p_nametype, &tmp_name, &maj_stat );
   if ( tmp_name!=GSS_C_NO_NAME ) {
      if ( p_gssfp->canonical_names_calls==FALSE ) {
	 *pp_out_name = tmp_name;
	 tmp_name     = GSS_C_NO_NAME;
      } else {
	 rc += canonicalize_name( verbose_level, p_gssfp, tmp_name,
				  p_gssfp->mech, pp_out_name, &maj_stat );
      }
   }

   rc += release_name( p_gssfp, &tmp_name );

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

   return(rc);

} /* import_and_canonicalize() */




/*
 * print_name()
 *
 *
 */
int
print_name(int		p_trclevel,    DLL_GSSFP_T	 * p_gssfp,
	   char	      * p_name_label,  gss_name_t	   p_name,
	   int        * pp_wsp_count,
	   gss_OID    * pp_oid,	       OM_uint32	 * pp_maj_stat )
{
   gss_buffer_desc   dsp_name;
   int		     rc	= 0;
   OM_uint32         i;

   rc += display_name( verbose_level, p_gssfp, p_name, &dsp_name, pp_oid, pp_maj_stat );
   if ( dsp_name.length>0  &&  p_trclevel>0 ) {
      XVEB((V_SHOW,"    %s = \"%.*s\"\n", p_name_label,
		   (int)dsp_name.length, dsp_name.value ));
   }

   if ( NULL!=pp_wsp_count ) {
      char * ptr       = (char *)dsp_name.value;
      int    wsp_count;

      /* count amount of embedded space chars ' ' -- we don't count tabs '\t' */
      /* because Kerberos converts them to escape sequences                   */
      for ( wsp_count=0,i=0 ; i<dsp_name.length ; i++ ) {
         if ( ptr[i]==' ' ) {
	    wsp_count++;
	 }
      }
      (*pp_wsp_count) = wsp_count;
   }

   rc += release_buffer( p_gssfp, &dsp_name );

   return(rc);

} /* print_name() */




/*
 * dump_exported_name()
 *
 *
 */
int
dump_exported_name( int          p_trclevel,    DLL_GSSFP_T    * p_gssfp,
		    char       * p_name_label,  gss_name_t	      p_name,
		    OM_uint32  * pp_maj_stat )
{
   gss_buffer_desc    exp_name_buf;
   gss_name_t	      tmp_name   = GSS_C_NO_NAME;
   OM_uint32	      maj_stat   = GSS_S_COMPLETE;
   int                rc  = 0;
   int		      rci = 0;

   exp_name_buf.value  = NULL;
   exp_name_buf.length = 0;

   if ( p_trclevel>0 && p_gssfp->canonical_names_calls!=FALSE ) {

      rc += canonicalize_name( verbose_level, p_gssfp, p_name, p_gssfp->mech, &tmp_name, &maj_stat );
      if ( tmp_name!=GSS_C_NO_NAME ) {
         rc += export_name( verbose_level, p_gssfp, tmp_name, &exp_name_buf, &maj_stat );
	 if ( exp_name_buf.length > 0 ) {
	    rci = dump_exported_name_framing( exp_name_buf.value, exp_name_buf.length );
	    if ( rci>0 ) {
	       XVEB((V_SHOW, "\n"));
	       print_buffer_head(    2, p_name_label, &exp_name_buf );
	       print_buffer_content( 2, &exp_name_buf );
	    }
	    rc += rci;
	 }

      } /* tmp_name!=GSS_C_NO_NAME */

   } /* have canonicalize_name&export_name */

   rc += release_name(   p_gssfp, &tmp_name     );
   rc += release_buffer( p_gssfp, &exp_name_buf );

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

   return(rc);

} /* dump_exported_name() */




/*
 * verify_same_name()
 *
 * verify that the two given names refer to the same identity
 * i.e. both names are != GSS_C_NO_NAME
 *      and considered equal by gss_compare_names()
 *
 */
int
verify_same_name( int    p_trclevel,   DLL_GSSFP_T  * p_gssfp,
		 char  * p_prefix,
		 char  * p_label1,     gss_name_t     p_name1,
		 char  * p_label2,     gss_name_t     p_name2 )
{
   int	      result;
   int	      rc       = 0;
   OM_uint32  maj_stat;

   if ( p_name1==GSS_C_NO_NAME ) {
      XVEB((V_ERR, "%s: %s is GSS_C_NO_NAME!\n", p_prefix, p_label1));
      rc++;
   }

   if ( p_name2==GSS_C_NO_NAME ) {
      XVEB((V_ERR, "%s: %s is GSS_C_NO_NAME!\n", p_prefix, p_label2));
      rc++;
   }

   if ( p_name1==GSS_C_NO_NAME || p_name2==GSS_C_NO_NAME )
      return(rc);

   rc += compare_names( p_trclevel-1, p_gssfp, p_name1, p_name2, &result, &maj_stat );
   if ( maj_stat!=GSS_S_COMPLETE || result==FALSE ) {

      XVEB((V_ERR, "%s: comparison of %s and %s failed!\n",
	           p_prefix, p_label1, p_label2 ));
      rc++;

   } else {

      if ( p_trclevel>2 ) {
	 XVEB((V_SHOW, "%s: compare_name(%s,%s)==TRUE\n",
	       p_prefix, p_label1, p_label2));
      }
   }

   return(rc);

} /* verify_same_name() */




/*
 * name_to_aclkey()
 *
 * Description:
 *   Convert a gss_name_t into a flat memory object
 *   that can be compared with memcmp(). Because of the fallback
 *   to gss_display_name() for pre-v2 gssapi mechanisms,
 *   a nametype oid is also returned and needs to be considered
 *   in the comparison.
 *
 * Return values:
 *   * a flat memory buffer (dynamically malloc()ed)
 *     described by pp_xpname and pp_xpname_len
 *   * a static nametype oid
 *
 */
int
name_to_aclkey( int	     p_trclevel,     DLL_GSSFP_T    * p_gssfp,
	        gss_name_t   p_name_in,
	        int	     p_canon_first,  int	      p_use_export,
	        void      ** pp_xpname,      size_t         * pp_xpname_len,
	        gss_OID	   * pp_nametype,    OM_uint32	    * pp_maj_stat )
{
   char		     * this_Call = "name_to_aclkey";
   gss_name_t	       tmp_name  = GSS_C_NO_NAME;
   gss_name_t	     * xname;
   gss_OID	       oid	 = GSS_C_NO_OID;
   gss_buffer_desc     tmp_buffer;
   OM_uint32	       maj_stat  = GSS_S_COMPLETE;
   int		       rc        = 0;

   (*pp_nametype)    = GSS_C_NO_OID;
   (*pp_xpname)      = NULL;
   (*pp_xpname_len)  = 0;
   tmp_buffer.value  = NULL;
   tmp_buffer.length = 0;

   if ( p_use_export!=FALSE && p_gssfp->canonical_names_calls!=FALSE ) {

      if ( p_canon_first==FALSE ) {
	 xname = &p_name_in;
      } else {
	 rc += canonicalize_name( verbose_level, p_gssfp, p_name_in,
				  p_gssfp->mech, &tmp_name, &maj_stat );
	 if ( maj_stat!=GSS_S_COMPLETE || tmp_name==GSS_C_NO_NAME ) {
	    goto error;
	 }
	 xname = &tmp_name;
      }

      rc += export_name( verbose_level, p_gssfp, *xname,
		         &tmp_buffer, &maj_stat );
      if ( maj_stat!=GSS_S_COMPLETE
	   || tmp_buffer.value==NULL  ||  tmp_buffer.length==0 ) {
	 goto error;
      }

      oid = nt_export_name;

   } else { /* export printable name instead */

      rc += display_name( verbose_level, p_gssfp, p_name_in,
			  &tmp_buffer, &oid, &maj_stat );
      if ( maj_stat!=GSS_S_COMPLETE
	 || tmp_buffer.value==NULL  ||  tmp_buffer.length==0 ) {
	 goto error;
      }

   }

   (*pp_xpname) = malloc( tmp_buffer.length );
   if ( *pp_xpname==NULL ) {

      XVEB((V_ERR, "%s: malloc(%lu) failed!\n",
		   this_Call, (Ulong) tmp_buffer.length ));
      goto error;

   } else {

      memcpy( *pp_xpname, tmp_buffer.value, tmp_buffer.length );
      (*pp_xpname_len) = tmp_buffer.length;
      (*pp_nametype)   = oid;

   }

error:	 
   rc += release_name(   p_gssfp, &tmp_name   );
   rc += release_buffer( p_gssfp, &tmp_buffer );

   return(rc);

} /* name_to_aclkey() */




/*
 * verify_same_aclkey()
 *
 * verify that the two given names refer to the same identity
 * i.e. both names are != GSS_C_NO_NAME
 *      and considered equal by gss_compare_names()
 *
 */
int
verify_same_aclkey( int		   p_trclevel,  char	    * p_prefix,
		    char	 * p_label1,	int	      p_do_can1,
		    DLL_GSSFP_T  * p_gssfp1,	gss_name_t    p_name1,
		    char	 * p_label2,	int	      p_do_can2,
		    DLL_GSSFP_T  * p_gssfp2,	gss_name_t    p_name2 )
{
   gss_OID	    nt1;
   gss_OID	    nt2;
   void		  * xpname1;
   void		  * xpname2;
   char           * which_name;
   int              both_have_export_name;
   size_t	    xpname1_len;
   size_t	    xpname2_len;
   gss_buffer_desc  tmpbuf;
   int		    rc       = 0;
   OM_uint32	    maj_stat;

   if ( p_name1==GSS_C_NO_NAME ) {
      XVEB((V_ERR, "%s: %s is GSS_C_NO_NAME!\n", p_prefix, p_label1));
      rc++;
   }

   if ( p_name2==GSS_C_NO_NAME ) {
      XVEB((V_ERR, "%s: %s is GSS_C_NO_NAME!\n", p_prefix, p_label2));
      rc++;
   }

   if ( p_name1==GSS_C_NO_NAME || p_name2==GSS_C_NO_NAME )
      return(rc);

   if ( p_gssfp1->canonical_names_calls!=FALSE
	&& p_gssfp2->canonical_names_calls!=FALSE ) {
	both_have_export_name = 1;
	which_name            = "BINARY EXPORTED";
   } else {
	both_have_export_name = 0;
	which_name            = "PRINTABLE";
   }

   rc += name_to_aclkey( p_trclevel, p_gssfp1, p_name1, p_do_can1,
			 both_have_export_name,
			 &xpname1, &xpname1_len, &nt1, &maj_stat );

   if ( xpname1!=NULL ) {
      /* succeeded with flattening of name 1 */

      rc += name_to_aclkey( p_trclevel, p_gssfp2, p_name2, p_do_can2,
			    both_have_export_name,
			    &xpname2, &xpname2_len, &nt2, &maj_stat );

      if ( xpname2!=NULL ) {

	 /* succeeded with flattening of name 2 */
	 if ( nt1==nt2
	      ||  ( nt1!=NULL && nt2!=NULL
		    && nt1->length==nt2->length && nt1->length>0
		    && !memcmp(nt1->elements, nt2->elements, nt1->length)) ) {

	    if ( xpname1_len==xpname2_len  &&  xpname1_len>0
	         && !memcmp( xpname1, xpname2, xpname1_len) ) {

	       if ( p_trclevel>2 ) {
		  XVEB((V_SHOW, "%s: compare_aclkey(%s,%s)==TRUE\n",
				p_prefix, p_label1, p_label2));
	       }

	    } else {

	       XVEB((V_ERR, "%s: %s %s DIFFERS from %s!\n",
			    p_prefix, which_name, p_label1, p_label2 ));
	       tmpbuf.value  = xpname1;
	       tmpbuf.length = xpname1_len;
	       print_buffer_head( p_trclevel+1, p_label1, &tmpbuf );
	       print_buffer_content( p_trclevel+1, &tmpbuf );
	       tmpbuf.value  = xpname2;
	       tmpbuf.length = xpname2_len;
	       print_buffer_head( p_trclevel+1, p_label2, &tmpbuf );
	       print_buffer_content( p_trclevel+1, &tmpbuf );
	       rc++;
	    }

	 } else {

	       XVEB((V_SHOW, "%s: %s and %s not comparable (lack of GSS_C_NT_EXPORTED_NAME)!\n",
			     p_prefix, p_label1, p_label2 ));

	 }

      }

   }

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

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

   return(rc);

} /* verify_same_aclkey() */




/*
 * name_transform_xp()
 *
 *
 */
int
name_transform_xp( int          p_trclevel,	  DLL_GSSFP_T   * p_gssfp,
		   gss_name_t   p_in_name,
		   gss_name_t * pp_out_name,	  OM_uint32	* pp_maj_stat )
{
   gss_name_t       tmp_name  = GSS_C_NO_NAME;
   gss_name_t	  * ptmp_name = NULL;
   gss_OID	    nametype  = GSS_C_NO_OID;
   gss_buffer_desc  pr_buffer;
   gss_buffer_desc  xp_buffer;
   void		  * xp_ptr    = NULL;
   size_t           xp_len    = 0;
   OM_uint32	    maj_stat  = GSS_S_COMPLETE;
   int              rc        = 0;

   pr_buffer.length = 0;
   pr_buffer.value  = NULL;
   xp_buffer.length = 0;
   xp_buffer.value  = NULL;
   (*pp_out_name)   = GSS_C_NO_NAME;

   if ( p_gssfp->canonical_names_calls==FALSE ) {

      ptmp_name = &p_in_name;

   } else {

      ptmp_name = &tmp_name;
      rc += canonicalize_name( verbose_level, p_gssfp, p_in_name, p_gssfp->mech,
			       ptmp_name, &maj_stat );
   }
   if ( *ptmp_name!=GSS_C_NO_NAME ) {
      /* first, create the "canonical" name                        */
      /* if gss_export_name() is missing from the gssapi           */
      /* implementation, we try to emulate with gss_display_name() */

      if ( p_gssfp->canonical_names_calls==FALSE ) {

	 rc += display_name( verbose_level, p_gssfp, *ptmp_name,
			     &xp_buffer, NULL, &maj_stat );
	 nametype = p_gssfp->can_nametype;

      } else {

	 rc += export_name( verbose_level, p_gssfp,
			    *ptmp_name, &xp_buffer, &maj_stat );
	 nametype = nt_export_name;

      }

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

	 /* ok, copy the gss_buffer_t into our private memory space */
	 /* we'll delay the release of the gss_buffer_t to simulate */
	 /* SNC's actual behaviour				    */
	 VERBOSE_DUPMEM( xp_buffer.value, xp_buffer.length, xp_ptr, xp_len );

	 if ( xp_ptr==NULL ) {

	    rc++;

	 } else {

	    rc += display_name( verbose_level, p_gssfp, *ptmp_name,
			        &pr_buffer, NULL, &maj_stat );

	    if ( maj_stat==GSS_S_COMPLETE ) {

	       rc += release_buffer( p_gssfp, &pr_buffer );
	       rc += release_buffer( p_gssfp, &xp_buffer );
	       rc += release_name( p_gssfp, &tmp_name );
	       rc += import_and_canonicalize( verbose_level, p_gssfp,
					      xp_ptr, xp_len, nametype,
					      &tmp_name, &maj_stat );
	       if ( tmp_name!=GSS_C_NO_NAME ) {
		  (*pp_out_name) = tmp_name;
		  tmp_name       = GSS_C_NO_NAME;

		  rc += verify_same_name( p_trclevel, p_gssfp,
					  "Name transformation",
					  "src_name",  p_in_name,
					  "dst_name", *pp_out_name );

	       }

	    }
	 }
      }

   }

   verbose_freemem( &xp_ptr, &xp_len );

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

   return(rc);
				  
} /* name_transform_xp() */




/*
 * name_transformations()
 *
 *
 */
int
name_transformations( int         p_trclevel,	   DLL_GSSFP_T     * p_gssfp,
		      gss_name_t  p_in_name,	   void		   * p_in_buffer,
		      size_t	  p_in_buffer_len, gss_OID	     p_in_nametype,
		      OM_uint32	* pp_maj_stat )
{
   char		   * this_Call = "name_transformations";
   OM_uint32         maj_stat  = GSS_S_FAILURE;
   gss_buffer_desc   prname1;
   gss_buffer_desc   prname2;
   gss_buffer_desc   prname3;
   gss_buffer_desc   xpname1;
   gss_buffer_desc   xpname2;
   gss_name_t	     name1     = GSS_C_NO_NAME;
   gss_name_t	     name2     = GSS_C_NO_NAME;
   gss_name_t	     name3     = GSS_C_NO_NAME;
   gss_name_t	     name4     = GSS_C_NO_NAME;
   gss_name_t	     name5     = GSS_C_NO_NAME;
   gss_name_t	     name6     = GSS_C_NO_NAME;
   gss_name_t	     name7     = GSS_C_NO_NAME;
   gss_name_t	   * porg_name = NULL;
   gss_OID	     oid1      = GSS_C_NO_OID;
   gss_OID	     oid2      = GSS_C_NO_OID;
   gss_OID	     oid3      = GSS_C_NO_OID;
   int		     rc	       = 0;

   xpname1.value  = xpname2.value  = NULL;
   xpname1.length = xpname2.length = 0;
   prname1.value  = prname2.value  = prname3.value  = NULL;
   prname1.length = prname2.length = prname3.length = 0;

   if ( p_gssfp->canonical_names_calls==FALSE ) {
      XVEB((V_SHOW, "%s() skipping test -- canonicalize_name/export_name not available!\n", this_Call));
      return(1);
   }

   if ( p_in_name!=NULL ) {

      rc += print_name( p_trclevel, p_gssfp, "using gss_name_t", p_in_name, NULL, NULL, NULL );
      porg_name = &p_in_name;

   } else { /* p_in_name==NULL */

      rc += import_name( verbose_level, p_gssfp, p_in_buffer, p_in_buffer_len,
			 p_in_nametype, &name1, &maj_stat );
      if ( name1==GSS_C_NO_NAME ) {
	 rc++;
	 XVEB((V_ERR, "%s() aborting, import of name failed!\n", this_Call));
	 goto error;
      }
      porg_name = &name1;
      rc += print_name( p_trclevel, p_gssfp, "using printable name", name1, NULL, NULL, NULL );

   } /* p_in_name == NULL */

   /* testing duplicate name */
   rc += display_name( verbose_level, p_gssfp, *porg_name, &prname1, &oid1, &maj_stat );
   rc += canonicalize_name( verbose_level, p_gssfp,
			    *porg_name, p_gssfp->mech, &name2, &maj_stat );
   if ( name2==GSS_C_NO_NAME ) {
      rc++;
      XVEB((V_ERR, "%s() aborting: canonicalize_name failed!\n", this_Call));
      goto error;
   }

   rc += verify_same_name( p_trclevel-1, p_gssfp, "canonicalize_name",
			   "input_name", *porg_name, "output_name", name2 );

   rc += display_name( verbose_level, p_gssfp, name2, &prname2, &oid2, &maj_stat );

   rc += export_name( verbose_level, p_gssfp, name2, &xpname1, &maj_stat );
   if ( xpname1.length==0 ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: export of canonical name failed!\n", this_Call));
      goto error;
   }

   rc += import_name( verbose_level, p_gssfp, xpname1.value, xpname1.length,
		      nt_export_name, &name3, &maj_stat );
   if ( name3==GSS_C_NO_NAME ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: re-import of exported name failed!\n", this_Call));
      goto error;
   }

   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported exported name",
			   "original name", *porg_name, "reimported name", name3 );
   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported exported name",
			   "canonical name", name2, "reimported name", name3 );

   rc += export_name( verbose_level, p_gssfp, name3, &xpname2, &maj_stat );
   if ( xpname2.length==0 ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: second export_name() failed!\n", this_Call));
      goto error;
   }

   if ( compare_buffers( p_trclevel-1, &xpname1, &xpname2 )!=0 ) {
      rc++;
      XVEB((V_ERR, "%s(): initial exported name differs from re-exported one!\n", this_Call));
   }

   /* compare  display_name(canonicalize_name(src_name))
    * with     display_name(import_name(export_name(canonicalize_name(src_name))))
    */
   rc += display_name( verbose_level, p_gssfp, name3, &prname3, &oid3, &maj_stat );
   if ( compare_buffers( p_trclevel-1, &prname2, &prname3 )!=0 ) {
      rc++;
      XVEB((V_ERR, "%s() differing output for display_name after re-import!\n", this_Call));
   }

   rc += import_name( verbose_level, p_gssfp, prname2.value, prname2.length,
		      oid2, &name4, &maj_stat );
   if ( name4==GSS_C_NO_NAME ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: reimport of displayed canonical name failed!\n", this_Call));
      goto error;
   }
   /* we have to re-canonicalize the imported printable name,  */
   /* since we need at least one canonical name (MN) for the   */
   /* comparisons, and the initial/original name may have been */
   /* a non-MN -- in fact, if it was printable, it was no MN!  */
   rc += canonicalize_name( verbose_level, p_gssfp, name4,
			    p_gssfp->mech, &name6, &maj_stat );
   if ( name6==GSS_C_NO_NAME ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: canonicalization of reimported displayed canonical name failed!\n", this_Call));
      goto error;
   }

   rc += import_name( verbose_level, p_gssfp, prname3.value, prname3.length,
		      oid3, &name5, &maj_stat );
   if ( name5==GSS_C_NO_NAME ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: reimport of displayed reimported exported name failed!\n", this_Call));
      goto error;
   }
   /* we have to re-canonicalize the imported printable name,  */
   /* since we need at least one canonical name (MN) for the   */
   /* comparisons, and the initial/original name may have been */
   /* a non-MN -- in fact, if it was printable, it was no MN!  */
   rc += canonicalize_name( verbose_level, p_gssfp, name5,
			    p_gssfp->mech, &name7, &maj_stat );
   if ( name7==GSS_C_NO_NAME ) {
      rc++;
      XVEB((V_ERR, "%s() aborts: canonicalization of reimported displayed canonical name failed!\n", this_Call));
      goto error;
   }

   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "canonical", name6, "reimported", name7 );

   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "originial", *porg_name, "reimported", name6 );
   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "canonical", name2, "reimported", name6 );
   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "reimported expname", name3, "reimported", name6 );

   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "originial", *porg_name, "reimported", name7 );
   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "canonical", name2, "reimported", name7 );
   rc += verify_same_name( p_trclevel-1, p_gssfp, "reimported displayed names",
			   "reimported expname", name3, "reimported", name7 );

error:
   release_name(   p_gssfp, &name1   );
   release_name(   p_gssfp, &name2   );
   release_name(   p_gssfp, &name3   );
   release_name(   p_gssfp, &name4   );
   release_name(   p_gssfp, &name5   );
   release_name(   p_gssfp, &name6   );
   release_name(   p_gssfp, &name7   );
   release_buffer( p_gssfp, &prname1 );
   release_buffer( p_gssfp, &prname2 );
   release_buffer( p_gssfp, &prname3 );
   release_buffer( p_gssfp, &xpname1 );
   release_buffer( p_gssfp, &xpname2 );

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

   return(rc);

} /* name_transformations() */




/*
 * decode_asn1_length()
 *
 *
 */
int
decode_asn1_length( unsigned char  * p_buf,        size_t    p_buf_len,
		    size_t         * pp_enc_len,   size_t  * pp_len_value )
{
   size_t   len_value;
   size_t   enc_len, i;

   (*pp_enc_len)   = 0;
   (*pp_len_value) = 0;
      
   if ( (p_buf[0]&0x80)==0 ) {
      (*pp_enc_len)   = 1;
      (*pp_len_value) = (p_buf[0] & 0x7f);
      return(0);
   }

   enc_len = (p_buf[0]&0x7f) + 1;
   if ( enc_len > p_buf_len /* buffer too short */
        || enc_len > sizeof(unsigned long) /* overflow */ ) {
      return(1);
   }

   len_value = 0;
   for ( len_value = 0 , i=1 ; i<enc_len ; i++ ) {
      len_value  = (len_value<<8);
      len_value += (Ulong)(p_buf[i]);
   }

   (*pp_len_value) = len_value;
   (*pp_enc_len)   = enc_len;

   return(0);

} /* decode_asn1_length() */



/*
 * dump_exported_name_framing()
 *
 * Parse the binary blob of the exported canonical name
 * to verify if the outer framing conforms to the GSS-API v2
 * high-level spec (Section 3.2)
 *
 */
int
dump_exported_name_framing( void * p_buf,  size_t  p_buf_len )
{
   char             * this_Call = "dump_exported_name_framing";
   gss_OID_desc	      oid_desc;
   unsigned char    * ptr;
   size_t             len;
   size_t	      inner_length;
   size_t             oid_len;
   size_t	      enc_len;
   size_t	      asn1_oid_len;
   size_t	      offset;
   size_t	      rlen;

   ptr = p_buf;
   len = p_buf_len;

   if ( len<9 ) {
truncated:
      XVEB((V_ERR, "%s(): exported name is too short! (truncated?)\n", this_Call));
      return(1);
   }

   if ( ptr[0]!=0x04 || ptr[1]!=0x01 ) {
      XVEB((V_ERR, "%s(): exported name does not start with 0x04 0x01!\n", this_Call));
      return(1);
   }

   XVEB((V_SHOW, "  Framing details for exported name (Section 3.2, GSS-API v2 spec):\n"));
   XVEB((V_SHOW, "    TOK_ID            :"));
   print_hexdump( &(ptr[0]), 0, 2, 16, FALSE );

   asn1_oid_len = (((unsigned int)ptr[2])<<8) + ptr[3];
   XVEB((V_SHOW, "    MECH_OID_LEN = %2lu :", (Ulong)asn1_oid_len));
   print_hexdump( &(ptr[2]), 2, 2, 16, FALSE );
   
   if ( len < 8 + asn1_oid_len )
      goto truncated;

   if ( ptr[4]!=0x06 ) {
      XVEB((V_ERR, "%s(): exported name lacks ASN.1 OID tag (0x06) at offset 4!\n", this_Call));
      return(1);
   }

   if ( decode_asn1_length( &(ptr[5]), p_buf_len-5, &enc_len, &oid_len )!=0 ) {
      XVEB((V_ERR, "%s(): exported name with malformed ASN.1 OID len encoding!\n", this_Call));
      return(1);
   }

   if ( (1+enc_len+oid_len)!=asn1_oid_len ) {
      XVEB((V_ERR, "%s(): exported name with bad length field for ASN.1 OID!\n",
		   this_Call ));
      return(1);
   }

   oid_desc.elements = &(ptr[4+1+enc_len]);
   oid_desc.length   = (OM_uint32)oid_len;

   XVEB((V_SHOW, "        OID tag       :"));
   print_hexdump( &(ptr[4]), 4, 1, 16, FALSE );

   XVEB((V_SHOW, "        OID len = %3lu :", (Ulong)oid_len));
   print_hexdump( &(ptr[5]), 5, enc_len, 16, FALSE );

   XVEB((V_SHOW, "        OID elements  :"));
   print_hexdump( &(ptr[5+enc_len]), 5+enc_len, oid_len, 14, FALSE );

   print_oid( 2, "       ", &oid_desc );

   offset = 4 + asn1_oid_len;
   ptr = ptr + offset ;
   len = len - offset ;

   if ( len < 4 ) {
      XVEB((V_ERR, "%s() exported name is too short! (truncated?)\n", this_Call));
      return(1);
   }

   inner_length = ((Ulong)ptr[0]<<24) + ((Ulong)ptr[1]<<16)
 	        + ((Ulong)ptr[2]<<8)  + (Ulong)ptr[3];

   XVEB((V_SHOW, "    NAME_LEN   = %4lu :", (Ulong)inner_length));
   print_hexdump( &(ptr[0]), offset, 4, 16, FALSE );

   if ( (inner_length + 4)!=len ) {
      XVEB((V_ERR, "%s() exported name with invalid length for inner part!\n",
		   this_Call));
      return(1);
   }

   offset += 4;

   XVEB((V_SHOW, "    NAME              :"));
   for ( ;; ) {
      rlen = p_buf_len - offset;
      print_hexdump( &(((Uchar *)p_buf)[offset]), offset, (rlen<=8) ? rlen : 8, 8, TRUE );
      offset += 8;
      if ( offset >= p_buf_len )
	 break;
      XVEB((V_SHOW, "                       "));
   }

   return(0);

} /* dump_exported_name_framing() */
