Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

882 lines
21 KiB

/*++
KEYTAB.C
Implementation of the actual keytab routines.
Copyright (C) 1997 Microsoft Corporation
Created 01-10-1997 DavidCHR
--*/
#include "master.h"
#include "keytab.h"
#include "keytypes.h"
#include "defs.h"
/* declaring KEYTAB_ALLOC and KEYTAB_FREE lets me hook into these
routines whenever I want. when it's done, I'll just #def them. */
PVOID
KEYTAB_ALLOC ( KTLONG32 numBytes ) {
return malloc(numBytes);
}
VOID
KEYTAB_FREE ( PVOID toFree ) {
free(toFree);
}
PKTENT
CloneKeyEntry( PKTENT pEntry ) {
KTLONG32 i;
PKTENT p=NULL;
BOOL ret=FALSE;
p = (PKTENT) KEYTAB_ALLOC( sizeof( KTENT ) );
BREAK_IF( p == NULL,
"Failed to alloc base key struct",
cleanup );
memcpy( p, pEntry, sizeof(KTENT) );
p->Components = NULL; // initialize these in case of failure.
p->KeyData = NULL;
p->Realm = (PCHAR) KEYTAB_ALLOC( p->szRealm );
BREAK_IF( p->Realm == NULL, "Failed to alloc realm data", cleanup );
memcpy( p->Realm, pEntry->Realm, p->szRealm );
p->Components = (PKTCOMPONENT) KEYTAB_ALLOC( p->cEntries *
sizeof(KTCOMPONENT) );
BREAK_IF( p->Components == NULL, "Failed to alloc components", cleanup );
for ( i = 0 ; i < p->cEntries ; i++ ) {
p->Components[i].szComponentData =
pEntry->Components[i].szComponentData;
memcpy( p ->Components[i].Component,
pEntry->Components[i].Component,
p ->Components[i].szComponentData );
}
p->KeyData = (PK5_OCTET) KEYTAB_ALLOC ( p->KeyLength );
BREAK_IF( p->KeyData == NULL, "Failed to alloc keydata", cleanup );
memcpy( p->KeyData, pEntry->KeyData, p->KeyLength );
return p;
cleanup:
FreeKeyEntry(p);
return NULL;
}
/* base linklist operations */
BOOL
AddEntryToKeytab( PKTFILE Keytab,
PKTENT Entry,
BOOL copy ) {
PKTENT p;
if (copy) {
p = CloneKeyEntry( Entry );
} else {
p = Entry;
}
if (p == NULL ) {
return FALSE;
}
if ( NULL == Keytab->FirstKeyEntry ) {
Keytab->FirstKeyEntry = Keytab->LastKeyEntry = p;
} else {
Keytab->LastKeyEntry->nextEntry = p;
Keytab->LastKeyEntry = p;
}
return TRUE;
}
BOOL
RemoveEntryFromKeytab( PKTFILE Keytab,
PKTENT Entry,
BOOL dealloc ) {
if ( (NULL == Keytab) || ( NULL == Entry ) ) {
return FALSE;
}
if ( Keytab->FirstKeyEntry == Entry ) {
// removing the first key
Keytab->FirstKeyEntry = Entry->nextEntry;
if ( Entry->nextEntry == NULL ) {
// we're the ONLY entry.
Keytab->LastKeyEntry = NULL;
}
} else {
BOOL found=FALSE;
PKTENT p;
// scroll through the keys, looking for this one.
// not very efficient, but keytabs shouldn't get very big.
for (p = Keytab->FirstKeyEntry;
p != NULL;
p = p->nextEntry ) {
if (p->nextEntry == Entry) {
found = TRUE;
p->nextEntry = Entry->nextEntry;
break;
}
}
if (!found) {
// wasn't in the linklist.
return FALSE;
}
if (Entry->nextEntry == NULL ) {
// removing the last key entry.
Keytab->LastKeyEntry = p;
}
}
if (dealloc) {
FreeKeyEntry(Entry);
}
return TRUE;
}
VOID
FreeKeyEntry( PKTENT pEntry) {
KTLONG32 i;
if (pEntry != NULL) {
if (pEntry->Realm != NULL ) {
KEYTAB_FREE(pEntry->Realm);
}
if (pEntry->KeyData != NULL) {
KEYTAB_FREE(pEntry->KeyData);
}
if (pEntry->Components != NULL) {
for (i = 0; i < pEntry->cEntries ; i++ ) {
if ( pEntry->Components[i].Component != NULL ) {
KEYTAB_FREE(pEntry->Components[i].Component);
}
}
KEYTAB_FREE(pEntry->Components);
}
KEYTAB_FREE(pEntry );
}
}
VOID
FreeKeyTab( PKTFILE pktf ) {
PKTENT pEntry=NULL;
PKTENT next=NULL;
if (pktf != NULL) {
for (pEntry = pktf->FirstKeyEntry ;
pEntry != NULL;
pEntry = next ) {
KTLONG32 i;
next = pEntry->nextEntry; /* must do this, because we're freeing
as we go */
FreeKeyEntry( pEntry );
KEYTAB_FREE(pEntry );
}
KEYTAB_FREE(pktf);
}
}
/* These macros make this somewhat LESS painful */
#define READ(readwhat, errormsg, statusmsg) { \
debug(statusmsg); \
BREAK_IF( !Read(hFile, &(readwhat), sizeof(readwhat), 1), \
errormsg, cleanup); \
debug("ok\n"); \
}
#define READSTRING(readwhat, howlong, errormsg, statusmsg) { \
debug(statusmsg); \
BREAK_IF( !Read(hFile, readwhat, sizeof(CHAR), howlong), \
errormsg, cleanup); \
debug("ok\n"); \
}
#define WRITE(writewhat, description) { \
debug("writing %hs...", description); \
BREAK_IF( !Write(hFile, &(writewhat), sizeof(writewhat), 1), \
"error writing " description, cleanup); \
debug("ok\n"); \
}
#define WRITE_STRING(writewhat, howlong, description) { \
debug("writing %hs...", description); \
BREAK_IF( !Write(hFile, writewhat, sizeof(CHAR), howlong), \
"error writing " description, cleanup); \
debug("ok\n"); \
}
#define WRITE_X( size, marshaller, writewhat, description ) { \
K5_INT##size k5_marshaller_variable; \
k5_marshaller_variable = marshaller( writewhat );\
WRITE( k5_marshaller_variable, description );\
}
// NBO-- Network Byte Order
#define WRITE_NBO( writewhat, description) {\
switch( sizeof( writewhat ) ) {\
case 1: /* marshall a char? */\
debug("marshalling a char(?)...");\
WRITE( writewhat, description );\
break;\
case 2:\
debug( #writewhat ": marshalling a short...");\
WRITE_X( 16, htons, ((unsigned short)writewhat), description);\
break;\
case 4:\
debug( #writewhat ": marshalling a long...");\
WRITE_X( 32, htonl, writewhat, description);\
break;\
default:\
fprintf(stderr, "Not written: argument is of unhandled size (%d)\n",\
sizeof(writewhat));\
}}
/* Write:
helper function to write raw bytes to disk. Takes:
hFile: handle to a file open for writing.
source: pointer to data to be written to the file
szSource: size of one data element in Source
numSources: number of data elements in Source
(basically, it tries to write szSource * numSources of raw bytes
from source to the file at hFile).
returns TRUE if it succeeds, and FALSE otherwise.
*/
BOOL
Write( IN HANDLE hFile,
IN PVOID source,
IN KTLONG32 szSource,
IN KTLONG32 numSources /* =1 */ ) {
#ifdef WINNT /* Windows NT implementation of the file write call */
KTLONG32 temp;
KTLONG32 i;
temp = szSource * numSources;
debug("(writing %d bytes: ", temp );
for (i = 0; i < temp ; i++ ) {
unsigned char byte;
byte = ((PCHAR) source)[i];
debug("%02x", byte );
}
debug(")");
return WriteFile( hFile, source, temp, &temp, NULL );
#else
ssize_t bytesToWrite, bytesWritten;
bytesToWrite = szSource * numSources;
bytesWritten = write( hFile, (const void *)source,
bytesToWrite );
if( bytesWritten == -1 ) {
debug("WARNING: nothing written to the file! Errno = 0x%x / %d\n",
errno , errno );
return FALSE;
}
if ( bytesWritten != bytesToWrite ) {
debug("WARNING: not all bytes made it to the file (?)\n"
" errno = 0x%x / %d\n", errno, errno );
return FALSE;
}
return TRUE;
#endif
}
/* Read:
Semantics and return are the same as for "Write", except that
target is filled with szTarget*numTargets bytes from hFile, and that
hFile must be open for read access.
*/
BOOL
Read( IN HANDLE hFile,
OUT PVOID target,
IN KTLONG32 szTarget,
IN KTLONG32 numTargets/* =1 */) {
BOOL ret = FALSE;
#ifdef WINNT /* the SetFilePointer shinanigens are me trying to check on
how many bytes have ACTUALLY been read/written from the
file */
KTLONG32 temp;
KTLONG32 filepos;
LONG zero=0L;
filepos = SetFilePointer( hFile, 0, &zero, FILE_CURRENT);
debug("reading %d bytes from pos 0x%x...", szTarget * numTargets,
filepos);
ret = ReadFile( hFile,
target,
(szTarget*numTargets),
&temp,
NULL );
if ( !ret ) {
debug( "ReadFile failed: 0x%x\n",
GetLastError() );
} else if ( !temp ) {
debug( "ReadFile read zero bytes. Assuming EOF\n" );
SetLastError( ERROR_HANDLE_EOF );
ret = FALSE;
} else {
temp = SetFilePointer( hFile, 0, &zero, FILE_CURRENT);
if ( filepos == temp ) {
debug("WARNING! file position has not changed!");
SetLastError( ERROR_NO_DATA );
return FALSE;
}
}
#else /* UNIX IMPLEMENTATION-- since read() returns the number of bytes
that we actually read from the file, the
SetFilePointer (fseek) nonsense is not
required. */
ssize_t bytesRead;
ssize_t bytesToRead;
bytesToRead = szTarget * numTargets;
bytesRead = read( hFile, target, bytesToRead );
if ( bytesRead == -1 ) {
debug("WARNING! An error occurred while writing to the file!\n"
"ERRNO: 0x%x / %d.\n", errno, errno );
}
ret = (bytesRead == bytesToRead );
#endif
return ret;
}
BOOL
ReadKeytabFromFile( PKTFILE *ppktfile, // free with FreeKeyTab when done
PCHAR filename ) {
PKTFILE ktfile=NULL;
HANDLE hFile = NULL;
BOOL ret=FALSE;
KTLONG32 i;
BREAK_IF( ppktfile == NULL,
"passed a NULL save-pointer",
cleanup );
debug("Opening keytab file \"%hs\"...", filename);
#ifdef WINNT
hFile = CreateFileA( filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
BREAK_IF ( (NULL == hFile) || ( INVALID_HANDLE_VALUE == hFile ),
"Failed to open file!", cleanup );
#else
hFile = open( filename, O_RDONLY,
/* file mask is 0x550: read-write by user and group */
S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP );
BREAK_IF( hFile == -1,
"Failed to open file!", cleanup );
#endif
debug("ok!\n");
ktfile = (PKTFILE) KEYTAB_ALLOC (sizeof(KTFILE));
/* Prefix bug 439480 resulted from the below BREAK_IF
being interchanged with initialization code. Duhhh. */
BREAK_IF( ktfile == NULL,
"Failed to allocate ktfile",
cleanup );
ktfile->FirstKeyEntry = ktfile->LastKeyEntry = NULL;
READ( ktfile->Version, "Failed to read KTVNO",
"reading KTVNO");
/* Version number is stored in network byte order */
ktfile->Version = ntohs( ktfile->Version);
debug("Keytab version 0x%x\n", ktfile->Version );
do {
PKTENT entry=NULL;
entry = (PKTENT) KEYTAB_ALLOC(sizeof(KTENT));
// PREFIX bug 439481: not checking the result of KEYTAB_ALLOC.
BREAK_IF( !entry,
"Unable to alloc a new KTENT.",
cleanup );
entry->Realm = NULL;
entry->Components = NULL;
entry->KeyData = NULL;
entry->nextEntry = NULL;
#if 1
if ( !Read( hFile,
&(entry->keySize),
sizeof( entry->keySize ),
1 ) ) {
if ( GetLastError() != ERROR_HANDLE_EOF ) {
fprintf( stderr,
"\n ** Failed to read Keytab %hs's leading bytes: 0x%x\n",
filename,
GetLastError() );
} else {
ret = TRUE;
}
break;
}
#else
BREAK_IF( !Read( hFile, &(entry->keySize), sizeof(entry->keySize), 1),
"Failed to read leading bytes (probably done)",
no_more_entries );
#endif
entry->keySize = htonl(entry->keySize);
debug("trash bytes: 0x%x\n", entry->keySize );
/* Quickly perform linklist operation on the new node */
if (NULL == ktfile->FirstKeyEntry) {
ktfile->FirstKeyEntry = ktfile->LastKeyEntry = entry;
} else {
ktfile->LastKeyEntry->nextEntry = entry;
ktfile->LastKeyEntry = entry;
}
READ( entry->cEntries,
"Failed to read key's number of components",
"reading key components...");
entry->cEntries = ntohs( entry->cEntries );
debug("components number %d\n", entry->cEntries );
READ( entry->szRealm,
"Failed to read key's realm size",
"reading key realm size...");
entry->szRealm = ntohs( entry->szRealm );
debug("realm size %d\n", entry->szRealm);
entry->Realm = (PCHAR) KEYTAB_ALLOC( ( entry->szRealm +1 )
* sizeof(CHAR) );
BREAK_IF ( !entry->Realm,
"Could not allocate key's realm storage",
cleanup );
READSTRING( entry->Realm, entry->szRealm,
"Could not read key's realmname",
"reading realmname...");
entry->Realm[ entry->szRealm ] = '\0';
debug("realm: \"%hs\"\n", entry->Realm );
entry->Components = (PKTCOMPONENT) KEYTAB_ALLOC (entry->cEntries *
sizeof(KTCOMPONENT));
BREAK_IF( !entry->Components,
"Could not allocate key components!",
cleanup );
for (i = 0 ; i < entry->cEntries ; i++ ) {
READ( entry->Components[i].szComponentData,
"Failed to read component size for one entry",
"reading key component size...");
entry->Components[i].szComponentData =
ntohs( entry->Components[i].szComponentData );
debug("Component size: %d\n",
entry->Components[i].szComponentData );
entry->Components[i].Component = (PCHAR) KEYTAB_ALLOC (
( entry->Components[i].szComponentData +1 ) *
sizeof(CHAR) );
entry->Components[i].Component[
entry->Components[i].szComponentData ] = '\0';
BREAK_IF( !entry->Components[i].Component,
"Could not allocate entry component storage",
cleanup );
READSTRING( entry->Components[i].Component,
entry->Components[i].szComponentData,
"Failed to read component data",
"reading component data...");
debug("Component data: \"%hs\"\n",
entry->Components[i].Component );
}
READ( entry->PrincType,
"Failed to read principal type",
"reading principal type...");
entry->PrincType = ntohl( entry->PrincType ); // in network byte order
debug("princtype: %d\n", entry->PrincType);
READ( entry->TimeStamp,
"Failed to read entry timestamp",
"reading timestamp...");
entry->TimeStamp = ntohl( entry->TimeStamp ); // also network bytes.
debug("Timestamp 0x%x\n", entry->TimeStamp );
READ( entry->Version,
"Failed to read kvno",
"reading kvno...");
// kvno is in host order already.
READ( entry->KeyType,
"Failed to read entry encryption type",
"reading encryption type...");
entry->KeyType = ntohs( entry->KeyType );
READ( entry->KeyLength,
"Failed to read entry keylength",
"reading keylength... ");
#if 1
entry->KeyLength = ntohs ( entry->KeyLength );
#else // I used to think this was 32-bit.
entry->KeyLength = ntohl ( entry->KeyLength );
#endif
debug("KeyLength is %d\n", entry->KeyLength);
entry->KeyData = (PK5_OCTET) KEYTAB_ALLOC (entry->KeyLength *
sizeof(K5_OCTET));
BREAK_IF( !entry->KeyData,
"Could not allocate entry keydata storage",
cleanup );
READSTRING( entry->KeyData, entry->KeyLength,
"Failed to read entry key data",
"reading key data")
} while (1);
cleanup:
#ifdef WINNT
if ((hFile != NULL) && ( hFile != INVALID_HANDLE_VALUE)) {
CloseHandle(hFile);
}
#else
if (hFile != -1 ) {
close(hFile);
}
#endif
if (ret) {
*ppktfile = ktfile;
} else {
FreeKeyTab( ktfile );
}
return ret;
}
/* define this macro only for DisplayKeytab.
It's a convenience routine to print this field only if the option
is set. */
#define PRINTFIELD( option, format, value ) { if (options & option) { fprintf(stream, format, value); } }
/* DisplayKeytab:
prints out the keytab, using options to define which fields we want
to actually see. (see keytab.hxx for what to put in "options") */
VOID
DisplayKeytab( FILE *stream,
PKTFILE ktfile,
KTLONG32 options) {
KTLONG32 i;
PKTENT ent;
if (options == 0L) {
return;
}
PRINTFIELD(KT_KTVNO, "Keytab version: 0x%x\n", ktfile->Version);
for (ent = ktfile->FirstKeyEntry ;
ent != NULL;
ent = ent->nextEntry ) {
PRINTFIELD( KT_RESERVED, "keysize %d ", ent->keySize );
for (i = 0 ; i < ent->cEntries ; i++ ) {
PRINTFIELD( KT_COMPONENTS,
(i == 0 ? "%hs" : "/%hs"),
ent->Components[i].Component );
}
PRINTFIELD( KT_REALM, "@%hs", ent->Realm );
PRINTFIELD( KT_PRINCTYPE, " ptype %d", ent->PrincType );
PRINTFIELD( KT_PRINCTYPE, " (%hs)", LookupTable( ent->PrincType, &K5PType_Strings ).string);
PRINTFIELD( KT_VNO, " vno %d", ent->Version );
PRINTFIELD( KT_KEYTYPE, " etype 0x%x", ent->KeyType );
PRINTFIELD( KT_KEYTYPE, " (%hs)", LookupTable( ent->KeyType,
&K5EType_Strings ).string );
PRINTFIELD( KT_KEYLENGTH, " keylength %d", ent->KeyLength );
if (options & KT_KEYDATA ) {
fprintf(stream, " (0x" );
for ( i = 0 ; i < ent->KeyLength ; i++ ) {
fprintf(stream, "%02x", ent->KeyData[i] );
}
fprintf(stream, ")" );
}
fprintf(stream, "\n");
}
}
#undef PRINTFIELD // we only need it for that function
/* computes the length of a kerberos keytab for the keySize field */
K5_INT32
ComputeKeytabLength( PKTENT p ) {
K5_INT32 ret=0L;
KTLONG32 i;
// these are the variables within this level
ret = p->szRealm + p->KeyLength;
// these are static
ret += ( sizeof( p->cEntries ) + sizeof(p->szRealm) +
sizeof( p->PrincType ) + sizeof( p->TimeStamp ) +
sizeof( p->Version ) + sizeof (p->KeyLength ) +
sizeof( p->KeyType ) );
for (i = 0 ; i < p->cEntries; i++ ) {
ret += ( p->Components[i].szComponentData +
sizeof(p->Components[i].szComponentData) );
}
debug("ComputeKeytabLength: returning %d\n", ret);
return ret;
}
/* This depends very much on the same keytab model that
the other functions do */
BOOL
WriteKeytabToFile( PKTFILE ktfile,
PCHAR filename ) {
HANDLE hFile = NULL;
BOOL ret=FALSE;
KTLONG32 i;
PKTENT entry;
BREAK_IF( ktfile == NULL,
"passed a NULL save-pointer",
cleanup );
debug("opening keytab file \"%hs\" for write...", filename);
#ifdef WINNT
hFile = CreateFileA( filename,
GENERIC_WRITE,
0L,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
BREAK_IF( ( hFile == INVALID_HANDLE_VALUE ),
"Failed to create file!", cleanup );
#else
hFile = open( filename, O_WRONLY | O_CREAT | O_TRUNC,
/* file mask is 0x550: read-write by user and group */
S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP );
BREAK_IF( hFile == -1,
"Failed to create file!", cleanup );
#endif
debug("ok\n");
WRITE_NBO( ktfile->Version, "KTFVNO" );
for( entry = ktfile->FirstKeyEntry ;
entry != NULL ;
entry = entry->nextEntry ) {
WRITE_NBO( entry->keySize, "key size (in bytes)");
WRITE_NBO( entry->cEntries, "number of components" );
WRITE_NBO( entry->szRealm, "Realm length" );
WRITE_STRING( entry->Realm, entry->szRealm, "Realm data" );
for (i = 0 ; i < entry->cEntries ; i++ ) {
WRITE_NBO( entry->Components[i].szComponentData,
"component datasize");
WRITE_STRING( entry->Components[i].Component,
entry->Components[i].szComponentData,
"component data");
}
WRITE_NBO( entry->PrincType, "Principal Type");
WRITE_NBO( entry->TimeStamp, "Timestamp" );
WRITE( entry->Version, "Key Version Number" );
WRITE_NBO( entry->KeyType, "Key Encryption Type" );
#if 0 // eh?
/* again, this is a kludge to get around the keylength
problem we can't explain */
#endif
ASSERT( sizeof( entry->KeyLength ) == 2 );
WRITE_NBO(entry->KeyLength, "key length" );
WRITE_STRING( entry->KeyData,
entry->KeyLength,
"key data itself" );
}
ret = TRUE;
cleanup:
CloseHandle(hFile);
return ret;
}