UpdateKerberosConfig.m [plain text]
#include <pwd.h>
#include <unistd.h>
#include <Foundation/Foundation.h>
#define keylistPath @"/usr/bin/klist"
#define kKadminPath @"/usr/sbin/kadmin"
#define kKadminLocalPath @"/usr/sbin/kadmin.local"
#define kSetupPath @"/usr/sbin/krbservicesetup"
char* ReplaceKerberosAddrs( const char *inValue, const char *inOldIP, const char *inNewIP,
const char *inOldHostName, const char *inNewHostName )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary* dict = NULL;
NSDictionary* subDict = NULL;
NSData* tempData;
NSMutableArray* addressArray;
NSString* oldIP = [NSString stringWithUTF8String: inOldIP];
NSString* newIP = [NSString stringWithUTF8String: inNewIP];
NSString* oldHost = @"";
NSString* newHost = @"";
if (inOldHostName != nil)
oldHost = [NSString stringWithUTF8String: inOldHostName];
if (inNewHostName != nil)
newHost = [NSString stringWithUTF8String: inNewHostName];
BOOL changed = false;
int index = 0;
char* returnStr = nil;
// printf("Entering Kerberos update. Plist:\n tempData = [NSData dataWithBytes: inValue length: strlen(inValue)];
dict = (NSMutableDictionary*)CFPropertyListCreateFromXMLData( NULL, (CFDataRef)tempData, kCFPropertyListMutableContainersAndLeaves, NULL);
subDict = [dict objectForKey: @"edu.mit.kerberos"];
if (subDict == nil) return nil;
subDict = [subDict objectForKey: @"realms"];
if (subDict == nil) return nil;
NSEnumerator* enumerator = [subDict objectEnumerator];
while (subDict = [enumerator nextObject])
{
addressArray = [subDict objectForKey: @"KDC_List"];
if ((addressArray != nil) && [addressArray isKindOfClass: [NSMutableArray class]])
{
//printf("Updating KDC_list\n");
while (index < [addressArray count])
{
NSString* oldValue = [addressArray objectAtIndex: index];
if ((oldValue == nil) || ![oldValue isKindOfClass: [NSString class]])
continue;
if ([oldValue isEqualToString: oldIP])
{
[addressArray replaceObjectAtIndex: index withObject: newIP];
changed = true;
}
else if ([oldValue isEqualToString: oldHost])
{
[addressArray replaceObjectAtIndex: index withObject: newHost];
changed = true;
}
index++;
}
}
addressArray = [subDict objectForKey: @"KADM_List"];
if ((addressArray != nil) && [addressArray isKindOfClass: [NSMutableArray class]])
{
//printf("Updating KADM_List\n");
index = 0;
while (index < [addressArray count])
{
NSString* oldValue = [addressArray objectAtIndex: index];
if ((oldValue == nil) || ![oldValue isKindOfClass: [NSString class]])
continue;
if ([oldValue isEqualToString: oldIP])
{
[addressArray replaceObjectAtIndex: index withObject: newIP];
changed = true;
}
else if ([oldValue isEqualToString: oldHost])
{
[addressArray replaceObjectAtIndex: index withObject: newHost];
changed = true;
}
index++;
}
}
}
if (changed)
{
NSNumber* genID = [dict objectForKey: @"generationID"];
genID = [NSNumber numberWithInt: ([genID intValue] + 1)];
[dict setObject: genID forKey: @"generationID"];
tempData = (NSData*)CFPropertyListCreateXMLData(NULL, dict);
returnStr = malloc([tempData length] + 1);
[tempData getBytes: returnStr];
returnStr[[tempData length]] = '\0';
//printf("Have changes, new plist = \n }
[pool release];
return returnStr;
}
/* for bug #rdar://problem/4379238
need to get a listing of the keytab entries that we manage
delete them
run sso_util configure with the new info.
*/
/*
service types are: "afpserver,ftp,imap,pop,host,HTTP,http,smtp,ssh,smb,CIFS,xmpp,ipp,vpn,xgrid,ldap"
*/
NSArray *GetKeytabEntries(void)
{
NSTask *theTask = [[NSTask alloc] init];
NSArray *theArgs = [NSArray arrayWithObjects: @"-k", nil ];
NSArray *theList = nil;
NSArray *lineArray = nil;
NSString *line = nil;
NSArray *parsedLine = nil;
NSString *principal = nil;
NSString *service = nil;
NSPipe *stdOut = [[NSPipe alloc] init];
NSString *tmpOutput = nil;
NSMutableSet *principalSet = nil;
NSSet *serviceSet = nil;
NSFileHandle *handle = nil;
int status;
unsigned index;
NSRange range;
[theTask setLaunchPath: keylistPath];
[theTask setArguments: theArgs];
[theTask setStandardOutput: stdOut];
handle = [stdOut fileHandleForReading];
[theTask launch];
tmpOutput = [[NSString alloc] initWithData:[handle readDataToEndOfFile] encoding:NSASCIIStringEncoding];
[theTask waitUntilExit];
status = [theTask terminationStatus];
[stdOut release];
if (status == 0)
{
serviceSet = [NSSet setWithObjects: @"afpserver", @"ftp", @"imap", @"host", @"pop", @"HTTP", @"http", @"smtp", @"ssh", @"smb", @"cifs", @"CIFS", @"xmpp", @"XMPP", @"ipp", @"vpn", @"xgrid", @"ldap", nil];
lineArray = [tmpOutput componentsSeparatedByString: @"\n"];
principalSet = [NSMutableSet setWithCapacity: [lineArray count]];
for(index = 3; index < [lineArray count]; index++)
{
line = [(NSString *)[lineArray objectAtIndex: index] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
//NSLog(@"processing if((line == nil) || ([line length] == 0))
{
continue;
}
parsedLine = [line componentsSeparatedByString: @" "];
if(parsedLine != nil)
{
// need to check against our service types
//NSLog(@"\tparsing as principal = [parsedLine objectAtIndex: 1];
range = [principal rangeOfString:@"/" options:NSLiteralSearch];
if(range.location != NSNotFound)
{
service = [principal substringToIndex: range.location];
//NSLog(@"\tservice = if([serviceSet containsObject: service] == YES)
{
[principalSet addObject: principal];
}
}
}
}
theList = [principalSet allObjects];
} else {
}
return theList;
}
int RemovePrincipal(NSString *inPrincipal, NSString *inRealm, NSString *inAdmin, UInt8 *inPassword)
{
NSTask *theTask = [[NSTask alloc] init];
NSMutableArray *theArgs = [NSMutableArray arrayWithObjects: @"-r", inRealm, nil];
int result = 0;
NSPipe *stdIn = [[NSPipe alloc] init];
NSFileHandle *handleIn;
UInt8 bytes[64];
NSData *passwordData = nil;
NSString *kadminPath = nil;
if (inAdmin == nil)
{
kadminPath = kKadminLocalPath;
}
else
{
kadminPath = kKadminPath;
[theArgs addObjectsFromArray: [NSArray arrayWithObjects: @"-p", inAdmin, nil]];
strlcpy((char *)bytes, (const char *)inPassword, sizeof(bytes) - 1);
strlcat((char *)bytes, "\n", sizeof(bytes));
passwordData = [NSData dataWithBytes: (void *)bytes length: strlen((const char *)bytes)];
handleIn = [stdIn fileHandleForWriting];
[theTask setStandardInput: stdIn];
}
[theArgs addObjectsFromArray: [NSArray arrayWithObjects: @"-q", [NSString stringWithFormat: @"ktremove
[theTask setLaunchPath: kadminPath];
[theTask setArguments: theArgs];
// launch the task, sleep then if the task is still running, write the password if needed.
[theTask launch];
if (inAdmin != nil)
{
sleep(1);
if([theTask isRunning] == YES)
{
[handleIn writeData: passwordData];
}
}
[theTask waitUntilExit];
result = [theTask terminationStatus];
[theTask release];
[stdIn release];
//NSLog(@"finished with the first operation result = if(result != 0)
{
return result;
}
// now delete the principal
theTask = [[NSTask alloc] init];
[theArgs replaceObjectAtIndex: [theArgs count] - 1 withObject: [NSString stringWithFormat: @"delprinc -force stdIn = [[NSPipe alloc] init];
handleIn = [stdIn fileHandleForWriting];
[theTask setLaunchPath: kadminPath];
[theTask setArguments: theArgs];
[theTask setStandardInput: stdIn];
// launch the task, sleep then if the task is still running, write the password if needed.
[theTask launch];
if (inAdmin != nil)
{
sleep(1);
if([theTask isRunning] == YES)
{
[handleIn writeData: passwordData];
}
}
[theTask waitUntilExit];
result = [theTask terminationStatus];
[theTask release];
//NSLog(@"finished with the second operation result = return result;
}
int RemovePrincipalArray(NSArray *inPrincipalArray, NSString *inRealm, NSString *inAdmin, UInt8 *inPassword)
{
unsigned index = 0;
NSString *principal = nil;
int result = 0;
for(index = 0; index < [inPrincipalArray count]; index++)
{
principal = [inPrincipalArray objectAtIndex: index];
result = RemovePrincipal(principal, inRealm, inAdmin, inPassword);
if(result != 0) break;
}
return result;
}
/*
create the dictionary of new principals.
write it out to a secure temp file
run krbservicesetup
*/
int SetupNewConfig(NSArray *inPrincipals, NSString *inNewHostName, NSString *inRealm, NSString *inAdmin, UInt8 *inPassword)
{
NSTask *theTask = [[NSTask alloc] init];
NSArray *theArgs = nil;
unsigned index = 0;
int result = 0;
NSDictionary *serviceMap = nil;
NSMutableDictionary *theConfig = nil;
NSMutableDictionary *tmpDict = nil;
NSMutableArray *serviceArray = nil;
NSString *tmpString = nil;
NSString *serviceType = nil;
NSString *newPrincipal = nil;
char path[32];
NSRange range;
// map serviceType to service name
serviceMap = [NSDictionary dictionaryWithObjectsAndKeys: @"afp", @"afpserver", @"ftp", @"ftp", @"pop", @"pop",
@"HTTP", @"HTTP", @"http", @"http", @"ldap", @"ldap",
@"imap", @"imap", @"smtp", @"smtp", @"ipp", @"ipp",
@"xgrid", @"xgrid", @"vpn", @"vpn", @"XMPP", @"XMPP",
@"xmpp", @"xmpp", @"smb", @"cifs", @"cifs", @"smb",
@"ssh", @"host", nil];
// create the new config dictionary
theConfig = [NSMutableDictionary dictionaryWithCapacity: 4];
serviceArray = [NSMutableArray arrayWithCapacity: [inPrincipals count]];
[theConfig setObject: serviceArray forKey: @"Services"];
// fill out the service array
for(index = 0; index < [inPrincipals count]; index++)
{
tmpString = [inPrincipals objectAtIndex: index];
range = [tmpString rangeOfString:@"/" options:NSLiteralSearch];
if(range.location != NSNotFound)
{
serviceType = [tmpString substringToIndex: range.location];
if ([serviceMap objectForKey: serviceType] == nil)
{
fprintf(stderr, "serviceType }
else
{
newPrincipal = [NSString stringWithFormat: @" //NSLog(@"original principal
tmpDict = [NSMutableDictionary dictionaryWithCapacity: 2];
[tmpDict setObject: newPrincipal forKey: @"servicePrincipal"];
[tmpDict setObject: [serviceMap objectForKey: serviceType] forKey: @"serviceType"];
[serviceArray addObject: tmpDict];
}
}
}
//NSLog(@"the config =
// create the setup file in a temp dir
memcpy(path,"/tmp/configure_XXXXXXXX", 23);
path[23] = 0;
if(mkdtemp(path) == NULL)
{
fprintf(stderr, "Cannot create temp dir return errno;
}
tmpString = [NSString stringWithFormat: @"
// write the config to the file
if([theConfig writeToFile: tmpString atomically: NO] == NO)
{
fprintf(stderr, "Cannot create temp file return -1;
}
if (inAdmin == nil)
{
theArgs = [NSArray arrayWithObjects: @"-r", inRealm, @"-x", @"-f", tmpString, nil];
}
else
{
theArgs = [NSArray arrayWithObjects: @"-r", inRealm, @"-a", inAdmin, @"-p", [NSString stringWithUTF8String: (const char *)inPassword], @"-f", tmpString, nil];
}
[theTask setLaunchPath: kSetupPath];
[theTask setArguments: theArgs];
[theTask launch];
[theTask waitUntilExit];
result = [theTask terminationStatus];
[theTask release];
//NSLog(@"deleting
// delete the temp file & dir
unlink([tmpString UTF8String]);
rmdir(path);
return result;
}
/*
get the list of principals in the keytab
figure out the proper realm from the list of principals in the keytab,
prompt the user for the admin for that realm
if the admin name does not have the principal stuff, add it
prompt the user for the password for that user
remove the principals from the kdc & keytab
run sso_util configure
*/
int UpdateKerberosPrincipals(const char* node, char *inNewHostName)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *principals = GetKeytabEntries();
NSString *tmpStr = nil;
NSString *realm = nil;
NSString *admin = nil;
int result = 0;
char entryBuffer[64];
char *passwd = nil;
int len = 0;
BOOL useKadminLocal = NO;
memset(entryBuffer, 0 , sizeof(entryBuffer));
useKadminLocal = ((strncmp(node, "/Local", sizeof("/Local") - 1) == 0) ||
(strstr(node, "ldapi") != NULL));
// figure out the realm
if([principals count] < 1) // nothing to do
{
[pool release];
return 0;
}
tmpStr = [principals objectAtIndex: 0];
realm = [tmpStr substringFromIndex: [tmpStr rangeOfString:@"@" options:NSLiteralSearch].location+1];
if (!useKadminLocal)
{
// get the admin name
printf("Please enter the name of the administrator account for
if(fgets(entryBuffer, sizeof(entryBuffer), stdin) == nil)
{
[pool release];
return -1;
}
// a '\n' char needs to be trimmed
len = strlen(entryBuffer);
if(len > 1)
{
entryBuffer[len -1] = 0;
}
tmpStr = [NSString stringWithUTF8String: entryBuffer];
if([tmpStr rangeOfString:@"@" options:NSLiteralSearch].location == NSNotFound) // need to add the realm
{
admin = [NSString stringWithFormat: @" } else {
admin = tmpStr;
}
// and password
tmpStr = [NSString stringWithFormat: @"Please enter the password for passwd = getpass([tmpStr UTF8String]);
if(passwd == nil)
{
[pool release];
return -1;
}
}
// clear out the old
result = RemovePrincipalArray(principals, realm, admin, (UInt8 *)passwd);
if(result != 0)
{
if (passwd)
{
memset(passwd, 0 , _PASSWORD_LEN);
}
[pool release];
return result;
}
// now create the new principals & set up the configuration
//NSLog(@"Creating new Kerberos principals & keytab entries");
result = SetupNewConfig(principals, [NSString stringWithUTF8String: inNewHostName], realm, admin, (UInt8 *)passwd);
if (passwd)
{
memset(passwd, 0 , _PASSWORD_LEN);
}
[pool release];
return result;
}