/*++

  REALMFLAGS.CXX

  Copyright (C) 1999 Microsoft Corporation, all rights reserved.

  DESCRIPTION: realm-flag manipulation code.

  Created, Jan 10, 2000 by DavidCHR.

  CONTENTS: CompareFlagIds
            VerboselyPrintAndRemoveFlagsById
            LookupRealmFlagByName
            PrintAndRemoveFlagNames
            SearchRealmFlagListByAttribute
            CompareFlagNames

--*/  


#include "everything.hxx"

typedef struct {

  ULONG  Id;          // from kerberos\client2\mitutil.h
  LPWSTR Name;        // short string identifier
  LPSTR  Explanation; // what this flag does.
  LPSTR  MoreExplanation; // if you have to run to the next line.

} KERB_REALM_FLAG_MAPPING, *PKERB_REALM_FLAG_MAPPING;

/* These flags are defined in kerberos\client2\mitutil.h.
   However, there's other gunk in there that I'd rather not
   copy out so I'll just duplicate them.

   I'd consider auto-generating code fragments from the file
   to keep this file instantly up-to-date, but it wouldn't
   be guaranteed to provide human readable information */

KERB_REALM_FLAG_MAPPING
KerbRealmFlagMappings[] = {

  /* The order of "none" in the list is important.  It must be
     before any of the other flags so that code that handles
     multiple flags as a mask will not hit this unless the 
     whole mask is zero. */

  { 0x0,
    L"None",
    "No Realm Flags"
  },

  { 0x1, 
    L"SendAddress",
    "Include IP numbers within tickets.",
    "Useful for solving SOME compatibility issues."
  },

  { 0x2,
    L"TcpSupported",
    "Indicates that this realm supports TCP.",
    "(as opposed to just UDP)" },

  { 0x4,
    L"Delegate",
    "Everyone in this realm is trusted for delegation" },

  { 0x8,
    L"NcSupported",
    "This realm supports Name Canonicalization" },

};

ULONG
RealmFlagCount = ( sizeof( KerbRealmFlagMappings ) / 
                   sizeof( KerbRealmFlagMappings[ 0 ] ) );


/* NOTES on REALMLISTCOMPAREFUNCTION:

   If your REALMLISTCOMPAREFUNCTION is designed to return multiple
   mappings or interpret multiple mappings (e.g. as a mask), then
   you must special case "None" in the array above.  Otherwise,
   your output may include "none", which doesn't make any sense. */

typedef BOOL REALMLISTCOMPAREFUNCTION( IN PVOID, // pvAttribute
                                       IN PKERB_REALM_FLAG_MAPPING );


/*++**************************************************************
  NAME:      CompareFlagIds

  compares a flag map to a flag id.  
  pvAttribute is a pointer to the desired flag id.

  RETURNS:   TRUE  if this is the correct flagid
             FALSE otherwise.
  CREATED:   Jan 10, 2000
  CALLED BY: SearchRealmFlagListByAttribute
  FREE WITH: n/a -- no resources are allocated
  
 **************************************************************--*/

BOOL // REALMLISTCOMPAREFUNCTION
CompareFlagIds( IN PVOID                    pvAttribute,
                IN PKERB_REALM_FLAG_MAPPING pMap ) {

    return ( pMap->Id == *(( PULONG ) pvAttribute) );

}


/*++**************************************************************
  NAME:      CompareFlagNames

  Compares a mapping to a string for the flagname.

  RETURNS:   TRUE  if this mapping corresponds to the given string
             FALSE otherwise.
  CREATED:   Jan 10, 2000
  CALLED BY: SearchRealmFlagListByAttribute
  FREE WITH: n/a -- no resources are allocated
  
 **************************************************************--*/


BOOL // REALMLISTCOMPAREFUNCTION
CompareFlagNames( IN PVOID                    pvAttribute,
                  IN PKERB_REALM_FLAG_MAPPING pMap ) {

    return ( 0 == _wcsicmp( (LPWSTR) pvAttribute,
                            pMap->Name ) );
}

