KLHClient.cp   [plain text]


#include <string.h>

#include <Kerberos/KerberosSupport.h>
#include <Kerberos/AEClassicWorkaround.h>
#include <Kerberos/CredentialsCacheInternal.h>

#include <Carbon/Carbon.h>

#include "KerberosLoginHelper.h"
#include "UString.h"
#include "UKLEnvironment.h"
#include "KLAppearanceIdleCursor.h"
#include "KLApplicationOptions.h"
#include <unistd.h>

static KLBoolean KLHIdleHaveEventFilter ();
static pascal KLBoolean KLHIdleCallback (
    EventRecord *theEvent, 
    SInt32 *sleepTime, 
    RgnHandle *mouseRgn);

static KLStatus QuitKerberosLoginHelper (AEDesc *helperDesc);
static KLStatus LaunchKerberosLoginHelper (AEDesc	*helperDesc);
   
static AEIdleUPP 				GetKLHIdleCallbackUPP (void);
static AEIdleUPP 				gKLHIdleCallbackUPP = NULL;
static KLAppearanceIdleCursor 	*gIdleCursor = NULL;


static KLBoolean
KLHIdleHaveEventFilter ()
{
	KLApplicationOptions options;
	
	UKLApplicationOptions::GetApplicationOptions (&options);
	return (options.eventFilter != nil);
}

static pascal Boolean 
KLHIdleCallback (
    EventRecord *theEvent, 
    SInt32 *sleepTime, 
    RgnHandle *mouseRgn)
{
	KLApplicationOptions options;
	
	UKLApplicationOptions::GetApplicationOptions (&options);
	if ((options.eventFilter) == nil && (gIdleCursor != NULL)) {
		gIdleCursor->IdleCursor ();
	}

	IdleHandleEvent (theEvent);
#warning Shouldnt hard code value for event sleep
	*sleepTime = 10;
	*mouseRgn = NULL;
	
	return false;
}

static AEIdleUPP 
GetKLHIdleCallbackUPP (void)
{
	if (gKLHIdleCallbackUPP == NULL) {
		gKLHIdleCallbackUPP = NewAEIdleUPP (KLHIdleCallback);
		Assert_ (gKLHIdleCallbackUPP != NULL);
	}
	return gKLHIdleCallbackUPP;
}

void 
DisposeKLHIdleCallbackUPP (void)
{
	if (gKLHIdleCallbackUPP != NULL)
		DisposeAEIdleUPP (gKLHIdleCallbackUPP);
}

