/*
* KLSController.m
*
* $Header: /cvs/kfm/KerberosFramework/KerberosLogin/Sources/KerberosLoginServer/KLSController.m,v 1.25 2003/09/12 15:53:09 lxs Exp $
*
* Copyright 2003 Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
#import "KLMachIPC.h"
#import "KerberosLoginIPCServer.h"
#import "KLSLifetimeFormatter.h"
#import "KLSController.h"
// Callback function for the Kerberos Framework
krb5_error_code __KLSPrompter (krb5_context context,
void *data,
const char *name,
const char *banner,
int num_prompts,
krb5_prompt prompts[])
{
krb5_error_code result = 0;
KLSController *controller = [NSApp delegate];
if (controller == NULL) {
result = klFatalDialogErr;
} else {
result = [controller promptWithTitle: name
banner: banner
prompts: prompts
promptCount: num_prompts];
}
return result;
}
// ---------------------------------------------------------------------------
#pragma mark -
@implementation KLSController
- (id) init
{
if (self = [super init]) {
loginState.acquiredPrincipalString = [[NSMutableString alloc] init];
loginState.acquiredCacheNameString = [[NSMutableString alloc] init];
}
return self;
}
- (void) dealloc
{
[loginState.acquiredPrincipalString release];
[loginState.acquiredCacheNameString release];
[super dealloc];
}
- (void) awakeFromNib
{
// Fill in the version fields in the dialogs
NSString *versionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"KLSDisplayVersion"];
if (versionString == NULL) {
versionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleGetInfoString"];
}
if (versionString != NULL) {
[loginVersionTextField setStringValue: versionString];
[changePasswordVersionTextField setStringValue: versionString];
[prompterVersionTextField setStringValue: versionString];
} else {
[loginVersionTextField setStringValue: @""];
[changePasswordVersionTextField setStringValue: @""];
[prompterVersionTextField setStringValue: @""];
}
// remember the dialog frame and height
singleResponseFrame = [prompterResponsesBox frame];
prompterWindowSize = [[prompterWindow contentView] frame].size;
prompterWindowSize.height -= singleResponseFrame.size.height;
// remember the size of the login window frame size with and without the options visible
loginWindowOptionsOnFrameHeight = loginWindowOptionsOffFrameHeight = [loginWindow frame].size.height;
loginWindowOptionsOffFrameHeight -= [loginOptionsBox frame].size.height;
// save these so we can add and remove them
[loginUsernameTextField retain];
[loginRealmComboBox retain];
[loginCallerProvidedUsernameTextField retain];
[loginCallerProvidedRealmTextField retain];
// by default, caller did not provide a principal
[loginCallerProvidedUsernameTextField removeFromSuperview];
[loginCallerProvidedRealmTextField removeFromSuperview];
}
#pragma mark -
// Notifications
- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
// Initialize the Login Library with our graphical prompter
__KLSetApplicationPrompter (__KLSPrompter);
// Launch the server after starting the application so that all the
// autorelease pools associated with the current run loop have been
// created and initialized. Otherwise we leak memory.
// Initialize the mach server immediately so we start queueing events
kern_return_t err = mach_server_init (LoginMachIPCServiceName, KerberosLoginIPC_server);
if (err != KERN_SUCCESS) {
dprintf (" mach_error_string (err), err);
exit (1);
}
err = mach_server_run_server ();
if (err != KERN_SUCCESS) {
dprintf ("KerberosLoginServer failed to start up...");
exit (1);
}
exit (0);
}
- (void) controlTextDidChange: (NSNotification *) aNotification
{
// Not sure which dialog it is, so just update both:
[self loginUpdateOkButton];
[self changePasswordUpdateOkButton];
}
- (void) comboBoxSelectionDidChange: (NSNotification *) aNotification
{
[self loginUpdateOkButton];
}
- (void) comboBoxWillDismiss: (NSNotification *) aNotification
{
[self loginUpdateOkButton];
}
#pragma mark -
// Login Window
- (KLStatus) getTicketsForPrincipal: (const char *) principalUTF8String
flags: (krb5_flags) flags
lifetime: (KLLifetime) lifetime
renewableLifetime: (KLLifetime) renewableLifetime
startTime: (KLTime) startTime
forwardable: (KLBoolean) forwardable
proxiable: (KLBoolean) proxiable
addressless: (KLBoolean) addressless
serviceName: (const char *) serviceName
applicationName: (const char *) applicationNameString
applicationIconPath: (const char *) applicationIconPathString
{
KLStatus err = klNoErr;
KLSize typeSize;
KLLifetime maxLifetime, minLifetime;
KLBoolean renewable = (flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE);
KLBoolean showOptions = false;
if (applicationNameString != NULL) {
[loginHeaderTextField setStringValue: [NSString stringWithFormat: NSLocalizedString (@"KLApplicationRequest", NULL),
[NSString stringWithUTF8String: applicationNameString]]];
} else {
[loginHeaderTextField setStringValue: NSLocalizedString (@"KLUnknownRequest", NULL)];
}
// Set up badge icon:
if (applicationIconPathString != NULL) {
NSImage *badgeIconImage = [[NSWorkspace sharedWorkspace] iconForFile:
[NSString stringWithCString: applicationIconPathString]];
[loginKerberosIconImageView setBadgeIconImage: badgeIconImage];
} else {
[loginKerberosIconImageView setBadgeIconImage: NULL];
}
////////////////////////////////////
// Setup Name, Realm and Password //
////////////////////////////////////
if (principalUTF8String != NULL) {
[self loginUpdateWithCallerProvidedPrincipal: YES];
loginState.usernameControl = loginCallerProvidedUsernameTextField;
loginState.realmControl = loginCallerProvidedRealmTextField;
#pragma warning Need support for "\@" in realm names
NSString *principalString = [NSString stringWithUTF8String: principalUTF8String];
NSRange separator = [principalString rangeOfString: @"@" options: (NSLiteralSearch | NSBackwardsSearch)];
[loginState.usernameControl setStringValue: [principalString substringToIndex: separator.location]];
[loginState.realmControl setStringValue: [principalString substringFromIndex: separator.location + separator.length]];
} else {
KLIndex defaultRealmIndex;
KLIndex realmCount = KLCountKerberosRealms ();
[self loginUpdateWithCallerProvidedPrincipal: NO];
loginState.usernameControl = loginUsernameTextField;
loginState.realmControl = loginRealmComboBox;
if (KLGetDefaultLoginOption (loginOption_LoginName, NULL, &typeSize) == klNoErr) {
char *name = (char *) malloc (sizeof (char) * (typeSize + 1));
if (name != NULL) {
if (KLGetDefaultLoginOption (loginOption_LoginName, name, &typeSize) == klNoErr) {
name [typeSize] = '\0'; // NUL-terminate so we can look it up as a UTF8String
[loginState.usernameControl setStringValue: [NSString stringWithUTF8String: name]];
}
free (name);
}
}
// Fill in the rest of the realms into the combo box
if (realmCount > 0) {
int i;
for (i = 0; i < realmCount; i++) {
char *realm;
if (KLGetKerberosRealm (i, &realm) == klNoErr) {
[loginRealmComboBox addItemWithObjectValue: [NSString stringWithUTF8String: realm]];
KLDisposeString (realm);
}
}
}
if (KLGetKerberosDefaultRealm (&defaultRealmIndex) == klNoErr) {
[loginRealmComboBox selectItemAtIndex: defaultRealmIndex];
} else if ([loginRealmComboBox numberOfItems] > 0) {
[loginRealmComboBox selectItemAtIndex: 0];
}
[loginRealmComboBox setObjectValue: [loginRealmComboBox numberOfItems] > 0 ? [loginRealmComboBox objectValueOfSelectedItem] : @""];
[loginRealmComboBox setNumberOfVisibleItems: [loginRealmComboBox numberOfItems]];
[loginRealmComboBox setCompletes: YES];
}
// Clear the password text field
[loginPasswordSecureTextField setObjectValue: @""];
// Set the text insertion pointer location
if ([[loginState.usernameControl stringValue] length] > 0) {
[loginWindow setInitialFirstResponder: loginPasswordSecureTextField];
} else {
[loginWindow setInitialFirstResponder: loginState.usernameControl];
}
//////////////////////////
// Setup Ticket Options //
//////////////////////////
// Fill in options with KLL defaults if not already overridden by client
if (!(flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)) {
typeSize = sizeof (forwardable);
err = KLGetDefaultLoginOption (loginOption_DefaultForwardableTicket, &forwardable, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_DefaultForwardableTicket) returned } else {
flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE;
}
}
if (!(flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)) {
typeSize = sizeof (proxiable);
err = KLGetDefaultLoginOption (loginOption_DefaultProxiableTicket, &proxiable, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_DefaultProxiableTicket) returned } else {
flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE;
}
}
if (!(flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) {
typeSize = sizeof (addressless);
err = KLGetDefaultLoginOption (loginOption_DefaultAddresslessTicket, &addressless, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_DefaultAddresslessTicket) returned } else {
flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
}
}
if (!(flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
typeSize = sizeof (renewable);
err = KLGetDefaultLoginOption (loginOption_DefaultRenewableTicket, &renewable, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_DefaultRenewableTicket) returned } else {
if (renewable) { flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE; }
}
typeSize = sizeof (renewableLifetime);
err = KLGetDefaultLoginOption (loginOption_DefaultRenewableLifetime, &renewableLifetime, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_DefaultRenewableLifetime) returned }
}
if (!(flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) {
typeSize = sizeof (lifetime);
err = KLGetDefaultLoginOption (loginOption_DefaultTicketLifetime, &lifetime, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_DefaultTicketLifetime) returned } else {
flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE;
}
}
// Load the options in the state variables
loginState.flags = flags;
loginState.lifetime = lifetime;
loginState.renewable = renewable;
loginState.renewableLifetime = renewableLifetime;
loginState.startTime = startTime;
loginState.forwardable = forwardable;
loginState.proxiable = proxiable;
loginState.addressless = addressless;
loginState.serviceName = serviceName;
/////////////////////////////////////
// Copy Ticket Options to Controls //
/////////////////////////////////////
[loginForwardableCheckbox setIntValue: loginState.forwardable];
[loginAddresslessCheckbox setIntValue: loginState.addressless];
[loginRenewableCheckbox setIntValue: loginState.renewable];
// Set up the slider ranges
typeSize = sizeof (KLLifetime);
err = KLGetDefaultLoginOption (loginOption_MinimalTicketLifetime, &minLifetime, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_MinimalTicketLifetime) returned minLifetime = loginState.lifetime;
}
err = KLGetDefaultLoginOption (loginOption_MaximalTicketLifetime, &maxLifetime, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_MaximalTicketLifetime) returned maxLifetime = loginState.lifetime;
}
[self loginSetupSlider: loginLifetimeSlider
textField: loginLifetimeText
minimum: minLifetime
maximum: maxLifetime
value: loginState.lifetime];
err = KLGetDefaultLoginOption (loginOption_MinimalRenewableLifetime, &minLifetime, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_MinimalRenewableLifetime) returned minLifetime = loginState.renewableLifetime;
}
err = KLGetDefaultLoginOption (loginOption_MaximalRenewableLifetime, &maxLifetime, &typeSize);
if (err != klNoErr) {
dprintf ("KLGetDefaultLoginOption (loginOption_MaximalRenewableLifetime) returned maxLifetime = loginState.renewableLifetime;
}
[self loginSetupSlider: loginRenewableLifetimeSlider
textField: loginRenewableLifetimeText
minimum: minLifetime
maximum: maxLifetime
value: loginState.renewableLifetime];
[self loginRenewableCheckboxWasHit: self]; // update the enabled/disabledness off the slider
// Update the state of the options:
typeSize = sizeof (KLBoolean);
err = KLGetDefaultLoginOption (loginOption_ShowOptions, &showOptions, &typeSize);
if (err != klNoErr) { showOptions = false; }
[loginOptionsButton setState: showOptions ? NSOnState : NSOffState];
[self loginOptionsButtonWasHit: self];
// Run the window until the user hits ok or cancel
return [self displayAndRunWindow: loginWindow];
}
- (void) loginUpdateOkButton
{
if (([[loginState.usernameControl stringValue] length] > 0) &&
([[loginState.realmControl stringValue] length] > 0) &&
([[loginPasswordSecureTextField stringValue] length] > 0)) {
[loginOkButton setEnabled: TRUE];
} else {
[loginOkButton setEnabled: FALSE];
}
}
- (void) loginUpdateWithCallerProvidedPrincipal: (KLBoolean) callerProvidedPrincipal
{
static KLBoolean callerProvidedPrincipalState = false;
if (callerProvidedPrincipalState != callerProvidedPrincipal) {
if (callerProvidedPrincipal) {
[[loginUsernameTextField superview] addSubview: loginCallerProvidedUsernameTextField];
[[loginRealmComboBox superview] addSubview: loginCallerProvidedRealmTextField];
[loginUsernameTextField removeFromSuperview];
[loginRealmComboBox removeFromSuperview];
} else {
[[loginCallerProvidedUsernameTextField superview] addSubview: loginUsernameTextField];
[[loginCallerProvidedRealmTextField superview] addSubview: loginRealmComboBox];
[loginCallerProvidedUsernameTextField removeFromSuperview];
[loginCallerProvidedRealmTextField removeFromSuperview];
}
callerProvidedPrincipalState = callerProvidedPrincipal;
}
}
- (void) loginSetupSlider: (NSSlider *) slider
textField: (NSTextField *) textField
minimum: (int) minimum
maximum: (int) maximum
value: (int) value
{
int min = minimum;
int max = maximum;
int increment = 0;
if (max < min) {
// swap values
int temp = max;
max = min;
min = temp;
}
int range = max - min;
if (range < 5*60) { increment = 1; // 1 second if under 5 minutes
} else if (range < 30*60) { increment = 5; // 5 seconds if under 30 minutes
} else if (range < 60*60) { increment = 15; // 15 seconds if under 1 hour
} else if (range < 2*60*60) { increment = 30; // 30 seconds if under 2 hours
} else if (range < 5*60*60) { increment = 60; // 1 minute if under 5 hours
} else if (range < 50*60*60) { increment = 5*60; // 5 minutes if under 50 hours
} else if (range < 200*60*60) { increment = 15*60; // 15 minutes if under 200 hours
} else if (range < 500*60*60) { increment = 30*60; // 30 minutes if under 500 hours
} else { increment = 60*60; } // 1 hour otherwise
int roundedMinimum = (min / increment) * increment;
if (roundedMinimum > min) { roundedMinimum -= increment; }
if (roundedMinimum <= 0) { roundedMinimum += increment; } // ensure it is positive
int roundedMaximum = (max / increment) * increment;
if (roundedMaximum < max) { roundedMaximum += increment; }
int roundedValue = (value / increment) * increment;
if (roundedValue < roundedMinimum) { roundedValue = roundedMinimum; }
if (roundedValue > roundedMaximum) { roundedValue = roundedMaximum; }
if (roundedMinimum == roundedMaximum) {
[textField setTextColor: [NSColor grayColor]];
[slider setEnabled: FALSE];
} else {
[textField setTextColor: [NSColor blackColor]];
[slider setEnabled: TRUE];
}
// Attach the formatter to the slider
NSDateFormatter *lifetimeFormatter = [[KLSLifetimeFormatter alloc] initWithMinimum: roundedMinimum
maximum: roundedMaximum
increment: increment];
[textField setFormatter: lifetimeFormatter];
[lifetimeFormatter release]; // the textField will retain it
[slider setMinValue: 0];
[slider setMaxValue: (roundedMaximum - roundedMinimum) / increment];
[slider setIntValue: (roundedValue - roundedMinimum) / increment];
[textField takeObjectValueFrom: slider];
}
- (IBAction) loginAddresslessCheckboxWasHit: (id) sender
{
loginState.addressless = [loginAddresslessCheckbox intValue];
}
- (IBAction) loginForwardableCheckboxWasHit: (id) sender
{
loginState.forwardable = [loginForwardableCheckbox intValue];
}
- (IBAction) loginRenewableCheckboxWasHit: (id) sender
{
loginState.renewable = [loginRenewableCheckbox intValue];
}
- (IBAction) loginLifetimeSliderChanged: (id) sender
{
[loginLifetimeText takeObjectValueFrom: loginLifetimeSlider];
loginState.lifetime = [[loginLifetimeText formatter] lifetimeForInt: [loginLifetimeSlider intValue]];
}
- (IBAction) loginRenewableLifetimeSliderChanged: (id) sender
{
[loginRenewableLifetimeText takeObjectValueFrom: loginRenewableLifetimeSlider];
loginState.renewableLifetime = [[loginRenewableLifetimeText formatter] lifetimeForInt: [loginRenewableLifetimeSlider intValue]];
}
- (IBAction) loginOptionsButtonWasHit: (id) sender
{
NSRect loginWindowFrameRect = [loginWindow frame];
if ([loginOptionsButton state] == NSOnState) {
if (loginWindowFrameRect.size.height != loginWindowOptionsOnFrameHeight) {
loginWindowFrameRect.origin.y -= (loginWindowOptionsOnFrameHeight - loginWindowOptionsOffFrameHeight);
loginWindowFrameRect.size.height = loginWindowOptionsOnFrameHeight;
[loginOptionsButton setTitle: NSLocalizedString (@"KLHideOptions", NULL)];
}
} else if ([loginOptionsButton state] == NSOffState) {
if (loginWindowFrameRect.size.height != loginWindowOptionsOffFrameHeight) {
loginWindowFrameRect.origin.y += (loginWindowOptionsOnFrameHeight - loginWindowOptionsOffFrameHeight);
loginWindowFrameRect.size.height = loginWindowOptionsOffFrameHeight;
[loginOptionsButton setTitle: NSLocalizedString (@"KLShowOptions", NULL)];
}
} else {
NSLog (@"loginOptionsButtonWasHit: Unknown state!");
}
[loginWindow setFrame: loginWindowFrameRect display: YES animate: YES];
}
- (IBAction) loginCancelButtonWasHit: (id) sender
{
[NSApp endSheet: loginWindow returnCode: klUserCanceledErr];
}
- (IBAction) loginOkButtonWasHit: (id) sender
{
KLStatus err = klNoErr;
NSString *principalString;
KLLoginOptions loginOptions = NULL;
KLPrincipal principal = NULL;
char *cacheName = NULL;
// Get Tickets:
principalString = [NSString stringWithFormat: @"
if (err == klNoErr) {
dprintf ("Creating principal for '%s'\n", [principalString UTF8String]);
err = KLCreatePrincipalFromString ([principalString UTF8String], kerberosVersion_V5, &principal);
}
if (err == klNoErr) {
err = KLCreateLoginOptions (&loginOptions);
}
if (err == klNoErr) {
err = KLLoginOptionsSetTicketLifetime (loginOptions, loginState.lifetime);
}
if (err == klNoErr) {
if (loginState.renewable) {
err = KLLoginOptionsSetRenewableLifetime (loginOptions, loginState.renewableLifetime);
} else {
err = KLLoginOptionsSetRenewableLifetime (loginOptions, 0L);
}
}
if (err == klNoErr) {
err = KLLoginOptionsSetTicketStartTime (loginOptions, loginState.startTime);
}
if (err == klNoErr) {
err = KLLoginOptionsSetServiceName (loginOptions, loginState.serviceName);
}
if (err == klNoErr) {
err = KLLoginOptionsSetForwardable (loginOptions, loginState.forwardable);
}
if (err == klNoErr) {
err = KLLoginOptionsSetProxiable (loginOptions, loginState.proxiable);
}
if (err == klNoErr) {
err = KLLoginOptionsSetAddressless (loginOptions, loginState.addressless);
}
if (err == klNoErr) {
err = KLAcquireNewInitialTicketsWithPassword (principal,
loginOptions,
[[loginPasswordSecureTextField stringValue] UTF8String],
&cacheName);
if (err == KRB5KDC_ERR_KEY_EXP) {
// Ask the user if s/he wants to change the password
if ([self askYesNoQuestion: NSLocalizedString (@"KLStringPasswordExpired", NULL)] == YES) {
if ([self changePasswordForPrincipal: [principalString UTF8String]] == klNoErr) {
// Okay, we changed the password, now try again with the new password:
err = KLAcquireNewInitialTicketsWithPassword (principal,
loginOptions,
[[changePasswordNewPasswordSecureTextField stringValue] UTF8String],
&cacheName);
}
}
}
}
if (err == klNoErr) {
// Save the current settings of the dialog if needed
[self loginSaveOptionsIfNeeded];
// Save the principal and cache name
[loginState.acquiredPrincipalString setString: principalString];
[loginState.acquiredCacheNameString setString: [NSString stringWithUTF8String: cacheName]];
[NSApp endSheet: loginWindow returnCode: klNoErr];
} else if (err != klUserCanceledErr) {
[self displayKLError: err];
}
if (loginOptions != NULL) {
KLDisposeLoginOptions (loginOptions);
}
if (principal != NULL) {
KLDisposePrincipal (principal);
}
if (cacheName != NULL) {
KLDisposeString (cacheName);
}
}
- (void) loginSaveOptionsIfNeeded
{
KLStatus err = klNoErr;
KLBoolean rememberShowOptions;
KLBoolean rememberPrincipal;
KLBoolean rememberExtras;
KLSize typeSize = sizeof (KLBoolean);
// Should we remember the principal in the user's preferences?
err = KLGetDefaultLoginOption (loginOption_RememberPrincipal, &rememberPrincipal, &typeSize);
if (err == klNoErr && rememberPrincipal) {
NSString *loginName = [loginState.usernameControl stringValue];
NSString *loginRealm = [loginState.realmControl stringValue];
KLIndex loginRealmIndex;
err = KLSetDefaultLoginOption (loginOption_LoginName, [loginName UTF8String], [loginName length]);
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_LoginName) returned
err = KLSetDefaultLoginOption (loginOption_LoginInstance, "", 0);
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_LoginInstance) returned
err = KLFindKerberosRealmByName ([loginRealm UTF8String], &loginRealmIndex);
if (err != klNoErr) {
err = KLInsertKerberosRealm (realmList_End, [loginRealm UTF8String]); // Add the realm
}
if (err == klNoErr) {
// Either we found the realm or we succeeded in adding it
err = KLSetKerberosDefaultRealmByName ([loginRealm UTF8String]);
if (err != klNoErr) dprintf ("KLSetKerberosDefaultRealmByName returned }
}
// Should we remember the state of the options button?
err = KLGetDefaultLoginOption (loginOption_RememberShowOptions, &rememberShowOptions, &typeSize);
if (err == klNoErr && rememberShowOptions) {
KLBoolean showOptions = ([loginOptionsButton state] == NSOnState);
err = KLSetDefaultLoginOption (loginOption_ShowOptions, &showOptions, sizeof (showOptions));
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_ShowOptions) returned }
// Should we remember the ticket options in the user's preferences?
err = KLGetDefaultLoginOption (loginOption_RememberExtras, &rememberExtras, &typeSize);
if ((err == klNoErr) && rememberExtras) {
err = KLSetDefaultLoginOption (loginOption_DefaultForwardableTicket, &loginState.forwardable, sizeof (loginState.forwardable));
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_DefaultForwardableTicket) returned
err = KLSetDefaultLoginOption (loginOption_DefaultAddresslessTicket, &loginState.addressless, sizeof (loginState.addressless));
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_DefaultAddresslessTicket) returned
err = KLSetDefaultLoginOption (loginOption_DefaultTicketLifetime, &loginState.lifetime, sizeof (loginState.lifetime));
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_DefaultTicketLifetime) returned
err = KLSetDefaultLoginOption (loginOption_DefaultRenewableTicket, &loginState.renewable, sizeof (loginState.renewable));
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_DefaultRenewableTicket) returned
if (loginState.renewable) {
err = KLSetDefaultLoginOption (loginOption_DefaultRenewableLifetime, &loginState.renewableLifetime, sizeof (loginState.renewableLifetime));
if (err != klNoErr) dprintf ("KLSetDefaultLoginOption (loginOption_DefaultRenewableLifetime) returned }
}
}
- (const char *) loginAcquiredPrincipal
{
return [loginState.acquiredPrincipalString UTF8String];
}
- (const char *) loginAcquiredCacheName
{
return [loginState.acquiredCacheNameString UTF8String];
}
#pragma mark -
// Change Password Window
- (KLStatus) changePasswordForPrincipal: (const char *) principalUTF8String
{
[changePasswordPrincipalTextField setObjectValue: [NSString stringWithUTF8String: principalUTF8String]];
[changePasswordOldPasswordSecureTextField setObjectValue: @""];
[changePasswordNewPasswordSecureTextField setObjectValue: @""];
[changePasswordVerifyPasswordSecureTextField setObjectValue: @""];
// Run until the user hits ok or cancel
return [self displayAndRunWindow: changePasswordWindow];
}
- (void) changePasswordUpdateOkButton
{
if ([[changePasswordOldPasswordSecureTextField stringValue] length] > 0
&& [[changePasswordNewPasswordSecureTextField stringValue] length] > 0
&& [[changePasswordVerifyPasswordSecureTextField stringValue] length] > 0) {
[changePasswordOkButton setEnabled:TRUE];
} else {
[changePasswordOkButton setEnabled:FALSE];
}
}
- (IBAction) changePasswordCancelButtonWasHit: (id) sender
{
[NSApp endSheet: changePasswordWindow returnCode: klUserCanceledErr];
}
- (IBAction) changePasswordOkButtonWasHit: (id) sender
{
KLStatus err = klNoErr;
KLPrincipal principal = NULL;
char *rejectionErrorUTF8String = NULL;
char *rejectionDescriptionUTF8String = NULL;
Boolean rejected = FALSE;
if ([[changePasswordNewPasswordSecureTextField stringValue]
isEqual: [changePasswordVerifyPasswordSecureTextField stringValue]] == false) {
err = klPasswordMismatchErr;
}
if (err == klNoErr) {
err = KLCreatePrincipalFromString ([[changePasswordPrincipalTextField stringValue] UTF8String], kerberosVersion_V5, &principal);
}
if (err == klNoErr) {
err = KLChangePasswordWithPasswords (principal,
[[changePasswordOldPasswordSecureTextField stringValue] UTF8String],
[[changePasswordNewPasswordSecureTextField stringValue] UTF8String],
&rejected, &rejectionErrorUTF8String, &rejectionDescriptionUTF8String);
}
if (err == klNoErr) {
if (rejected) {
[self displayServerError: rejectionErrorUTF8String description: rejectionDescriptionUTF8String];
} else {
[NSApp endSheet: changePasswordWindow returnCode: klNoErr];
}
} else if (err != klUserCanceledErr) {
[self displayKLError: err];
}
if (principal != NULL) {
KLDisposePrincipal (principal);
}
if (rejectionErrorUTF8String != NULL) {
KLDisposeString (rejectionErrorUTF8String);
}
if (rejectionDescriptionUTF8String != NULL) {
KLDisposeString (rejectionDescriptionUTF8String);
}
}
#pragma mark -
- (krb5_error_code) promptWithTitle: (const char *) title
banner: (const char *) banner
prompts: (krb5_prompt *) prompts
promptCount: (int) promptCount
{
KLStatus err = klNoErr;
int i;
NSRect frameRect;
NSSize windowSize;
NSMutableArray *responsesArray = [NSMutableArray arrayWithCapacity: promptCount];
NSString *titleString = (title != NULL) ? [NSString stringWithUTF8String: title] : @"";
NSString *bannerString = (banner != NULL) ? [NSString stringWithUTF8String: banner] :
NSLocalizedString(@"KLStringPrompterChangeNotice", NULL);
[prompterWindow setTitle: titleString];
[prompterBannerTextField setStringValue: bannerString];
[prompterPromptsMatrix renewRows: promptCount columns: 1];
[prompterPromptsMatrix sizeToCells];
// Calculate resize the window to hold the new cells.
// The matrix cells will automatically be moved down.
windowSize = prompterWindowSize;
frameRect = [prompterWindow frame];
windowSize.height += (singleResponseFrame.size.height * promptCount) + (kPrompterResponseSpacing * (promptCount - 1));
[prompterWindow setContentSize: windowSize];
for (i = 0; i < promptCount; i++) {
NSTextField *responseTextField = NULL;
[[prompterPromptsMatrix cellAtRow: i column: 0] setObjectValue: [NSString stringWithUTF8String: prompts[i].prompt]];
frameRect = singleResponseFrame;
frameRect.origin.y += ((promptCount - 1) - i) * (kPrompterResponseSpacing + frameRect.size.height);
if (prompts[i].hidden) {
NSSecureTextField *responseSecureTextField = [[NSSecureTextField alloc] initWithFrame: frameRect];
[[responseSecureTextField cell] setEchosBullets: YES];
responseTextField = responseSecureTextField;
} else {
responseTextField = [[NSTextField alloc] initWithFrame: frameRect];
}
[responseTextField setEnabled: YES];
[responseTextField setEditable: YES];
[responseTextField setBezeled: YES];
[responseTextField setBezelStyle: NSTextFieldSquareBezel];
[responseTextField setAlignment: NSLeftTextAlignment];
[responseTextField setDrawsBackground: YES];
[responseTextField setBackgroundColor: [NSColor whiteColor]];
[responseTextField setFont: [NSFont systemFontOfSize: 13]];
[responseTextField setStringValue: @""];
[responseTextField autorelease];
[responsesArray addObject: responseTextField];
[[prompterWindow contentView] addSubview: responseTextField];
if (i == 0) {
[prompterWindow setInitialFirstResponder: responseTextField];
} else {
// chain up the text field so we can tab between them
[[responsesArray objectAtIndex: i - 1] setNextKeyView: responseTextField];
}
}
// connect the last text field to the first one
if (promptCount > 1) {
[[responsesArray objectAtIndex: promptCount - 1] setNextKeyView: [responsesArray objectAtIndex: 0]];
}
// Run until the user hits ok or cancel
err = [self displayAndRunWindow: prompterWindow];
if (err == klNoErr) {
for (i = 0; i < promptCount; i++) {
NSTextField *responseTextField = [responsesArray objectAtIndex: i];
NSString *response = response = [responseTextField stringValue];
int length = [response length] + 1;
// Make sure it won't overflow:
if (length > prompts[i].reply->length)
length = prompts[i].reply->length;
memmove (prompts[i].reply->data, [response UTF8String], length * sizeof (char));
prompts[i].reply->data[length - 1] = '\0';
prompts[i].reply->length = length;
[responseTextField removeFromSuperview]; // Remove from prompter window
}
}
return err;
}
- (IBAction) prompterCancelButtonWasHit: (id) sender
{
[NSApp endSheet: prompterWindow returnCode: klUserCanceledErr];
}
- (IBAction) prompterOkButtonWasHit: (id) sender
{
[NSApp endSheet: prompterWindow returnCode: klNoErr];
}
#pragma mark -
- (void) displayKLError: (KLStatus) error
{
NSWindow *parentWindow = [self frontWindow];
KLStatus err = klNoErr;
char *descriptionUTF8String;
err = KLGetErrorString (error, &descriptionUTF8String);
if (err == klNoErr) {
KLDialogIdentifier identifier = loginLibrary_UnknownDialog;
if (parentWindow == loginWindow) {
identifier = loginLibrary_LoginDialog;
} else if (parentWindow == changePasswordWindow) {
identifier = loginLibrary_ChangePasswordDialog;
} else if (parentWindow == prompterWindow) {
identifier = loginLibrary_PrompterDialog;
}
[self displayKLError: error windowIdentifier: identifier];
}
}
- (void) displayKLError: (KLStatus) error
windowIdentifier: (KLDialogIdentifier) identifier
{
KLStatus err = klNoErr;
char *descriptionUTF8String;
// Use a special error when the caps lock key is down and the password was incorrect
// if the caps lock key is down accidentally, it will still be down now
switch (error) {
case INTK_BADPW:
case KRB5KRB_AP_ERR_BAD_INTEGRITY:
case KRBET_INTK_BADPW:
case KRB5KDC_ERR_PREAUTH_FAILED:
case klBadPasswordErr: {
unsigned int modifiers = [[NSApp currentEvent] modifierFlags];
if (modifiers & NSAlphaShiftKeyMask) {
error = klCapsLockErr;
}
}
}
err = KLGetErrorString (error, &descriptionUTF8String);
if (err == klNoErr) {
NSString *key = @"KLStringUnknownError";
// Get the header string:
if (identifier == loginLibrary_LoginDialog) {
key = @"KLStringLoginFailed";
} else if (identifier == loginLibrary_ChangePasswordDialog) {
key = @"KLStringChangePasswordFailed";
} else if (identifier == loginLibrary_PrompterDialog) {
key = @"KLStringPrompterFailed";
}
[self displayError: NSLocalizedString (key, NULL)
description: [NSString stringWithUTF8String: descriptionUTF8String]];
}
}
- (void) displayServerError: (const char *) errorUTF8String
description: (const char *) descriptionUTF8String
{
// Remove all newlines from the server responses (they look goofy in the dialog):
NSMutableString *errorString = [NSMutableString stringWithUTF8String: errorUTF8String];
[errorString replaceOccurrencesOfString: @"\n" withString: @""
options: NSLiteralSearch
range: NSMakeRange (0, [errorString length])];
NSMutableString *descriptionString = [NSMutableString stringWithUTF8String: descriptionUTF8String];
[descriptionString replaceOccurrencesOfString: @"\n" withString: @""
options: NSLiteralSearch
range: NSMakeRange (0, [descriptionString length])];
[self displayError: errorString description: descriptionString];
}
- (void) displayError: (NSString *) errorString
description: (NSString *) descriptionString
{
NSPanel *errorPanel = NSGetAlertPanel (errorString, descriptionString,
NSLocalizedString (@"KLStringOK", NULL), NULL, NULL);
[self displayAndRunWindow: errorPanel];
NSReleaseAlertPanel (errorPanel);
}
- (BOOL) askYesNoQuestion: (NSString *) question
{
NSWindow *parentWindow = [self frontWindow];
NSString *key = @"KLStringUnknownError";
int response;
if (parentWindow == loginWindow) {
key = @"KLStringLoginFailed";
} else if (parentWindow == changePasswordWindow) {
key = @"KLStringChangePasswordFailed";
} else if (parentWindow == prompterWindow) {
key = @"KLStringPrompterFailed";
}
NSPanel *questionPanel = NSGetAlertPanel (NSLocalizedString (key, NULL),
question,
NSLocalizedString (@"KLStringYes", NULL),
NSLocalizedString (@"KLStringNo", NULL),
NULL);
response = [self displayAndRunWindow: questionPanel];
NSReleaseAlertPanel (questionPanel);
return (response == NSAlertDefaultReturn) ? YES : NO;
}
#pragma mark -
- (int) displayAndRunWindow: (NSWindow *) window
{
NSWindow *parent = [NSApp modalWindow];
int response = 0;
// Can't display two sheets on the same window so make it a modal dialog
if (parent != NULL && [parent attachedSheet] != NULL) {
dprintf ("Can't display a sheet on a sheet");
parent = NULL;
}
// Bring KLS to the front
[NSApp activateIgnoringOtherApps: YES];
// Prepare the window:
if (parent != NULL) {
[NSApp beginSheet: window
modalForWindow: parent
modalDelegate: self
didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo: window];
} else {
[window center];
}
// Run the main event loop:
response = [NSApp runModalForWindow: window];
// Remove the window:
[window orderOut: self];
return response;
}
- (void) sheetDidEnd: (NSWindow *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo
{
if (sheet == (NSWindow *) contextInfo) {
[NSApp stopModalWithCode: returnCode];
}
}
- (NSWindow *) frontWindow
{
return [NSApp modalWindow];
}
@end