/*++**************************************************************
  NAME:      PrintAndRemoveFlagsById

  if the flag id matches, it is removed from the passed-in value,
  and the flagname is printed.

  MODIFIES:  pvAttribute -- may be stripped of a bit

  TAKES:     pvAttribute -- flagId to check
             pMap        -- table entry to check against

  RETURNS:   TRUE  if pvAttribute is zero (stop searching)
             FALSE otherwise (keep searching)

  CREATED:   Jan 10, 2000
  CALLED BY: SearchRealmFlagListByAttribute
  FREE WITH: n/a -- no resources are allocated
  
 **************************************************************--*/

BOOL
PrintAndRemoveFlagsById( IN PVOID pvAttribute,
                         IN PKERB_REALM_FLAG_MAPPING pMap ) {

    if ( !pMap->Id ) {

      /* We special-case "none" so that we only print it
	 if there are no other flags-- if other flags exist,
	 "none" will be skipped over in the array */

      if ( !*(( PULONG ) pvAttribute ) ) {
	printf( " %ws",
		pMap->Name );

	return TRUE;
      } else {
	return FALSE;
      }
    }

    if ( ( *(( PULONG ) pvAttribute) & pMap->Id )
         == pMap->Id ) {

      *( (PULONG) pvAttribute ) &= ~pMap->Id;
      printf( " %ws",
              pMap->Name );

    }

    return *( (PULONG) pvAttribute ) == 0;

}

/*++**************************************************************
  NAME:      VerboselyPrintAndRemoveFlagsById

  like PrintAndRemoveFlagsById, but it also dumps the
  flag id and explanation field.

  LOGGING:   lots of it.
  CREATED:   Jan 10, 2000
  CALLED BY: SearchRealmFlagListByAttribute
  FREE WITH: n/a -- no resources are allocated
  
 **************************************************************--*/

BOOL
VerboselyPrintAndRemoveFlagsById( IN PVOID pvAttribute,
                                  IN PKERB_REALM_FLAG_MAPPING pMap ) {

#if 0
    if ( !pMap->Id ) {

      /* We special-case "none" so that we only print it
	 if there are no other flags-- if other flags exist,
	 "none" will be skipped over in the array */

      if ( !*(( PULONG ) pvAttribute ) ) {
	printf( "0x%02x %ws   \t%hs\n",
		pMap->Id,
		pMap->Name,
		pMap->Explanation );
	
	return TRUE;
      } else {
	return FALSE;
      }
    }
#endif

    if ( ( *(( PULONG ) pvAttribute) & pMap->Id )
         == pMap->Id ) {

      *( (PULONG) pvAttribute ) &= ~pMap->Id;
      printf( "0x%02x %-12ws %hs\n",
              pMap->Id,
              pMap->Name,
              pMap->Explanation );

      if ( pMap->MoreExplanation ) {

	printf( "%-17hs %hs\n",
		"",
		pMap->MoreExplanation );
      }

    }

    return *( (PULONG) pvAttribute ) == 0;

}
      

/*++**************************************************************
  NAME:      SearchRealmFlagListByAttribute

  searches the realmlist for a particular attribute.

  MODIFIES:  ppMapping -- receives the given mapping.

  TAKES:     pvAttribute -- attribute value to search for
             pFunc       -- function to use to find it

  RETURNS:   TRUE  if the value could be found
             FALSE otherwise.
  LASTERROR: not set

  LOGGING:   none
  CREATED:   Jan 10, 2000
  LOCKING:   none
  CALLED BY: anyone
  FREE WITH: n/a -- no resources are allocated
  
 **************************************************************--*/