static KLStatus
LaunchKerberosLoginHelper (
	AEDesc	*helperDesc)
{
	KLStatus 			err = noErr;
	LaunchParamBlockRec	launchParams;
    FSSpec				kerberosLoginHelperSpec;
    ProcessInfoRec		pir;
        
    // If we are running under Classic and the KLH in X has a matching version...
    if (::RunningUnderClassic () && (::CheckClassicIntegrationCompatibility () == noErr)) {
        // We need to launch the yellow-side Kerberos Login Helper
		// Before we launch it, we have to find it, and to do that 
		// we have to discover where the system is. We do that by looking
		// at where the Finder is, since there is no other way that I know
		// of to find the X volume from Classic
		
		static bool sHaveYellowVolume = false;
		static SInt16 sYellowVolumeRefNum = 0;
		
		if (!sHaveYellowVolume) {
			FSSpec appSpec;
			pir.processInfoLength = sizeof (pir);
			pir.processAppSpec = &appSpec;
			pir.processName = nil;
			ProcessSerialNumber psn = {0, kNoProcess};
            
			while (GetNextProcess (&psn) == noErr) {
				if (GetProcessInformation (&psn, &pir) == noErr) {
					if ((pir.processSignature == 'MACS') && (pir.processType == 'FNDR')) {
						sHaveYellowVolume = true;
						sYellowVolumeRefNum = appSpec.vRefNum;
						break;
					}
				}
			}
		}
		
		if (!sHaveYellowVolume) {
			return fnfErr;
		}
		
		err = FSMakeFSSpec (sYellowVolumeRefNum, fsRtDirID, "\p:System:Library:Frameworks:Kerberos.framework:Versions:A:Servers:KerberosLoginHelper.app:Contents:MacOS:KerberosLoginHelper", &kerberosLoginHelperSpec);
		if (err != noErr) {
			return err;
		}
        launchParams.launchAppSpec = &kerberosLoginHelperSpec;
    } else {
        // We are booted into Mac OS 9... launch the library
        launchParams.launchAppSpec = &gLibraryFile;
    }
    
	launchParams.launchAppParameters	= nil;	
	launchParams.launchBlockID = extendedBlock;
	launchParams.launchEPBLength = extendedBlockLen;
	launchParams.launchFileFlags = 0;
	launchParams.launchControlFlags = launchNoFileFlags | launchContinue;
	launchParams.launchAppParameters = nil;

	err = LaunchApplication (&launchParams);
	dprintf ("Launching Kerberos Login Helper returned '%ld'\n", err);
		
    // Wait for KLH to exist.  
    pir.processInfoLength = sizeof (pir);
	pir.processName = nil;
	pir.processAppSpec = nil;
    while (GetProcessInformation (&launchParams.launchProcessSN, &pir) != noErr) {
        EventRecord event;
        
        WaitNextEvent (everyEvent, &event, 1, nil);
        IdleHandleEvent (&event);
    }

	if (err == noErr) {
		// On classic we use creator-based address because John Montbriand said so
		if (::RunningUnderClassic () && (::CheckClassicIntegrationCompatibility () == noErr)) {
			OSType		helperSignature = FOUR_CHAR_CODE ('KLHa');
			err = AECreateDesc (typeApplSignature, (Ptr) &helperSignature, 
								sizeof (helperSignature), helperDesc);
		} else {
			err = AECreateDesc (typeProcessSerialNumber, (Ptr) &launchParams.launchProcessSN, 
								sizeof (ProcessSerialNumber), helperDesc);
		}
	}
    
	return err;
}

static KLStatus
QuitKerberosLoginHelper (AEDesc *helperDesc)
{		
	OSStatus err = noErr;
	
	// Quit LoginHelper
	AppleEvent	quitEvent = {typeNull, nil};

	/* Quit the Kerberos Login Helper now that we are done */
	err = AECreateAppleEvent (kCoreEventClass, kAEQuitApplication, helperDesc, 
								kAutoGenerateReturnID, kAnyTransactionID, &quitEvent);
	
	if (err == noErr) {
		if (::RunningUnderClassic () && (KLHIdleHaveEventFilter () == false)) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
		err = AESend (&quitEvent, nil, kAENoReply, kAENormalPriority, 
					kNoTimeOut, GetKLHIdleCallbackUPP (), nil);
		dprintf("Sending a quit event to Kerberos Login Helper returned '%ld'\n", err);
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
	}
	
	if (quitEvent.dataHandle != nil)
		AEDisposeDesc (&quitEvent);

	return err;
}	


