|
|
/*++
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;
}
|