BOOL
SearchRealmFlagListByAttribute( IN  PVOID                     pvAttribute,
                                IN  REALMLISTCOMPAREFUNCTION *pFunc,
                                OUT PKERB_REALM_FLAG_MAPPING *ppMapping ) {

    ULONG i;
    PKERB_REALM_FLAG_MAPPING pMapping = &KerbRealmFlagMappings[ 0 ];

    for ( i = 0 ; 
          i < RealmFlagCount ;
          i ++, pMapping++ ) {

      if ( pFunc( pvAttribute,
                  pMapping ) ) {

        if ( ppMapping ) *ppMapping = pMapping;
        return TRUE;

      }
    }

    return FALSE;

}


/*++**************************************************************
  NAME:      LookupRealmFlagByName

  given a name, maps it to a realm flag mapping structure

  MODIFIES:  ppMapping     -- receives the entry pointer
  TAKES:     RealmFlagName -- name for which to search

  RETURNS:   TRUE  if the realmflag could be found.
             FALSE otherwise.
  LASTERROR: 

  LOGGING:   
  CREATED:   Jan 10, 2000
  LOCKING:   
  CALLED BY: anyone
  FREE WITH: n/a -- no resources are allocated
  
 **************************************************************--*/

BOOL
LookupRealmFlagByName( IN  LPWSTR RealmFlagName,
                       OUT PKERB_REALM_FLAG_MAPPING *ppMapping ) {

    return SearchRealmFlagListByAttribute( RealmFlagName,
                                           CompareFlagNames,
                                           ppMapping );

}

VOID
PrintRealmFlags( IN ULONG RealmFlags ) {

    ULONG i;
    ULONG ioFlags = RealmFlags;

    if ( RealmFlags == 0 ) {

      printf( " none" );

    } else {

      if ( !SearchRealmFlagListByAttribute( &ioFlags,
                                            PrintAndRemoveFlagsById,
                                            NULL ) ) {
        printf( " [unknown" );

        if ( ioFlags != RealmFlags ) {

          printf( ": 0x%x",
                  ioFlags );

        }

        printf( "]" );

      }

    }
}
    
    
      

NTSTATUS
ListRealmFlags( LPWSTR * Ignored) {

    ULONG RealmFlags = ~0;

    printf( "\n"
            "Ksetup knows the following realm flags: \n" );
    
    SearchRealmFlagListByAttribute( &RealmFlags,
                                    VerboselyPrintAndRemoveFlagsById,
                                    NULL );
    printf( "\n" );

    exit( 0 ); /* Jump out. */

    return STATUS_SUCCESS;

}

NTSTATUS
GetRealmFlags( IN  LPWSTR RealmName,
               OUT PULONG pulRealmFlags ) {

    HKEY  hKey;
    NTSTATUS N = STATUS_UNSUCCESSFUL;
    DWORD dwErr, Type, Len = sizeof( *pulRealmFlags );

    dwErr = OpenSubKey( &RealmName,
                        &hKey );

    if ( dwErr == ERROR_SUCCESS ) {

      dwErr = RegQueryValueEx( hKey,
                               KERB_DOMAIN_REALM_FLAGS_VALUE,
                               NULL, // mbz
                               &Type,
                               (LPBYTE) pulRealmFlags,
                               &Len );

      switch ( dwErr ) {

       case ERROR_SUCCESS:
         
         N = STATUS_SUCCESS;
         break;

       case ERROR_FILE_NOT_FOUND:
       case ERROR_PATH_NOT_FOUND:

         /*  453545: if the realm flags aren't specified,
             don't complain about it. */

         N = STATUS_SUCCESS;
         *pulRealmFlags = 0;
         break;

       default:
         
         printf( "Failed to query %ws for %ws: 0x%x\n",
                 KERB_DOMAIN_REALM_FLAGS_VALUE,
                 RealmName,
         dwErr );
      }

      RegCloseKey( hKey );

    } // else error has already been printed.

    return N;
}