KLStatus
KLHAcquireNewInitialTickets (
	KLPrincipal		inPrincipal,
	KLPrincipal*	outPrincipal,
	char**			outCacheName)
{
	AppleEvent 	helperEvent = {typeNull, nil};
	AppleEvent 	replyEvent = {typeNull, nil};
	AEDesc 		replyDesc = {typeNull, nil};
	AEDesc 		helperDesc = {typeNull, nil};
	char*		klPrincipalString = nil;
	
	OSStatus 	err = noErr;
	KLStatus	klErr = klNoErr;

	if (gKerberosLoginDialogExists) {
		// There is already a login dialog on screen somewhere
		return klDialogAlreadyExistsErr;
	}

	/* Launch the Kerberos Login Helper and get a AE descriptor for it */	
	err = LaunchKerberosLoginHelper (&helperDesc);

	if (err == noErr) {
		err = AECreateAppleEvent (kKLHEventClass, kAEAcquireNewInitialTickets, &helperDesc, 
									kAutoGenerateReturnID, kAnyTransactionID, &helperEvent);
	}
	
	if ((err == noErr) && (inPrincipal != nil)) {
		err = KLGetStringFromPrincipal (inPrincipal, kerberosVersion_V5, &klPrincipalString);
		
		if (err == noErr) {
			err = AEPutParamPtr (&helperEvent, keyKLPrincipal, typeKLPrincipalString, 
									klPrincipalString, (Size)(strlen (klPrincipalString) + 1));
		}
	}
	
#if DCON
	dprintae (&helperEvent);
#endif

	if (err == noErr) {
		if (::RunningUnderClassic () && (KLHIdleHaveEventFilter () == false)) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
		err = AESendWorkaround (&helperEvent, &replyEvent, kAEWaitReply | kAECanInteract, kAENormalPriority, 
						kNoTimeOut, GetKLHIdleCallbackUPP (), nil);
		dprintf ("Sending a kAEAcquireNewInitialTickets event to Kerberos Login Helper returned '%ld'\n", err);
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
	}
	
	if (err == noErr) {
		DescType	actualType;
		Size		actualSize;
		
		/* Check whether we have an error reply or not */
		err = AEGetParamPtr (&replyEvent, keyKLError, typeLongInteger, &actualType, &klErr, sizeof (klErr), &actualSize);
		if (err != noErr) {
			/* KLAcquireNewInitialTickets returned noErr */
			
			if (outPrincipal != nil) {
				/* extract the principal */
				Size 		principalSize = 0;
				DescType	principalType;
				Handle		principalHandle = nil;
				
				err = AESizeOfParam (&replyEvent, keyKLPrincipal, &principalType, &principalSize);
				
				principalHandle = NewHandle (principalSize);
				if (principalHandle == nil ) {
					err = klMemFullErr;
				}
				
				if (err == noErr) {
					HLock (principalHandle);
					err = AEGetParamPtr (&replyEvent, keyKLPrincipal, principalType, &actualType, *principalHandle, principalSize, &actualSize);
				}
				
				if (err == noErr) {
					char *principal = *(char **) principalHandle;
					dprintf ("Kerberos Login Helper returned principal name: '%s'\n", principal);
					err = KLCreatePrincipalFromString (principal, kerberosVersion_V5, outPrincipal);
				}
				
				if (principalHandle != nil) {
					DisposeHandle (principalHandle);
				}
			}
			
			if (outCacheName != nil) {
				/* extract the cache name */
				Size 		cacheNameSize = 0;
				DescType	cacheNameType;
				Handle		cacheNameHandle = nil;
				
				err = AESizeOfParam (&replyEvent, keyKLCacheName, &cacheNameType, &cacheNameSize);
				
				cacheNameHandle = NewHandle (cacheNameSize);
				if (cacheNameHandle == nil ) {
					err = klMemFullErr;
				}
				
				if (err == noErr) {
					HLock (cacheNameHandle);
					err = AEGetParamPtr (&replyEvent, keyKLCacheName, cacheNameType, &actualType, 
                                        *cacheNameHandle, cacheNameSize, &actualSize);
				}
				
				if (err == noErr) {
					char *cacheName = *(char **) cacheNameHandle;
					*outCacheName = UString::NewCString (cacheName);
					dprintf ("Kerberos Login Helper returned cache name: '%s'\n", cacheName);
				}
				
				if (cacheNameHandle != nil) {
					DisposeHandle (cacheNameHandle);
				}
			}
		}
	}
	
	/* Quit Kerberos Login Helper */
	err = QuitKerberosLoginHelper (&helperDesc);
    
    /* Unlike the other KLH operations, this one can modify the CCache.  
       On Classic, we must update the cache synchronously */
    if (::RunningUnderClassic ()) {
		if (KLHIdleHaveEventFilter () == false) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
        __CredentialsCacheInternalSyncWithYellowCache (GetKLHIdleCallbackUPP ());
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
    }

	/* Cleanup memory */
	if (helperDesc.dataHandle != nil)
		AEDisposeDesc (&helperDesc);

	if (helperEvent.dataHandle != nil)
		AEDisposeDesc (&helperEvent);
		
	if (replyDesc.dataHandle != nil)
		AEDisposeDesc (&replyDesc);

	if (replyEvent.dataHandle != nil)
		AEDisposeDesc (&replyEvent);
			
	if (klPrincipalString != nil)
		KLDisposeString (klPrincipalString);
	
	if (err != noErr)
		return err;
	else
		return klErr;
}



