/* * RealmsEditor.m * * $Header$ * * Copyright 2004 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 "RealmsConfiguration.h" #import "UNIXReadWrite.h" //#import "krbports.h" // Kerberos 4 port names //#import "osconf.h" // Kerberos 5 port names #pragma mark - @implementation KerberosServer // Note: typeMenuIndex is the index of menu items in the type popup menu typedef struct __ServerType { unsigned int typeMenuIndex; NSString *string; } ServerType; #define kdcType 0 #define adminType 1 #define krb524Type 2 #define kpasswdType 3 const ServerType kServerTypes[] = { { kdcType, @"kdc" }, { adminType, @"admin_server" }, { krb524Type, @"krb524_server" }, { kpasswdType, @"kpasswd_server" }, { 4, NULL } }; // --------------------------------------------------------------------------- + (KerberosServer *) emptyServer { return [[[KerberosServer alloc] init] autorelease]; } // --------------------------------------------------------------------------- - (id) init { if ((self = [super init])) { hostString = NULL; customPort = 0; hasCustomPort = FALSE; typeMenuIndex = 0; version = kerberosVersion_V5; } return self; } // --------------------------------------------------------------------------- - (id) initWithTypeString: (NSString *) serverType version: (KLKerberosVersion) serverVersion profileString: (NSString *) profileString { if ((self = [self init])) { const ServerType *typesPtr; for (typesPtr = kServerTypes; typesPtr->string != NULL; typesPtr++) { if ([serverType compare: typesPtr->string] == NSOrderedSame) { typeMenuIndex = typesPtr->typeMenuIndex; } } NSRange separator = [profileString rangeOfString: @":" options: (NSLiteralSearch | NSBackwardsSearch)]; if (separator.location == NSNotFound) { hostString = [profileString retain]; } else { hostString = [[profileString substringToIndex: separator.location] retain]; customPort = [[profileString substringFromIndex: separator.location + separator.length] intValue]; hasCustomPort = TRUE; } version = serverVersion; } return self; } // --------------------------------------------------------------------------- - (void) dealloc { if (hostString != NULL) { [hostString release]; } [super dealloc]; } // --------------------------------------------------------------------------- - (NSString *) profileString { NSMutableString *string = [NSMutableString stringWithCapacity: [[self host] length]]; if (string != NULL) { [string appendFormat: @"%s:%d", [[self host] UTF8String], [[self port] intValue]]; } return (string != NULL) ? string : [self host]; } // --------------------------------------------------------------------------- - (NSString *) typeString { const ServerType *typesPtr; for (typesPtr = kServerTypes; typesPtr->string != NULL; typesPtr++) { if (typeMenuIndex == typesPtr->typeMenuIndex) { return typesPtr->string; } } return NULL; } // --------------------------------------------------------------------------- - (unsigned int) typeMenuIndex { return typeMenuIndex; } // --------------------------------------------------------------------------- - (void) setTypeMenuIndex: (unsigned int) newTypeMenuIndex { typeMenuIndex = newTypeMenuIndex; } // --------------------------------------------------------------------------- - (KLKerberosVersion) version { return version; } // --------------------------------------------------------------------------- - (void) setVersion: (KLKerberosVersion) newVersion { version = newVersion; } // --------------------------------------------------------------------------- - (NSString *) host { return (hostString != NULL) ? hostString : @""; } // --------------------------------------------------------------------------- - (void) setHost: (NSString *) newHost { if (hostString != NULL) { [hostString release]; } hostString = [newHost retain]; //NSLog (@"setting host string to '%@'", hostString); } // --------------------------------------------------------------------------- - (NSNumber *) port { if (hasCustomPort) { return [NSNumber numberWithInt: customPort]; } else { return [self defaultPort]; } } // --------------------------------------------------------------------------- - (void) setPort: (NSNumber *) newPort { hasCustomPort = (newPort > 0); customPort = (hasCustomPort) ? [newPort intValue] : [[self defaultPort] intValue]; } // --------------------------------------------------------------------------- - (NSNumber *) defaultPort { int port = 0; if (typeMenuIndex == kdcType) { port = (version == kerberosVersion_V4) ? 750 /* KERBEROS_PORT */ : 88 /* KRB5_DEFAULT_PORT */; } else if (typeMenuIndex == adminType) { port = (version == kerberosVersion_V4) ? 751 /* KADM_PORT */ : 749 /* DEFAULT_KADM5_PORT */; } else if (typeMenuIndex == krb524Type) { port = 4444; // KRB524_PORT } else if (typeMenuIndex == kpasswdType) { port = 464; // DEFAULT_KPASSWD_PORT } return [NSNumber numberWithInt: port]; } @end #pragma mark - @implementation KerberosDomain // --------------------------------------------------------------------------- + (KerberosDomain *) emptyDomain { return [[[KerberosDomain alloc] init] autorelease]; } // --------------------------------------------------------------------------- - (id) init { if ((self = [super init])) { nameString = NULL; } return self; } // --------------------------------------------------------------------------- - (id) initWithName: (NSString *) name { if ((self = [self init])) { nameString = [name retain]; } return self; } // --------------------------------------------------------------------------- - (void) dealloc { if (nameString != NULL) { [nameString release]; } [super dealloc]; } // --------------------------------------------------------------------------- - (NSString *) name { return ((nameString != NULL) ? nameString : @""); } // --------------------------------------------------------------------------- - (void) setName: (NSString *) newName { if (nameString != NULL) { [nameString release]; } nameString = [newName retain]; } @end #pragma mark - @implementation KerberosRealm // --------------------------------------------------------------------------- + (KerberosRealm *) emptyRealm { return [[[KerberosRealm alloc] init] autorelease]; } // --------------------------------------------------------------------------- - (id) init { if ((self = [super init])) { krb5_error_code err = 0; nameInProfileString = NULL; nameString = NULL; v4NameString = NULL; defaultDomainString = NULL; serversArray = NULL; displayInDialogPopup = YES; version = 0; if (!err) { serversArray = [[NSMutableArray alloc] init]; if (serversArray == NULL) { err = ENOMEM; } } if (!err) { domainsArray = [[NSMutableArray alloc] init]; if (domainsArray == NULL) { err = ENOMEM; } } if (err) { [self release]; return NULL; } } return self; } // --------------------------------------------------------------------------- - (id) initWithName: (NSString *) name profile: (profile_t) profile { if ((self = [self init])) { krb5_error_code err = 0; nameInProfileString = [name retain]; nameString = [name retain]; if (!err) { const char *v4NamesList[] = { "realms", [name UTF8String], "v4_realm", NULL }; char **v4Names = NULL; err = profile_get_values (profile, v4NamesList, &v4Names); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v4Names != NULL)) { v4NameString = [[NSString alloc] initWithUTF8String: v4Names[0]]; if (v4NameString == NULL) { err = ENOMEM; } } if (v4Names != NULL) { profile_free_list (v4Names); } } if (!err) { const char *defaultDomainsList[] = { "realms", [name UTF8String], "default_domain", NULL }; char **defaultDomains = NULL; err = profile_get_values (profile, defaultDomainsList, &defaultDomains); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (defaultDomains != NULL) && (defaultDomains[0] != NULL)) { defaultDomainString = [[NSString alloc] initWithUTF8String: defaultDomains[0]]; if (defaultDomainString == NULL) { err = ENOMEM; } } if (defaultDomains != NULL) { profile_free_list (defaultDomains); } } if (!err) { err = [self addServersForVersion: kerberosVersion_V5 profile: profile]; } if (!err) { err = [self addServersForVersion: kerberosVersion_V4 profile: profile]; } // Is the realm in the KLL realms list? if (!err) { KLIndex popupIndex = 0; [self setDisplayInDialogPopup: (KLFindKerberosRealmByName ([name UTF8String], &popupIndex) == klNoErr)]; } // The reason we don't have the realms load the domains is that some domains don't have a // realm listed in the profile -- this is common if you have DNS SRV records // instead the realms configuration will load them so it can create KerberosRealm objects // for these extra domain_realm mappings if (err) { [self release]; return NULL; } } return self; } // --------------------------------------------------------------------------- - (void) dealloc { if (nameString != NULL) { [nameString release]; } if (v4NameString != NULL) { [v4NameString release]; } if (serversArray != NULL) { [serversArray release]; } if (domainsArray != NULL) { [domainsArray release]; } [super dealloc]; } // --------------------------------------------------------------------------- - (krb5_error_code) flushToProfile: (profile_t) profile { krb5_error_code err = 0; if ([[self name] length] <= 0) { err = EINVAL; } // Note: we do not need to deal with the case of creating empty realm sections. // Any nonexistent realm sections will be created when we try to write into them. // Check the v5 realm name to see if we need to rename it: if (!err) { const char *realmList[] = { "realms", [[self nameInProfile] UTF8String], NULL }; err = profile_rename_section (profile, realmList, [[self name] UTF8String]); if (err == PROF_NO_SECTION) { err = 0; } // OK if there isn't one yet } // Change the v4 realm name if necesssary if (!err) { const char *v4NamesList[] = { "realms", [[self name] UTF8String], "v4_realm", NULL }; char **v4Names = NULL; const char *v4NameInProfile = NULL; if (profile_get_values (profile, v4NamesList, &v4Names) == 0) { v4NameInProfile = v4Names[0]; // profile has a separate v4 realm if ([self hasV4Name]) { err = profile_update_relation (profile, v4NamesList, v4NameInProfile, [[self v4Name] UTF8String]); } else { err = profile_clear_relation (profile, v4NamesList); } } else { v4NameInProfile = [[self nameInProfile] UTF8String]; // Is just the old v5 name if ([self hasV4Name]) { err = profile_add_relation (profile, v4NamesList, [[self v4Name] UTF8String]); } } if (!err) { const char *v4RealmList[] = { REALMS_V4_PROF_REALMS_SECTION, v4NameInProfile, NULL }; err = profile_rename_section (profile, v4RealmList, [[self v4Name] UTF8String]); if (err == PROF_NO_SECTION) { err = 0; } // OK if there isn't one yet } if (v4Names != NULL) { profile_free_list (v4Names); } } // Write out the default domain: if (!err) { const char *defaultDomainsList[] = { "realms", [[self name] UTF8String], "default_domain", NULL }; char **defaultDomains = NULL; if (profile_get_values (profile, defaultDomainsList, &defaultDomains) == 0) { if ([self hasDefaultDomain]) { err = profile_update_relation (profile, defaultDomainsList, defaultDomains[0], [[self defaultDomain] UTF8String]); } else { err = profile_clear_relation (profile, defaultDomainsList); } } else { if ([self hasDefaultDomain]) { err = profile_add_relation (profile, defaultDomainsList, [[self defaultDomain] UTF8String]); } } if (defaultDomains != NULL) { profile_free_list (defaultDomains); } } // Write out the servers: if (!err) { err = [self flushServersForVersion: kerberosVersion_V5 toProfile: profile]; } if (!err) { err = [self flushServersForVersion: kerberosVersion_V4 toProfile: profile]; } // Write out the domains: if (!err) { err = [self flushDomainsForVersion: kerberosVersion_V5 toProfile: profile]; } if (!err) { err = [self flushDomainsForVersion: kerberosVersion_V4 toProfile: profile]; } // Is the realm in the KLL realms list? if (!err) { KLIndex popupIndex = 0; BOOL inPopup = (KLFindKerberosRealmByName ([[self name] UTF8String], &popupIndex) == klNoErr); if (inPopup && ![self displayInDialogPopup]) { err = KLRemoveKerberosRealm (popupIndex); // remove it } else if (!inPopup && [self displayInDialogPopup]) { err = KLInsertKerberosRealm (realmList_End, [[self name] UTF8String]); // add it } } if (err) { NSLog (@"[KerberosRealm flush] for realm %@ returning err %d (%s)", [self name], err, error_message (err)); } return err; } // --------------------------------------------------------------------------- - (krb5_error_code) addServersForVersion: (KLKerberosVersion) serverVersion profile: (profile_t) profile { krb5_error_code err = 0; const char *section = (serverVersion == kerberosVersion_V5) ? "realms" : REALMS_V4_PROF_REALMS_SECTION; NSString *realmString = (serverVersion == kerberosVersion_V5) ? [self name] : [self v4Name]; const char *serversList[] = { section, [realmString UTF8String], NULL, NULL }; const ServerType *typesPtr = NULL; for (typesPtr = kServerTypes; typesPtr->string != NULL && !err; typesPtr++) { NSString *typeString = typesPtr->string; char **servers = NULL; serversList[2] = [typeString UTF8String]; err = profile_get_values (profile, serversList, &servers); if (!err && (servers != NULL)) { char **s; for (s = servers; *s != NULL && !err; s++) { NSString *profileString = [NSString stringWithUTF8String: *s]; KerberosServer *server = [[KerberosServer alloc] initWithTypeString: typeString version: serverVersion profileString: profileString]; if (server == NULL) { err = ENOMEM; } else { //NSLog (@"Adding server '%@' of type '%@' for realm '%@'", // realmString, typeString, profileString); [serversArray addObject: server]; [server release]; } } } // These errors are ok... there just aren't servers of this type if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (servers != NULL) { profile_free_list (servers); } } return err; } // --------------------------------------------------------------------------- - (krb5_error_code) flushServersForVersion: (KLKerberosVersion) serverVersion toProfile: (profile_t) profile { krb5_error_code err = 0; const char *section = (serverVersion == kerberosVersion_V5) ? "realms" : REALMS_V4_PROF_REALMS_SECTION; NSString *realmString = (serverVersion == kerberosVersion_V5) ? [self name] : [self v4Name]; const char *serversList[] = { section, [realmString UTF8String], NULL, NULL }; // Clear out old servers: if (!err) { const ServerType *typesPtr = NULL; for (typesPtr = kServerTypes; typesPtr->string != NULL && !err; typesPtr++) { serversList[2] = [typesPtr->string UTF8String]; err = profile_clear_relation (profile, serversList); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } } // Store the new servers: if (!err) { unsigned int i = 0; for (i = 0; i < [serversArray count] && !err; i++) { KerberosServer *server = [serversArray objectAtIndex: i]; if ((server != NULL) && ([server version] == serverVersion)) { serversList[2] = [[server typeString] UTF8String]; err = profile_add_relation (profile, serversList, [[server profileString] UTF8String]); } } } return err; } // --------------------------------------------------------------------------- - (krb5_error_code) flushDomainsForVersion: (KLKerberosVersion) serverVersion toProfile: (profile_t) profile { krb5_error_code err = 0; const char *section = (serverVersion == kerberosVersion_V5) ? "domain_realm" : REALMS_V4_PROF_DOMAIN_SECTION; NSString *realmString = (serverVersion == kerberosVersion_V5) ? [self name] : [self v4Name]; const char *domainMappingList[] = { section, NULL, NULL }; unsigned int i = 0; if (profile == NULL) { err = ENOMEM; } for (i = 0; i < [self numberOfDomains] && !err; i++) { KerberosDomain *domain = [self domainAtIndex: i]; if (domain != NULL) { char **domainMapping = NULL; domainMappingList[1] = [[domain name] UTF8String]; if (!err) { err = profile_get_values (profile, domainMappingList, &domainMapping); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err) { if (domainMapping != NULL) { if ([realmString compare: [NSString stringWithUTF8String: domainMapping[0]]] != NSOrderedSame) { // Eek! this domain is already mapped to another realm! NSLog (@"WARNING! Domain '%s' is mapped to realms '%s' and '%s'", domainMappingList[1], [realmString UTF8String], domainMapping[0]); } } else { err = profile_add_relation (profile, domainMappingList, [realmString UTF8String]); } } } } if (err) { NSLog (@"[KerberosRealm flush] for realm %@ returning err %d (%s)", [self name], err, error_message (err)); } return err; } // --------------------------------------------------------------------------- - (NSString *) nameInProfile { return (nameInProfileString != NULL) ? nameInProfileString : @""; } // --------------------------------------------------------------------------- - (NSString *) name { return (nameString != NULL) ? nameString : @""; } // --------------------------------------------------------------------------- - (void) setName: (NSString *) newName { if (nameString != NULL) { [nameString release]; } nameString = ((newName != NULL) && ([newName length] > 0)) ? [newName retain] : NULL; } // --------------------------------------------------------------------------- - (BOOL) hasV4Name { return ((v4NameString != NULL) && ([v4NameString length] > 0)); } // --------------------------------------------------------------------------- - (NSString *) v4Name { if (v4NameString != NULL) { return v4NameString; } else { return [self name]; } } // --------------------------------------------------------------------------- - (void) setV4Name: (NSString *) newV4Name { if (v4NameString != NULL) { [v4NameString release]; } v4NameString = ((newV4Name != NULL) && ([newV4Name length] > 0)) ? [newV4Name retain] : NULL; } // --------------------------------------------------------------------------- - (BOOL) hasDefaultDomain { return ((defaultDomainString != NULL) && ([defaultDomainString length] > 0)); } // --------------------------------------------------------------------------- - (NSString *) defaultDomain { return (defaultDomainString != NULL) ? defaultDomainString : @""; } // --------------------------------------------------------------------------- - (void) setDefaultDomain: (NSString *) defaultDomain { if (defaultDomainString != NULL) { [defaultDomainString release]; } defaultDomainString = ((defaultDomain != NULL) && ([defaultDomain length] > 0)) ? [defaultDomain retain] : NULL; } // --------------------------------------------------------------------------- - (BOOL) displayInDialogPopup { return displayInDialogPopup; } // --------------------------------------------------------------------------- - (void) setDisplayInDialogPopup: (BOOL) newDisplayInDialogPopup { displayInDialogPopup = newDisplayInDialogPopup; } // --------------------------------------------------------------------------- - (unsigned int) numberOfServers { return [serversArray count]; } // --------------------------------------------------------------------------- - (KerberosServer *) serverAtIndex: (unsigned int) serverIndex { if (serverIndex < [serversArray count]) { return [serversArray objectAtIndex: serverIndex]; } else { return NULL; } } // --------------------------------------------------------------------------- - (unsigned int) indexOfServer: (KerberosServer *) server { return [serversArray indexOfObject: server]; } // --------------------------------------------------------------------------- - (void) addServer: (KerberosServer *) server { [serversArray addObject: server]; } // --------------------------------------------------------------------------- - (void) removeServer: (KerberosServer *) server { [serversArray removeObject: server]; } // --------------------------------------------------------------------------- - (unsigned int) numberOfDomains { return [domainsArray count]; } // --------------------------------------------------------------------------- - (KerberosDomain *) domainAtIndex: (unsigned int) domainIndex { if (domainIndex < [domainsArray count]) { return [domainsArray objectAtIndex: domainIndex]; } else { return NULL; } } // --------------------------------------------------------------------------- - (unsigned int) indexOfDomain: (KerberosDomain *) domain { return [domainsArray indexOfObject: domain]; } // --------------------------------------------------------------------------- - (BOOL) mappedToByDomainString: (NSString *) domain { // This differs from indexOfDomain in that it does a string comparison! unsigned int i = 0; for (i = 0; i < [domainsArray count]; i++) { KerberosDomain *d = [domainsArray objectAtIndex: i]; if ([[d name] compare: domain] == NSOrderedSame) { return YES; } } return NO; } // --------------------------------------------------------------------------- - (void) addDomain: (KerberosDomain *) domain { // Make sure this isn't a duplicate (use string comparison) if (![self mappedToByDomainString: [domain name]]) { [domainsArray addObject: domain]; } } // --------------------------------------------------------------------------- - (void) removeDomain: (KerberosDomain *) domain { [domainsArray removeObject: domain]; } @end #pragma mark - @implementation RealmsConfiguration // --------------------------------------------------------------------------- - (id) init { if ((self = [super init])) { krb5_error_code err = 0; useDNS = YES; configurationPathString = NULL; profile = NULL; realmsArray = NULL; defaultRealmString = NULL; if (!err) { realmsArray = [[NSMutableArray alloc] init]; if (realmsArray == NULL) { err = ENOMEM; } } if (!err) { err = [self load]; } if (err) { [self release]; return NULL; } } return self; } // --------------------------------------------------------------------------- - (void) dealloc { if (profile != NULL) { profile_abandon (profile); } if (configurationPathString != NULL) { [configurationPathString release]; } if (defaultRealmString != NULL) { [defaultRealmString release]; } if (realmsArray != NULL) { [realmsArray release]; } [super dealloc]; } // --------------------------------------------------------------------------- - (krb5_error_code) load { krb5_error_code err = 0; // If the profile is NULL, initialize it if (profile == NULL) { NSString *configurationPathStrings[] = { @"~/Library/Preferences/edu.mit.Kerberos", @"/Library/Preferences/edu.mit.Kerberos", @"/etc/krb5.conf", NULL }; NSString **paths = NULL; NSString *newConfigurationPath = NULL; // Figure out which config file we should look at for (paths = configurationPathStrings; *paths != NULL; paths++) { NSString *path = [*paths stringByExpandingTildeInPath]; if ([[NSFileManager defaultManager] isReadableFileAtPath: path]) { newConfigurationPath = path; break; } } if (newConfigurationPath == NULL) { err = EACCES; } if (!err) { if (configurationPathString != NULL) { [configurationPathString release]; } configurationPathString = [newConfigurationPath retain]; } // initialize the profile if (!err) { const_profile_filespec_t configFiles[] = { [configurationPathString UTF8String], NULL }; profile_t newProfile = NULL; err = profile_init (configFiles, &newProfile); if (!err) { profile = newProfile; } } } // Clear out any existing realm state if (!err) { [realmsArray removeAllObjects]; } // Get the default realm if (!err) { const char *defaultRealmList[3] = {"libdefaults", "default_realm", NULL}; char **defaultRealm = NULL; if (!err) { err = profile_get_values (profile, defaultRealmList, &defaultRealm); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err && (defaultRealm != NULL)) { defaultRealmString = [[NSString alloc] initWithUTF8String: defaultRealm[0]]; if (defaultRealmString == NULL) { err = ENOMEM; } } if (defaultRealm != NULL) { profile_free_list (defaultRealm); } } // Do we DNS for realm configuration if (!err) { const char *dnsFallbackList[3] = {"libdefaults", "dns_fallback", NULL}; char **dnsFallback = NULL; if (!err) { err = profile_get_values (profile, dnsFallbackList, &dnsFallback); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err && (dnsFallback != NULL)) { // In krb5, everything else is considered false [self setUseDNS: (strcasecmp (dnsFallback[0], "y") == 0 || strcasecmp (dnsFallback[0], "yes") == 0 || strcasecmp (dnsFallback[0], "t") == 0 || strcasecmp (dnsFallback[0], "true") == 0 || strcasecmp (dnsFallback[0], "1") == 0 || strcasecmp (dnsFallback[0], "on") == 0)]; } if (dnsFallback != NULL) { profile_free_list (dnsFallback); } } // Do the v5 realms if (!err) { const char *v5RealmsList[] = { "realms", NULL }; char **v5Realms = NULL; err = profile_get_subsection_names (profile, v5RealmsList, &v5Realms); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v5Realms != NULL)) { char **r; for (r = v5Realms; *r != NULL && !err; r++) { NSString *name = [NSString stringWithUTF8String: *r]; KerberosRealm *realm = [[KerberosRealm alloc] initWithName: name profile: profile]; if (realm == NULL) { err = ENOMEM; } else { [realmsArray addObject: realm]; [realm release]; } } } if (v5Realms != NULL) { profile_free_list (v5Realms); } } // Do the v4 realms if (!err) { const char *v4RealmsList[] = { REALMS_V4_PROF_REALMS_SECTION, NULL }; char **v4Realms = NULL; err = profile_get_subsection_names (profile, v4RealmsList, &v4Realms); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v4Realms != NULL)) { char **r; for (r = v4Realms; *r != NULL && !err; r++) { NSString *name = [NSString stringWithUTF8String: *r]; KerberosRealm *existingRealm = [self findRealmByV4Name: name]; if (existingRealm == NULL) { KerberosRealm *realm = [[KerberosRealm alloc] initWithName: name profile: profile]; if (realm == NULL) { err = ENOMEM; } else { [realmsArray addObject: realm]; [realm release]; } } } } if (v4Realms != NULL) { profile_free_list (v4Realms); } } // The reason we don't have the realms load the domains is that some domains don't have a // realm listed in the profile -- this is common if you have DNS SRV records // Now do the v5 domain-realm mappings (which depend on the realms) if (!err) { const char *v5DomainsList[] = { "domain_realm", NULL }; char **v5Domains = NULL; err = profile_get_relation_names (profile, v5DomainsList, &v5Domains); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v5Domains != NULL)) { char **d; for (d = v5Domains; *d != NULL && !err; d++) { NSString *domainString = [NSString stringWithUTF8String: *d]; if (domainString != NULL) { const char *domainMappingList[] = { "domain_realm", *d, NULL }; char **domainMapping = NULL; if (!err) { err = profile_get_values (profile, domainMappingList, &domainMapping); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err) { NSString *realmName = [NSString stringWithUTF8String: domainMapping[0]]; KerberosRealm *realm = [self findRealmByName: realmName]; if (realm == NULL) { // Create it realm = [KerberosRealm emptyRealm]; if (realm != NULL) { [realm setName: realmName]; } } if (realm != NULL) { KerberosDomain *domain = [[KerberosDomain alloc] initWithName: domainString]; if (domain == NULL) { err = ENOMEM; } else { [realm addDomain: domain]; [domain release]; } } } if (domainMapping != NULL) { profile_free_list (domainMapping); } } } } if (v5Domains != NULL) { profile_free_list (v5Domains); } } // Now do the v4 domain-realm mappings (which depend on the realms) if (!err) { const char *v4DomainsList[] = { REALMS_V4_PROF_DOMAIN_SECTION, NULL }; char **v4Domains = NULL; err = profile_get_relation_names (profile, v4DomainsList, &v4Domains); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v4Domains != NULL)) { char **d; for (d = v4Domains; *d != NULL && !err; d++) { NSString *domainString = [NSString stringWithUTF8String: *d]; if (domainString != NULL) { const char *v4DomainMappingList[] = { REALMS_V4_PROF_DOMAIN_SECTION, *d, NULL }; char **v4DomainMapping = NULL; if (!err) { err = profile_get_values (profile, v4DomainMappingList, &v4DomainMapping); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err) { NSString *realmName = [NSString stringWithUTF8String: v4DomainMapping[0]]; KerberosRealm *realm = [self findRealmByV4Name: realmName]; if (realm == NULL) { // Create it realm = [KerberosRealm emptyRealm]; if (realm != NULL) { [realm setName: realmName]; } } if (realm != NULL) { KerberosDomain *domain = [[KerberosDomain alloc] initWithName: domainString]; if (domain == NULL) { err = ENOMEM; } else { [realm addDomain: domain]; [domain release]; } } } if (v4DomainMapping != NULL) { profile_free_list (v4DomainMapping); } } } } if (v4Domains != NULL) { profile_free_list (v4Domains); } } return err; } // --------------------------------------------------------------------------- - (krb5_error_code) abandon { if (profile != NULL) { profile_abandon (profile); profile = NULL; } return [self load]; } // --------------------------------------------------------------------------- - (krb5_error_code) flush { krb5_error_code err = 0; int writable = 0; int modified = 1; // First flush the realms so all the names, servers and domains get updated: if (!err) { unsigned int i = 0; for (i = 0; i < [self numberOfRealms] && !err; i++) { KerberosRealm *realm = [self realmAtIndex: i]; if (realm != NULL) { err = [realm flushToProfile: profile]; } } } // Walk over the profile looking for deleted v5 realms: if (!err) { const char *v5RealmsList[] = { "realms", NULL }; char **v5Realms = NULL; err = profile_get_subsection_names (profile, v5RealmsList, &v5Realms); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v5Realms != NULL)) { char **r; for (r = v5Realms; *r != NULL && !err; r++) { NSString *name = [NSString stringWithUTF8String: *r]; KerberosRealm *existingRealm = [self findRealmByName: name]; if (existingRealm == NULL) { // Realm is not in our array! Delete it. const char *deletedRealmList[] = { "realms", *r, NULL }; err = profile_rename_section (profile, deletedRealmList, NULL); } } } if (v5Realms != NULL) { profile_free_list (v5Realms); } } // Walk over the profile looking for deleted v4 realms: if (!err) { const char *v4RealmsList[] = { REALMS_V4_PROF_REALMS_SECTION, NULL }; char **v4Realms = NULL; err = profile_get_subsection_names (profile, v4RealmsList, &v4Realms); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v4Realms != NULL)) { char **r; for (r = v4Realms; *r != NULL && !err; r++) { NSString *name = [NSString stringWithUTF8String: *r]; KerberosRealm *existingRealm = [self findRealmByV4Name: name]; if (existingRealm == NULL) { // Realm is not in our array! Delete it. const char *deletedV4RealmList[] = { REALMS_V4_PROF_REALMS_SECTION, *r, NULL }; err = profile_rename_section (profile, deletedV4RealmList, NULL); } } } if (v4Realms != NULL) { profile_free_list (v4Realms); } } // Walk over the profile looking for deleted v5 domains: if (!err) { const char *v5DomainsList[] = { "domain_realm", NULL }; char **v5Domains = NULL; err = profile_get_relation_names (profile, v5DomainsList, &v5Domains); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v5Domains != NULL)) { char **d; for (d = v5Domains; *d != NULL && !err; d++) { NSString *domain = [NSString stringWithUTF8String: *d]; if (domain != NULL) { const char *domainMappingList[] = { "domain_realm", *d, NULL }; char **domainMapping = NULL; if (!err) { err = profile_get_values (profile, domainMappingList, &domainMapping); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err) { KerberosRealm *realm = [self findRealmByName: [NSString stringWithUTF8String: domainMapping[0]]]; if ((realm == NULL) || (![realm mappedToByDomainString: domain])) { // Domain is not in our array or no such realm! Delete it. const char *deletedRealmList[] = { "domain_realm", *d, NULL }; err = profile_clear_relation (profile, deletedRealmList); } } if (domainMapping != NULL) { profile_free_list (domainMapping); } } } } if (v5Domains != NULL) { profile_free_list (v5Domains); } } // Walk over the profile looking for deleted v4 domains: if (!err) { const char *v4DomainsList[] = { REALMS_V4_PROF_DOMAIN_SECTION, NULL }; char **v4Domains = NULL; err = profile_get_relation_names (profile, v4DomainsList, &v4Domains); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } if (!err && (v4Domains != NULL)) { char **d; for (d = v4Domains; *d != NULL && !err; d++) { NSString *domain = [NSString stringWithUTF8String: *d]; if (domain != NULL) { const char *v4DomainMappingList[] = { REALMS_V4_PROF_DOMAIN_SECTION, *d, NULL }; char **v4DomainMapping = NULL; if (!err) { err = profile_get_values (profile, v4DomainMappingList, &v4DomainMapping); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err && (v4DomainMapping != NULL)) { KerberosRealm *realm = [self findRealmByV4Name: [NSString stringWithUTF8String: v4DomainMapping[0]]]; if ((realm == NULL) || (![realm mappedToByDomainString: domain])) { // Domain is not in our array or no such realm! Delete it. const char *deletedV4RealmList[] = { REALMS_V4_PROF_DOMAIN_SECTION, *d, NULL }; err = profile_clear_relation (profile, deletedV4RealmList); } if (v4DomainMapping != NULL) { profile_free_list (v4DomainMapping); } } } } } if (v4Domains != NULL) { profile_free_list (v4Domains); } } // Write out the default realm: if (!err) { const char *defaultRealmList[3] = {"libdefaults", "default_realm", NULL}; char **defaultRealm = NULL; if (profile_get_values (profile, defaultRealmList, &defaultRealm) == 0) { if ([self hasDefaultRealm]) { err = profile_update_relation (profile, defaultRealmList, defaultRealm[0], [[self defaultRealm] UTF8String]); } else { err = profile_clear_relation (profile, defaultRealmList); } } else { if ([self hasDefaultRealm]) { err = profile_add_relation (profile, defaultRealmList, [[self defaultRealm] UTF8String]); } } if (defaultRealm != NULL) { profile_free_list (defaultRealm); } } // use DNS for realm configuration if (!err) { const char *dnsFallbackList[3] = {"libdefaults", "dns_fallback", NULL}; char **dnsFallback = NULL; char *newValue = [self useDNS] ? "yes" : "no"; if (!err) { err = profile_get_values (profile, dnsFallbackList, &dnsFallback); if ((err == PROF_NO_SECTION) || (err == PROF_NO_RELATION)) { err = 0; } } if (!err) { if (dnsFallback != NULL) { err = profile_update_relation (profile, dnsFallbackList, dnsFallback[0], newValue); } else { err = profile_add_relation (profile, dnsFallbackList, newValue); } } if (dnsFallback != NULL) { profile_free_list (dnsFallback); } } // See if the profile even needs to be updated if (!err) { err = profile_is_modified (profile, &modified); } // And see if the user can just flush the profile as themselves: if (!err) { err = profile_is_writable (profile, &writable); } if (!err && modified) { if (writable) { err = profile_flush (profile); } else { char *profileFileData = NULL; NSString *resourcePath = NULL; NSString *toolString = NULL; FILE *toolPipe = NULL; AuthorizationRef authorizationRef = NULL; err = profile_flush_to_buffer (profile, &profileFileData); // Locate the tool to copy the temporary file to the correct location if (!err) { resourcePath = [[NSBundle mainBundle] resourcePath]; if (resourcePath == NULL) { err = EINVAL; } } if (!err) { toolString = [NSString stringWithFormat: @"%@/%@", resourcePath, @"SaveNewProfile"]; if (toolString == NULL) { err = ENOMEM; } } // Make sure the user is authorized to use the tool if (!err) { err = AuthorizationCreate (NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); } if (!err) { AuthorizationItem items = { kAuthorizationRightExecute, 0, NULL, 0 }; AuthorizationRights rights = { 1, &items }; AuthorizationFlags flags = (kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights); err = AuthorizationCopyRights (authorizationRef, &rights, NULL, flags, NULL ); } // Run the tool if (!err) { const char * arguments[] = { [configurationPathString UTF8String], NULL }; err = AuthorizationExecuteWithPrivileges (authorizationRef, [toolString UTF8String], kAuthorizationFlagDefaults, (char **) arguments, &toolPipe); } if (!err) { int toolDescriptor = fileno (toolPipe); pid_t toolPID = WAIT_ANY; // look for any child by default int toolStatus = 0; // Read the child's pid err = ReadBuffer (toolDescriptor, sizeof (toolPID), (char *) &toolPID); // Write the file to the tool if (!err) { err = WriteDynamicLengthBuffer (toolDescriptor, profileFileData, strlen (profileFileData)); } // Wait on the child so we don't drip zombies pid_t pid = waitpid (toolPID, &toolStatus, 0); if (pid == -1) { if (errno != ECHILD) { err = errno; } // okay if Security.framework already waited } else { if (WIFEXITED(toolStatus) && WEXITSTATUS (toolStatus)) { err = WEXITSTATUS (toolStatus); } } } // free these before abandoning the profile if (profileFileData != NULL) { profile_free_buffer (profile, profileFileData); } if (toolPipe != NULL) { fclose (toolPipe); } if (authorizationRef != NULL) { AuthorizationFree (authorizationRef, kAuthorizationFlagDefaults); } // Toss the profile since it still thinks its dirty if (!err) { err = [self abandon]; } } } if (!err) { err = [self load]; } if (err) { NSLog (@"[RealmsConfiguration flush] returning err %d (%s)", err, error_message (err)); } return err; } // --------------------------------------------------------------------------- - (BOOL) useDNS { return useDNS; } // --------------------------------------------------------------------------- - (void) setUseDNS: (BOOL) newUseDNS { useDNS = newUseDNS; } // --------------------------------------------------------------------------- - (BOOL) hasDefaultRealm { return ((defaultRealmString != NULL) && ([defaultRealmString length] > 0)); } // --------------------------------------------------------------------------- - (NSString *) defaultRealm { return (defaultRealmString != NULL) ? defaultRealmString : @""; } // --------------------------------------------------------------------------- - (void) setDefaultRealm: (NSString *) defaultRealm { if (defaultRealmString != NULL) { [defaultRealmString release]; } defaultRealmString = ((defaultRealm != NULL) && ([defaultRealm length] > 0)) ? [defaultRealm retain] : NULL; } // --------------------------------------------------------------------------- - (void) addRealm: (KerberosRealm *) realm { [realmsArray addObject: realm]; } // --------------------------------------------------------------------------- - (void) removeRealm: (KerberosRealm *) realm { [realmsArray removeObject: realm]; } // --------------------------------------------------------------------------- - (KerberosRealm *) findRealmByName: (NSString *) realmName { unsigned int i = 0; for (i = 0; i < [realmsArray count]; i++) { KerberosRealm *r = [realmsArray objectAtIndex: i]; if ((r != NULL) && ([[r name] compare: realmName] == NSOrderedSame)) { return r; } } return NULL; } // --------------------------------------------------------------------------- - (KerberosRealm *) findRealmByV4Name: (NSString *) realmName { unsigned int i = 0; for (i = 0; i < [realmsArray count]; i++) { KerberosRealm *r = [realmsArray objectAtIndex: i]; if ((r != NULL) && ([[r v4Name] compare: realmName] == NSOrderedSame)) { return r; } } return NULL; } // --------------------------------------------------------------------------- - (unsigned int) numberOfRealms { return [realmsArray count]; } // --------------------------------------------------------------------------- - (KerberosRealm *) realmAtIndex: (unsigned int) realmIndex { if (realmIndex < [realmsArray count]) { return [realmsArray objectAtIndex: realmIndex]; } else { return NULL; } } // --------------------------------------------------------------------------- - (unsigned int) indexOfRealm: (KerberosRealm *) realm { return [realmsArray indexOfObject: realm]; } // --------------------------------------------------------------------------- - (BOOL) allowDomainString: (NSString *) domainString mappedToNewRealm: (KerberosRealm *) realm currentRealm: (KerberosRealm **) outCurrentRealm { BOOL allow = YES; unsigned int i = 0; for (i = 0; (i < [realmsArray count]) && allow; i++) { KerberosRealm *r = [realmsArray objectAtIndex: i]; if ((r != NULL) && ([[r name] compare: [realm name]] != NSOrderedSame)) { if ([r mappedToByDomainString: domainString]) { // Already mapped to a realm of a different name if (outCurrentRealm != NULL) { *outCurrentRealm = r; } allow = NO; } } } return allow; } @end