NTSTATUS
SetRealmFlags( IN LPWSTR RealmName,
               IN ULONG  ulRealmFlags ) {

    HKEY  hKey;
    NTSTATUS N = STATUS_UNSUCCESSFUL;
    DWORD dwErr, Len = sizeof( ulRealmFlags );

    dwErr = OpenSubKey( &RealmName,
                        &hKey );

    if ( dwErr == ERROR_SUCCESS ) {

      dwErr = RegSetValueEx( hKey,
                             KERB_DOMAIN_REALM_FLAGS_VALUE,
                             NULL, // mbz
                             REG_DWORD,
                             (LPBYTE) &ulRealmFlags,
                             Len );
      
      switch ( dwErr ) {

       case ERROR_SUCCESS:

         DEBUGPRINT( DEBUG_REGISTRY,
                     ( "Set Realm Flags for %ws to 0x%x\n",
                       RealmName,
                       ulRealmFlags ) ) ;
         
         N = STATUS_SUCCESS;
         break;

       default:
         
         printf( "Failed to write %ws for %ws: 0x%x\n",
                 KERB_DOMAIN_REALM_FLAGS_VALUE,
                 RealmName,
         dwErr );
      }

      RegCloseKey( hKey );

    } // else error has already been printed.

    return N;
}



NTSTATUS
ResolveRealmFlags( IN     LPWSTR *Params,
                   IN OUT PULONG pulFlags ) {

    ULONG                    id;
    LPWSTR                   Cursor, *pFlagCursor = Params;
    PKERB_REALM_FLAG_MAPPING pMap;
    NTSTATUS                 N = STATUS_SUCCESS;

    do {

      DEBUGPRINT( DEBUG_OPTIONS,
                  ( "Checking realmflag \"%ws\"...\n",
                    *pFlagCursor ) );

      // first, try to convert to hex.  

      id = wcstoul( *pFlagCursor,
                    &Cursor,
                    0 ); // use defaults

      if ( *Cursor != '\0' ) {

        if ( !LookupRealmFlagByName( *pFlagCursor,
                                     &pMap ) ) {

          printf( "Unknown Realm Flag: \"%ws\"\n",
                  *pFlagCursor );

          N = STATUS_INVALID_PARAMETER;
          break;

        } else {

          id = pMap->Id;

        }

      } // otherwise, the work's already been done.

      pFlagCursor++;
      *pulFlags |= id;

    } while( *pFlagCursor != NULL );

    return N;

}

NTSTATUS
SetRealmFlags( IN LPWSTR *Params ) {

    LPWSTR   RealmName  = Params[ 0 ];
    ULONG    RealmFlags = 0;
    NTSTATUS N          = STATUS_SUCCESS; // 279621: this was uninitialized.

    if( Params[1] != NULL )
    {
	N = ResolveRealmFlags( Params+1,
			       &RealmFlags );

	if ( NT_SUCCESS( N ) ) 
	{
	    N = SetRealmFlags( RealmName,
			       RealmFlags );
	}
    }
    else // Clear all realm flags
    {
	SetRealmFlags( RealmName, 0 );
    }
    
    return N;

}

NTSTATUS
AddRealmFlags( IN LPWSTR *Params ) {

    LPWSTR   RealmName  = Params[ 0 ];
    ULONG    RealmFlags = 0;
    NTSTATUS N;

    N = GetRealmFlags( RealmName,
                       &RealmFlags );

    if ( NT_SUCCESS( N ) ) {
      N = ResolveRealmFlags( Params+1,
                             &RealmFlags );

      if ( NT_SUCCESS( N ) ) {
        
        N = SetRealmFlags( RealmName,
                           RealmFlags );

      }
    }

    return N;

}

    

NTSTATUS
DelRealmFlags( IN LPWSTR *Params ) {

    LPWSTR   RealmName  = Params[ 0 ];
    ULONG    RealmFlags = 0;
    ULONG    DeleteFlags = 0;
    NTSTATUS N;

    N = GetRealmFlags( RealmName,
                       &RealmFlags );
    
    if ( NT_SUCCESS( N ) ) {
      N = ResolveRealmFlags( Params+1,
                             &DeleteFlags );

      if ( NT_SUCCESS( N ) ) {
        
        N = SetRealmFlags( RealmName,
                           RealmFlags &~ DeleteFlags );

      }
    }

    return N;

}