KLStatus
KLHChangePassword (
	KLPrincipal		inPrincipal)
{
	AppleEvent 	helperEvent = {typeNull, nil};
	AppleEvent 	replyEvent = {typeNull, nil};
	AEDesc 		replyDesc = {typeNull, nil};
	AEDesc 		principalDesc = {typeNull, nil};
	AEDesc 		helperDesc = {typeNull, nil};
	char*		klPrincipalString = nil;
	
	OSStatus 	err = noErr;
	KLStatus	klErr = klNoErr;

	if (gKerberosLoginDialogExists) {
		// There is already a login dialog on screen somewhere
		return klDialogAlreadyExistsErr;
	}

	/* Launch the Kerberos Login Helper and get a AE descriptor for it */	
	err = LaunchKerberosLoginHelper (&helperDesc);

	if (err == noErr) {
		err = AECreateAppleEvent (kKLHEventClass, kAEChangePassword, &helperDesc, kAutoGenerateReturnID, kAnyTransactionID, &helperEvent);
	}
	
	if ((err == noErr) && (inPrincipal != nil)) {
		err = KLGetStringFromPrincipal (inPrincipal, kerberosVersion_V5, &klPrincipalString);
		
		if (err == noErr) {
			err = AEPutParamPtr (&helperEvent, keyKLPrincipal, typeKLPrincipalString, 
                                    klPrincipalString, (Size)(strlen (klPrincipalString) + 1));
		}
	}

#if DCON
	dprintae (&helperEvent);
#endif

	if (err == noErr) {
		if (::RunningUnderClassic () && (KLHIdleHaveEventFilter () == false)) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
		err = AESendWorkaround (&helperEvent, &replyEvent, kAEWaitReply | kAECanInteract, kAENormalPriority, 
						kNoTimeOut, GetKLHIdleCallbackUPP (), nil);
		dprintf("Sending a kAEChangePassword event to Kerberos Login Helper returned '%ld'\n", err);
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
	}
	
	if (err == noErr) {
		DescType	actualType;
		Size		actualSize;
		
		/* Check whether we have an error reply or not */
		err = AEGetParamPtr (&replyEvent, keyKLError, typeLongInteger, &actualType, 
                            &klErr, sizeof (klErr), &actualSize);
	}
		
	/* Quit Kerberos Login Helper */
	err = QuitKerberosLoginHelper (&helperDesc);

	/* Cleanup memory */
	if (helperDesc.dataHandle != nil)
		AEDisposeDesc (&helperDesc);

	if (helperEvent.dataHandle != nil)
		AEDisposeDesc (&helperEvent);
		
	if (principalDesc.dataHandle != nil)
		AEDisposeDesc (&principalDesc);

	if (replyDesc.dataHandle != nil)
		AEDisposeDesc (&replyDesc);

	if (replyEvent.dataHandle != nil)
		AEDisposeDesc (&replyEvent);
			
	if (klPrincipalString != nil)
		KLDisposeString (klPrincipalString);
		
	if (err != noErr)
		return err;
	else
		return klErr;
}


KLStatus
KLHHandleError (
		KLStatus					inError,
		KLDialogIdentifier			inDialogIdentifier,
		KLBoolean					inShowAlert)
{
	AppleEvent 	helperEvent = {typeNull, nil};
	AppleEvent 	replyEvent = {typeNull, nil};
	AEDesc 		replyDesc = {typeNull, nil};
	AEDesc 		helperDesc = {typeNull, nil};
	
	OSStatus 	err = noErr;
	KLStatus	klErr = klNoErr;

	if (gKerberosLoginDialogExists) {
		// There is already a login dialog on screen somewhere
		return klDialogAlreadyExistsErr;
	}

	/* Launch the Kerberos Login Helper and get a AE descriptor for it */	
	err = LaunchKerberosLoginHelper (&helperDesc);

	if (err == noErr) {
		err = AECreateAppleEvent (kKLHEventClass, kAEHandleError, &helperDesc, 
                                    kAutoGenerateReturnID, kAnyTransactionID, &helperEvent);
	}
	
	/* Add the inError argument */
	if (err == noErr) {
		err = AEPutParamPtr (&helperEvent, keyKLError, typeSInt32, &inError, sizeof(inError));
	}
	
	/* Add the inDialogIdentifer argument */
	if (err == noErr) {
		err = AEPutParamPtr (&helperEvent, keyKLDialogIdentifier, typeUInt32, 
                            &inDialogIdentifier, sizeof(inDialogIdentifier));
	}

	/* Add the inShowAlert argument */
	if (err == noErr) {
		err = AEPutParamPtr (&helperEvent, keyKLShowAlert, typeBoolean, &inShowAlert, sizeof(inShowAlert));
	}
	
#if DCON
	dprintae (&helperEvent);
#endif

	if (err == noErr) {
		if (::RunningUnderClassic () && (KLHIdleHaveEventFilter () == false)) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
		err = AESendWorkaround (&helperEvent, &replyEvent, kAEWaitReply | kAECanInteract, kAENormalPriority, 
						kNoTimeOut, GetKLHIdleCallbackUPP (), nil);
		dprintf("Sending a kAEHandleError event to Kerberos Login Helper returned '%ld'\n", err);
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
	}
	
	if (err == noErr) {
		DescType	actualType;
		Size		actualSize;
		
		/* Check whether we have an error reply or not */
		err = AEGetParamPtr (&replyEvent, keyKLError, typeLongInteger, &actualType, 
                            &klErr, sizeof (klErr), &actualSize);
	}

	/* Quit Kerberos Login Helper */
	err = QuitKerberosLoginHelper (&helperDesc);

	/* Cleanup memory */
	if (helperDesc.dataHandle != nil)
		AEDisposeDesc (&helperDesc);

	if (helperEvent.dataHandle != nil)
		AEDisposeDesc (&helperEvent);
		
	if (replyDesc.dataHandle != nil)
		AEDisposeDesc (&replyDesc);

	if (replyEvent.dataHandle != nil)
		AEDisposeDesc (&replyEvent);
			
	if (err != noErr)
		return err;
	else
		return klErr;
}

KLStatus
KLHCancelAllDialogs ()
{
	AppleEvent 	helperEvent = {typeNull, nil};
	AppleEvent 	replyEvent = {typeNull, nil};
	AEDesc 		replyDesc = {typeNull, nil};
	AEDesc 		helperDesc = {typeNull, nil};
	
	OSStatus 	err = noErr;
	KLStatus	klErr = klNoErr;

	/* Launch the Kerberos Login Helper and get a AE descriptor for it */	
	err = LaunchKerberosLoginHelper (&helperDesc);

	if (err == noErr) {
		err = AECreateAppleEvent (kKLHEventClass, kAECancelAllDialogs, &helperDesc, kAutoGenerateReturnID, kAnyTransactionID, &helperEvent);
	}
	
#if DCON
	dprintae (&helperEvent);
#endif

	if (err == noErr) {
		if (::RunningUnderClassic () && (KLHIdleHaveEventFilter () == false)) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
		err = AESendWorkaround (&helperEvent, &replyEvent, kAEWaitReply | kAECanInteract, kAENormalPriority, 
						kNoTimeOut, GetKLHIdleCallbackUPP (), nil);
		dprintf("Sending a kAECancelAllDialogs event to Kerberos Login Helper returned '%ld'\n", err);
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
	}
	
	if (err == noErr) {
		DescType	actualType;
		Size		actualSize;
		
		/* Check whether we have an error reply or not */
		err = AEGetParamPtr (&replyEvent, keyKLError, typeLongInteger, &actualType, 
                                &klErr, sizeof (klErr), &actualSize);
	}

	/* Quit Kerberos Login Helper */
	err = QuitKerberosLoginHelper (&helperDesc);

	/* Cleanup memory */
	if (helperDesc.dataHandle != nil)
		AEDisposeDesc (&helperDesc);

	if (helperEvent.dataHandle != nil)
		AEDisposeDesc (&helperEvent);
		
	if (replyDesc.dataHandle != nil)
		AEDisposeDesc (&replyDesc);

	if (replyEvent.dataHandle != nil)
		AEDisposeDesc (&replyEvent);
			
	if (err != noErr)
		return err;
	else
		return klErr;
}

krb5_error_code KLHPrompter (
			krb5_context	context,
			void 			*data,
	const	char			*name,
	const	char			*banner,
			int				num_prompts,
			krb5_prompt		prompts[])
{
#pragma unused(context)
#pragma unused(data)

	AppleEvent 	helperEvent = {typeNull, nil};
	AppleEvent 	replyEvent = {typeNull, nil};
	AEDesc 		replyDesc = {typeNull, nil};
	AEDesc 		helperDesc = {typeNull, nil};
	UInt32	 	i;
	OSStatus 	err = noErr;
	KLStatus	klErr = klNoErr;

	if (gKerberosLoginDialogExists) {
		// There is already a login dialog on screen somewhere
		return klDialogAlreadyExistsErr;
	}

	/* Launch the Kerberos Login Helper and get a AE descriptor for it */	
	err = LaunchKerberosLoginHelper (&helperDesc);

	if (err == noErr) {
		err = AECreateAppleEvent (kKLHEventClass, kAEPrompter, &helperDesc, 
									kAutoGenerateReturnID, kAnyTransactionID, &helperEvent);
	}
	
	/* We ignore the context and data arguments for now.  
	   The context is @#$&%*#!! hard to flatten and the data isn't used */
	
	/* Add the name argument */
	if ((err == noErr) && (name != NULL)) {
		err = AEPutParamPtr (&helperEvent, keyKLPrompterName, typeKLPrompterName, 
								name, (Size) (strlen (name) + 1));
	}

	/* Add the banner argument */
	if ((err == noErr) && (banner != NULL)) {
		err = AEPutParamPtr (&helperEvent, keyKLPrompterBanner, typeKLPrompterBanner, 
							banner, (Size) (strlen (banner) + 1));
	}
	
	/* Add the num_prompts argument */
	if (err == noErr) {
		err = AEPutParamPtr (&helperEvent, keyKLPrompterNumPrompts, typeKLPrompterNumPrompts, 
							&num_prompts, sizeof (num_prompts));
	}

	/* Add the prompter strings argument */
	if (err == noErr) {
		UInt32 	 promptSize = 0;
		char	*promptStrings = NULL;
		char	*currentPrompt;
		
		for (i = 0; i < num_prompts; i++) {
			promptSize += strlen (prompts[i].prompt) + 1;
		}
		
		promptStrings = (char *) malloc (promptSize * sizeof(char));
		if (promptStrings == NULL) 
			err = memFullErr;
		
		if (err == noErr) {
			currentPrompt = promptStrings;
			for (i = 0; i < num_prompts; i++) {
				memcpy (currentPrompt, prompts[i].prompt, strlen (prompts[i].prompt) + 1);
				currentPrompt += strlen (prompts[i].prompt) + 1;
			}

			err = AEPutParamPtr (&helperEvent, keyKLPrompterPromptStrings, typeKLPrompterStrings, 
								promptStrings, (Size) promptSize);
								
			free (promptStrings);
		}
	}
	
	/* Add the hidden flags */
	if (err == noErr) {
		std::string	promptBooleans;
		
		for (i = 0; i < num_prompts; i++) {
			promptBooleans += prompts[i].hidden ? '1' : '0';
		}

		err = AEPutParamPtr (&helperEvent, keyKLPrompterPromptHidden, typeKLPrompterBooleans, 
							promptBooleans.c_str(), (Size) (promptBooleans.length() + 1));
	}

	/* Add the max sizes of the replies. Replies are allocated for the prompter by krb5.  
	   We need to know what to allocate for the prompter. */
	if (err == noErr) {
		int		*promptReplyMaxSizes = (int *) malloc (num_prompts * sizeof (int));
		
		if (promptReplyMaxSizes == NULL)
			err = memFullErr;
		
		if (err == noErr) {
			for (i = 0; i < num_prompts; i++) {
				memcpy (&promptReplyMaxSizes[i], &prompts[i].reply->length, sizeof (prompts[i].reply->length));
			}

			err = AEPutParamPtr (&helperEvent, keyKLPrompterReplyMaxSizes, typeKLPrompterMaxSizes, 
								promptReplyMaxSizes, (Size) (num_prompts * sizeof (int)));
								
			free (promptReplyMaxSizes);
		}
	}

	
#if DCON
	dprintae (&helperEvent);
#endif

	if (err == noErr) {
		if (::RunningUnderClassic () && (KLHIdleHaveEventFilter () == false)) {
			gIdleCursor = new KLAppearanceIdleCursor;
		}
		err = AESendWorkaround (&helperEvent, &replyEvent, kAEWaitReply | kAECanInteract, kAENormalPriority, 
						kNoTimeOut, GetKLHIdleCallbackUPP (), nil);
		dprintf("Sending a kAEAcquireNewInitialTickets event to Kerberos Login Helper returned '%ld'\n", err);
		if (gIdleCursor != NULL) {
			delete gIdleCursor;
			gIdleCursor = NULL;
		}
	}
	
	if (err == noErr) {
		DescType	actualType;
		Size		actualSize;
		
		/* Check whether we have an error reply or not */
		err = AEGetParamPtr (&replyEvent, keyKLError, typeLongInteger, &actualType, 
                            &klErr, sizeof (klErr), &actualSize);
		if (err != noErr) {
			/* KLPrompter returned noErr */
			/* extract the replies */
			
			Size 		prompterRepliesSize = 0;
			DescType	prompterReplyType;
			Handle		prompterReplyHandle = nil;
			
			err = AESizeOfParam (&replyEvent, keyKLPrincipal, &prompterReplyType, &prompterRepliesSize);
			
			prompterReplyHandle = NewHandle (prompterRepliesSize);
			if (prompterReplyHandle == nil ) {
				err = klMemFullErr;
			}
			
			if (err == noErr) {
				HLock (prompterReplyHandle);
				err = AEGetParamPtr (&replyEvent, keyKLPrompterReplies, prompterReplyType, &actualType, 
									*prompterReplyHandle, prompterRepliesSize, &actualSize);
			}
			
			/* The replies are just a bunch of strings appended to each other.
			   The dialog is guaranteed to only return valid c strings to us.  */
			if (err == noErr) {
				UInt32	 i;
				char	*currentReply = *prompterReplyHandle;

				for (i = 0; i < num_prompts; i++) {
					UInt32	replyLength = strlen (currentReply);
				
					Assert_ (replyLength < prompts[i].reply->length); 
					memcpy (prompts[i].reply->data, currentReply, replyLength + 1);
					prompts[i].reply->length = (Size) replyLength;
					
					currentReply += replyLength + 1;
				}
			}
		}
	}
	
	/* Quit Kerberos Login Helper */
	err = QuitKerberosLoginHelper (&helperDesc);

	/* Cleanup memory */
	if (helperDesc.dataHandle != nil)
		AEDisposeDesc (&helperDesc);

	if (helperEvent.dataHandle != nil)
		AEDisposeDesc (&helperEvent);
		
	if (replyDesc.dataHandle != nil)
		AEDisposeDesc (&replyDesc);

	if (replyEvent.dataHandle != nil)
		AEDisposeDesc (&replyEvent);

	if (err != noErr)
		return err;
	else
		return klErr;
}